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:
11 files changed
+182
-15
No files matched your search
@@ -10449,6 +10449,18 @@ pub fn v2_is_running_in_flatpak() -> bool {
|
|||||||
crate::flatpak::is_running_in_flatpak()
|
crate::flatpak::is_running_in_flatpak()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn v2_is_running_in_snap() -> bool {
|
||||||
|
crate::snap::is_running_in_snap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn v2_mark_snap_welcome_shown(
|
||||||
|
state: State<'_, crate::updates::UpdatesState>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
crate::updates::mark_snap_welcome_shown(state)
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn v2_detect_legacy_cached_files(
|
pub async fn v2_detect_legacy_cached_files(
|
||||||
cache_state: State<'_, OfflineCacheState>,
|
cache_state: State<'_, OfflineCacheState>,
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ pub mod config;
|
|||||||
pub mod credentials;
|
pub mod credentials;
|
||||||
pub mod discogs;
|
pub mod discogs;
|
||||||
pub mod flatpak;
|
pub mod flatpak;
|
||||||
|
pub mod snap;
|
||||||
pub mod lastfm;
|
pub mod lastfm;
|
||||||
pub mod library;
|
pub mod library;
|
||||||
pub mod listenbrainz;
|
pub mod listenbrainz;
|
||||||
@@ -1183,6 +1184,7 @@ pub fn run() {
|
|||||||
flatpak::get_flatpak_help_text,
|
flatpak::get_flatpak_help_text,
|
||||||
config::legal_settings::get_qobuz_tos_accepted,
|
config::legal_settings::get_qobuz_tos_accepted,
|
||||||
updates::has_flatpak_welcome_been_shown,
|
updates::has_flatpak_welcome_been_shown,
|
||||||
|
updates::has_snap_welcome_been_shown,
|
||||||
lyrics::commands::lyrics_get,
|
lyrics::commands::lyrics_get,
|
||||||
config::favorites_cache::is_track_favorite,
|
config::favorites_cache::is_track_favorite,
|
||||||
commands_v2::v2_set_api_locale,
|
commands_v2::v2_set_api_locale,
|
||||||
@@ -1356,6 +1358,8 @@ pub fn run() {
|
|||||||
commands_v2::v2_remote_control_regenerate_token,
|
commands_v2::v2_remote_control_regenerate_token,
|
||||||
commands_v2::v2_remote_control_get_pairing_qr,
|
commands_v2::v2_remote_control_get_pairing_qr,
|
||||||
commands_v2::v2_is_running_in_flatpak,
|
commands_v2::v2_is_running_in_flatpak,
|
||||||
|
commands_v2::v2_is_running_in_snap,
|
||||||
|
commands_v2::v2_mark_snap_welcome_shown,
|
||||||
commands_v2::v2_detect_legacy_cached_files,
|
commands_v2::v2_detect_legacy_cached_files,
|
||||||
commands_v2::v2_reco_log_event,
|
commands_v2::v2_reco_log_event,
|
||||||
commands_v2::v2_reco_train_scores,
|
commands_v2::v2_reco_train_scores,
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
//! Snap runtime detection and utilities
|
||||||
|
//!
|
||||||
|
//! Detects when QBZ is running inside a Snap sandbox
|
||||||
|
//! and provides sandbox-specific guidance.
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
/// Check if QBZ is running inside a Snap sandbox
|
||||||
|
pub fn is_snap() -> bool {
|
||||||
|
env::var("SNAP").is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn is_running_in_snap() -> bool {
|
||||||
|
is_snap()
|
||||||
|
}
|
||||||
@@ -143,6 +143,11 @@ impl UpdatesStore {
|
|||||||
CREATE TABLE IF NOT EXISTS flatpak_welcome_shown (
|
CREATE TABLE IF NOT EXISTS flatpak_welcome_shown (
|
||||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||||
shown_at INTEGER NOT NULL
|
shown_at INTEGER NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS snap_welcome_shown (
|
||||||
|
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||||
|
shown_at INTEGER NOT NULL
|
||||||
);",
|
);",
|
||||||
)
|
)
|
||||||
.map_err(|e| format!("Failed to create updates tables: {}", e))?;
|
.map_err(|e| format!("Failed to create updates tables: {}", e))?;
|
||||||
@@ -273,6 +278,26 @@ impl UpdatesStore {
|
|||||||
.map_err(|e| format!("Failed to mark flatpak_welcome_shown: {}", e))?;
|
.map_err(|e| format!("Failed to mark flatpak_welcome_shown: {}", e))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_snap_welcome_been_shown(&self) -> bool {
|
||||||
|
self.conn
|
||||||
|
.query_row(
|
||||||
|
"SELECT 1 FROM snap_welcome_shown WHERE id = 1 LIMIT 1",
|
||||||
|
[],
|
||||||
|
|_row| Ok(()),
|
||||||
|
)
|
||||||
|
.is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mark_snap_welcome_shown(&self) -> Result<(), String> {
|
||||||
|
self.conn
|
||||||
|
.execute(
|
||||||
|
"INSERT OR REPLACE INTO snap_welcome_shown (id, shown_at) VALUES (1, ?1)",
|
||||||
|
params![now_epoch_seconds()],
|
||||||
|
)
|
||||||
|
.map_err(|e| format!("Failed to mark snap_welcome_shown: {}", e))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Thread-safe state wrapper
|
/// Thread-safe state wrapper
|
||||||
@@ -543,6 +568,16 @@ pub fn mark_flatpak_welcome_shown(state: tauri::State<UpdatesState>) -> Result<(
|
|||||||
state.with_store(|store| store.mark_flatpak_welcome_shown())?
|
state.with_store(|store| store.mark_flatpak_welcome_shown())?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn has_snap_welcome_been_shown(state: tauri::State<UpdatesState>) -> Result<bool, String> {
|
||||||
|
state.with_store(|store| store.has_snap_welcome_been_shown())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn mark_snap_welcome_shown(state: tauri::State<UpdatesState>) -> Result<(), String> {
|
||||||
|
state.with_store(|store| store.mark_snap_welcome_shown())?
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn get_current_version(state: tauri::State<UpdatesState>) -> String {
|
pub fn get_current_version(state: tauri::State<UpdatesState>) -> String {
|
||||||
state.current_version()
|
state.current_version()
|
||||||
|
|||||||
@@ -279,6 +279,9 @@
|
|||||||
// Flatpak detection state
|
// Flatpak detection state
|
||||||
let isFlatpak = $state(false);
|
let isFlatpak = $state(false);
|
||||||
let flatpakHelpText = $state('');
|
let flatpakHelpText = $state('');
|
||||||
|
|
||||||
|
// Snap detection state
|
||||||
|
let isSnap = $state(false);
|
||||||
let isCheckingNetwork = $state(false);
|
let isCheckingNetwork = $state(false);
|
||||||
|
|
||||||
// Updates state
|
// Updates state
|
||||||
@@ -383,13 +386,13 @@
|
|||||||
{ id: 'developer', labelKey: 'settings.developer.title' },
|
{ id: 'developer', labelKey: 'settings.developer.title' },
|
||||||
];
|
];
|
||||||
|
|
||||||
// Navigation section definitions (dynamic: includes Flatpak only when running in Flatpak)
|
// Navigation section definitions (dynamic: includes sandbox sections when detected)
|
||||||
// NOTE: If adding new sections, add them BEFORE Flatpak. Flatpak must always be last.
|
const navSectionDefs = $derived.by(() => {
|
||||||
const navSectionDefs = $derived(
|
const sections = [...navSectionIds];
|
||||||
isFlatpak
|
if (isFlatpak) sections.push({ id: 'flatpak', labelKey: 'nav.flatpak' });
|
||||||
? [...navSectionIds, { id: 'flatpak', labelKey: 'nav.flatpak' }]
|
if (isSnap) sections.push({ id: 'snap', labelKey: 'nav.snap' });
|
||||||
: navSectionIds
|
return sections;
|
||||||
);
|
});
|
||||||
|
|
||||||
|
|
||||||
async function handleUpdateCheckOnLaunchToggle(enabled: boolean): Promise<void> {
|
async function handleUpdateCheckOnLaunchToggle(enabled: boolean): Promise<void> {
|
||||||
@@ -1382,8 +1385,9 @@
|
|||||||
updatesCurrentVersion = getUpdatesCurrentVersion();
|
updatesCurrentVersion = getUpdatesCurrentVersion();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Detect if running in Flatpak
|
// Detect sandbox environments
|
||||||
loadFlatpakStatus();
|
loadFlatpakStatus();
|
||||||
|
loadSnapStatus();
|
||||||
|
|
||||||
// Check for legacy cached files
|
// Check for legacy cached files
|
||||||
checkLegacyCachedFiles();
|
checkLegacyCachedFiles();
|
||||||
@@ -2539,6 +2543,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadSnapStatus() {
|
||||||
|
try {
|
||||||
|
isSnap = await invoke<boolean>('v2_is_running_in_snap');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to check Snap status:', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function loadAudioSettings() {
|
async function loadAudioSettings() {
|
||||||
try {
|
try {
|
||||||
const settings = await invoke<AudioSettings>('v2_get_audio_settings');
|
const settings = await invoke<AudioSettings>('v2_get_audio_settings');
|
||||||
@@ -5395,8 +5407,42 @@
|
|||||||
|
|
||||||
<LogsModal isOpen={showLogsModal} onClose={() => showLogsModal = false} />
|
<LogsModal isOpen={showLogsModal} onClose={() => showLogsModal = false} />
|
||||||
|
|
||||||
|
<!-- Snap Section (only shown when running in Snap) -->
|
||||||
|
{#if activeSection === 'snap' && isSnap}
|
||||||
|
<section class="section snap-section" id="snap">
|
||||||
|
<h3 class="section-title">Snap Sandbox</h3>
|
||||||
|
<div class="flatpak-info">
|
||||||
|
<p class="flatpak-intro">
|
||||||
|
QBZ is running inside a Snap sandbox. Some audio interfaces need to be connected manually for the best experience.
|
||||||
|
</p>
|
||||||
|
<div class="flatpak-guide">
|
||||||
|
<h4>Required Plug Connections</h4>
|
||||||
|
<p>Run these commands to enable full audio support:</p>
|
||||||
|
<div class="copyable-command">
|
||||||
|
<pre class="code-block">sudo snap connect qbz-player:alsa
|
||||||
|
sudo snap connect qbz-player:pulseaudio
|
||||||
|
sudo snap connect qbz-player:pipewire
|
||||||
|
sudo snap connect qbz-player:mpris</pre>
|
||||||
|
<button class="copy-btn" onclick={() => copyCommand('snap-required', 'sudo snap connect qbz-player:alsa\nsudo snap connect qbz-player:pulseaudio\nsudo snap connect qbz-player:pipewire\nsudo snap connect qbz-player:mpris')}>
|
||||||
|
{copiedCommands['snap-required'] ? 'Copied!' : 'Copy'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<h4>Optional (External Drives / NAS)</h4>
|
||||||
|
<div class="copyable-command">
|
||||||
|
<pre class="code-block">sudo snap connect qbz-player:removable-media</pre>
|
||||||
|
<button class="copy-btn" onclick={() => copyCommand('snap-optional', 'sudo snap connect qbz-player:removable-media')}>
|
||||||
|
{copiedCommands['snap-optional'] ? 'Copied!' : 'Copy'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<p class="flatpak-note">
|
||||||
|
<strong>Note:</strong> These settings persist across reboots and updates. You only need to run them once, then restart QBZ.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<!-- Flatpak Section (only shown when running in Flatpak) -->
|
<!-- Flatpak Section (only shown when running in Flatpak) -->
|
||||||
<!-- NOTE: Keep this section LAST. If adding new settings sections, add them BEFORE this one. -->
|
|
||||||
{#if activeSection === 'flatpak' && isFlatpak}
|
{#if activeSection === 'flatpak' && isFlatpak}
|
||||||
<section class="section flatpak-section" id="flatpak">
|
<section class="section flatpak-section" id="flatpak">
|
||||||
<h3 class="section-title">Flatpak Sandbox</h3>
|
<h3 class="section-title">Flatpak Sandbox</h3>
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
"backTo": "Zurück zu",
|
"backTo": "Zurück zu",
|
||||||
"updates": "Updates",
|
"updates": "Updates",
|
||||||
"flatpak": "Flatpak",
|
"flatpak": "Flatpak",
|
||||||
|
"snap": "Snap",
|
||||||
"purchases": "Einkäufe"
|
"purchases": "Einkäufe"
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
"backTo": "Back to",
|
"backTo": "Back to",
|
||||||
"updates": "Updates",
|
"updates": "Updates",
|
||||||
"flatpak": "Flatpak",
|
"flatpak": "Flatpak",
|
||||||
|
"snap": "Snap",
|
||||||
"purchases": "Purchases"
|
"purchases": "Purchases"
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
"backTo": "Volver a",
|
"backTo": "Volver a",
|
||||||
"updates": "Actualizaciones",
|
"updates": "Actualizaciones",
|
||||||
"flatpak": "Flatpak",
|
"flatpak": "Flatpak",
|
||||||
|
"snap": "Snap",
|
||||||
"purchases": "Compras"
|
"purchases": "Compras"
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
"backTo": "Retour à",
|
"backTo": "Retour à",
|
||||||
"updates": "Mises à jour",
|
"updates": "Mises à jour",
|
||||||
"flatpak": "Flatpak",
|
"flatpak": "Flatpak",
|
||||||
|
"snap": "Snap",
|
||||||
"purchases": "Achats"
|
"purchases": "Achats"
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ function getDevFakeRelease(currentVersion: string): ReleaseInfo | null {
|
|||||||
export interface LaunchUpdateDecision {
|
export interface LaunchUpdateDecision {
|
||||||
currentVersion: string;
|
currentVersion: string;
|
||||||
showFlatpakWelcome: boolean;
|
showFlatpakWelcome: boolean;
|
||||||
|
showSnapWelcome: boolean;
|
||||||
whatsNewRelease: ReleaseInfo | null;
|
whatsNewRelease: ReleaseInfo | null;
|
||||||
updateRelease: 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.
|
* Decide which modals should be shown on launch.
|
||||||
*
|
*
|
||||||
@@ -186,6 +212,7 @@ export async function decideLaunchModals(): Promise<LaunchUpdateDecision> {
|
|||||||
return {
|
return {
|
||||||
currentVersion: getCurrentVersion(),
|
currentVersion: getCurrentVersion(),
|
||||||
showFlatpakWelcome: false,
|
showFlatpakWelcome: false,
|
||||||
|
showSnapWelcome: false,
|
||||||
whatsNewRelease: null,
|
whatsNewRelease: null,
|
||||||
updateRelease: null,
|
updateRelease: null,
|
||||||
};
|
};
|
||||||
@@ -203,17 +230,21 @@ export async function decideLaunchModals(): Promise<LaunchUpdateDecision> {
|
|||||||
const currentVersion = getCurrentVersion();
|
const currentVersion = getCurrentVersion();
|
||||||
|
|
||||||
if (getSessionFlag(SESSION_CHECK_DONE_KEY)) {
|
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);
|
setSessionFlag(SESSION_CHECK_DONE_KEY);
|
||||||
|
|
||||||
// Collect all modals that need to be shown
|
// Collect all modals that need to be shown
|
||||||
let showFlatpakWelcome = false;
|
let showFlatpakWelcome = false;
|
||||||
|
let showSnapWelcome = false;
|
||||||
let whatsNewRelease: ReleaseInfo | null = null;
|
let whatsNewRelease: ReleaseInfo | null = null;
|
||||||
let updateRelease: 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();
|
showFlatpakWelcome = await shouldShowFlatpakWelcome();
|
||||||
|
if (!showFlatpakWelcome) {
|
||||||
|
showSnapWelcome = await shouldShowSnapWelcome();
|
||||||
|
}
|
||||||
|
|
||||||
// Phase 2: What's New for current version
|
// Phase 2: What's New for current version
|
||||||
if (prefs.showWhatsNewOnLaunch && currentVersion) {
|
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> {
|
export async function openReleasePageAndAcknowledge(release: ReleaseInfo): Promise<void> {
|
||||||
|
|||||||
+22
-3
@@ -375,6 +375,7 @@
|
|||||||
import UpdateReminderModal from '$lib/components/updates/UpdateReminderModal.svelte';
|
import UpdateReminderModal from '$lib/components/updates/UpdateReminderModal.svelte';
|
||||||
import WhatsNewModal from '$lib/components/updates/WhatsNewModal.svelte';
|
import WhatsNewModal from '$lib/components/updates/WhatsNewModal.svelte';
|
||||||
import FlatpakWelcomeModal from '$lib/components/updates/FlatpakWelcomeModal.svelte';
|
import FlatpakWelcomeModal from '$lib/components/updates/FlatpakWelcomeModal.svelte';
|
||||||
|
import SnapWelcomeModal from '$lib/components/updates/SnapWelcomeModal.svelte';
|
||||||
import KeyboardShortcutsModal from '$lib/components/KeyboardShortcutsModal.svelte';
|
import KeyboardShortcutsModal from '$lib/components/KeyboardShortcutsModal.svelte';
|
||||||
import KeybindingsSettings from '$lib/components/KeybindingsSettings.svelte';
|
import KeybindingsSettings from '$lib/components/KeybindingsSettings.svelte';
|
||||||
import LinkResolverModal from '$lib/components/LinkResolverModal.svelte';
|
import LinkResolverModal from '$lib/components/LinkResolverModal.svelte';
|
||||||
@@ -386,6 +387,7 @@
|
|||||||
disableUpdateChecks,
|
disableUpdateChecks,
|
||||||
ignoreReleaseVersion,
|
ignoreReleaseVersion,
|
||||||
markFlatpakWelcomeShown,
|
markFlatpakWelcomeShown,
|
||||||
|
markSnapWelcomeShown,
|
||||||
openReleasePageAndAcknowledge,
|
openReleasePageAndAcknowledge,
|
||||||
resetLaunchFlow,
|
resetLaunchFlow,
|
||||||
} from '$lib/services/updatesService';
|
} from '$lib/services/updatesService';
|
||||||
@@ -437,6 +439,7 @@
|
|||||||
let isReminderModalOpen = $state(false);
|
let isReminderModalOpen = $state(false);
|
||||||
let isWhatsNewModalOpen = $state(false);
|
let isWhatsNewModalOpen = $state(false);
|
||||||
let isFlatpakWelcomeOpen = $state(false);
|
let isFlatpakWelcomeOpen = $state(false);
|
||||||
|
let isSnapWelcomeOpen = $state(false);
|
||||||
let updatesLaunchTriggered = $state(false);
|
let updatesLaunchTriggered = $state(false);
|
||||||
let sessionReady = $state(false);
|
let sessionReady = $state(false);
|
||||||
|
|
||||||
@@ -511,17 +514,21 @@
|
|||||||
updatesCurrentVersion = decision.currentVersion;
|
updatesCurrentVersion = decision.currentVersion;
|
||||||
|
|
||||||
// Store pending modals for sequential display
|
// Store pending modals for sequential display
|
||||||
// Order: Flatpak → What's new → Update available
|
// Order: Flatpak/Snap → What's new → Update available
|
||||||
pendingWhatsNewRelease = decision.whatsNewRelease;
|
pendingWhatsNewRelease = decision.whatsNewRelease;
|
||||||
pendingUpdateRelease = decision.updateRelease;
|
pendingUpdateRelease = decision.updateRelease;
|
||||||
|
|
||||||
// Show first modal in queue (Flatpak has highest priority)
|
// Show first modal in queue (sandbox welcome has highest priority)
|
||||||
if (decision.showFlatpakWelcome) {
|
if (decision.showFlatpakWelcome) {
|
||||||
isFlatpakWelcomeOpen = true;
|
isFlatpakWelcomeOpen = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (decision.showSnapWelcome) {
|
||||||
|
isSnapWelcomeOpen = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// No Flatpak modal, try What's New
|
// No sandbox modal, try What's New
|
||||||
showNextModalInQueue();
|
showNextModalInQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -581,6 +588,13 @@
|
|||||||
showNextModalInQueue();
|
showNextModalInQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleSnapWelcomeClose(): void {
|
||||||
|
isSnapWelcomeOpen = false;
|
||||||
|
void markSnapWelcomeShown();
|
||||||
|
// Show next modal in queue
|
||||||
|
showNextModalInQueue();
|
||||||
|
}
|
||||||
|
|
||||||
function handleWhatsNewClose(): void {
|
function handleWhatsNewClose(): void {
|
||||||
isWhatsNewModalOpen = false;
|
isWhatsNewModalOpen = false;
|
||||||
whatsNewRelease = null;
|
whatsNewRelease = null;
|
||||||
@@ -4628,6 +4642,11 @@
|
|||||||
onClose={handleFlatpakWelcomeClose}
|
onClose={handleFlatpakWelcomeClose}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<SnapWelcomeModal
|
||||||
|
isOpen={isSnapWelcomeOpen}
|
||||||
|
onClose={handleSnapWelcomeClose}
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- Track Info Modal -->
|
<!-- Track Info Modal -->
|
||||||
<TrackInfoModal
|
<TrackInfoModal
|
||||||
isOpen={isTrackInfoOpen}
|
isOpen={isTrackInfoOpen}
|
||||||
|
|||||||
Reference in new issue
Block a user