Browse Source

Enhance/native bottom tabs (#12202)

* enhance(ux): native bottom tabs on iOS

* fix: hide bottom tabs when sheet has been opened
Tienson Qin 2 months ago
parent
commit
9ebe370ec6

+ 3 - 2
.github/workflows/build.yml

@@ -73,7 +73,8 @@ jobs:
         run: clojure -A:cljs -P
 
       - name: Fetch yarn deps
-        run: yarn install --frozen-lockfile
+        # run: yarn install --frozen-lockfile
+        run: yarn install
 
       - name: Build test asset
         run: clojure -M:test compile test
@@ -179,4 +180,4 @@ jobs:
         run: cd deps/db && yarn nbb-logseq script/export_graph.cljs ../../scripts/properties-graph -f properties.edn -T
 
       - name: Create graph from the export and diff the two graphs
-        run: cd deps/db && yarn nbb-logseq -cp src:../outliner/src:script script/create_graph.cljs ./properties-graph2 properties.edn -iv && yarn nbb-logseq script/diff_graphs.cljs ../../scripts/properties-graph ./properties-graph2 -T
+        run: cd deps/db && yarn nbb-logseq -cp src:../outliner/src:script script/create_graph.cljs ./properties-graph2 properties.edn -iv && yarn nbb-logseq script/diff_graphs.cljs ../../scripts/properties-graph ./properties-graph2 -T

+ 1 - 0
ios/App/Podfile

@@ -26,6 +26,7 @@ def capacitor_pods
   pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'
   pod 'CapgoCapacitorNavigationBar', :path => '../../node_modules/@capgo/capacitor-navigation-bar'
   pod 'SendIntent', :path => '../../node_modules/send-intent'
+  pod 'StayLiquid', :path => '../../node_modules/stay-liquid'
   pod 'JcesarmobileSslSkip', :path => '../../node_modules/@jcesarmobile/ssl-skip'
 end
 

+ 24 - 18
ios/App/Podfile.lock

@@ -34,6 +34,8 @@ PODS:
     - Capacitor
   - SendIntent (7.0.0):
     - Capacitor
+  - StayLiquid (0.1.0):
+    - Capacitor (>= 6.0.0)
 
 DEPENDENCIES:
   - "Capacitor (from `../../node_modules/@capacitor/ios`)"
@@ -54,6 +56,7 @@ DEPENDENCIES:
   - "CapgoCapacitorNavigationBar (from `../../node_modules/@capgo/capacitor-navigation-bar`)"
   - "JcesarmobileSslSkip (from `../../node_modules/@jcesarmobile/ssl-skip`)"
   - SendIntent (from `../../node_modules/send-intent`)
+  - StayLiquid (from `../../node_modules/stay-liquid`)
 
 EXTERNAL SOURCES:
   Capacitor:
@@ -92,27 +95,30 @@ EXTERNAL SOURCES:
     :path: "../../node_modules/@jcesarmobile/ssl-skip"
   SendIntent:
     :path: "../../node_modules/send-intent"
+  StayLiquid:
+    :path: "../../node_modules/stay-liquid"
 
 SPEC CHECKSUMS:
-  Capacitor: 106e7a4205f4618d582b886a975657c61179138d
-  CapacitorActionSheet: 24609588961cc27c87e8b033be92b5eee65b5d4c
-  CapacitorApp: d63334c052278caf5d81585d80b21905c6f93f39
-  CapacitorCamera: eb8687d8687fed853598ec9460d94bcd5e16babe
-  CapacitorClipboard: b98aead5dc7ec595547fc2c5d75bacd2ae3338bc
-  CapacitorCommunitySafeArea: cc370b4f8d4aa340e4616acef9b73eda41ba0914
+  Capacitor: 03bc7cbdde6a629a8b910a9d7d78c3cc7ed09ea7
+  CapacitorActionSheet: 4213427449132ae4135674d93010cb011725647e
+  CapacitorApp: febecbb9582cb353aed037e18ec765141f880fe9
+  CapacitorCamera: 6e73f1fc6c629a672658705a02409b60854bc0f1
+  CapacitorClipboard: 70bfdb42b877b320a6e511ab94fa7a6a55d57ecb
+  CapacitorCommunitySafeArea: 3f049619072ab5d0da2529bcb05b358ff6c13dc1
   CapacitorCordova: 5967b9ba03915ef1d585469d6e31f31dc49be96f
-  CapacitorDevice: a50a45f0d075e55e2392c7a4be5404d4f69515de
-  CapacitorFilesystem: 307f97c27a265edf8396a1c9c235592fd8572fe3
-  CapacitorHaptics: 70e47470fa1a6bd6338cd102552e3846b7f9a1b3
-  CapacitorKeyboard: 969647d0ca2e5c737d7300088e2517aa832434e2
-  CapacitorNetwork: 07ec4c69c1bb696f41c23e00d31bda1bbb221bba
-  CapacitorShare: 58d6c2da63b093e8693287b2d36db92435538435
-  CapacitorSplashScreen: 19cd3573e57507e02d6f34597a8c421e00931487
-  CapacitorStatusBar: 275cbf2f4dfc00388f519ef80c7ec22edda342c9
-  CapgoCapacitorNavigationBar: 028e6cc0f00357381c46edb7b0d7cd1cda73493b
-  JcesarmobileSslSkip: b0f921e9d397a57f7983731209ca1ee244119c1f
-  SendIntent: 1f4f65c7103eb423067c566682dfcda973b5fb29
+  CapacitorDevice: 81ae78d5d1942707caad79276badd458bf6ec603
+  CapacitorFilesystem: e6261c410436f54908c11f94336c5b58286b1db0
+  CapacitorHaptics: 1f1e17041f435d8ead9ff2a34edd592c6aa6a8d6
+  CapacitorKeyboard: 09fd91dcde4f8a37313e7f11bde553ad1ed52036
+  CapacitorNetwork: 15cb4385f0913a8ceb5e9a4d7af1ec554bdb8de8
+  CapacitorShare: e573823f511f260f598d0423c33b1e3d7bbe5fd1
+  CapacitorSplashScreen: 1d67815a422a9b61539c94f283c08ed56667c0fc
+  CapacitorStatusBar: 6e7af040d8fc4dd655999819625cae9c2d74c36f
+  CapgoCapacitorNavigationBar: 067b1c1d1ede5ce96200a730ce7fd498e9641509
+  JcesarmobileSslSkip: 5fa98636a64c36faa50f32ab4daf34e38f4d45b9
+  SendIntent: 8a6f646a4489f788d253ffbd1082a98ea388d870
+  StayLiquid: dac4b6cd7761472754f97d367ba4651ca79fcd2e
 
-PODFILE CHECKSUM: 00fbb7ba3788966b68cb8a5a6f2abc380d7b7b9a
+PODFILE CHECKSUM: 858411d5b4560fd80593ea76b0fd6359bccabec3
 
 COCOAPODS: 1.16.2

+ 1 - 0
package.json

@@ -190,6 +190,7 @@
         "sanitize-filename": "1.6.3",
         "send-intent": "^7.0.0",
         "shepherd.js": "^9.1.0",
+        "stay-liquid": "https://github.com/logseq/stay-liquid.git#b2fde7447e2fff96f16b7715c6857d91c992359d",
         "tailwind-capitalize-first-letter": "^1.0.4",
         "threads": "1.6.5",
         "url": "^0.11.0",

+ 81 - 0
src/main/mobile/bottom_tabs.cljs

@@ -0,0 +1,81 @@
+(ns mobile.bottom-tabs
+  "iOS bottom tabs"
+  (:require ["stay-liquid" :refer [TabsBar]]
+            [cljs-bean.core :as bean]
+            [frontend.handler.editor :as editor-handler]
+            [frontend.mobile.util :as mobile-util]
+            [frontend.state :as state]
+            [frontend.util :as util]
+            [mobile.state :as mobile-state]
+            [promesa.core :as p]))
+
+(defn- tab-options
+  [theme visible?]
+  {:visible visible?
+   :initialId "home"
+   :items [{:id "home"
+            :title "Journals"
+            :systemIcon "house"}
+
+           {:id "search"
+            :title "Search"
+            :systemIcon "magnifyingglass"}
+
+           {:id "quick-add"
+            :title "Quick add"
+            :systemIcon "plus"}
+
+           {:id "settings"
+            :title "Settings"
+            :systemIcon "gear"}]
+   :selectedIconColor (if (= "light" theme)
+                        "rgb(0, 105, 182)"
+                        "#8ec2c2")
+   :unselectedIconColor "#8E8E93"
+   :titleOpacity 0.7})
+
+(defn- configure-tabs
+  [theme visible?]
+  (.configure ^js TabsBar
+              (bean/->js
+               (tab-options theme visible?))))
+
+(defn configure
+  []
+  (p/do!
+   (configure-tabs (:ui/theme @state/state) true)
+   (.addListener ^js TabsBar
+                 "selected"
+                 (fn [^js data]
+                   (let [tab (.-id data)
+                         ;; interaction (.-interaction data)
+                         ]
+                     (when-not (= tab "quick-add")
+                       (mobile-state/set-tab! tab))
+                     (case tab
+                       "home"
+                       (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))
+                       nil)))))
+
+  ;; Update selected icon color according to current theme
+  (add-watch state/state
+             :theme-changed
+             (fn [_ _ old new]
+               (when-not (= (:ui/theme old) (:ui/theme new))
+                 (configure-tabs (:ui/theme new) true)))))
+
+(defn hide!
+  []
+  (when (mobile-util/native-ios?)
+    (.hide ^js TabsBar)))
+
+(defn show!
+  []
+  (when (mobile-util/native-ios?)
+    (.show ^js TabsBar)))

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

@@ -4,6 +4,7 @@
             [frontend.components.journal :as journal]
             [frontend.handler.common :as common-handler]
             [frontend.handler.user :as user-handler]
+            [frontend.mobile.util :as mobile-util]
             [frontend.rum :as frum]
             [frontend.state :as state]
             [frontend.ui :as ui]
@@ -14,6 +15,7 @@
             [logseq.shui.silkhq :as silkhq]
             [logseq.shui.toaster.core :as shui-toaster]
             [logseq.shui.ui :as shui]
+            [mobile.bottom-tabs :as bottom-tabs]
             [mobile.components.editor-toolbar :as editor-toolbar]
             [mobile.components.header :as mobile-header]
             [mobile.components.left-sidebar :as mobile-left-sidebar]
@@ -27,7 +29,6 @@
             [mobile.state :as mobile-state]
             [rum.core :as rum]))
 
-
 (defn- sidebar-not-allowed-to-open?
   []
   (or (seq @mobile-state/*modal-blocks)
@@ -185,6 +186,8 @@
     (use-theme-effects! current-repo)
     (hooks/use-effect!
      (fn []
+       (when (mobile-util/native-ios?)
+         (bottom-tabs/configure))
        (when-let [element (util/mobile-page-scroll)]
          (common-handler/listen-to-scroll! element))) [])
     (silkhq/depth-sheet-stack
@@ -213,7 +216,8 @@
       (mobile-left-sidebar/left-sidebar)
 
       ;; bottom tabs
-      (ui-silk/app-silk-tabs)
+      (when-not (mobile-util/native-ios?)
+        (ui-silk/app-silk-tabs))
 
       (ui-component/keep-keyboard-virtual-input)
       (ui-component/install-notifications)

+ 5 - 1
src/main/mobile/components/left_sidebar.cljs

@@ -2,6 +2,7 @@
   "Mobile left sidebar"
   (:require [frontend.components.left-sidebar :as app-left-sidebar]
             [logseq.shui.silkhq :as silkhq]
+            [mobile.bottom-tabs :as bottom-tabs]
             [mobile.state :as mobile-state]
             [rum.core :as rum]))
 
@@ -18,11 +19,14 @@
   []
   (when (empty? (rum/react mobile-state/*modal-blocks))
     (let [open? (rum/react mobile-state/*left-sidebar-open?)]
+      (when open?
+        (bottom-tabs/hide!))
       (silkhq/sidebar-sheet
        {:presented (boolean open?)
         :onPresentedChange (fn [v]
                              (when (false? v)
-                               (mobile-state/close-left-sidebar!)))}
+                               (mobile-state/close-left-sidebar!)
+                               (bottom-tabs/show!)))}
        (silkhq/sidebar-sheet-portal
         (silkhq/sidebar-sheet-view
          {:class "app-silk-sidebar-sheet-view"}

+ 4 - 1
src/main/mobile/components/modal.cljs

@@ -13,6 +13,7 @@
             [logseq.shui.hooks :as hooks]
             [logseq.shui.silkhq :as silkhq]
             [logseq.shui.ui :as shui]
+            [mobile.bottom-tabs :as bottom-tabs]
             [mobile.components.ui :as mobile-ui]
             [mobile.init :as init]
             [mobile.state :as mobile-state]
@@ -199,6 +200,7 @@
     (hooks/use-effect!
      (fn []
        (when open?
+         (bottom-tabs/hide!)
          (state/clear-edit!)
          (init/keyboard-hide)))
      [open?])
@@ -209,7 +211,8 @@
                            (when (false? v?)
                              (mobile-state/close-block-modal!)
                              (state/clear-edit!)
-                             (state/pub-event! [:mobile/keyboard-will-hide])))}
+                             (state/pub-event! [:mobile/keyboard-will-hide])
+                             (bottom-tabs/show!)))}
      (silkhq/depth-sheet-portal
       (silkhq/depth-sheet-view
        {:class "block-modal-page"

+ 5 - 1
src/main/mobile/components/popup.cljs

@@ -7,6 +7,7 @@
             [logseq.shui.popup.core :as shui-popup]
             [logseq.shui.silkhq :as silkhq]
             [logseq.shui.ui :as shui]
+            [mobile.bottom-tabs :as bottom-tabs]
             [mobile.state :as mobile-state]
             [rum.core :as rum]))
 
@@ -74,6 +75,7 @@
         default-height (:default-height opts)]
 
     (when open?
+      (bottom-tabs/hide!)
       (silkhq/bottom-sheet
        (merge
         {:presented (boolean open?)
@@ -81,7 +83,9 @@
                               (when (false? v?)
                                 (state/pub-event! [:mobile/clear-edit])
                                 ;; allows closing animation
-                                (js/setTimeout #(mobile-state/set-popup! nil) 150)))}
+                                (js/setTimeout #(do
+                                                  (mobile-state/set-popup! nil)
+                                                  (bottom-tabs/show!)) 150)))}
         (:modal-props opts))
        (silkhq/bottom-sheet-portal
         (silkhq/bottom-sheet-view

+ 13 - 0
yarn.lock

@@ -291,6 +291,13 @@
   dependencies:
     tslib "^2.1.0"
 
+"@capacitor/core@^7.4.4":
+  version "7.4.4"
+  resolved "https://registry.yarnpkg.com/@capacitor/core/-/core-7.4.4.tgz#b752dd7a12141e40d1c8ce2e37a79571d9f83518"
+  integrity sha512-xzjxpr+d2zwTpCaN0k+C6wKSZzWFAb9OVEUtmO72ihjr/NEDoLvsGl4WLfjWPcCO2zOy0b2X52tfRWjECFUjtw==
+  dependencies:
+    tslib "^2.1.0"
+
 "@capacitor/device@^7.0.2":
   version "7.0.2"
   resolved "https://registry.yarnpkg.com/@capacitor/device/-/device-7.0.2.tgz#406bde129d3fcf55f0de0b691509535e2a00e315"
@@ -9539,6 +9546,12 @@ statuses@~1.5.0:
   resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
   integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==
 
+"stay-liquid@https://github.com/logseq/stay-liquid":
+  version "0.1.0"
+  resolved "https://github.com/logseq/stay-liquid#b2fde7447e2fff96f16b7715c6857d91c992359d"
+  dependencies:
+    "@capacitor/core" "^7.4.4"
+
 stop-iteration-iterator@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz#f481ff70a548f6124d0312c3aa14cbfa7aa542ad"