wip: native bottom sheet

This commit is contained in:
Tienson Qin
2025-11-24 10:28:18 +08:00
parent f2224e6a3f
commit fbd456476d
10 changed files with 306 additions and 91 deletions

View File

@@ -24,9 +24,11 @@
(defonce folder-picker (registerPlugin "FolderPicker"))
(defonce ui-local (registerPlugin "UILocal"))
(defonce native-top-bar nil)
(defonce native-bottom-sheet nil)
(defonce ios-utils nil)
(when (native-ios?)
(set! native-top-bar (registerPlugin "NativeTopBarPlugin"))
(set! native-bottom-sheet (registerPlugin "NativeBottomSheetPlugin"))
(set! ios-utils (registerPlugin "Utils")))
(defn hide-splash []

View File

@@ -152,17 +152,21 @@
(shui-toaster/install-toaster)
(shui-dialog/install-modals)
(shui-popup/install-popups)
(popup/popup)]))
(shui-popup/install-popups)]))
(rum/defc main < rum/reactive
[]
(let [current-repo (state/sub :git/current-repo)
login? (and (state/sub :auth/id-token)
(user-handler/logged-in?))
show-action-bar? (state/sub :mobile/show-action-bar?)]
[:<>
(app current-repo {:login? login?})
(editor-toolbar/mobile-bar)
(when show-action-bar?
(selection-toolbar/action-bar))]))
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.app-main.w-full.h-full
[:div.flex.flex-col.flex-1.w-full.h-full {:class (when show-popup? "hidden")}
(app current-repo {:login? login?})
(editor-toolbar/mobile-bar)
(when show-action-bar?
(selection-toolbar/action-bar))]
(when show-popup?
(popup/popup opts content-fn))]))

View File

@@ -564,3 +564,11 @@ body, #root {
-webkit-overflow-scrolling: touch;
padding-bottom: 48px;
}
.cp__select-main {
width: 100%;
}
.cp__select {
--palettle-container-height: 100%;
}

View File

@@ -97,7 +97,7 @@
(defn- open-settings-actions! []
(ui-component/open-popup!
(fn []
[:div.-mx-2
[:div
(when (user-handler/logged-in?)
(ui/menu-link {:on-click #(user-handler/logout)}
[:span.text-lg.flex.gap-2.items-center.text-red-700
@@ -115,8 +115,7 @@
[:span.text-lg.flex.gap-2.items-center
"Check log"])])
{:title "Actions"
:default-height false
:type :action-sheet}))
:default-height false}))
(defn- open-graph-switcher! []
(ui-component/open-popup!
@@ -124,8 +123,7 @@
[:div.px-1
(repo/repos-dropdown-content {})])
{:title "Select a Graph"
:default-height false
:type :action-sheet}))
:default-height false}))
(defn- register-native-top-bar-events! []
(when (and (mobile-util/native-ios?)

View File

@@ -1,18 +1,53 @@
(ns mobile.components.popup
"Mobile popup"
(:require [frontend.handler.editor :as editor-handler]
[frontend.mobile.util :as mobile-util]
[frontend.state :as state]
[frontend.ui :as ui]
[goog.object :as gobj]
[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]))
(defonce *last-popup-modal? (atom nil))
(defn- popup-min-height
[default-height]
(cond
(false? default-height) nil
(number? default-height) default-height
:else 400))
(defn- present-native-sheet!
[opts]
(when-let [plugin mobile-util/native-bottom-sheet]
(.present
plugin
(clj->js
(let [height (popup-min-height (:default-height opts))]
(cond-> {:allowFullHeight (not= (:type opts) :action-sheet)}
height (assoc :defaultHeight height)))))))
(defn- dismiss-native-sheet!
[]
(when-let [plugin mobile-util/native-bottom-sheet]
(.dismiss plugin #js {})))
(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!))
(when (some? @mobile-state/*popup-data)
(state/pub-event! [:mobile/clear-edit])
(mobile-state/set-popup! nil)))))
(defonce native-sheet-listener
(when (mobile-util/native-ios?)
(when-let [plugin mobile-util/native-bottom-sheet]
(.addListener plugin "state" handle-native-sheet-state!))))
(defn wrap-calc-commands-popup-side
[pos opts]
(let [[side mh] (let [[_x y _ height] pos
@@ -28,7 +63,7 @@
(assoc-in [:content-props :side] side))))
(defn popup-show!
[event content-fn {:keys [id dropdown-menu?] :as opts}]
[event content-fn {:keys [id] :as opts}]
(cond
(and (keyword? id) (= "editor.commands" (namespace id)))
(let [opts (wrap-calc-commands-popup-side event opts)
@@ -39,94 +74,49 @@
pid (shui-popup/show! event content-fn opts)]
(reset! *last-popup-modal? false) pid)
dropdown-menu?
(let [pid (shui-popup/show! event content-fn opts)]
(reset! *last-popup-modal? false) pid)
:else
(when content-fn
(mobile-state/set-popup! {:open? true
:content-fn content-fn
:opts opts})
(reset! *last-popup-modal? true))))
(reset! *last-popup-modal? true)
(when (mobile-util/native-ios?)
(present-native-sheet! opts)))))
(defn popup-hide!
[& args]
(cond
(= :download-rtc-graph (first args))
(do
(when (mobile-util/native-ios?)
(dismiss-native-sheet!))
(mobile-state/set-popup! nil)
(mobile-state/redirect-to-tab! "home"))
:else
(if (and @*last-popup-modal? (not (= (first args) :editor.commands/commands)))
(mobile-state/set-popup! nil)
(if (mobile-util/native-ios?)
(dismiss-native-sheet!)
(mobile-state/set-popup! nil))
(apply shui-popup/hide! args))))
(set! shui/popup-show! popup-show!)
(set! shui/popup-hide! popup-hide!)
(rum/defc popup < rum/reactive
[]
(let [{:keys [open? content-fn opts]} (rum/react mobile-state/*popup-data)
quick-add? (= :ls-quick-add (:id opts))
audio-record? (= :ls-audio-record (:id opts))
action-sheet? (= :action-sheet (:type opts))
default-height (:default-height opts)]
(when open?
(bottom-tabs/hide!)
(silkhq/bottom-sheet
(merge
{:presented (boolean open?)
:onPresentedChange (fn [v?]
(when (false? v?)
(state/pub-event! [:mobile/clear-edit])
;; allows closing animation
(js/setTimeout #(do
(mobile-state/set-popup! nil)
(bottom-tabs/show!)) 150)))}
(:modal-props opts))
(silkhq/bottom-sheet-portal
(silkhq/bottom-sheet-view
{:class (str "app-silk-popup-sheet-view as-" (name (or (:type opts) "default")))
:inertOutside false
:onTravelStatusChange (fn [status]
(when (and quick-add? (= status "entering"))
(editor-handler/quick-add-open-last-block!)))
:onPresentAutoFocus #js {:focus false}}
(silkhq/bottom-sheet-backdrop
(when (or quick-add? audio-record?)
{:travelAnimation {:opacity (fn [data]
(let [progress (gobj/get data "progress")]
(js/Math.min (* progress 0.9) 0.9)))}}))
(silkhq/bottom-sheet-content
{:class "flex flex-col items-center p-2"}
(silkhq/bottom-sheet-handle)
(silkhq/scroll
{:as-child true}
(silkhq/scroll-view
{:class "app-silk-scroll-view overflow-y-scroll"
:scrollGestureTrap {:yEnd true}
:style {:min-height (cond
(false? default-height)
nil
(number? default-height)
default-height
:else
400)
:max-height "80vh"}}
(silkhq/scroll-content
(let [title (or (:title opts) (when (string? content-fn) content-fn))
content (if (fn? content-fn)
(content-fn)
(if-let [buttons (and action-sheet? (:buttons opts))]
[:div.-mx-2
(for [{:keys [role text]} buttons]
(ui/menu-link {:on-click #(some-> (:on-action opts) (apply [{:role role}]))
:data-role role}
[:span.text-lg.flex.items-center text]))]
(when-not (string? content-fn) content-fn)))]
[:div.w-full.app-silk-popup-content-inner.p-2
(when title [:h2.py-2.opacity-40 title])
content])))))))))))
(rum/defc popup
[opts content-fn]
(let [title (or (:title opts) (when (string? content-fn) content-fn))
content (if (fn? content-fn)
(content-fn)
(if-let [buttons (:buttons opts)]
[:div.-mx-2
(for [{:keys [role text]} buttons]
(ui/menu-link
{:on-click #(some-> (:on-action opts) (apply [{:role role}]))
:data-role role}
[:span.text-lg.flex.items-center text]))]
(when-not (string? content-fn) content-fn)))]
[:div {:class "flex flex-col items-center p-2 w-full h-full"}
[:div.app-silk-popup-content-inner.w-full.h-full
(when title [:h2.py-2.opacity-40 title])
content]]))

View File

@@ -4,6 +4,7 @@
[frontend.rum :as r]
[frontend.state :as state]
[logseq.shui.ui :as shui]
[mobile.components.popup :as popup]
[mobile.state :as mobile-state]
[react-transition-group :refer [CSSTransition TransitionGroup]]
[rum.core :as rum]))
@@ -111,7 +112,4 @@
(defn open-popup!
[content-fn opts]
(mobile-state/set-popup!
{:open? true
:content-fn content-fn
:opts opts}))
(popup/popup-show! nil content-fn opts))