Repository navigation
Conversation
Adds collections/user.ts: a local-only TanStack DB collection holding the signed-in user, read through getUser() (module-level reactive mirror) and written through named functions only: direct writes for what the server already did, optimistic actions for user changes. All writers move over; the states/snapshot mirror and DB.setSnapshot/initSnapshot are deleted. DB.getSnapshot stays as a read-only shim over getUser until U4. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🔵 Needs a closer look
It is a large two-way-door refactor of all user-data writers plus a subtle optimistic-action concurrency model, and it introduces a stale cross-user testActivityByYear cache that needs human verification.
1 open issue
What changed in this PR
Implements roadmap step U1: replaces the mutable Snapshot object and its reactive Solid mirror with a single-row, local-only TanStack DB collection keyed by uid (collections/user.ts). Reads go through one reactive getUser(); DB.getSnapshot becomes a read-only shim (= getUser, returning DeepReadonly<Snapshot>). Writers are split into named direct writes (xp, streak, typing stats, PBs/PB reset, lb memory, badges, inbox count, Discord link) and optimistic actions (name, Discord unlink, streak offset, quote rating, favourite quote, profile) that apply immediately and roll back on failure. The auth flow now calls loadUserData()/clearUser(), and the last-result signal moves to states/last-result.ts.
Changes:
- New
collections/user.tssingle-row collection with reactive mirror, direct writes, and reusable optimistic-action helper. db.tsslimmed to agetSnapshot = getUsershim; snapshot init/mutation code and the reactive mirror (states/snapshot.ts) deleted; all writers/readers migrated.- Spec/roadmap updated (U1 marked done, intended behaviour deviations documented) and new
user.spec.ts(16 tests) added.
| File | Description |
|---|---|
| frontend/src/ts/collections/user.ts | New single-row collection: load/clear, direct writes, optimistic actions, PB/test-activity helpers. |
| frontend/src/ts/db.ts | Removes snapshot state/mutators; getSnapshot shim; rebuilds calendar from plain data; module-level per-year cache. |
| frontend/src/ts/states/snapshot.ts | Deleted reactive mirror + mini-snapshot. |
| frontend/src/ts/states/last-result.ts | New home for the last-result signal. |
| frontend/src/ts/constants/default-snapshot.ts | testActivity is now plain TestActivity; drops calendar classes/isMiniSnapshot. |
| frontend/src/ts/utils/types.ts | Adds DeepReadonly<T>. |
| frontend/src/ts/auth.tsx | Uses loadUserData()/clearUser() instead of initSnapshot/setSnapshot. |
| frontend/src/ts/controllers/quotes-controller.ts | Delegates favourite toggling to setQuoteFavorite. |
| frontend/src/ts/components/.../*.tsx | Many components/modals switch reads to getUser() and writes to the new named actions. |
| frontend/src/ts/collections/{results,inbox,connections}.ts | Import moved/renamed user helpers. |
| frontend/__tests__/collections/user.spec.ts | New coverage for load, direct writes, optimistic apply/rollback. |
| frontend/__tests__/__harness__/app-shell.tsx | Uses userTesting.setUser instead of setSnapshot. |
| docs/specs/solid-monkeytype/{spec,roadmap}.md | Documents U1 as done and the intended behaviour deviations. |
Review effort: Balanced
|
|
||
| let dbSnapshot: Snapshot | undefined; | ||
| const firstDayOfTheWeek = getFirstDayOfTheWeek(); | ||
| let testActivityByYear: Record<string, TestActivityCalendar> | undefined; |
@tanstack/db 0.15 holds direct writes back while an optimistic action is pending, so the user collection's actions dropped writes like addXp made meanwhile. Its optimistic actions are now direct writes: apply, request, reapply with the server's values, or roll back the changed fields that weren't changed again since. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NZNRfGDYsGYMUTp2zYUTrv
# Conflicts: # frontend/src/ts/components/layout/header/Nav.tsx # frontend/src/ts/components/layout/overlays/Banners.tsx

Summary
U1 from
docs/specs/solid-monkeytype/roadmap.md. A single-row user collection replaces the mutable snapshot and its reactive mirror.On success, an optimistic action accepts the current row with its change re-applied, not the mutation's start-of-action copy. Otherwise a result save that lands while a name change is pending would get overwritten.
Intended deviations (added to the spec):
Evidence
__tests__/collections/user.spec.ts(16 tests): every direct write, load success/failure, optimistic apply/rollback, a direct write during a pending action surviving, row without$synced/$originoxlint --type-aware --type-checkcleanmaxFavorites: 0) → server error shown, row rolled backsnapshot()undefined; sign back in → row reloadedMerge Danger
Door: two-way
Blast Radius: account data
Every user-data writer and the sign-in load path changed. A missed field in the API→row mapping would show as a blank or stale value on account, profile, leaderboard or result screens. Readers go through the
getSnapshotshim, soDeepReadonlyturns any leftover mutation into a type error.🤖 Generated with Claude Code