Fix desktop showing the mobile layout: decide layout from the actual build platform (cfg!(mobile)) instead of sniffing the webview user-agent, which some WebKitGTK builds report mobile-looking (#63)

This commit is contained in:
Yuri Karamian
2026-06-11 23:35:24 +02:00
parent 15c6e6388a
commit 4aa399e96e
6 changed files with 25 additions and 5 deletions
+7
View File
@@ -2364,6 +2364,13 @@ fn save_app_config(config: &AppConfig) -> Result<(), String> {
// ── Install Type Detection ── // ── 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] #[tauri::command]
pub fn get_install_type() -> String { pub fn get_install_type() -> String {
// Build-time override for distro packagers (e.g. Solus): build with // Build-time override for distro packagers (e.g. Solus): build with
+1
View File
@@ -171,6 +171,7 @@ pub fn run() {
commands::test_sync_connection, commands::test_sync_connection,
commands::sync_now, commands::sync_now,
commands::get_install_type, commands::get_install_type,
commands::is_mobile_platform,
commands::get_pending_open_file, commands::get_pending_open_file,
]) ])
.register_asynchronous_uri_scheme_protocol("imgproxy", |_ctx, request, responder| { .register_asynchronous_uri_scheme_protocol("imgproxy", |_ctx, request, responder| {
+4
View File
@@ -463,6 +463,10 @@ export async function getInstallType(): Promise<string> {
return invoke("get_install_type"); return invoke("get_install_type");
} }
export async function isMobilePlatform(): Promise<boolean> {
return invoke("is_mobile_platform");
}
export async function getPendingOpenFile(): Promise<string | null> { export async function getPendingOpenFile(): Promise<string | null> {
return invoke("get_pending_open_file"); return invoke("get_pending_open_file");
} }
+4 -3
View File
@@ -48,12 +48,13 @@
viewerNote, viewerNote,
notebookSortMode, notebookSortMode,
notebookOrder, notebookOrder,
syncState syncState,
platformIsMobile
} from '$lib/stores/app'; } from '$lib/stores/app';
const appWindow = getCurrentWindow(); const appWindow = getCurrentWindow();
const isMac = navigator.platform.startsWith('Mac'); 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); 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 { loadVaultState, saveVaultState, readNote, createDailyNote, createBackup, getPendingOpenFile, addQuickAccess, removeQuickAccess, getQuickAccess, setTheme, syncNow, setTaskDone, setTaskPriority, setTaskDue } from '$lib/api';
import { debounce } from '$lib/utils/debounce'; import { debounce } from '$lib/utils/debounce';
@@ -329,7 +330,7 @@
let historyDepth = 0; let historyDepth = 0;
let navFromPopstate = false; let navFromPopstate = false;
if (isMobile) { if (get(platformIsMobile)) {
history.replaceState({ mobileView: 'sidebar', depth: 0 }, ''); history.replaceState({ mobileView: 'sidebar', depth: 0 }, '');
$effect(() => { $effect(() => {
+5
View File
@@ -85,6 +85,11 @@ export const updateAvailable = writable<{
} | null>(null); } | null>(null);
export const updateObj = writable<any>(null); export const updateObj = writable<any>(null);
export const installType = writable<string>("native"); export const installType = writable<string>("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<boolean>(
typeof navigator !== "undefined" && /android|iphone|ipad|ipod/i.test(navigator.userAgent),
);
export const androidApkUrl = writable<string | null>(null); export const androidApkUrl = writable<string | null>(null);
// Install types that handle their own updates (in-app auto-updater, or a // Install types that handle their own updates (in-app auto-updater, or a
+4 -2
View File
@@ -1,8 +1,8 @@
<script lang="ts"> <script lang="ts">
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import '../app.css'; import '../app.css';
import { theme, appConfig, activeNote, activeNotePath, installType, checkForUpdate, checkForUpdateMobile, isManagedInstall } from '$lib/stores/app'; import { theme, appConfig, activeNote, activeNotePath, installType, platformIsMobile, checkForUpdate, checkForUpdateMobile, isManagedInstall } from '$lib/stores/app';
import { openFile, openUrl, readNote, getInstallType } from '$lib/api'; import { openFile, openUrl, readNote, getInstallType, isMobilePlatform } from '$lib/api';
import { get } from 'svelte/store'; import { get } from 'svelte/store';
import ResizeHandles from '$lib/components/ResizeHandles.svelte'; import ResizeHandles from '$lib/components/ResizeHandles.svelte';
@@ -88,6 +88,8 @@
// Detect install type and check for updates on startup // Detect install type and check for updates on startup
onMount(() => { onMount(() => {
// Authoritative platform from the backend (compile-time), overriding the UA guess. (#63)
isMobilePlatform().then((m) => platformIsMobile.set(m)).catch(() => {});
if (isMobile) { if (isMobile) {
installType.set('android'); installType.set('android');
checkForUpdateMobile(); checkForUpdateMobile();