Commit 030f9f5
authored
feat(editor): adopt TanStack Query, convert three read-only fetches (#7264)
# Description of Changes
## The problem
The editor has no query client. ~295 `apiClient` call sites, each mount
refetching what the last one just got, and three module-level caches
reimplementing dedupe, retry and invalidation by hand — each shaped
differently.
The Processor (`frontend/editor/src/portal`) has run on TanStack Query
since #7135. The editor never got it.
## End state
The editor has a query client, and the three read-only fetch sites that
convert safely now use it. `@tanstack/react-query` is already a
dependency — no new package.
**Foundation**
| File | |
|---|---|
| `core/query/queryClient.ts` | `baseQueryOptions` + client factory. The
portal now builds its client from the same options. `networkMode:
"always"` — `navigator.onLine` describes internet reachability, which
says nothing about a bundled backend on 127.0.0.1 or a self-hosted
server on the LAN. |
| `core/query/keys.ts` | `["editor", resource, ...params]` |
| `core/query/staleTime.ts` + `desktop/query/staleTime.ts` | Config
staleTime: `Infinity` on web, 5 min on desktop |
| `core/api/config.ts`, `core/api/users.ts` | Fetch functions, mirroring
`portal/api/*` |
| `core/tests/utils/TestQueryProvider.tsx` | |
| `desktop/components/DesktopQueryCacheReset.tsx` | |
`QueryClientProvider` mounts at the top of
`core/components/AppProviders.tsx`. That diff looks large but is one
wrapper plus the reindent underneath it.
**Converted.** All three keep their existing return shape, so no
consumer changes.
| | Before |
|---|---|
| `useFooterInfo` | Fetched twice — Footer and admin legal section |
| `useGroupEnabled` | Refetched on every mount |
| `UserSelector` | Refetched the whole roster on each of two mount
sites, and again whenever `t` or `user` changed identity |
**Desktop needs more than the provider.** `operationRouter` resolves the
same relative path to the local bundled backend, a self-hosted server,
or the SaaS backend. Query caches by key, not by resolved URL, so a
cached entry can outlive the backend that filled it. `group-enabled`
routes this way, so this PR introduces the hazard and carries the fix:
`DesktopQueryCacheReset` calls `resetQueries()` when the connection mode
changes or the self-hosted server goes up or down, and
`CONFIG_STALE_TIME` is finite on desktop as a backstop.
**Behaviour changes**
- All three sites now retry once on failure (client default). None
retried before, so a failing request sits in `loading` for one extra
attempt plus backoff.
- `staleTime: Infinity` on web means admin edits to legal links no
longer appear on remount within a session. Saving those already prompts
a restart, so this is accepted rather than incidental.
- Desktop `useGroupEnabled` shows the *translated* offline reason on
first render. The old code showed raw English for one render.
- `UserSelector` drops three `console.log`s that were dumping user
records to the console.
## Decisions
**1. The foundation doesn't ship alone.** A provider nothing consumes
gives a reviewer nothing to react to and rots if the follow-up stalls,
so it lands with the cheapest safe conversions.
**2. Hooks keep their existing return shape.** The alternative is
switching to `{ data, isPending, error }` and updating consumers now.
Cost of my choice: we carry a `loading`-shaped façade indefinitely, and
consumers don't get `isFetching`/`refetch` without a second pass. Taken
because it's what keeps each later migration a one-file diff.
**3. Shared defaults, separate instances.** The editor and the Processor
mount as *sibling* routes, not nested — they never coexist in one tree.
Both clients now come from the same `baseQueryOptions`, so behaviour
can't drift. A single shared instance would only buy cache surviving
navigation between the two products, which is worth little while they
share no keys, and it breaks the contract three portal tests rely on
(`createPortalQueryClient()` returning a fresh client per test). That
belongs in the collapse PR. Consequence meanwhile: the desktop reset
covers the editor client only — harmless, since the portal isn't in
desktop builds.
**4. The desktop reset is wholesale.** A mode switch already remounts
the SaaS provider tree, so there's nothing to preserve, and an allowlist
of "mode-sensitive" keys would be a trap every new query has to remember
to join.
## Coming next
Ordered by consumers per line changed.
| PR | Scope |
|---|---|
| 2 | `AppConfigContext` + `useEndpointConfig` — ~80 consumers, deletes
~200 lines of hand-rolled cache, retry and dedupe |
| 3 | `useAdminSettings` (20 consumers) and the config sections |
| 4 | Polling loops → `refetchInterval` |
| 5 | Finish the Processor's remaining files, collapse to one client |
| 6 | Tool execution — mutation state only, narrowly scoped |
Not in scope, deliberately: `usePdfLibLinks` (its cache is a refcounted
ArrayBuffer lifetime manager), thumbnail hooks, watched-folder IndexedDB
reads, the desktop health monitors. Unifying
`endpointAvailabilityService` / `saasAppConfigService` with the query
cache would mean handing `operationRouter` a query client — its own PR
if a second reason appears.
## Testing
`task frontend:check` green: 1666 tests across 191 files, typecheck on
all five flavours, eslint `--max-warnings=0`, dpdm, prettier.
New tests cover request de-duplication, per-group key isolation, the
desktop offline short-circuit, and the cache reset. The reset test was
verified to fail against the `clear()` implementation it replaced.
`UserSelector` has no test beyond its existing stories.
One existing test needed a wrapper: `Login.test.tsx` renders `<Login />`
in isolation, and `AuthLayout` → `Footer` → `useFooterInfo` now needs a
client. The real `/login` route is already inside `AppProviders`, so
this is test isolation, not a runtime gap.
Rollback is a clean revert — nothing persists outside the React tree.1 parent 921bdac commit 030f9f5
21 files changed
Lines changed: 638 additions & 272 deletions
File tree
- frontend/editor/src
- core
- api
- components
- hooks
- query
- tests/utils
- desktop
- components
- hooks
- query
- portal
- proprietary/routes
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
| 2 | + | |
| 3 | + | |
2 | 4 | | |
3 | 5 | | |
4 | 6 | | |
| |||
119 | 121 | | |
120 | 122 | | |
121 | 123 | | |
| 124 | + | |
122 | 125 | | |
123 | | - | |
124 | | - | |
125 | | - | |
126 | | - | |
127 | | - | |
128 | | - | |
129 | | - | |
130 | | - | |
131 | | - | |
132 | | - | |
133 | | - | |
134 | | - | |
135 | | - | |
136 | | - | |
137 | | - | |
138 | | - | |
139 | | - | |
140 | | - | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
141 | 134 | | |
142 | | - | |
143 | | - | |
144 | | - | |
145 | | - | |
146 | | - | |
147 | | - | |
148 | | - | |
149 | | - | |
150 | | - | |
151 | | - | |
152 | | - | |
153 | | - | |
154 | | - | |
155 | | - | |
156 | | - | |
157 | | - | |
158 | | - | |
159 | | - | |
160 | | - | |
161 | | - | |
162 | | - | |
163 | | - | |
164 | | - | |
165 | | - | |
166 | | - | |
167 | | - | |
168 | | - | |
169 | | - | |
170 | | - | |
171 | | - | |
172 | | - | |
173 | | - | |
174 | | - | |
175 | | - | |
176 | | - | |
177 | | - | |
178 | | - | |
179 | | - | |
180 | | - | |
181 | | - | |
182 | | - | |
183 | | - | |
184 | | - | |
185 | | - | |
186 | | - | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
187 | 192 | | |
188 | 193 | | |
Lines changed: 43 additions & 63 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
2 | 2 | | |
| 3 | + | |
3 | 4 | | |
4 | 5 | | |
5 | 6 | | |
6 | 7 | | |
7 | | - | |
8 | | - | |
| 8 | + | |
9 | 9 | | |
| 10 | + | |
10 | 11 | | |
11 | 12 | | |
12 | 13 | | |
| |||
30 | 31 | | |
31 | 32 | | |
32 | 33 | | |
33 | | - | |
34 | | - | |
35 | 34 | | |
36 | 35 | | |
37 | | - | |
38 | | - | |
39 | | - | |
40 | | - | |
41 | | - | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | | - | |
46 | | - | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
47 | 41 | | |
48 | | - | |
49 | | - | |
50 | | - | |
51 | | - | |
52 | | - | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | | - | |
65 | | - | |
66 | | - | |
67 | | - | |
68 | | - | |
69 | | - | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
70 | 50 | | |
71 | | - | |
72 | | - | |
73 | | - | |
74 | | - | |
75 | | - | |
76 | | - | |
77 | | - | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
78 | 54 | | |
79 | | - | |
80 | | - | |
81 | | - | |
82 | | - | |
83 | | - | |
84 | | - | |
85 | | - | |
86 | | - | |
87 | | - | |
88 | | - | |
89 | | - | |
90 | | - | |
91 | | - | |
92 | | - | |
93 | | - | |
94 | | - | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
95 | 73 | | |
96 | | - | |
97 | | - | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
98 | 79 | | |
99 | 80 | | |
100 | 81 | | |
101 | 82 | | |
102 | 83 | | |
103 | 84 | | |
104 | 85 | | |
105 | | - | |
106 | 86 | | |
107 | 87 | | |
108 | 88 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
0 commit comments