zustand stores
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
import { create } from 'zustand'
|
||||
|
||||
/**
|
||||
* Per-session player state - things that should reset on a new playback
|
||||
* session and shouldn't pollute the persisted preferences store.
|
||||
*
|
||||
* Examples:
|
||||
* - A-B loop markers (per item)
|
||||
* - Theater / zen mode toggle (transient)
|
||||
* - Per-session subtitle / audio delay overrides on top of the persisted
|
||||
* defaults from `usePreferencesStore`
|
||||
* - Secondary subtitle index
|
||||
* - Picture filters during this session (overrides persisted defaults)
|
||||
*
|
||||
* Anything you'd want to persist across episodes / app restarts belongs
|
||||
* in `usePreferencesStore`, not here.
|
||||
*/
|
||||
export interface PlayerRuntimeState {
|
||||
// A-B loop
|
||||
loopA: number | null
|
||||
loopB: number | null
|
||||
// Subtitle + audio offsets in ms. These add to the persisted default
|
||||
// from settings; combined effective offset = settings + runtime.
|
||||
subtitleOffsetMs: number
|
||||
audioOffsetMs: number
|
||||
// Secondary subtitle stream index (Jellyfin stream Index, not array index)
|
||||
secondarySubtitleIndex: number | null
|
||||
// Theater / zen mode
|
||||
theaterMode: boolean
|
||||
// Picture filters - if a value is null we fall back to the persisted pref.
|
||||
// Overrides for *this* session only.
|
||||
brightnessOverride: number | null
|
||||
contrastOverride: number | null
|
||||
saturationOverride: number | null
|
||||
// Auto-recap dismissal flag - true once the user has dismissed (or
|
||||
// skipped) the recap card for the current item, so it doesn't reappear
|
||||
// if playback restarts.
|
||||
recapDismissed: boolean
|
||||
|
||||
setLoopA(t: number | null): void
|
||||
setLoopB(t: number | null): void
|
||||
clearLoop(): void
|
||||
setSubtitleOffsetMs(ms: number): void
|
||||
bumpSubtitleOffsetMs(deltaMs: number): void
|
||||
setAudioOffsetMs(ms: number): void
|
||||
bumpAudioOffsetMs(deltaMs: number): void
|
||||
setSecondarySubtitleIndex(i: number | null): void
|
||||
setTheaterMode(on: boolean): void
|
||||
setBrightnessOverride(v: number | null): void
|
||||
setContrastOverride(v: number | null): void
|
||||
setSaturationOverride(v: number | null): void
|
||||
setRecapDismissed(v: boolean): void
|
||||
/** Reset everything except theaterMode (which is window-scoped, not item-scoped). */
|
||||
resetForNewItem(): void
|
||||
}
|
||||
|
||||
export const usePlayerRuntimeStore = create<PlayerRuntimeState>(set => ({
|
||||
loopA: null,
|
||||
loopB: null,
|
||||
subtitleOffsetMs: 0,
|
||||
audioOffsetMs: 0,
|
||||
secondarySubtitleIndex: null,
|
||||
theaterMode: false,
|
||||
brightnessOverride: null,
|
||||
contrastOverride: null,
|
||||
saturationOverride: null,
|
||||
recapDismissed: false,
|
||||
|
||||
setLoopA: t => set({ loopA: t }),
|
||||
setLoopB: t => set({ loopB: t }),
|
||||
clearLoop: () => set({ loopA: null, loopB: null }),
|
||||
setSubtitleOffsetMs: ms => set({ subtitleOffsetMs: ms }),
|
||||
bumpSubtitleOffsetMs: deltaMs => set(s => ({ subtitleOffsetMs: s.subtitleOffsetMs + deltaMs })),
|
||||
setAudioOffsetMs: ms => set({ audioOffsetMs: ms }),
|
||||
bumpAudioOffsetMs: deltaMs => set(s => ({ audioOffsetMs: s.audioOffsetMs + deltaMs })),
|
||||
setSecondarySubtitleIndex: i => set({ secondarySubtitleIndex: i }),
|
||||
setTheaterMode: on => set({ theaterMode: on }),
|
||||
setBrightnessOverride: v => set({ brightnessOverride: v }),
|
||||
setContrastOverride: v => set({ contrastOverride: v }),
|
||||
setSaturationOverride: v => set({ saturationOverride: v }),
|
||||
setRecapDismissed: v => set({ recapDismissed: v }),
|
||||
resetForNewItem: () =>
|
||||
set({
|
||||
loopA: null,
|
||||
loopB: null,
|
||||
subtitleOffsetMs: 0,
|
||||
audioOffsetMs: 0,
|
||||
secondarySubtitleIndex: null,
|
||||
brightnessOverride: null,
|
||||
contrastOverride: null,
|
||||
saturationOverride: null,
|
||||
recapDismissed: false,
|
||||
}),
|
||||
}))
|
||||
Reference in New Issue
Block a user