Replace the remaining ~155 untyped invoke() calls across stores, services, components, and routes with the generated commands.* wrappers from $lib/api/bindings, so every IPC call is compile-time-checked against the command signatures. - Register repository_get_subtitle_url and repository_get_video_download_url in specta_builder() and the invoke_handler; regenerate bindings.ts. - Source duplicated wire types (AutoplaySettings, CacheConfig, Session, ConnectivityStatus, audio/video settings, etc.) from bindings. - Fix two bugs surfaced by the typed wrappers: - VideoDownloadButton passed an un-awaited Promise as the stream URL. - setAutoplaySettings omitted the required userId argument. - Update unit tests asserting the old invoke(name, args) shape. - Remove the five param-naming guard tests; the compiler and codegen now enforce what they checked. svelte-check: 0 errors. vitest: green. cargo test --lib: green.
77 lines
2.0 KiB
TypeScript
77 lines
2.0 KiB
TypeScript
/**
|
|
* Sleep Timer Store (Backend-First Architecture)
|
|
*
|
|
* This store reflects sleep timer state from the backend.
|
|
* All logic is in the Rust backend (PlayerController).
|
|
*
|
|
* The backend emits SleepTimerChanged events to update this store.
|
|
* Components call store methods which invoke the backend API.
|
|
*
|
|
* TRACES: UR-026 | DR-029
|
|
*/
|
|
|
|
import { writable, derived } from "svelte/store";
|
|
import { commands } from "$lib/api/bindings";
|
|
|
|
export type SleepTimerMode =
|
|
| { kind: "off" }
|
|
| { kind: "time"; endTime: number }
|
|
| { kind: "endOfTrack" }
|
|
| { kind: "episodes"; remaining: number };
|
|
|
|
interface SleepTimerState {
|
|
mode: SleepTimerMode;
|
|
remainingSeconds: number;
|
|
}
|
|
|
|
function createSleepTimerStore() {
|
|
const initialState: SleepTimerState = {
|
|
mode: { kind: "off" },
|
|
remainingSeconds: 0,
|
|
};
|
|
|
|
const { subscribe, set } = writable<SleepTimerState>(initialState);
|
|
|
|
return {
|
|
subscribe,
|
|
set, // Updated by playerEvents.ts when backend emits SleepTimerChanged event
|
|
|
|
// Methods that invoke the backend API
|
|
async setTimeTimer(minutes: number): Promise<void> {
|
|
const endTime = Date.now() + minutes * 60 * 1000;
|
|
await commands.playerSetSleepTimer({ kind: "time", endTime });
|
|
},
|
|
|
|
async setEndOfTrackTimer(): Promise<void> {
|
|
await commands.playerSetSleepTimer({ kind: "endOfTrack" });
|
|
},
|
|
|
|
async setEpisodesTimer(count: number): Promise<void> {
|
|
await commands.playerSetSleepTimer({ kind: "episodes", remaining: count });
|
|
},
|
|
|
|
async cancel(): Promise<void> {
|
|
await commands.playerCancelSleepTimer();
|
|
},
|
|
};
|
|
}
|
|
|
|
export const sleepTimer = createSleepTimerStore();
|
|
|
|
// Derived stores for convenient access
|
|
export const sleepTimerMode = derived(sleepTimer, ($s) => $s.mode);
|
|
|
|
export const sleepTimerActive = derived(
|
|
sleepTimer,
|
|
($s) => $s.mode.kind !== "off"
|
|
);
|
|
|
|
export const sleepTimerRemainingSeconds = derived(
|
|
sleepTimer,
|
|
($s) => $s.remainingSeconds
|
|
);
|
|
|
|
export const sleepTimerRemainingEpisodes = derived(sleepTimer, ($s) =>
|
|
$s.mode.kind === "episodes" ? $s.mode.remaining : 0
|
|
);
|