Skip to content

refactor(session): extract useFileSession, fixing in-place state mutation - #27

Merged
killerwolf merged 1 commit into
mainfrom
claude/deepen-session-state
Sep 2, 2026
Merged

refactor(session): extract useFileSession, fixing in-place state mutation#27
killerwolf merged 1 commit into
mainfrom
claude/deepen-session-state

Conversation

@killerwolf

Copy link
Copy Markdown
Owner

Summary

  • App.tsx owned SessionState via useState, mutating arrays in place inside setState updaters (.push()/.pop() on the previous state's arrays after only a shallow copy) — a real correctness bug, not just style. FileViewer.tsx co-owned the "perform an action" flow with no single place owning what happens on delete.
  • Extract src/hooks/useFileSession.ts: a 5-method interface (startSession/keep/deleteFile/undo/reset) owning the whole session, doing immutable state transitions throughout.
  • deleteFile is the one async method — it awaits moveToTrash and only commits the "deleted" transition on success, so a failed trash operation can no longer be recorded as if it succeeded. Failure propagates as a rejection; FileViewer catches it and shows an alert instead of the previous fire-and-forget .catch(console.error).
  • The hook exposes a derived isComplete flag; App.tsx reacts to it via an effect instead of re-deriving "did we finish" from raw currentIndex/files.length itself (a Feature Envy finding from review, fixed pre-commit).
  • Updates CONTRIBUTING.md's stale test-coverage note.

Test plan

  • npm run typecheck passes (all 3 configs)
  • npm test — 50/50 pass (12 new: useFileSession.test.ts covers all 5 methods via renderHook, including the core fix — state is byte-for-byte unchanged when moveToTrash rejects — and the new isComplete derivation)
  • npm run build — Vite + Electron compile cleanly
  • npx biome check clean on changed files
  • Reviewed against CONTRIBUTING.md standards and the original design (two-axis review); both Standards findings (a Feature Envy leak, duplicated undo branches) fixed pre-commit, Spec review clean
  • Confirmed via git diff that CompletionScreen.tsx, playActionSound, the confirm dialog, and keyboard shortcuts are untouched
  • Not verified interactively: this is an Electron renderer that depends on window.electronAPI (only present via Electron's preload bridge) — running it under plain Vite crashes immediately on an unrelated pre-existing component (UpdateNotifier), confirmed this isn't a regression by reproducing the same crash's cause. No tooling available here to drive a real Electron window, so this change is verified via the automated test suite only, not a manual click-through.

🤖 Generated with Claude Code

…tion

App.tsx owned SessionState (files/currentIndex/deletedFiles/keptFiles/
folderPath/undoStack) via useState, mutating arrays in place inside setState
updaters (.push()/.pop() on the previous state's arrays after only a shallow
copy) — a real correctness bug, not just a style issue. FileViewer.tsx
co-owned the "perform an action" flow with no single place owning what
happens on delete.

Extract src/hooks/useFileSession.ts: a 5-method interface (startSession,
keep, deleteFile, undo, reset) that owns the whole session and does
immutable state transitions throughout. deleteFile is the one async
method — it awaits moveToTrash and only commits the "deleted" transition
on success, so a failed trash operation can no longer be recorded as if
it succeeded. A failure propagates as a rejection for FileViewer to catch
and show as an alert, instead of the previous fire-and-forget
.catch(console.error).

The hook also exposes a derived isComplete flag, so App.tsx no longer
reaches into currentIndex/files.length itself to guess whether the
session just finished; it reacts to isComplete via an effect instead.

Adds src/hooks/useFileSession.test.ts, testing the hook's public
interface via renderHook, including the core fix: state stays byte-for-
byte unchanged when moveToTrash rejects.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@killerwolf
killerwolf merged commit 86f4e6a into main Sep 2, 2026
2 checks passed
@killerwolf
killerwolf deleted the claude/deepen-session-state branch September 2, 2026 18:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant