From da71eb5bccbdbd22da7cd74b6ddcec031f069482 Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Thu, 28 May 2026 15:59:53 +0800
Subject: [PATCH] Stabilize empty-drop tag removal confirmation
---
Apptag/AppGridCollectionView.swift | 4 +
Apptag/ContentView.swift | 28 +++++++++-----
Apptag/EditModeViews.swift | 36 ++++++++++++++---
3 files changed, 50 insertions(+), 18 deletions(-)
diff --git a/Apptag/AppGridCollectionView.swift b/Apptag/AppGridCollectionView.swift
index b2884d7..3516782 100644
--- a/Apptag/AppGridCollectionView.swift
+++ b/Apptag/AppGridCollectionView.swift
@@ -339,7 +339,9 @@
guard let coordinator,
coordinator.displayStyle != .flat
else { return }
- coordinator.onDropOutsideGroup(path, source, copy)
+ DispatchQueue.main.async {
+ coordinator.onDropOutsideGroup(path, source, copy)
+ }
}
private func setup() {
diff --git a/Apptag/ContentView.swift b/Apptag/ContentView.swift
index d6ca151..3c68021 100644
--- a/Apptag/ContentView.swift
+++ b/Apptag/ContentView.swift
@@ -593,12 +593,11 @@
quickSearchErrorMessage = nil
refreshQuickSearchResults()
}
- .onChange(of: pendingUncategorizedDrop != nil) { _, active in
- NotificationCenter.default.post(
- name: .tagLauncherModalInteractionChanged,
- object: nil,
- userInfo: ["active": active]
- )
+ .onChange(of: pendingUncategorizedDrop != nil) { _, _ in
+ publishModalInteractionState()
+ }
+ .onChange(of: pendingTagRemovalDrop != nil) { _, _ in
+ publishModalInteractionState()
}
.onDisappear {
NotificationCenter.default.post(
@@ -607,6 +606,14 @@
userInfo: ["active": false]
)
}
+ }
+
+ private func publishModalInteractionState() {
+ NotificationCenter.default.post(
+ name: .tagLauncherModalInteractionChanged,
+ object: nil,
+ userInfo: ["active": pendingUncategorizedDrop != nil || pendingTagRemovalDrop != nil]
+ )
}
/// Set edit phase with synchronous notification BEFORE state change.
@@ -2111,7 +2118,6 @@
}
private func dismissTagRemovalDropConfirm() {
- resetTransientDragState(keepingPendingTagRemovalDrop: true)
tagRemovalDropSuppressFuturePrompt = false
withAnimation(.easeOut(duration: 0.18)) {
pendingTagRemovalDrop = nil
@@ -2122,15 +2128,17 @@
guard let pendingDrop = pendingTagRemovalDrop else { return }
let app = pendingDrop.app
let tagName = pendingDrop.tagName
- if tagRemovalDropSuppressFuturePrompt {
+ let shouldSuppressFuturePrompt = tagRemovalDropSuppressFuturePrompt
+ if shouldSuppressFuturePrompt {
skipTagRemovalDropConfirm = true
}
- resetTransientDragState(keepingPendingTagRemovalDrop: true)
tagRemovalDropSuppressFuturePrompt = false
withAnimation(.easeOut(duration: 0.16)) {
pendingTagRemovalDrop = nil
}
- removeTagFromDroppedApp(app: app, tagName: tagName)
+ DispatchQueue.main.async {
+ removeTagFromDroppedApp(app: app, tagName: tagName)
+ }
}
private func removeTagFromDroppedApp(app: AppInfo, tagName: String) {
diff --git a/Apptag/EditModeViews.swift b/Apptag/EditModeViews.swift
index 2a2c60e..fd4f68e 100644
--- a/Apptag/EditModeViews.swift
+++ b/Apptag/EditModeViews.swift
@@ -406,7 +406,7 @@
.foregroundStyle(.white)
.frame(maxWidth: .infinity, alignment: .leading)
- Button(action: onCancel) {
+ Button(action: { DispatchQueue.main.async(execute: onCancel) }) {
Image(systemName: "xmark")
.font(.system(size: 12, weight: .bold))
.foregroundStyle(.white.opacity(0.82))
@@ -427,15 +427,37 @@
.fixedSize(horizontal: false, vertical: true)
.frame(maxWidth: .infinity, alignment: .leading)
- Toggle(doNotRemindTitle, isOn: $doNotRemind)
- .toggleStyle(.checkbox)
- .font(.system(size: 13, weight: .medium))
- .foregroundStyle(.white.opacity(0.80))
+ Button {
+ doNotRemind.toggle()
+ } label: {
+ HStack(spacing: 10) {
+ ZStack {
+ RoundedRectangle(cornerRadius: 4, style: .continuous)
+ .fill(doNotRemind ? Color.accentColor : Color.white.opacity(0.10))
+ RoundedRectangle(cornerRadius: 4, style: .continuous)
+ .stroke(Color.white.opacity(0.92), lineWidth: 1.6)
+ if doNotRemind {
+ Image(systemName: "checkmark")
+ .font(.system(size: 12, weight: .bold))
+ .foregroundStyle(.white)
+ }
+ }
+ .frame(width: 18, height: 18)
+
+ Text(doNotRemindTitle)
+ .font(.system(size: 13, weight: .medium))
+ .foregroundStyle(.white.opacity(0.82))
+ .lineLimit(2)
+ .multilineTextAlignment(.leading)
+ }
+ .contentShape(Rectangle())
+ }
+ .buttonStyle(.plain)
HStack(spacing: 12) {
Spacer(minLength: 0)
- Button(action: onCancel) {
+ Button(action: { DispatchQueue.main.async(execute: onCancel) }) {
Text(cancelTitle)
.font(.system(size: 13, weight: .semibold))
.foregroundStyle(.white.opacity(0.86))
@@ -447,7 +469,7 @@
}
.buttonStyle(.plain)
- Button(action: onConfirm) {
+ Button(action: { DispatchQueue.main.async(execute: onConfirm) }) {
Text(confirmTitle)
.font(.system(size: 13, weight: .semibold))
.foregroundStyle(.white)
--
Gitblit v1.9.3