Browse Source

fix(mobile): sometimes search requires double taps

Tienson Qin 2 months ago
parent
commit
000280080f
2 changed files with 28 additions and 47 deletions
  1. 22 43
      ios/App/App/LiquidTabsRootView.swift
  2. 6 4
      src/main/mobile/bottom_tabs.cljs

+ 22 - 43
ios/App/App/LiquidTabsRootView.swift

@@ -114,7 +114,6 @@ private struct LiquidTabs26View: View {
     let navController: UINavigationController
 
     @State private var searchText: String = ""
-    @State private var isSearchPresented: Bool = false
     @FocusState private var isSearchFocused: Bool
 
     @State private var hackShowKeyboard: Bool = false
@@ -158,6 +157,13 @@ private struct LiquidTabs26View: View {
         return .search
     }
 
+    private func focusSearchField() {
+        // Drive focus (and keyboard) only through searchFocused.
+        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
+            isSearchFocused = true
+        }
+    }
+
     var body: some View {
         if store.tabs.isEmpty {
             // bootstrap webview so JS can configure tabs
@@ -187,7 +193,6 @@ private struct LiquidTabs26View: View {
                     Tab(value: .search, role: .search) {
                         SearchTabHost26(
                             navController: navController,
-                            isSearchFocused: $isSearchFocused,
                             selectedTab: $selectedTab,
                             firstTabId: store.tabs.first?.id,
                             store: store
@@ -196,8 +201,7 @@ private struct LiquidTabs26View: View {
                     }
                 }
                 .searchable(
-                    text: $searchText,
-                    isPresented: $isSearchPresented
+                    text: $searchText
                 )
                 .searchFocused($isSearchFocused)
                 .searchToolbarBehavior(.minimize)
@@ -206,16 +210,13 @@ private struct LiquidTabs26View: View {
                 }
                 .background(Color.logseqBackground)
 
-                // Hidden UITextField that pre-invokes keyboard
+                // Hidden UITextField that pre-invokes keyboard (optional)
                 KeyboardHackField(shouldShow: $hackShowKeyboard)
                     .frame(width: 0, height: 0)
             }
             .onAppear {
                 let initial = initialSelection()
                 selectedTab = initial
-                if case .search = initial {
-                    isSearchPresented = true
-                }
 
                 let appearance = UITabBarAppearance()
                 appearance.configureWithTransparentBackground()
@@ -248,23 +249,20 @@ private struct LiquidTabs26View: View {
 
                 switch newValue {
                 case .search:
-                    isSearchPresented = true
-                case .content:
-                    hackShowKeyboard = false
-                    isSearchFocused = false
-                    isSearchPresented = false
-                }
-            }
-            .onChange(of: isSearchPresented) { presented in
-                if presented {
-                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
+                    // Every time we switch to the search tab, re-focus the search
+                    // field so the search bar auto-focuses and keyboard appears.
+                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
                         hackShowKeyboard = true
-                        isSearchFocused = true
                     }
+
                     DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                         hackShowKeyboard = false
                     }
-                } else {
+
+                    focusSearchField()
+
+                case .content:
+                    // Leaving search tab – drop focus and stop hack keyboard.
                     isSearchFocused = false
                     hackShowKeyboard = false
                 }
@@ -284,11 +282,12 @@ private struct LiquidTabs26View: View {
     }
 }
 
-// Search host for 26+ (unchanged)
+// Search host for 26+
+// Only responsible for cancel behaviour and tab switching.
+// It does NOT own the focus anymore.
 @available(iOS 26.0, *)
 private struct SearchTabHost26: View {
     let navController: UINavigationController
-    @FocusState.Binding var isSearchFocused: Bool
     var selectedTab: Binding<LiquidTabsTabSelection>
     let firstTabId: String?
     let store: LiquidTabsStore
@@ -300,14 +299,6 @@ private struct SearchTabHost26: View {
         NavigationStack {
             NativeNavHost(navController: navController)
                 .ignoresSafeArea()
-                .onAppear {
-                    DispatchQueue.main.async {
-                        isSearchFocused = true
-                    }
-                }
-                .onDisappear {
-                    isSearchFocused = false
-                }
                 .onChange(of: isSearching) { searching in
                     if searching {
                         wasSearching = true
@@ -315,7 +306,7 @@ private struct SearchTabHost26: View {
                               case .search = selectedTab.wrappedValue,
                               let firstId = firstTabId {
 
-                        // Cancel logic - Programmatic switch back to first content tab
+                        // User tapped Cancel: switch back to first normal tab.
                         wasSearching = false
                         selectedTab.wrappedValue = .content(0)
                         store.selectedId = firstId
@@ -364,18 +355,6 @@ private struct LiquidTabs16View: View {
                                 store.selectedId = id
                                 LiquidTabsPlugin.shared?.notifyTabSelected(id: id)
                             }
-
-                            // Basic keyboard hack when selecting Search tab
-                            if id == "search" {
-                                DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
-                                    hackShowKeyboard = true
-                                }
-                                DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
-                                    hackShowKeyboard = false
-                                }
-                            } else {
-                                hackShowKeyboard = false
-                            }
                         }
                     )) {
                         // --- Normal dynamic tabs ---

+ 6 - 4
src/main/mobile/bottom_tabs.cljs

@@ -84,10 +84,12 @@
   (do
     (add-tab-selected-listener!
      (fn [tab]
-       (let [exit-quick-add? (= @*previous-tab "quick-add")]
+       (let [exit-quick-add? (= @*previous-tab "quick-add")
+             search? (= "search" tab)]
          (reset! mobile-state/*search-input "")
-         (when-not (contains? #{"quick-add"} tab)
-           (when-not exit-quick-add?
+         (when-not (or (contains? #{"quick-add"} tab)
+                       (= tab @*previous-tab))
+           (when-not (or exit-quick-add? search?)
              (mobile-nav/reset-route!))
            (mobile-state/set-tab! tab))
 
@@ -114,7 +116,7 @@
        (js/console.log "Native search query" q)
        (reset! mobile-state/*search-input q)
        (reset! mobile-state/*search-last-input-at (common-util/time-ms))
-       (when (= :page (get-in (state/get-route-match) [:data :name]))
+       (when (= :page (state/get-current-route))
          (mobile-nav/reset-route!))))
     (add-keyboard-hack-listener!)))