refactor(web): shared errorMessage and createAsyncAction primitives #125
No reviewers
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/deckle!125
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "afk/117"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #117
What
Two mechanical extractions in the web editor (Solid.js), per the issue's agent brief:
One canonical
errorMessage. The helper now lives in a new leaf moduleweb/src/asyncAction.ts; the declaration instores/mutations.tsand the five verbatim local copies (MediaModule, PageSettings, LinkPicker, TreeBrowser, MediaPicker) are gone. Every consumer — includingstores/mutations.ts,stores/pageEditor.ts, and PagePane — imports from the new home, so UI components no longer import error formatting from the mutations store.grep -rn "function errorMessage" web/srcyields exactly one hit.createAsyncAction(fn)wraps the dialog submit dance once:{ busy, error, setError, run }, whererunis a no-op while busy, clears the previous error, awaitsfn, recordserrorMessage(err)on rejection, and always clears busy. All seven MediaModule dialogs adopt it and drop their per-dialogbusy/errorsignals and try/catch shells (net −105 lines in MediaModule):setErrorwithout flipping busy or calling the API;blockedoutcome and takes onlybusy/run.Dialog UI, data-testids, disabled conditions, error rendering, and parent-facing props are unchanged. The one behavioral tightening — sanctioned by the issue — is that Enter-to-submit during a pending call is now a no-op, matching what the busy-disabled buttons already enforce.
Tests
web/src/test/asyncAction.test.ts(vitest): busy lifecycle around a hand-settled promise, Error and non-Error rejection recording, error cleared before the next run's fn executes, re-entrantrunwhile busy invokes fn exactly once,setErrorpre-flight + clear on success, argument forwarding. TheerrorMessagecases moved here frommutations.test.ts.tsc --noEmitclean, web suite 47 files / 954 tests green,vite buildOK,go vet ./...+go test ./...green,go build ./cmd/harnessOK.scaffold/site/AGENTS.mdis untouched on purpose: this changes web-editor internals only, nothing the scaffolded manual documents.🤖 Generated with Claude Code
Five components re-declared the mutations store's errorMessage helper verbatim, and every MediaModule dialog hand-rolled the same busy/error/ try-catch submit shell around one API call. Both now live once in web/src/asyncAction.ts, a leaf module with no store dependencies: errorMessage moves there from stores/mutations.ts (every former call site imports it), and the new createAsyncAction(fn) primitive exposes { busy, error, setError, run } — run is a no-op while busy, clears the previous error, records errorMessage(err) on rejection, and always clears busy. All seven MediaModule dialogs adopt it; NewFolderDialog keeps its pre-flight slug check via setError, and DeleteFileDialog keeps its structured blocked outcome, taking only busy/run. Dialog UI, testids, disabled conditions, and parent callbacks are unchanged; the one behavioral tightening is that Enter-to-submit during a pending call is now a no-op, matching what the busy-disabled buttons already enforce. Closes #117 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>Landing audit — PASS
Verification signal relied on: Forgejo Actions
ci / build-and-test— success (6m19s, run 115). Not re-run. The workflow is unconditional and frontend-first (npm ci && npm run build && npm test, thengo vet/test/build), so this web-only diff was fully exercised by the green light.Checks performed
refactor(web): shared errorMessage and createAsyncAction primitivesis Conventional Commits. Head isafk/117; body carries a workingCloses #117.main(merge-tree clean).grep -rn "function errorMessage" web/src→ exactly 1 hit (web/src/asyncAction.ts:14); all six former sites import it, and PagePane/pageEditor no longer pull error formatting from the mutations store.createAsyncActionunit-tested inweb/src/test/asyncAction.test.ts: busy lifecycle over a hand-settled deferred,Errorand non-Errorrejection recording, error cleared before the next run's fn executes, re-entrantruninvoking fn exactly once,setErrorpre-flight, argument forwarding.busy/errorsignal or try/catch shell. The twocreateSignalsurvivors (gridError,uploading, MediaModule.tsx:962-963) are grid-level upload state, correctly out of scope.data-testids, disabled conditions and parent-facing props unchanged.NewFolderDialogkeeps its slug pre-flight viasetErrorwithout flipping busy or calling the API; a later successfulrunclears it — matches the pre-refactor sequencing.DeleteFileDialogtakes onlybusy/run, keeping its structuredblockedoutcome. Its unrendered error channel now swallows what was previously an unhandled rejection — a small improvement, and documented in-line.Non-blocking nit:
MediaModule.tsx:34places the../asyncActionimport after../pagemanage, breaking the alphabetical ordering the other five converted files follow. Cosmetic; no lint enforces it.Verdict: PASS — awaiting the human's merge go-ahead.