first run, updates, installer: a setup tour stands in front of a fresh install, releases arrive through an in-app updater with stable and nightly channels behind a read-only token, and the branded installer builds again
This commit is contained in:
1 parent
07810f7543
commit
cd2391198e
19 files changed
+2010
-15
No files matched your search
@@ -226,6 +226,13 @@ export const refreshAlbumArt = (albumId) => invoke("refresh_album_art", { albumI
|
||||
export const refreshAllAlbumArt = () => invoke("refresh_all_album_art");
|
||||
export const getSettings = () => invoke("get_settings");
|
||||
export const setSettings = (patch) => invoke("set_settings", patch);
|
||||
// The in-app updater (updater.rs): status, a check on the current channel,
|
||||
// settings (channel / autoCheck / endpointStable / endpointNightly), and the
|
||||
// install — which on Windows ends this process when it succeeds.
|
||||
export const updaterStatus = () => invoke("updater_status");
|
||||
export const updaterCheck = () => invoke("updater_check");
|
||||
export const updaterSet = (patch) => invoke("updater_set", patch);
|
||||
export const updaterInstall = () => invoke("updater_install");
|
||||
export const windowGlass = (on) => invoke("window_glass", { on });
|
||||
export const forgetCredentials = () => invoke("forget_credentials");
|
||||
export const pickFolder = () => invoke("pick_folder");
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// The first-run setup's one piece of shared state: whether the tour stands
|
||||
// in front of the app. App.svelte opens it when the settings arrive without
|
||||
// `setup_done`; Settings → Account opens it again on request; the tour
|
||||
// itself closes it and stamps the flag. The flag lives in config.json (per
|
||||
// install, so it travels with a portable data dir), not in the webview.
|
||||
import { setSettings } from "./api.js";
|
||||
|
||||
export const firstRun = $state({
|
||||
open: false,
|
||||
/** "boot" = a fresh install, "settings" = run again by hand. */
|
||||
reason: "boot",
|
||||
});
|
||||
|
||||
export function openFirstRun(reason = "settings") {
|
||||
firstRun.reason = reason;
|
||||
firstRun.open = true;
|
||||
}
|
||||
|
||||
/** Close the tour and remember that it ran. The flag write is best effort:
|
||||
* a failed save re-opens the tour on the next launch, which is the honest
|
||||
* outcome — nothing else in the app depends on it. */
|
||||
export async function finishFirstRun() {
|
||||
firstRun.open = false;
|
||||
try {
|
||||
await setSettings({ setupDone: true });
|
||||
} catch {
|
||||
// see above
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
notePlaybackGeneration,
|
||||
} from "./preview/previewSession.svelte.js";
|
||||
import { CROSSFADE_MS as PREVIEW_CROSSFADE_MS } from "./preview/service.js";
|
||||
import { firstRun, openFirstRun, finishFirstRun } from "./firstRun.svelte.js";
|
||||
|
||||
export const app = $state({
|
||||
connection: "offline", // offline | connecting | online
|
||||
@@ -7836,5 +7837,8 @@ if (import.meta.env.DEV) {
|
||||
followNewsToast, announceFollowNews,
|
||||
searchTabs, selectSearchTab, closeSearchTab, filesForGroup,
|
||||
downloads, network, sendDm, openChat,
|
||||
// The first-run tour: probes on a fresh data dir need to open it,
|
||||
// drive it, or clear it out of the way.
|
||||
firstRun, openFirstRun, finishFirstRun,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
// The updater's view of things: the backend's status snapshot (updater.rs),
|
||||
// download progress, and whether the toast was waved away. App.svelte
|
||||
// starts the watch once; Settings → Updates and UpdateToast read it.
|
||||
import * as api from "./api.js";
|
||||
|
||||
export const updater = $state({
|
||||
/** updater.rs `Status`, or null before the first read. */
|
||||
status: null,
|
||||
/** { received, total } while an install downloads. */
|
||||
progress: null,
|
||||
/** The toast for the current find was dismissed. */
|
||||
dismissed: false,
|
||||
error: "",
|
||||
});
|
||||
|
||||
export const updateAvailable = () => updater.status?.available ?? null;
|
||||
|
||||
export async function refreshUpdater() {
|
||||
try {
|
||||
updater.status = await api.updaterStatus();
|
||||
} catch (e) {
|
||||
updater.error = String(e);
|
||||
}
|
||||
}
|
||||
|
||||
export async function checkForUpdates() {
|
||||
updater.error = "";
|
||||
updater.dismissed = false;
|
||||
try {
|
||||
updater.status = await api.updaterCheck();
|
||||
} catch (e) {
|
||||
// The status carries last_error too; this is the same text, live.
|
||||
updater.error = String(e);
|
||||
await refreshUpdater();
|
||||
}
|
||||
}
|
||||
|
||||
export async function setUpdater(patch) {
|
||||
updater.error = "";
|
||||
try {
|
||||
updater.status = await api.updaterSet(patch);
|
||||
updater.dismissed = false;
|
||||
} catch (e) {
|
||||
updater.error = String(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** On success the app exits into the installer; on failure the error
|
||||
* lands here and in the status. */
|
||||
export async function installUpdate() {
|
||||
updater.error = "";
|
||||
try {
|
||||
await api.updaterInstall();
|
||||
} catch (e) {
|
||||
updater.error = String(e);
|
||||
await refreshUpdater();
|
||||
}
|
||||
}
|
||||
|
||||
export function dismissUpdate() {
|
||||
updater.dismissed = true;
|
||||
}
|
||||
|
||||
let started = false;
|
||||
export function startUpdaterWatch() {
|
||||
if (started) return;
|
||||
started = true;
|
||||
api.onEvent("updater:status", (s) => {
|
||||
const before = updater.status?.available?.version ?? null;
|
||||
updater.status = s;
|
||||
// A new find un-dismisses the toast; the same one stays dismissed.
|
||||
if ((s?.available?.version ?? null) !== before) updater.dismissed = false;
|
||||
if (!s?.installing) updater.progress = null;
|
||||
});
|
||||
api.onEvent("updater:progress", (p) => {
|
||||
updater.progress = p;
|
||||
});
|
||||
void refreshUpdater();
|
||||
}
|
||||
Reference in new issue
Block a user