bottom_tabs.cljs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. (ns mobile.bottom-tabs
  2. "iOS bottom tabs"
  3. (:require [cljs-bean.core :as bean]
  4. [clojure.string :as string]
  5. [frontend.handler.editor :as editor-handler]
  6. [frontend.state :as state]
  7. [frontend.util :as util]
  8. [logseq.common.util :as common-util]
  9. [mobile.navigation :as mobile-nav]
  10. [mobile.state :as mobile-state]))
  11. ;; Capacitor plugin instance:
  12. ;; Make sure the plugin is registered as `LiquidTabs` on the native side.
  13. (def ^js liquid-tabs
  14. (.. js/Capacitor -Plugins -LiquidTabsPlugin))
  15. (defn configure-tabs
  16. "Configure the native tab bar.
  17. `tabs` is a vector of maps:
  18. [{:id \"home\" :title \"Home\" :system-image \"house\" :role \"normal\"}
  19. {:id \"search\" :title \"Search\" :system-image \"magnifyingglass\" :role \"search\"}]"
  20. [tabs]
  21. ;; Returns the underlying JS Promise from Capacitor
  22. (.configureTabs
  23. liquid-tabs
  24. (bean/->js {:tabs tabs})))
  25. (defn select!
  26. "Programmatically select a tab by id. Returns a JS Promise."
  27. [id]
  28. (.selectTab
  29. liquid-tabs
  30. #js {:id id}))
  31. (defn add-tab-selected-listener!
  32. "Listen to native tab selection.
  33. `f` receives the tab id string.
  34. Returns the Capacitor listener handle; call `(.remove handle)` to unsubscribe."
  35. [f]
  36. (.addListener
  37. liquid-tabs
  38. "tabSelected"
  39. (fn [data]
  40. ;; data is like { id: string }
  41. (when-let [id (.-id data)]
  42. (f id)))))
  43. (defn add-search-listener!
  44. "Listen to native search query changes from the SwiftUI search tab.
  45. `f` receives a query string.
  46. Returns the Capacitor listener handle; call `(.remove handle)` to unsubscribe."
  47. [f]
  48. (.addListener
  49. liquid-tabs
  50. "searchChanged"
  51. (fn [data]
  52. ;; data is like { query: string }
  53. (f (.-query data)))))
  54. (defn add-keyboard-hack-listener!
  55. "Listen for Backspace or Enter while the invisible keyboard field is focused."
  56. []
  57. (.addListener
  58. liquid-tabs
  59. "keyboardHackKey"
  60. (fn [data]
  61. ;; data is like { key: string }
  62. (when-let [k (.-key data)]
  63. (case k
  64. "backspace"
  65. (editor-handler/delete-block-when-zero-pos! nil)
  66. "enter"
  67. (when-let [input (state/get-input)]
  68. (let [value (.-value input)]
  69. (when (string/blank? value)
  70. (editor-handler/keydown-new-block-handler nil))))
  71. nil)))))
  72. (defonce *previous-tab (atom nil))
  73. (defonce add-tab-listeners!
  74. (do
  75. (add-tab-selected-listener!
  76. (fn [tab]
  77. (if (= tab "capture")
  78. (editor-handler/show-quick-add)
  79. (let [exit-search? (= "search" @*previous-tab)]
  80. (when-not (= tab @*previous-tab)
  81. (when-not exit-search?
  82. (mobile-nav/reset-route!))
  83. (mobile-state/set-tab! tab))
  84. (case tab
  85. "home"
  86. (util/scroll-to-top false)
  87. ;; TODO: support longPress detection
  88. ;; (if (= "longPress" interaction)
  89. ;; (state/pub-event! [:mobile/start-audio-record])
  90. ;; (editor-handler/show-quick-add))
  91. nil)
  92. (reset! *previous-tab tab)))))
  93. (add-watch mobile-state/*tab ::select-tab
  94. (fn [_ _ _old new]
  95. (when new (select! new))))
  96. (add-search-listener!
  97. (fn [q]
  98. ;; wire up search handler
  99. (js/console.log "Native search query" q)
  100. (reset! mobile-state/*search-input q)
  101. (reset! mobile-state/*search-last-input-at (common-util/time-ms))
  102. (when (= :page (state/get-current-route))
  103. (mobile-nav/reset-route!))))
  104. (add-keyboard-hack-listener!)))
  105. (defn configure
  106. []
  107. (configure-tabs
  108. [{:id "home" :title "Home" :systemImage "house" :role "normal"}
  109. {:id "favorites" :title "Favorites" :systemImage "star" :role "normal"}
  110. {:id "capture" :title "Capture" :systemImage "tray" :role "action"}
  111. {:id "settings" :title "Settings" :systemImage "gear" :role "normal"}]))