Browse Source

fix: tricky conflicts between appDelegate and bottomSheet

Tienson Qin 1 week ago
parent
commit
03cf2938fe

+ 4 - 2
ios/App/App/AppDelegate.swift

@@ -257,8 +257,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UINavigationControllerDel
     ) {
         guard let current = viewController as? NativePageViewController else { return }
 
-        SharedWebViewController.instance.clearPlaceholder()
-        SharedWebViewController.instance.attach(to: current)
+        if !NativeBottomSheetPlugin.isPresentingSheet {
+            SharedWebViewController.instance.clearPlaceholder()
+            SharedWebViewController.instance.attach(to: current)
+        }
 
         attachNavigationSwipeGesture()
     }

+ 31 - 3
ios/App/App/NativeBottomSheetPlugin.swift

@@ -14,6 +14,11 @@ public class NativeBottomSheetPlugin: CAPPlugin, CAPBridgedPlugin {
     private weak var previousParent: UIViewController?
     private var sheetController: NativeBottomSheetViewController?
 
+    /// Single source of truth: when true, the bottom sheet owns the shared webview.
+    public static var isPresentingSheet: Bool = false
+
+    // MARK: - Public API
+
     @objc func present(_ call: CAPPluginCall) {
         guard #available(iOS 15.0, *) else {
             call.reject("Native sheet requires iOS 15 or newer")
@@ -21,6 +26,7 @@ public class NativeBottomSheetPlugin: CAPPlugin, CAPBridgedPlugin {
         }
 
         DispatchQueue.main.async {
+            // If a sheet is already visible, just resolve.
             if self.sheetController != nil {
                 call.resolve()
                 return
@@ -42,17 +48,27 @@ public class NativeBottomSheetPlugin: CAPPlugin, CAPBridgedPlugin {
             }
 
             self.previousParent = host
-            let hasSnapshot = self.showSnapshot(in: host)
+
+            // Optional: snapshot for a nice frozen background (does not affect ownership)
+            _ = self.showSnapshot(in: host)
+
+            // MARK: sheet takes ownership of the shared webview
+            NativeBottomSheetPlugin.isPresentingSheet = true
+
             SharedWebViewController.instance.attach(
                 to: controller,
-                leavePlaceholderInPreviousParent: !hasSnapshot
+                // IMPORTANT: do not leave a live webview behind the sheet
+                leavePlaceholderInPreviousParent: false
             )
 
+            self.notifyListeners("state", data: ["presenting": true])
+
             host.present(controller, animated: true) {
                 self.notifyListeners("state", data: ["presented": true])
+                call.resolve()
             }
+
             self.sheetController = controller
-            call.resolve()
         }
     }
 
@@ -63,18 +79,25 @@ public class NativeBottomSheetPlugin: CAPPlugin, CAPBridgedPlugin {
                 return
             }
             controller.dismiss(animated: true) {
+                // handleSheetDismissed will run via viewDidDisappear / delegate
                 call.resolve()
             }
         }
     }
 
+    // MARK: - Internal helpers
+
     private func handleSheetDismissed() {
         guard sheetController != nil else { return }
 
         DispatchQueue.main.async {
+            // MARK: sheet releases ownership
+            NativeBottomSheetPlugin.isPresentingSheet = false
+
             if let previous = self.previousParent {
                 SharedWebViewController.instance.attach(to: previous)
             }
+
             self.clearSnapshot()
             SharedWebViewController.instance.clearPlaceholder()
             self.sheetController = nil
@@ -85,12 +108,17 @@ public class NativeBottomSheetPlugin: CAPPlugin, CAPBridgedPlugin {
 
     private func showSnapshot(in host: UIViewController) -> Bool {
         clearSnapshot()
+
+        // Make sure layout is up-to-date (important after tab switches)
+        host.view.layoutIfNeeded()
+
         guard let snapshot = SharedWebViewController.instance.makeSnapshotView() else {
             return false
         }
         snapshot.frame = host.view.bounds
         snapshot.autoresizingMask = [.flexibleWidth, .flexibleHeight]
         host.view.addSubview(snapshot)
+        host.view.bringSubviewToFront(snapshot)
         backgroundSnapshotView = snapshot
         return true
     }

+ 0 - 1
src/main/frontend/components/quick_add.cljs

@@ -31,7 +31,6 @@
                  (state/clear-selection!)
                  state)
    :will-unmount (fn [state]
-                   (state/clear-edit!)
                    (state/clear-selection!)
                    state)
    :did-mount (fn [state]

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

@@ -72,10 +72,10 @@
            (util/scroll-to-top false))
          "quick-add"
          (editor-handler/show-quick-add)
-                       ;; TODO: support longPress detection
-                       ;; (if (= "longPress" interaction)
-                       ;;   (state/pub-event! [:mobile/start-audio-record])
-                       ;;   (editor-handler/show-quick-add))
+         ;; TODO: support longPress detection
+         ;; (if (= "longPress" interaction)
+         ;;   (state/pub-event! [:mobile/start-audio-record])
+         ;;   (editor-handler/show-quick-add))
          nil)))
     (add-search-listener!
      (fn [q]

+ 4 - 2
src/main/mobile/components/app.cljs

@@ -154,9 +154,11 @@
         show-action-bar? (state/sub :mobile/show-action-bar?)
         {:keys [open? content-fn opts]} (rum/react mobile-state/*popup-data)
         show-popup? (and open? content-fn)]
-    [:<>
+    [:div.w-full.h-full
      (if show-popup?
-       (popup/popup opts content-fn)
+       [:div.h-full
+        (ui-component/keep-keyboard-virtual-input)
+        (popup/popup opts content-fn)]
        (app current-repo {:login? login?}))
      (editor-toolbar/mobile-bar)
      (when show-action-bar?

+ 25 - 13
src/main/mobile/components/popup.cljs

@@ -7,6 +7,7 @@
             [logseq.shui.popup.core :as shui-popup]
             [logseq.shui.ui :as shui]
             [mobile.state :as mobile-state]
+            [promesa.core :as p]
             [rum.core :as rum]))
 
 (defonce *last-popup-modal? (atom nil))
@@ -20,18 +21,19 @@
     :else 400))
 
 (defn- present-native-sheet!
-  [opts]
+  [data]
   (when-let [plugin mobile-util/native-bottom-sheet]
-    (let [id (:id opts)
-          popup-exists? (and id (= id (:id @*last-popup-data)))]
+    (let [{:keys [opts]} data
+          id (:id opts)
+          popup-exists? (and id (= id (get-in @*last-popup-data [:opts :id])))]
       (when-not popup-exists?
-        (reset! *last-popup-data opts)
+        (reset! *last-popup-data data)
         (.present
          plugin
          (clj->js
           (let [height (popup-min-height (:default-height opts))]
             (cond-> {:allowFullHeight (not= (:type opts) :action-sheet)}
-              height (assoc :defaultHeight height)))))))))
+              (int? height) (assoc :defaultHeight height)))))))))
 
 (defn- dismiss-native-sheet!
   []
@@ -42,10 +44,20 @@
 
 (defn- handle-native-sheet-state!
   [^js data]
-  (let [presented? (.-presented data)]
-    (if presented?
-      (when (mobile-state/quick-add-open?)
-        (editor-handler/quick-add-open-last-block!))
+  (let [presented? (.-presented data)
+        presenting? (.-presenting data)]
+    (cond
+      presenting?
+      (p/do!
+       (when (mobile-state/quick-add-open?)
+         (editor-handler/quick-add-open-last-block!))
+       (when-let [data @*last-popup-data]
+         (mobile-state/set-popup! data)))
+
+      presented?
+      nil
+
+      :else
       (when (some? @mobile-state/*popup-data)
         (state/pub-event! [:mobile/clear-edit])
         (mobile-state/set-popup! nil)))))
@@ -64,12 +76,12 @@
 
     :else
     (when content-fn
-      (mobile-state/set-popup! {:open? true
-                                :content-fn content-fn
-                                :opts opts})
       (reset! *last-popup-modal? true)
       (when (mobile-util/native-ios?)
-        (present-native-sheet! opts)))))
+        (let [data {:open? true
+                    :content-fn content-fn
+                    :opts opts}]
+          (present-native-sheet! data))))))
 
 (defn popup-hide!
   [& args]

+ 2 - 1
src/main/mobile/events.cljs

@@ -16,7 +16,8 @@
   (shui/popup-show! nil
                     (fn []
                       (quick-add/quick-add))
-                    {:id :ls-quick-add}))
+                    {:id :ls-quick-add
+                     :default-height 600}))
 
 (defmethod events/handle :mobile/start-audio-record [_]
   (recorder/record! {:save-to-today? true}))