Files
jellytau/src/lib/stores/nextEpisode.ts
T
dtourolle ad48d89dfe chore(format): run prettier over src/ and scripts/
Formatting was configured but never enforced: `bun run format:check`
reported 199 unformatted files and ran in no workflow and in no git hook,
so .prettierrc (printWidth 100, trailing commas) described an intention
rather than the tree.

This is the one-time sweep that makes the check gateable. Whitespace and
token-reflow only -- no behavioural change: `bun run check` reports 0
errors and all 1053 frontend tests pass before and after.

Kept out of every other commit on purpose. A 199-file diff mixed with
real changes is unreviewable, and the next commit turns format:check
into a hard CI gate so this cannot silently accumulate again.
2026-08-21 17:41:44 +02:00

112 lines
2.8 KiB
TypeScript

/**
* Next Episode Store (Display Only - Backend-First Architecture)
*
* This store reflects next episode popup state from the backend.
* The backend handles all countdown logic and decisions.
*
* The backend emits ShowNextEpisodePopup and CountdownTick events to update this store.
*
* TRACES: UR-023 | DR-026, DR-047, DR-048
*/
import { writable, derived } from "svelte/store";
import type { MediaItem } from "$lib/api/types";
export interface NextEpisodeState {
// Popup visibility
isVisible: boolean;
// Episode data
nextEpisode: MediaItem | null;
currentEpisode: MediaItem | null;
// Countdown state (managed by backend)
countdownSeconds: number;
initialCountdownSeconds: number;
// Settings
autoPlayEnabled: boolean;
}
function createNextEpisodeStore() {
const initialState: NextEpisodeState = {
isVisible: false,
nextEpisode: null,
currentEpisode: null,
countdownSeconds: 10,
initialCountdownSeconds: 10,
autoPlayEnabled: true,
};
const { subscribe, set, update } = writable<NextEpisodeState>(initialState);
/**
* Show the next episode popup (called by playerEvents when backend emits event)
*/
function showPopup(
currentEpisode: MediaItem,
nextEpisode: MediaItem,
countdownSeconds: number,
autoPlayEnabled: boolean,
): void {
update((s) => ({
...s,
isVisible: true,
currentEpisode,
nextEpisode,
countdownSeconds,
initialCountdownSeconds: countdownSeconds,
autoPlayEnabled,
}));
}
/**
* Update countdown value (called by playerEvents on CountdownTick event)
*/
function updateCountdown(remainingSeconds: number): void {
update((s) => ({
...s,
countdownSeconds: remainingSeconds,
}));
}
/**
* Hide the popup
*/
function hidePopup(): void {
update((s) => ({
...s,
isVisible: false,
}));
}
/**
* Reset store to initial state
*/
function reset(): void {
set(initialState);
}
return {
subscribe,
showPopup,
updateCountdown,
hidePopup,
reset,
};
}
export const nextEpisode = createNextEpisodeStore();
// Derived stores for convenient access
export const isNextEpisodePopupVisible = derived(nextEpisode, ($ne) => $ne.isVisible);
export const nextEpisodeItem = derived(nextEpisode, ($ne) => $ne.nextEpisode);
export const currentEpisodeItem = derived(nextEpisode, ($ne) => $ne.currentEpisode);
export const countdownSeconds = derived(nextEpisode, ($ne) => $ne.countdownSeconds);
export const initialCountdownSeconds = derived(nextEpisode, ($ne) => $ne.initialCountdownSeconds);
export const isAutoPlayEnabled = derived(nextEpisode, ($ne) => $ne.autoPlayEnabled);
export const isCountdownActive = derived(
nextEpisode,
($ne) => $ne.isVisible && $ne.countdownSeconds > 0,
);