Skip to content

refactor(app-shell): move ObjectView's realtime refresh and record count into a sibling hook (slice 4) - #26336

Merged
objectstack-fleet[bot] merged 8 commits into
mainfrom
claude/project-thread-x004t9
Oct 26, 2026
Merged

objectstack-fleet[bot] merged 8 commits into
mainfrom
claude/project-thread-x004t9

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steve · project thread

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.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz

…tViewOptions.ts (slice 2)

The timeline/calendar/gantt/gallery/kanban ViewOptions, storedLegacyOptions,
isSameOptionsValue and dispatchViewPatches move verbatim out of
views/ObjectView.tsx into views/objectViewOptions.ts. ObjectView.tsx imports
them back and re-exports every name it exported before.

Test edits forced by the move: the four source scans that read the
ObjectView face (calendarBinding-7029, galleryBinding-7547, ganttBinding-7070,
titleFieldConvergence) now read ObjectView.tsx plus objectViewOptions.ts, and
the writeRefusalCensus-11583 LEDGER row for dispatchViewPatches is keyed by
its new file.

Claude-Session: https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
Co-authored-by: Claude <noreply@anthropic.com>
…ome.tsx (slice 3)

ObjectNotFoundState, the saved-view tab strip's viewport check
(VIEW_TABS_MIN_WIDTH, viewTabsFitNow, useViewTabsFit) and
keepInlineEditModeForTheSession move verbatim out of views/ObjectView.tsx
into views/objectViewChrome.tsx. ObjectView.tsx imports them back and
re-exports ObjectNotFoundState, the one name it exported. No test edits.

Claude-Session: https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
Co-authored-by: Claude <noreply@anthropic.com>
…unt into a sibling hook

Slice 4 of the move-only ObjectView split. The realtime subscription,
the reconnection conflict resolution and the footer record-count fetch
move verbatim from ObjectViewInner into useObjectViewLiveData in
views/objectViewLiveData.ts. The component calls the hook at the same
position (hook order unchanged) and passes its inputs explicitly; the
recordCount state stays in the component. No test edits.

Claude-Session: https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tooling labels Oct 26, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · seat domain:ui#1 · 2026-10-26T21:56Z

Head 0531281f (read from the PR object).

  • Shape. Draft on main, Part of #25992 (slice 4, the first component-body slice, under the standing claim and continuation 6427036268). 3 files, +87/-52: ObjectView.tsx, the new sibling objectViewLiveData.ts, and a root changeset (@object-ui/app-shell patch). Clause-②: no, Metadata-protocol: none.
  • Checks pinned to the head. All check runs completed: 0 failing, 0 pending. The seat's landing script checked this before posting.
  • Governed surface. check-governed-merges.mjs --pr 26336: not governed.
  • Move-only, checked by the seat. Concern K of ObjectViewInner (the realtime refresh, the reconnection conflict resolution and the footer $top: 0 record-count fetch) moves verbatim into useObjectViewLiveData.
    • The seat's own line-multiset check of base ObjectView.tsx against head ObjectView.tsx plus objectViewLiveData.ts finds no base line missing. The head-only lines are the import, the call site, the hook signature with its explicit inputs, and the module header.
    • The hook is called at the same spot, so hook order is unchanged. The recordCount state stays in the component. No new context, and no test edits.
    • The one new exhaustive-deps warning, on setRecordCount, comes from it now arriving as a parameter. The deps array is kept verbatim, as move-only requires.
  • Evidence (dev). app-shell type-check. 142 files and 2699 tests: every ObjectView.* test, AppContent*, and every source reader. Console build. After the main merge, the same checks re-run green. 30/30 gates, 0 NOT-MEASURED. eslint 0 errors.
  • Seam table. 17 regions A–Q are in the PR body, and the remaining slices are named there, which sets the card's end.
  • Serial constraints. No other open PR touches ObjectView.tsx or its siblings. app-shell is outside the sdui closure. The ⛔ paths are untouched.

Landing: ready + auto-merge through the relay.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants