feat(snap): add snap sandbox detection and welcome modal wiring

Detect when QBZ runs inside a Snap sandbox (via SNAP env var) and show
a one-time welcome modal with required plug connection commands. Adds a
Snap Sandbox section in Settings with copyable commands, mirroring the
existing Flatpak pattern.

Backend: snap.rs module, DB table snap_welcome_shown, V2 commands.
Frontend: updatesService, +page.svelte modal queue, SettingsView section.
This commit is contained in:
vicrodh committed 2026-03-02 11:10:50 -06:00
1 parent 8529477778
commit 00675de6b8
11 files changed
+182 -15

No files matched your search

+34 -3
View File
@@ -144,6 +144,7 @@ function getDevFakeRelease(currentVersion: string): ReleaseInfo | null {
export interface LaunchUpdateDecision {
currentVersion: string;
showFlatpakWelcome: boolean;
showSnapWelcome: boolean;
whatsNewRelease: ReleaseInfo | null;
updateRelease: ReleaseInfo | null;
}
@@ -173,6 +174,31 @@ export async function markFlatpakWelcomeShown(): Promise<void> {
}
}
/**
* Check if running in Snap and if welcome modal has been shown.
*/
async function shouldShowSnapWelcome(): Promise<boolean> {
try {
const isSnap = await invoke<boolean>('v2_is_running_in_snap');
if (!isSnap) return false;
const alreadyShown = await invoke<boolean>('has_snap_welcome_been_shown');
return !alreadyShown;
} catch {
return false;
}
}
/**
* Mark Snap welcome modal as shown.
*/
export async function markSnapWelcomeShown(): Promise<void> {
try {
await invoke('v2_mark_snap_welcome_shown');
} catch (error) {
console.debug('[Updates] Failed to mark snap welcome shown:', error);
}
}
/**
* Decide which modals should be shown on launch.
*
@@ -186,6 +212,7 @@ export async function decideLaunchModals(): Promise<LaunchUpdateDecision> {
return {
currentVersion: getCurrentVersion(),
showFlatpakWelcome: false,
showSnapWelcome: false,
whatsNewRelease: null,
updateRelease: null,
};
@@ -203,17 +230,21 @@ export async function decideLaunchModals(): Promise<LaunchUpdateDecision> {
const currentVersion = getCurrentVersion();
if (getSessionFlag(SESSION_CHECK_DONE_KEY)) {
return { currentVersion, showFlatpakWelcome: false, whatsNewRelease: null, updateRelease: null };
return { currentVersion, showFlatpakWelcome: false, showSnapWelcome: false, whatsNewRelease: null, updateRelease: null };
}
setSessionFlag(SESSION_CHECK_DONE_KEY);
// Collect all modals that need to be shown
let showFlatpakWelcome = false;
let showSnapWelcome = false;
let whatsNewRelease: ReleaseInfo | null = null;
let updateRelease: ReleaseInfo | null = null;
// Phase 1: Flatpak welcome (highest priority, shown first)
// Phase 1: Sandbox welcome (highest priority, shown first)
showFlatpakWelcome = await shouldShowFlatpakWelcome();
if (!showFlatpakWelcome) {
showSnapWelcome = await shouldShowSnapWelcome();
}
// Phase 2: What's New for current version
if (prefs.showWhatsNewOnLaunch && currentVersion) {
@@ -254,7 +285,7 @@ export async function decideLaunchModals(): Promise<LaunchUpdateDecision> {
}
}
return { currentVersion, showFlatpakWelcome, whatsNewRelease, updateRelease };
return { currentVersion, showFlatpakWelcome, showSnapWelcome, whatsNewRelease, updateRelease };
}
export async function openReleasePageAndAcknowledge(release: ReleaseInfo): Promise<void> {