From 8fec0d26e8637a1c96dbcac841417fdd176d480a Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Sun, 07 Jun 2026 01:28:37 +0800
Subject: [PATCH] docs: add App Store Connect filling guide
---
Apptag/ContentView.swift | 93 +++++++++++++++++++++++++++++++++++++++-------
1 files changed, 79 insertions(+), 14 deletions(-)
diff --git a/Apptag/ContentView.swift b/Apptag/ContentView.swift
index 0fbfc7f..b9cd83f 100644
--- a/Apptag/ContentView.swift
+++ b/Apptag/ContentView.swift
@@ -376,6 +376,7 @@
@State private var refreshInProgress = false
@State private var refreshAgainAfterCurrent = false
@State private var refreshAgainForceLayout = false
+ @State private var refreshAgainUseCache = true
@FocusState private var bubbleNoteFocused: Bool
// Quick Search
@@ -389,6 +390,7 @@
@State private var quickSearchSelectionScrollToken = 0
@State private var quickSearchCloseHidesOverlay = false
@State private var quickSearchOnlySession = false
+ @State private var quickSearchOpenedAt: Date? = nil
@State private var quickSearchErrorMessage: String? = nil
@State private var initialQuickSearchConsumed = false
@@ -531,7 +533,7 @@
}
.onAppear {
refreshNotchHeight()
- refreshAppsIfNeeded()
+ refreshAppsForOverlay()
if let initialQuickSearchSource, !initialQuickSearchConsumed {
initialQuickSearchConsumed = true
if initialQuickSearchSource == QuickSearchOpenSource.globalHidden {
@@ -542,7 +544,9 @@
quickSearchVisible = true
quickSearchFocusToken &+= 1
}
+ quickSearchOpenedAt = Date()
refreshQuickSearchResults()
+ refreshAppsForQuickSearch()
NotificationCenter.default.post(
name: .tagLauncherQuickSearchVisibilityChanged,
object: nil,
@@ -556,7 +560,7 @@
.onReceive(NotificationCenter.default.publisher(for: .tagLauncherOverlayDidShow)) { _ in
resetTransientDragState()
refreshNotchHeight()
- refreshAppsIfNeeded()
+ refreshAppsForOverlay()
}
.onReceive(NotificationCenter.default.publisher(for: .tagLauncherOverlayDidHide)) { _ in
resetTransientDragState()
@@ -568,8 +572,9 @@
let source = notification.userInfo?["source"] as? String ?? QuickSearchOpenSource.mainOverlay
openQuickSearch(source: source)
}
- .onReceive(NotificationCenter.default.publisher(for: .tagLauncherQuickSearchDismissRequested)) { _ in
- closeQuickSearch()
+ .onReceive(NotificationCenter.default.publisher(for: .tagLauncherQuickSearchDismissRequested)) { notification in
+ let source = notification.userInfo?["source"] as? String
+ closeQuickSearch(dismissalSource: source)
}
.onReceive(NotificationCenter.default.publisher(for: NSApplication.didBecomeActiveNotification)) { _ in
guard allApps.isEmpty, !refreshInProgress else { return }
@@ -663,7 +668,7 @@
.ignoresSafeArea()
.contentShape(Rectangle())
.onTapGesture {
- closeQuickSearch()
+ closeQuickSearch(dismissalSource: QuickSearchDismissSource.backdrop)
}
.zIndex(899)
@@ -673,7 +678,7 @@
selectedID: quickSearchSelectedID,
focusToken: quickSearchFocusToken,
selectionScrollToken: quickSearchSelectionScrollToken,
- isLoading: quickSearchDocuments.isEmpty && refreshInProgress,
+ isLoading: quickSearchShouldShowLoading,
maxVisibleRows: quickSearchMaxVisibleRows(in: proxy.size),
panelTopY: quickSearchPanelTopY(in: proxy.size),
panelHeight: quickSearchPanelContentHeight(in: proxy.size),
@@ -692,7 +697,7 @@
}
private func quickSearchPanelContentHeight(in size: CGSize) -> CGFloat {
- let hasResultList = !(quickSearchDocuments.isEmpty && refreshInProgress)
+ let hasResultList = !quickSearchShouldShowLoading
&& quickSearchErrorMessage == nil
&& !quickSearchResults.isEmpty
let visibleRows = min(max(1, quickSearchResults.count), quickSearchMaxVisibleRows(in: size))
@@ -726,12 +731,13 @@
quickSearchCloseHidesOverlay = quickSearchCloseHidesOverlay
|| source == QuickSearchOpenSource.globalHidden
quickSearchVisible = true
+ quickSearchOpenedAt = Date()
quickSearchQuery = ""
quickSearchManualSelection = false
quickSearchErrorMessage = nil
quickSearchFocusToken &+= 1
refreshQuickSearchResults()
- refreshApps()
+ refreshAppsForQuickSearch()
NotificationCenter.default.post(
name: .tagLauncherQuickSearchVisibilityChanged,
object: nil,
@@ -750,10 +756,30 @@
&& !quickSearchVisible
}
- private func closeQuickSearch(notify: Bool = true, hideOverlayIfNeeded: Bool = true) {
+ private var quickSearchShouldShowLoading: Bool {
+ quickSearchVisible && refreshInProgress && quickSearchResults.isEmpty
+ }
+
+ private func closeQuickSearch(
+ notify: Bool = true,
+ hideOverlayIfNeeded: Bool = true,
+ dismissalSource: String? = nil
+ ) {
+ Diagnostics.log("content.quickSearch.closeRequest", [
+ "source": dismissalSource,
+ "notify": notify,
+ "hideOverlayIfNeeded": hideOverlayIfNeeded,
+ "quickSearchVisible": quickSearchVisible,
+ "quickSearchCloseHidesOverlay": quickSearchCloseHidesOverlay,
+ "quickSearchOnlySession": quickSearchOnlySession
+ ])
+ if shouldIgnoreEarlyQuickSearchDismiss(from: dismissalSource) {
+ return
+ }
let shouldHideOverlay = quickSearchVisible && quickSearchCloseHidesOverlay && hideOverlayIfNeeded
guard quickSearchVisible else { return }
quickSearchVisible = false
+ quickSearchOpenedAt = nil
quickSearchQuery = ""
quickSearchResults = []
quickSearchSelectedID = nil
@@ -774,6 +800,23 @@
} else {
quickSearchCloseHidesOverlay = false
}
+ }
+
+ private func shouldIgnoreEarlyQuickSearchDismiss(from source: String?) -> Bool {
+ guard quickSearchVisible,
+ let quickSearchOpenedAt,
+ source == QuickSearchDismissSource.mouse || source == QuickSearchDismissSource.backdrop
+ else { return false }
+
+ let age = Date().timeIntervalSince(quickSearchOpenedAt)
+ let gracePeriod: TimeInterval = quickSearchOnlySession ? 1.25 : 0.30
+ guard age < gracePeriod else { return false }
+ Diagnostics.log("content.quickSearch.ignoredEarlyDismiss", [
+ "source": source,
+ "age": String(format: "%.3f", age),
+ "gracePeriod": String(format: "%.2f", gracePeriod)
+ ])
+ return true
}
private func refreshQuickSearchResults() {
@@ -800,6 +843,11 @@
}
private func handleQuickSearchCommand(_ command: QuickSearchCommand) {
+ Diagnostics.log("content.quickSearch.command", [
+ "command": String(describing: command),
+ "quickSearchCloseHidesOverlay": quickSearchCloseHidesOverlay,
+ "quickSearchOnlySession": quickSearchOnlySession
+ ])
switch command {
case .moveUp:
moveQuickSearchSelection(by: -1)
@@ -1765,6 +1813,22 @@
refreshApps()
}
+ private func refreshAppsForOverlay() {
+ // Each overlay show creates a fresh ContentView with empty `allApps`. The indexer
+ // cache may still be warm from a prior overlay/settings scan, so path-signature
+ // alone must not skip the first hydrate or the grid spinner never clears.
+ guard allApps.isEmpty || AppIndexer.shouldRefreshForSearchPathChanges() else { return }
+ refreshApps()
+ }
+
+ private func refreshAppsForQuickSearch() {
+ guard allApps.isEmpty
+ || quickSearchDocuments.isEmpty
+ || AppIndexer.shouldRefreshForSearchPathChanges()
+ else { return }
+ refreshApps()
+ }
+
private func refreshNotchHeight() {
let mousePoint = NSEvent.mouseLocation
let activeScreen = NSScreen.screens.first(where: {
@@ -2375,10 +2439,9 @@
func refreshApps(forceLayoutRefresh: Bool = false, useCache: Bool = true) {
guard !refreshInProgress else {
- if forceLayoutRefresh {
- refreshAgainAfterCurrent = true
- refreshAgainForceLayout = true
- }
+ refreshAgainAfterCurrent = true
+ refreshAgainForceLayout = refreshAgainForceLayout || forceLayoutRefresh
+ refreshAgainUseCache = refreshAgainUseCache && useCache
return
}
@@ -2394,9 +2457,11 @@
refreshInProgress = false
if refreshAgainAfterCurrent {
let shouldForceLayout = refreshAgainForceLayout
+ let shouldUseCache = refreshAgainUseCache
refreshAgainAfterCurrent = false
refreshAgainForceLayout = false
- refreshApps(forceLayoutRefresh: shouldForceLayout)
+ refreshAgainUseCache = true
+ refreshApps(forceLayoutRefresh: shouldForceLayout, useCache: shouldUseCache)
}
}
}
--
Gitblit v1.9.3