refactor(player): E.36 — extract schedule-pause/resume factory (#600)

Four scheduled-timer actions extracted into `createScheduleActions`
(`src/store/scheduleActions.ts`):

- `schedulePauseIn` / `scheduleResumeIn` — clamp the delay to ≥ 500 ms,
  store absolute target + start timestamps (for the countdown UI),
  and arm a single-shot timer.
- `clearScheduledPause` / `clearScheduledResume` — cancel the timer
  and blank the timestamps.

Pure code-move. playerStore.ts: 1030 → 991 LOC (−39, first sub-1000
state since Phase E started).
This commit is contained in:
Frank Stellmacher
2026-05-12 21:06:48 +02:00
committed by GitHub
parent 1e2c651196
commit 55b2b12fa5
2 changed files with 65 additions and 41 deletions
+2 -41
View File
@@ -45,10 +45,6 @@ import {
} from './loudnessGainCache'; } from './loudnessGainCache';
import { import {
clearAllPlaybackScheduleTimers, clearAllPlaybackScheduleTimers,
clearScheduledPauseTimers,
clearScheduledResumeTimers,
schedulePauseTimer,
scheduleResumeTimer,
} from './scheduleTimers'; } from './scheduleTimers';
import { invokeAudioUpdateReplayGainDeduped } from './normalizationIpcDedupe'; import { invokeAudioUpdateReplayGainDeduped } from './normalizationIpcDedupe';
import { touchHotCacheOnPlayback } from './hotCacheTouch'; import { touchHotCacheOnPlayback } from './hotCacheTouch';
@@ -154,6 +150,7 @@ export type { PlayerState, Track };
import { createLastfmActions } from './lastfmActions'; import { createLastfmActions } from './lastfmActions';
import { createMiscActions } from './miscActions'; import { createMiscActions } from './miscActions';
import { createQueueMutationActions } from './queueMutationActions'; import { createQueueMutationActions } from './queueMutationActions';
import { createScheduleActions } from './scheduleActions';
import { createTransportLightActions } from './transportLightActions'; import { createTransportLightActions } from './transportLightActions';
import { createUiStateActions } from './uiStateActions'; import { createUiStateActions } from './uiStateActions';
import { createUndoRedoActions } from './undoRedoActions'; import { createUndoRedoActions } from './undoRedoActions';
@@ -211,6 +208,7 @@ export const usePlayerStore = create<PlayerState>()(
...createTransportLightActions(set, get), ...createTransportLightActions(set, get),
...createUndoRedoActions(set, get), ...createUndoRedoActions(set, get),
...createMiscActions(set, get), ...createMiscActions(set, get),
...createScheduleActions(set, get),
// ── playTrack ──────────────────────────────────────────────────────────── // ── playTrack ────────────────────────────────────────────────────────────
playTrack: (track, queue, manual = true, _orbitConfirmed = false, targetQueueIndex) => { playTrack: (track, queue, manual = true, _orbitConfirmed = false, targetQueueIndex) => {
@@ -658,43 +656,6 @@ export const usePlayerStore = create<PlayerState>()(
} }
}, },
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 / previous ──────────────────────────────────────────────────────
next: (manual = true) => { next: (manual = true) => {
const { queue, queueIndex, repeatMode, currentTrack } = get(); const { queue, queueIndex, repeatMode, currentTrack } = get();
+63
View File
@@ -0,0 +1,63 @@
import type { PlayerState } from './playerStoreTypes';
import {
clearScheduledPauseTimers,
clearScheduledResumeTimers,
schedulePauseTimer,
scheduleResumeTimer,
} from './scheduleTimers';
type SetState = (
partial: Partial<PlayerState> | ((state: PlayerState) => Partial<PlayerState>),
) => 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();
});
},
};
}