refactor(player): E.23 — extract engine state + seek debounce cluster (#587)

Two thematic cuts in one PR:

  - `src/store/engineState.ts` — `isAudioPaused` (warm-vs-cold-resume
    decision flag) + `playGeneration` (monotonically bumped guard
    counter used by long-running async callbacks to detect concurrent
    playTrack and bail). Get/set accessors + `bumpPlayGeneration()`
    that returns the new value (mirrors the original `++playGeneration`
    pattern).
  - `src/store/seekDebounce.ts` — seek-slider debounce timer behind
    `armSeekDebounce(delayMs, onFire)` / `clearSeekDebounce()` /
    `isSeekDebouncePending()`. Collapses the recurring inline
    `if (seekDebounce) { clearTimeout(...); seekDebounce = null; }`
    triple into single calls.

Sed-driven bulk rewrite for the >60 direct-access sites preserved
semantics verbatim (one JSDoc reference to `isAudioPaused` kept as
comment text). 14 tests across the two modules.

playerStore 2869 → 2867 LOC net (small delta — the imports are bigger
than the savings, but the mutables are no longer reachable from
outside their owning modules).
This commit is contained in:
Frank Stellmacher
2026-05-12 17:14:37 +02:00
committed by GitHub
parent 0eb084c5f8
commit 6355946610
5 changed files with 257 additions and 68 deletions
+54
View File
@@ -0,0 +1,54 @@
import { afterEach, describe, expect, it } from 'vitest';
import {
_resetEngineStateForTest,
bumpPlayGeneration,
getIsAudioPaused,
getPlayGeneration,
setIsAudioPaused,
} from './engineState';
afterEach(() => {
_resetEngineStateForTest();
});
describe('isAudioPaused', () => {
it('starts false', () => {
expect(getIsAudioPaused()).toBe(false);
});
it('round-trips through get/set', () => {
setIsAudioPaused(true);
expect(getIsAudioPaused()).toBe(true);
setIsAudioPaused(false);
expect(getIsAudioPaused()).toBe(false);
});
});
describe('playGeneration', () => {
it('starts at 0', () => {
expect(getPlayGeneration()).toBe(0);
});
it('bumpPlayGeneration increments + returns the new value', () => {
expect(bumpPlayGeneration()).toBe(1);
expect(bumpPlayGeneration()).toBe(2);
expect(getPlayGeneration()).toBe(2);
});
it('captures a snapshot that a later bump invalidates', () => {
const snap = bumpPlayGeneration();
bumpPlayGeneration();
expect(getPlayGeneration()).not.toBe(snap);
});
});
describe('_resetEngineStateForTest', () => {
it('resets both fields', () => {
setIsAudioPaused(true);
bumpPlayGeneration();
bumpPlayGeneration();
_resetEngineStateForTest();
expect(getIsAudioPaused()).toBe(false);
expect(getPlayGeneration()).toBe(0);
});
});