diff --git a/src/store/playerStore.ts b/src/store/playerStore.ts index 06641f9f..980deffa 100644 --- a/src/store/playerStore.ts +++ b/src/store/playerStore.ts @@ -45,10 +45,6 @@ import { } from './loudnessGainCache'; import { clearAllPlaybackScheduleTimers, - clearScheduledPauseTimers, - clearScheduledResumeTimers, - schedulePauseTimer, - scheduleResumeTimer, } from './scheduleTimers'; import { invokeAudioUpdateReplayGainDeduped } from './normalizationIpcDedupe'; import { touchHotCacheOnPlayback } from './hotCacheTouch'; @@ -154,6 +150,7 @@ export type { PlayerState, Track }; import { createLastfmActions } from './lastfmActions'; import { createMiscActions } from './miscActions'; import { createQueueMutationActions } from './queueMutationActions'; +import { createScheduleActions } from './scheduleActions'; import { createTransportLightActions } from './transportLightActions'; import { createUiStateActions } from './uiStateActions'; import { createUndoRedoActions } from './undoRedoActions'; @@ -211,6 +208,7 @@ export const usePlayerStore = create()( ...createTransportLightActions(set, get), ...createUndoRedoActions(set, get), ...createMiscActions(set, get), + ...createScheduleActions(set, get), // ── playTrack ──────────────────────────────────────────────────────────── playTrack: (track, queue, manual = true, _orbitConfirmed = false, targetQueueIndex) => { @@ -658,43 +656,6 @@ export const usePlayerStore = create()( } }, - clearScheduledPause: () => { - clearScheduledPauseTimers(); - set({ scheduledPauseAtMs: null, scheduledPauseStartMs: null }); - }, - - clearScheduledResume: () => { - clearScheduledResumeTimers(); - set({ scheduledResumeAtMs: null, scheduledResumeStartMs: null }); - }, - - schedulePauseIn: (seconds) => { - const s = get(); - if (!s.isPlaying) return; - const delayMs = Math.max(500, Math.round(Number(seconds) * 1000)); - const startedAt = Date.now(); - const at = startedAt + delayMs; - set({ scheduledPauseAtMs: at, scheduledPauseStartMs: startedAt }); - schedulePauseTimer(delayMs, () => { - set({ scheduledPauseAtMs: null, scheduledPauseStartMs: null }); - get().pause(); - }); - }, - - scheduleResumeIn: (seconds) => { - const s = get(); - if (s.isPlaying) return; - if (!s.currentTrack && !s.currentRadio) return; - const delayMs = Math.max(500, Math.round(Number(seconds) * 1000)); - const startedAt = Date.now(); - const at = startedAt + delayMs; - set({ scheduledResumeAtMs: at, scheduledResumeStartMs: startedAt }); - scheduleResumeTimer(delayMs, () => { - set({ scheduledResumeAtMs: null, scheduledResumeStartMs: null }); - get().resume(); - }); - }, - // ── next / previous ────────────────────────────────────────────────────── next: (manual = true) => { const { queue, queueIndex, repeatMode, currentTrack } = get(); diff --git a/src/store/scheduleActions.ts b/src/store/scheduleActions.ts new file mode 100644 index 00000000..0cf6638b --- /dev/null +++ b/src/store/scheduleActions.ts @@ -0,0 +1,63 @@ +import type { PlayerState } from './playerStoreTypes'; +import { + clearScheduledPauseTimers, + clearScheduledResumeTimers, + schedulePauseTimer, + scheduleResumeTimer, +} from './scheduleTimers'; + +type SetState = ( + partial: Partial | ((state: PlayerState) => Partial), +) => void; +type GetState = () => PlayerState; + +/** + * User-facing scheduled-pause / scheduled-resume actions. Each setter + * clamps the delay to ≥ 500 ms, stores the absolute target + start + * timestamps in the store (so countdown UI can render a progress arc), + * and arms a single-shot timer in `scheduleTimers.ts`. The matching + * `clearScheduled*` actions cancel the timer and blank the timestamps. + */ +export function createScheduleActions(set: SetState, get: GetState): Pick< + PlayerState, + 'clearScheduledPause' | 'clearScheduledResume' | 'schedulePauseIn' | 'scheduleResumeIn' +> { + return { + clearScheduledPause: () => { + clearScheduledPauseTimers(); + set({ scheduledPauseAtMs: null, scheduledPauseStartMs: null }); + }, + + clearScheduledResume: () => { + clearScheduledResumeTimers(); + set({ scheduledResumeAtMs: null, scheduledResumeStartMs: null }); + }, + + schedulePauseIn: (seconds) => { + const s = get(); + if (!s.isPlaying) return; + const delayMs = Math.max(500, Math.round(Number(seconds) * 1000)); + const startedAt = Date.now(); + const at = startedAt + delayMs; + set({ scheduledPauseAtMs: at, scheduledPauseStartMs: startedAt }); + schedulePauseTimer(delayMs, () => { + set({ scheduledPauseAtMs: null, scheduledPauseStartMs: null }); + get().pause(); + }); + }, + + scheduleResumeIn: (seconds) => { + const s = get(); + if (s.isPlaying) return; + if (!s.currentTrack && !s.currentRadio) return; + const delayMs = Math.max(500, Math.round(Number(seconds) * 1000)); + const startedAt = Date.now(); + const at = startedAt + delayMs; + set({ scheduledResumeAtMs: at, scheduledResumeStartMs: startedAt }); + scheduleResumeTimer(delayMs, () => { + set({ scheduledResumeAtMs: null, scheduledResumeStartMs: null }); + get().resume(); + }); + }, + }; +}