diff --git a/neode-ui/src/views/dashboard/DashboardMobileNav.vue b/neode-ui/src/views/dashboard/DashboardMobileNav.vue index dbe88cd8..12f5dc3b 100644 --- a/neode-ui/src/views/dashboard/DashboardMobileNav.vue +++ b/neode-ui/src/views/dashboard/DashboardMobileNav.vue @@ -208,6 +208,11 @@ function onResize() { updateTabBarHeight() } +function onInsetsInjected() { + readSafeAreaTop() + updateTabBarHeight() +} + onMounted(() => { updateTabBarHeight() // Re-measure after the first paint: on mount the bar may not have its final @@ -216,13 +221,21 @@ onMounted(() => { requestAnimationFrame(updateTabBarHeight) readSafeAreaTop() window.addEventListener('resize', onResize) - // Re-read after WebView injection has had time to run. The injected - // safe-area-bottom padding changes the bar's height, so re-measure too. - setTimeout(() => { readSafeAreaTop(); updateTabBarHeight() }, 500) + // The Android WebView injects --safe-area-top asynchronously and fires this + // event when it lands. An authenticated session mounts the dashboard BEFORE + // the injection (fresh installs mount after login, long after it), so a + // one-shot read here bakes in 0 and content slides under the growing fixed + // tab bar — the update-install-only overlap bug. + window.addEventListener('archy-insets', onInsetsInjected) + // Fallback retry ladder for APKs that predate the event. + for (const delay of [500, 1500, 3000, 6000]) { + setTimeout(onInsetsInjected, delay) + } }) onBeforeUnmount(() => { window.removeEventListener('resize', onResize) + window.removeEventListener('archy-insets', onInsetsInjected) }) // Re-measure on route changes