Repository navigation
Commit b9cd980
refactor(app-shell): move ObjectView's realtime refresh and record count into a sibling hook (slice 4) (#26336)
<!-- claude-projects-attribution: {"github_login":"os-steve"} -->
_Requested by **Steve** · [project
thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi9VtKf3F9dzJPJM4RRtABaEc)_
Part of #25992 (slice 4 of the move-only split, the first slice inside
`ObjectViewInner`; slices 1 to 3 landed as #26152, #26233 and #26310,
and the card stays open for the slices listed below).
Clause-②: no
Metadata-protocol: none
## What moved
Base `e6381930` (slice 3 merged).
`ui/packages/app-shell/src/views/ObjectView.tsx` is 3171 lines there:
imports, re-exports, `ConsoleObjectViewProps`, the `ObjectView` wrapper,
and `ObjectViewInner` from line 222 to the end.
This slice moves concern **K** of the seam table below, verbatim, into a
custom hook `useObjectViewLiveData` in a new sibling module
`ui/packages/app-shell/src/views/objectViewLiveData.ts`:
- the realtime subscription on `object:<name>` that calls
`refreshData()` when the server reports a change,
- the reconnection conflict resolution (`useConflictResolution`,
server-wins),
- the footer's record-count fetch (`dataSource.find(name, { $top: 0 })`
into `setRecordCount`), with its whole comment block.
`ObjectViewInner` calls `useObjectViewLiveData({ dataSource, objectDef,
refreshData, refreshKey, setRecordCount })` at the exact place the moved
lines stood, so the order of React hooks in the component does not
change. The `recordCount` state stays in the component (line 413),
because the JSX return reads it; the hook gets its setter and every
other input as arguments. No identifier is renamed, no new context is
added, and the hook declares no state. The `@object-ui/collaboration`
import moves with its only two readers. Nothing new is exported from
`ObjectView.tsx`, and the package entry is untouched. `ObjectView.tsx`
goes from 3171 to 3123 lines.
Move-only proof: the multiset of non-blank lines of the base
`ObjectView.tsx` equals new `ObjectView.tsx` plus
`objectViewLiveData.ts`. Nothing is missing. The only added lines are
the new module's header comment, its three imports, the hook's signature
and parameter type (6 lines) and closing brace, and in `ObjectView.tsx`
the new import plus the call with its two-line comment.
## Test edits
None. No test reads the moved lines as source (checked every
`readFileSync` reader of `ObjectView.tsx`), and the tests that
`vi.mock('@object-ui/collaboration')` mock the module, so they cover the
hook's import too.
## Seam table of `ObjectViewInner` (measured on `e6381930`)
Lines are in the base file. "Reads" lists inputs from other regions
(props `dataSource`, `objects`, `onEdit`, `externalRefreshKey` are left
out). "Read by" lists the regions that use what a region declares.
| # | Concern | Lines | Declares (main) | Reads | Read by | Slice |
|---|---|---|---|---|---|---|
| A | Route, i18n, metadata and preview inputs | 223–256 | `objectName`,
`viewId`, `t`, `navigate`, `location`, `searchParams`, `servedViews`,
`viewLabel`, `metadataClient`, `previewDrafts` | (hooks only) | all |
stays (the inputs every slice receives) |
| B | View config panel and create dialog state, `handleViewConfigSave`,
`handleViewCreate` | 257–412 | `viewDraft`, panel/dialog open state, the
two handlers | A, C (`objectDef`, `setRefreshKey`) | F, G, H, I, N, P, Q
| 11 (census-pinned) |
| C | Page identity: record count state, auth, permissions, `objectDef`,
favourite, refresh key, action runtime | 413–509 | `recordCount`,
`objectDef`, `user`, `perms`, `isAdmin`, `refreshKey`, `refreshData`,
`actionRuntime` | A | all | stays (shared state) |
| D | Environment entitlements, toolbar actions, create and import
verdicts | 510–685 | `listActions`, `createVisible/Disabled`,
`importVisible/Disabled`, `objectCanCreate/Import`, `isEnvironmentList`,
`localizedToolbarActions` | A, C, J (`actions`) | G, O, P, Q | 7 |
| E | Import wizard state, identity import, managed users | 686–753 |
`showImport`, `identityImportEnabled`, `identityDataSource`,
`identityPasswordPolicy`, `managedUsers`, `managedUserMarker` | A (`t`),
C | O, P, Q | 5 |
| F | Saved views, list preferences, overrides, views list, active view
resolution | 754–1001 | `savedViews`, `listPrefs`, `viewOverrides`,
`views`, `activeView`, `activeViewId`, `viewAccess` | A, B
(`viewDraft`), C | most | stays (shared state) |
| G | Header toolbar actions and the nav-run deep link | 1002–1085 |
`headerToolbarActions`, `toolbarBar`, `toolbarActionsWithDeepLink`,
`activeViewDeclaresColumns`, `activeViewIsSaved` | A, B, D, F, J | O, P,
Q | 8 |
| H | List URL state, preference seed, reset | 1086–1211 |
`writeListUrlState`, `listSeedRef`, `listIdentityKey`,
`handleViewUpdate`, `handleResetView` | A, B (`setViewDraft`), C, F | L,
O, P, Q | 8 |
| I | View switching, mobile switcher, view-tab CRUD callbacks |
1212–1420 | `handleViewChange`, `handleAddView`, `handleRenameView`,
`handleDeleteView`, `handlePinView`, `handleSetDefaultView`, … | A, B,
C, F | N, Q | 11 (census-pinned) |
| J | Object actions and filter scope | 1421–1450 | `actions`,
`filterScope` | A, C | D, G, O, P, Q | stays (shared) |
| **K** | **Realtime refresh, conflict resolution, record count** |
**1452–1502** | (nothing anyone reads) | C (`objectDef`, `refreshData`,
`refreshKey`, `setRecordCount`) | none | **4, this PR** |
| L | Navigation overlay, drawer, URL and user filters, row click |
1503–1740 | `navOverlay`, `handleRowClick`, `handleDrawerClose`,
`urlFilters`, `listRecordActions` | A, C, F, H | O, Q | 6 |
| M | Held list identity refs | 1741–1759 | `heldListFilter`,
`heldListOptions` | (local) | O | 6 (with L) |
| N | View tab bar | 1760–1825 | `viewTabBar`, `viewTabRow`,
`viewTabItems` | A, B, C, F, I | O, Q | 9 |
| O | `renderListView` | 1826–2593 | `renderListView` | A, C, D, E, F,
G, H, J, L, M, N | Q | 11 (scan-pinned) |
| P | Plugin `ObjectView` schema, create button and FAB, folded menu and
header actions | 2594–2764 | `objectViewSchema`, `createButton`,
`foldedHeaderActions`, `listFabClearance`, `refusedViewFace` | A, B, C,
D, E, F, G, H, J | Q | 9 |
| Q | JSX return (providers, `PageHeader`, FAB, import wizard, tabs,
content, `NavigationOverlay`) | 2765–3170 | — | everything | — | 10 |
## Remaining slices (one PR each; the last closes the card)
- **Slice 5 — E:** import wizard state, identity import and managed
users into a sibling hook.
- **Slice 6 — L + M:** navigation overlay, drawer, URL and user filters,
row click and the held list refs into a sibling hook.
- **Slice 7 — D:** environment entitlements, toolbar actions and the
create and import verdicts into a sibling hook.
- **Slice 8 — G + H:** header toolbar actions and the list URL state
into sibling hooks.
- **Slice 9 — N + P:** the view tab bar and the plugin schema plus
header chrome into a sibling hook or sub-component.
- **Slice 10 — Q:** the JSX return's `PageHeader`, import wizard and
`NavigationOverlay` blocks into sub-components with explicit props.
- **Slice 11 — B + I + O (closes the card):** the view-config and
view-tab CRUD handlers and `renderListView`. They are pinned by file and
symbol in `writeRefusalCensus-11583.test.ts` (`handleViewConfigSave`,
`handleViewCreate`, `handleRenameView`, `handleDeleteView`,
`handlePinView`, `handleSetDefaultView`) and by
`ObjectView.relayRungCensus-7559.test.ts` (`renderListView`). Moving
them needs those census rows re-keyed to the new file, which is more
than the import-path edits the card allows; slice 2 had that allowance
for one row. If the seat does not grant it, slice 10 closes the card and
B, I and O stay in `ObjectView.tsx` as the component's core.
A, C, F and J stay in the component in every plan: they are the shared
state most other concerns read. Each later slice re-measures the line
spans and the source-scan pins at its claim, because the file moves
daily.
## Verification
See the os-dev-report comment on the card for the full readings:
app-shell `type-check`, every `ObjectView.*` test in `views/`, the
`AppContent*` tests, every other test that reads `ObjectView.tsx` as
source, eslint on the 2 touched files, the derived dispatch gates, and a
Console build.
## Acceptance notes
- eslint reports one new warning (0 errors):
`react-hooks/exhaustive-deps` in the moved record-count effect now names
`setRecordCount`, because the rule only treats a `useState` setter as
stable inside the component that declares it. The dependency array is
kept verbatim because the slice is move-only; the setter is still the
component's stable `useState` setter.
Changeset: `.changeset/25992-objectview-live-data-split.md` (patch,
`@object-ui/app-shell`).
---
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 3ae7c6f commit b9cd980
3 files changed
Lines changed: 87 additions & 52 deletions
File tree
- .changeset
- ui/packages/app-shell/src/views
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
89 | 89 | | |
90 | 90 | | |
91 | 91 | | |
92 | | - | |
93 | 92 | | |
94 | 93 | | |
95 | 94 | | |
| |||
126 | 125 | | |
127 | 126 | | |
128 | 127 | | |
| 128 | + | |
129 | 129 | | |
130 | 130 | | |
131 | 131 | | |
| |||
1449 | 1449 | | |
1450 | 1450 | | |
1451 | 1451 | | |
1452 | | - | |
1453 | | - | |
1454 | | - | |
1455 | | - | |
1456 | | - | |
1457 | | - | |
1458 | | - | |
1459 | | - | |
1460 | | - | |
1461 | | - | |
1462 | | - | |
1463 | | - | |
1464 | | - | |
1465 | | - | |
1466 | | - | |
1467 | | - | |
1468 | | - | |
1469 | | - | |
1470 | | - | |
1471 | | - | |
1472 | | - | |
1473 | | - | |
1474 | | - | |
1475 | | - | |
1476 | | - | |
1477 | | - | |
1478 | | - | |
1479 | | - | |
1480 | | - | |
1481 | | - | |
1482 | | - | |
1483 | | - | |
1484 | | - | |
1485 | | - | |
1486 | | - | |
1487 | | - | |
1488 | | - | |
1489 | | - | |
1490 | | - | |
1491 | | - | |
1492 | | - | |
1493 | | - | |
1494 | | - | |
1495 | | - | |
1496 | | - | |
1497 | | - | |
1498 | | - | |
1499 | | - | |
1500 | | - | |
1501 | | - | |
1502 | | - | |
| 1452 | + | |
| 1453 | + | |
| 1454 | + | |
1503 | 1455 | | |
1504 | 1456 | | |
1505 | 1457 | | |
| |||
| 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 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
0 commit comments