Skip to content

Commit 47074e2

Browse files
tyler-daneclaude
andauthored
refactor(web): move persisted events to query state (#1922)
* refactor: use tanstack-query * test(web): stabilize event query migration tests * refactor(web): simplify event query configuration * docs: design query-owned event server state * feat(web): add event query cache utilities * refactor(web): move persisted events to query state * test(web): cover event mutation lifecycle * refactor(web): simplify event query state * refactor(web): simplify event mutations and test harness - collapse the seven near-identical useMutation blocks behind a buildMutation helper so the snapshot/optimistic/rollback/invalidate lifecycle is defined once - drop the unused eventMutationKeys.event key builder - extract toNormalizedEventQueryData and reuse it across the three test harnesses that hand-built the { ids, entities } cache shape Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 586dd05 commit 47074e2

87 files changed

Lines changed: 2272 additions & 2282 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/acceptance/events.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ Use this guide to validate:
1818
- scheduling Someday events onto the grid by drag
1919
- duplicating events (Cmd+D)
2020
- undoing an event deletion (Cmd+Z / Ctrl+Z)
21+
- failed-write optimistic rollback
22+
- Week/Day navigation cache reuse
23+
- Google revocation and SSE-driven query refresh
2124

2225
Do not use this guide to validate:
2326

@@ -36,6 +39,9 @@ Helpful notes:
3639
- All event interactions require a loaded calendar grid. If the grid is blank, reload and wait for events to fetch.
3740
- The right-click context menu on an event opens a small overlay. It closes if you click elsewhere.
3841
- The Someday sidebar must be open (toggle with `[`) for sidebar drag scenarios.
42+
- For rollback checks, reject one repository write and verify the optimistic Event returns exactly to its prior state.
43+
- Navigate away from and back to a recently viewed Week/Day range; cached Events should render immediately.
44+
- Validate both anonymous local writes and authenticated remote writes so query and mutation sources remain aligned.
3945

4046
---
4147

docs/development/feature-file-map.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ Use this document to find the first files to inspect for common Compass changes.
2727

2828
- Shared event schema/types: `packages/core/src/types/event.types.ts`
2929
- Event helpers and recurrence utilities: `packages/core/src/util/event`
30-
- Web event listeners/operations: `packages/web/src/ducks/events/listeners`, `packages/web/src/ducks/events/operations`
31-
- Web event slices/selectors: `packages/web/src/ducks/events/slices`, `packages/web/src/ducks/events/selectors`
30+
- Web Event reads, cache utilities, and view models: `packages/web/src/ducks/events/queries`
31+
- Web Event persisted mutations and pending state: `packages/web/src/ducks/events/mutations`
32+
- Web Event draft/interaction Redux state: `packages/web/src/ducks/events/slices/draft.slice.ts`, `packages/web/src/ducks/events/selectors/draft.selectors.ts`
3233
- Event API/repositories: `packages/web/src/ducks/events/event.api.ts`, `packages/web/src/common/repositories/event`
3334
- Backend event routes: `packages/backend/src/event/event.routes.config.ts`
3435
- Backend event controller/service: `packages/backend/src/event/controllers/event.controller.ts`, `packages/backend/src/event/services/event.service.ts`

docs/frontend/frontend-runtime-flow.md

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -172,23 +172,21 @@ The web app uses multiple state layers:
172172

173173
| Concern | Use | Key files |
174174
| --- | --- | --- |
175-
| Loading states, modal visibility, async status | Redux Toolkit slices | `packages/web/src/ducks/events/slices/` |
176-
| Event mutation orchestration (create/edit/delete/convert/reorder) | Redux Toolkit listeners and async operations | `packages/web/src/ducks/events/listeners/`, `packages/web/src/ducks/events/operations/` |
177-
| Event read fetching + caching (day, week, someday) | TanStack Query `useQuery` hooks + `queryOptions` | `packages/web/src/ducks/events/queries/` |
178-
| Event entity CRUD, active event, and draft state | `packages/web/src/store/events.ts` |
175+
| Event loading, fetching, read errors, and persisted entities | TanStack Query range caches | `packages/web/src/ducks/events/queries/` |
176+
| Event create/edit/delete/convert/reorder state | TanStack Query mutations | `packages/web/src/ducks/events/mutations/` |
177+
| Draft Event and calendar interaction state | Redux Toolkit draft slice | `packages/web/src/ducks/events/slices/draft.slice.ts` |
179178
| Offline persistence | IndexedDB offline data store | `packages/web/src/common/storage/offline-data/indexeddb-offline-data.store.ts` |
180179
| Local vs remote persistence choice | Repository factory | `packages/web/src/common/repositories/event/event.repository.util.ts` |
181180

182-
These layers are intentional. Do not collapse event entities into Redux slices
181+
These layers are intentional. Do not mirror persisted Event entities into Redux
183182
or call IndexedDB directly from components.
184183

185184
Read these together for event work:
186185

187186
- `packages/web/src/store/index.ts`
188-
- `packages/web/src/ducks/events/queries` (reads)
189-
- `packages/web/src/ducks/events/listeners`
190-
- `packages/web/src/ducks/events/operations`
191-
- `packages/web/src/store/events.ts`
187+
- `packages/web/src/ducks/events/queries` (reads, cache utilities, and view models)
188+
- `packages/web/src/ducks/events/mutations` (persisted writes and pending state)
189+
- `packages/web/src/ducks/events/slices/draft.slice.ts` (transient drafts only)
192190

193191
## Event Flow
194192

@@ -197,22 +195,20 @@ Typical event **read** flow:
197195
1. a view hook mounts a `useXEventsQuery` hook (day/week/someday)
198196
2. TanStack Query fetches via the pure query function against the repository
199197
for the reactive source (`event.repository.source.store.ts`)
200-
3. a per-hook effect syncs the normalized result into Redux (entities +
201-
id-list slice), so selectors/grid render unchanged
198+
3. the normalized result remains in the source- and range-aware query entry;
199+
pure view models derive render data directly from it
202200
4. changing the view range re-keys the query (fetch on new ranges, instant
203201
render from cache on revisits within `staleTime`)
204202

205203
Typical event **mutation** flow:
206204

207-
1. a hook or component dispatches a Redux mutation action
208-
2. Redux Toolkit listener middleware invokes an async event operation
209-
3. the selected repository writes locally or remotely
210-
4. the operation updates the event entity store and, on success, calls
211-
`queryClient.invalidateQueries` so reads refetch instead of re-syncing
212-
pre-mutation cache into Redux
213-
5. Redux slices update async status
214-
6. React re-renders from selectors
215-
7. SSE events invalidate the relevant query scope (day/week/someday) to
205+
1. a hook or interaction calls the narrow `EventMutations` interface
206+
2. the mutation captures the active repository source, cancels Event reads,
207+
and snapshots affected query entries
208+
3. immutable cache utilities apply the optimistic update to matching ranges
209+
4. failures restore every snapshot; settlement invalidates `eventQueryKeys.all`
210+
5. pending guards derive Event IDs from TanStack Query mutation state
211+
6. SSE events invalidate the relevant query scope (day/week/someday) to
216212
refetch later; auth transitions refresh the source store and drop stale
217213
cache entries
218214

@@ -222,8 +218,8 @@ or treat them as stable.
222218

223219
Important consequence:
224220

225-
- event behavior is not owned by a single state system
226-
- when debugging, inspect the action, listener, operation, repository, and store layer together
221+
- persisted Event behavior is owned by TanStack Query; Redux owns only draft and interaction state
222+
- when debugging, inspect the query key, cache utility, mutation lifecycle, and repository source together
227223

228224
## Styling Systems
229225

@@ -334,6 +330,6 @@ Connect-later guardrail:
334330
## What To Read Before Editing
335331

336332
- Auth/session issue: read session provider, user provider, router loaders.
337-
- Event refresh issue: read the SSE hooks (which invalidate query scopes), the `useXEventsQuery` read hooks, `event.query.options.ts`, and the mutation operations (which invalidate on success).
333+
- Event refresh issue: read the SSE hooks (which invalidate query scopes), the `useXEventsQuery` read hooks, `event.query.options.ts`, and `useEventMutations.ts` (which invalidates after settlement).
338334
- Offline issue: read storage adapter and migration runner.
339335
- Rendering issue in day/week: start at the route view, then its hooks.

0 commit comments

Comments
 (0)