refactor: move SaveState into debounced-save.ts, delete action-state.ts (RD-14)

RD-12 and RD-13 moved both ActionState consumers into their machines. This
left ActionState with zero users, so this ticket deletes it outright.

SaveState is different. It has two four-way consumers (brief.page.ts and
org-template.page.ts) that still switch on all four cases, so it survives.
It moves into debounced-save.ts, next to createDebouncedSave, the only
function that sets it. Both store imports merge into the existing
debounced-save import line. The doc comments that named ActionState are
reworded, since the type no longer exists. No store's public saveState
signature changes, so brief/ui/ needs no edit.

This completes the phase. Two encodings survive: RemoteData for fetched
data, and each machine's own state union. SaveState stays as an explicitly
justified third encoding, for a separate concern (debounced autosave status)
that the other two do not cover.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
eho
2026-09-04 18:45:04 +02:00
co-authored by Claude Sonnet 5
parent c599fee8e2
commit a8c7a573fc
6 changed files with 135 additions and 15 deletions
@@ -1,9 +0,0 @@
/** Transient state of a one-shot action (submit/approve/publish/reset/…): one tagged
union instead of a busy boolean + a nullable error sitting side by side. Shared by the
editor stores (WP-31). */
export type ActionState = { tag: 'Idle' } | { tag: 'Busy' } | { tag: 'Failed'; error: string };
/** Debounced-autosave indicator, shown in a small status line near a toolbar — a separate
concern from ActionState (a stale autosave error doesn't block submit/approve), but
tag-aligned with it for one consistent idiom. */
export type SaveState = { tag: 'Idle' } | { tag: 'Saving' } | { tag: 'Saved' } | { tag: 'Error' };
@@ -1,3 +1,8 @@
/** Debounced-autosave indicator, shown in a small status line near a toolbar — a separate
concern from a store's one-shot action lifecycle (a stale autosave error doesn't block
submit/approve), but tag-aligned with it for one consistent idiom. */
export type SaveState = { tag: 'Idle' } | { tag: 'Saving' } | { tag: 'Saved' } | { tag: 'Error' };
export interface DebouncedSave {
/** (Re)arm the debounce timer; no-op when `canSave()` is false. */
schedule(): void;
@@ -13,7 +18,7 @@ export interface DebouncedSave {
/**
* The debounced-autosave timer shared by the editor stores (WP-31). It owns ONLY the timer
* bookkeeping; the actual write + save-state transitions live in the caller's `flush`
* (store-specific — it touches that store's SaveState/ActionState + adapter). The handle is
* (store-specific — it touches that store's SaveState + adapter). The handle is
* nulled the moment it fires, so `hasPendingSave()` means "a write is still owed". Integrates
* with the `PendingSave` seam (pending-saves.ts): a store delegates hasPendingSave/flushPending
* here so the CanDeactivate guard / beforeunload handler can flush a pending edit.