fix: mask native tab transition briefly

This commit is contained in:
Tienson Qin
2026-05-19 05:54:42 +08:00
parent 4803a30327
commit 3852d18822
5 changed files with 80 additions and 0 deletions

View File

@@ -19,6 +19,7 @@ public class LiquidTabsPlugin: CAPPlugin, CAPBridgedPlugin {
CAPPluginMethod(name: "selectTab", returnType: CAPPluginReturnPromise),
CAPPluginMethod(name: "updateNativeSearchResults", returnType: CAPPluginReturnPromise),
CAPPluginMethod(name: "updateNativeGraphs", returnType: CAPPluginReturnPromise),
CAPPluginMethod(name: "markTabContentReady", returnType: CAPPluginReturnPromise),
]
public override func load() {
@@ -214,6 +215,18 @@ public class LiquidTabsPlugin: CAPPlugin, CAPBridgedPlugin {
call.resolve()
}
/// Clear the native transition cover after JS has rendered a WebView-backed tab.
/// { id: string }
@objc func markTabContentReady(_ call: CAPPluginCall) {
guard let id = call.getString("id") else {
call.reject("Missing 'id'")
return
}
store.markWebTabReady(id)
call.resolve()
}
func openGraph(_ graph: NativeGraphItem) {
notifyListeners("nativeGraphAction", data: [
"action": "open",

View File

@@ -183,7 +183,25 @@ private struct LiquidTabs26View: View {
isSearchFocused = true
}
private func webBackedTabId(for selection: LiquidTabsTabSelection) -> String? {
guard let id = store.tabId(for: selection),
id != "graphs",
id != "search" else {
return nil
}
return id
}
private func waitForWebBackedTab(_ selection: LiquidTabsTabSelection) {
if let id = webBackedTabId(for: selection) {
store.waitForWebTab(id)
}
}
private func prepareForSelectionChange(to selection: LiquidTabsTabSelection) {
waitForWebBackedTab(selection)
switch selection {
case .search:
store.suppressSearchNotifications = true
@@ -279,6 +297,12 @@ private struct LiquidTabs26View: View {
SearchFocusBridge(isActive: selectedTab == .search)
.frame(width: 0, height: 0)
}
.overlay {
if store.pendingWebTabId != nil {
Color.logseqBackground
.ignoresSafeArea()
}
}
}
.onAppear {

View File

@@ -78,6 +78,8 @@ final class LiquidTabsStore: ObservableObject {
@Published var graphSections: [NativeGraphSection] = []
@Published var graphLabels = NativeGraphLabels()
@Published var nativeGraphsVisible = true
@Published var pendingWebTabId: String?
private var pendingWebTabRequestId = 0
// Helper to get a stable selection if JS forgets
func effectiveSelectedId() -> String? {
@@ -107,4 +109,28 @@ final class LiquidTabsStore: ObservableObject {
}
}
func waitForWebTab(_ id: String) {
DispatchQueue.main.async {
self.pendingWebTabRequestId += 1
let requestId = self.pendingWebTabRequestId
self.pendingWebTabId = id
DispatchQueue.main.asyncAfter(deadline: .now() + 0.18) {
guard self.pendingWebTabRequestId == requestId,
self.pendingWebTabId == id else {
return
}
self.pendingWebTabId = nil
}
}
}
func markWebTabReady(_ id: String) {
DispatchQueue.main.async {
guard self.pendingWebTabId == id else { return }
self.pendingWebTabRequestId += 1
self.pendingWebTabId = nil
}
}
}

View File

@@ -54,6 +54,11 @@
(when (and (mobile-util/native-ios?) liquid-tabs (.-updateNativeGraphs liquid-tabs))
(.updateNativeGraphs liquid-tabs (clj->js payload))))
(defn mark-tab-content-ready!
[id]
(when (and (mobile-util/native-ios?) liquid-tabs (.-markTabContentReady liquid-tabs))
(.markTabContentReady liquid-tabs #js {:id id})))
(defn add-tab-selected-listener!
"Listen to native tab selection.

View File

@@ -294,6 +294,18 @@
[theme] (frum/use-atom-in state/state :ui/theme)]
(use-screen-size-effects!)
(use-theme-effects! current-repo theme)
(hooks/use-effect!
(fn []
(when (mobile-util/native-ios?)
(.requestAnimationFrame
js/window
(fn []
(.requestAnimationFrame
js/window
(fn []
(bottom-tabs/mark-tab-content-ready! tab))))))
nil)
[tab route-match])
(hooks/use-effect!
(fn []
(when-let [element (util/app-scroll-container-node)]