diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index 5121be6..d61d438 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -2364,6 +2364,13 @@ fn save_app_config(config: &AppConfig) -> Result<(), String> { // ── Install Type Detection ── +#[tauri::command] +pub fn is_mobile_platform() -> bool { + // Compile-time platform: true only for the Android/iOS builds. Authoritative, unlike the + // webview user-agent, which some desktop WebKitGTK builds report mobile-looking (issue #63). + cfg!(mobile) +} + #[tauri::command] pub fn get_install_type() -> String { // Build-time override for distro packagers (e.g. Solus): build with diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 72afb66..1d8c648 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -171,6 +171,7 @@ pub fn run() { commands::test_sync_connection, commands::sync_now, commands::get_install_type, + commands::is_mobile_platform, commands::get_pending_open_file, ]) .register_asynchronous_uri_scheme_protocol("imgproxy", |_ctx, request, responder| { diff --git a/src/lib/api.ts b/src/lib/api.ts index 6ec5d59..254ac09 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -463,6 +463,10 @@ export async function getInstallType(): Promise { return invoke("get_install_type"); } +export async function isMobilePlatform(): Promise { + return invoke("is_mobile_platform"); +} + export async function getPendingOpenFile(): Promise { return invoke("get_pending_open_file"); } diff --git a/src/lib/components/AppLayout.svelte b/src/lib/components/AppLayout.svelte index 7a1b2ba..b6ee6e7 100644 --- a/src/lib/components/AppLayout.svelte +++ b/src/lib/components/AppLayout.svelte @@ -48,12 +48,13 @@ viewerNote, notebookSortMode, notebookOrder, - syncState + syncState, + platformIsMobile } from '$lib/stores/app'; const appWindow = getCurrentWindow(); const isMac = navigator.platform.startsWith('Mac'); - const isMobile = /android|iphone|ipad|ipod/i.test(navigator.userAgent); + const isMobile = $derived($platformIsMobile); const isAndroid = /android/i.test(navigator.userAgent); import { loadVaultState, saveVaultState, readNote, createDailyNote, createBackup, getPendingOpenFile, addQuickAccess, removeQuickAccess, getQuickAccess, setTheme, syncNow, setTaskDone, setTaskPriority, setTaskDue } from '$lib/api'; import { debounce } from '$lib/utils/debounce'; @@ -329,7 +330,7 @@ let historyDepth = 0; let navFromPopstate = false; - if (isMobile) { + if (get(platformIsMobile)) { history.replaceState({ mobileView: 'sidebar', depth: 0 }, ''); $effect(() => { diff --git a/src/lib/stores/app.ts b/src/lib/stores/app.ts index a1b23c5..2b0c110 100644 --- a/src/lib/stores/app.ts +++ b/src/lib/stores/app.ts @@ -85,6 +85,11 @@ export const updateAvailable = writable<{ } | null>(null); export const updateObj = writable(null); export const installType = writable("native"); +// True only on the Android/iOS build. Defaults to a user-agent guess for the first paint, then +// gets the authoritative compile-time value from the backend at startup (see +layout). (#63) +export const platformIsMobile = writable( + typeof navigator !== "undefined" && /android|iphone|ipad|ipod/i.test(navigator.userAgent), +); export const androidApkUrl = writable(null); // Install types that handle their own updates (in-app auto-updater, or a diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 44cf073..ee08609 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,8 +1,8 @@