Skip to content

refactor(app-shell): move ObjectView's view management handlers into a sibling hook (slice 11b) - #26542

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

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

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steve · project thread

Fixes #25992 (slice 11b, the last slice of the move-only split; slices 1 to 11a landed as #26152, #26233, #26310, #26336, #26353, #26374, #26396, #26412, #26434 and #26492). With this PR, every region of the seam table in #26336 that was marked for a slice has moved; what stays in ObjectView.tsx is the shared state (A, C, F, J), the panel and dialog state, the hook calls and the JSX.

Clause-②: no
Metadata-protocol: none

What moved

Base a2e4b760 (the branch's merge of main at 73683ee1, slice 11a merged). ui/packages/app-shell/src/views/ObjectView.tsx is 1480 lines there. Three blocks of ObjectViewInner move verbatim into one custom hook, useObjectViewViewCrud, in the new sibling module objectViewViewCrud.tsx. Every input is passed as an argument, typed from its source (no any), and the component destructures the 13 handlers under their original names. No identifier is renamed and no context is added.

  • B: the view-config save and create doors (lines 228–371). The objectui#12504 note on the retired persistViewPatch, the objectui#4155 block comment on the absent persistViewFilter, handleViewConfigSave and handleViewCreate, with their docblocks and dependency lists.
  • B: handleViewUpdate (lines 789–796), the panel's draft field update, with its docblock.
  • I: view switching and the view-tab CRUD callbacks (lines 798–1002). handleViewChange, the mobile view switcher items and useMobileViewSwitcherRegistration, handleCreateView, handleViewAction, isSavedView, handleRenameView, handleDeleteView, handlePinView, handleSetDefaultView, the objectui#12418 docblock and handleReorderViews, handleConfigView and handleAddView.

The hook is called where handleViewUpdate stood, after useObjectViewListUrlState. It returns handleViewConfigSave, handleViewCreate, handleViewUpdate, handleViewChange, handleCreateView, handleViewAction, handleRenameView, handleDeleteView, handlePinView, handleSetDefaultView, handleReorderViews, handleConfigView and handleAddView. isSavedView and the mobile switcher items stay inside the hook, their only readers.

What stays. The five useState calls of B (showViewConfigPanel, viewConfigPanelMode, showCreateViewDialog, manageViewsOpen, viewDraft, lines 217–226) stay in the component. activeView (F, line 758) reads viewDraft before the hook's inputs exist, and the JSX reads the panel and dialog flags. The hook receives the setters.

Hook order. handleViewConfigSave and handleViewCreate (two useCallbacks) used to run before the record count state, useAuth and every hook down to useObjectViewListUrlState. They now run after it, inside the new hook, right before handleViewUpdate. They could not be called at their old spot: their bodies call setRefreshKey, declared at line 401, so passing it to a hook there would hit the TDZ. Nothing between their old and new positions reads them (measured: no reference between lines 372 and 788). The hooks of handleViewUpdate and I keep their order and position. The seat's ruling allows this shift when stated.

Imports

Each import moves with its only readers: formatMetadataError, viewTabLabel, persistRuntimeMetadata / createRuntimeMetadata / viewEnvelope, PREVIEW_QUERY_FLAG / PREVIEW_QUERY_VALUE, useMobileViewSwitcherRegistration, MobileViewSwitcherItem, isSavedViewId, buildAddViewSpec, setDefaultViewPatches, buildViewConfigSaveBody and dispatchViewPatches. These names are still re-exported from ObjectView.tsx where they were before, and those lines are unchanged. Names the component still reads elsewhere (useCallback, useMemo, toast, VIEW_TYPE_ICONS) are imported by the module as well. Nothing new is exported from ObjectView.tsx, and the package entry is untouched. ObjectView.tsx goes from 1480 to 1151 lines.

Move-only proof: the multiset of non-blank lines of the base ObjectView.tsx is contained in the new ObjectView.tsx plus the module, except 9 lines. Every one of the 9 is an import line or an import-list entry that lost a moved name: the PREVIEW_QUERY_FLAG and PREVIEW_QUERY_VALUE entries, the ../utils/viewIdentity.js import (replaced by its trimmed form), the buildAddViewSpec, setDefaultViewPatches and buildViewConfigSaveBody entries of the ./objectViewTabs.js list, and the three-line ./objectViewOptions.js import of dispatchViewPatches. The module imports each of those names itself. The added lines are:

  • the trimmed import and the new import;
  • the module header and imports;
  • the hook signature and parameter types;
  • the return;
  • in ObjectView.tsx, the call with its destructuring and a two-line comment.

Test edits

Five source-scan readers move to the file that now holds what they read. Each edit is a path or file-list change, and no assertion changed:

  1. writeRefusalCensus-11583.test.ts: the six LEDGER rows for handleViewConfigSave, handleViewCreate, handleRenameView, handleDeleteView, handlePinView and handleSetDefaultView change file: 'ObjectView.tsx' to file: 'objectViewViewCrud.tsx'. Callees, counts, pins and reasons are unchanged. The census walks views/, so it finds the sites in the new file.
  2. ui/scripts/__tests__/check-i18n-call-site-keys.test.ts: the deleteViewConfirm row of the deleted-fallbacks list → packages/app-shell/src/views/objectViewViewCrud.tsx. Its absent and present markers are unchanged.
  3. ObjectView.overlayPatchOnly.test.ts: the joined objectViewCode list gains 'objectViewViewCrud.tsx'. The control's handleViewConfigSave and persistRuntimeMetadata reads are there now.
  4. view-filter-fold.ratchet.test.ts: the joined objectViewSrc list gains objectViewViewCrud.tsx, for the handleViewConfigSave initializer the ratchet parses.
  5. ObjectView.unboundAdapterCall.test.ts: the detach-then-call ratchet reads ObjectView.tsx and objectViewViewCrud.tsx joined, the same way as the two above. Without this edit it would still pass, but it would no longer read handleSetDefaultView and handlePinView, the handlers it guards.

Edit 5 is the one the seat's list did not name. It is listed for the same reason as the others: the scan's subject moved.

Seam table rows this PR changes (measured on a2e4b760)

The full table (regions A to Q) is in #26336.

# Concern Lines on a2e4b760 Reads Read by Slice
B (state) Panel and dialog open state, viewDraft 217–226 — F, H, I, N, O, P, Q stays (shared state)
B handleViewConfigSave, handleViewCreate and their notes 228–371 A, C (setRefreshKey) Q 11b, this PR
B handleViewUpdate 789–796 B (setViewDraft), F (baseView) Q 11b, this PR
I View switching, mobile switcher, view-tab CRUD callbacks 798–1002 A, B, C, F N, Q 11b, this PR

Verification

The full readings are in the os-dev-report comment on the card:

  • app-shell type-check after rebuilding its dependency closure;
  • 208 test files: every ObjectView.* test in views/ (the view CRUD, refusal, create-view, default-view, view-config save and mobile FAB tests among them), the AppContent* tests, and every other test that reads ObjectView.tsx as source, including the five re-pointed ones;
  • eslint on the touched files;
  • the derived dispatch gates;
  • a Console build.

Acceptance notes

  • eslint: 0 errors, and no new no-explicit-any. ObjectView.tsx plus the module report 83 warnings, against 80 for the base ObjectView.tsx alone.
    • Added: 10 react-hooks/exhaustive-deps warnings in the module, and 1 existing one (the objectName warning on handleDeleteView) now also names setRefreshKey. Each added warning names a state setter that is now a hook parameter (setRefreshKey, setViewDraft, setShowViewConfigPanel, setViewConfigPanelMode, setShowCreateViewDialog). The rule treats a useState setter as stable only in the component that declares it. The dependency lists are kept verbatim, because adding the setters would change code; the setters are stable, so behaviour is the same. Slice 8 drew the same class of warning at its hook boundary.
    • Gone: the 2 exhaustive-deps warnings on handleViewChange, the 4 preserve-manual-memoization skips on these handlers, and 1 react-hooks/immutability finding (handleViewConfigSave reading setRefreshKey before its declaration).
    • Unchanged: the other exhaustive-deps warnings (listPrefs in handleReorderViews, and the two on the objectDef effect) only moved lines.
    • Net: 80 + 10 − 2 − 4 − 1 = 83.
  • The test edits add no warning.

Changeset: .changeset/25992-objectview-view-crud-split.md (patch, @object-ui/app-shell).


🤖 Generated with Claude Code

https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz

claude added 28 commits October 26, 2026 14:40
…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>
…igation into sibling hooks

Slices 5 and 6 of the move-only ObjectView split, paired in one PR.

- The identity-import gate, its password-policy state and data source,
  and the managed-environment Users marker move verbatim into
  useObjectViewIdentity in views/objectViewIdentity.tsx.
- The record navigation overlay (detail navigation config, drawer
  recordId URL state, row click, expand to page, list record links)
  moves verbatim into useObjectViewRecordNavigation in
  views/objectViewRecordNavigation.ts. The URL equality filters and the
  user-filter selections between its two blocks stay in the component.

Inputs are passed explicitly; the component destructures the returned
values under their original names. No test edits.

Claude-Session: https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
Co-authored-by: Claude <noreply@anthropic.com>
…m their sources

activeView, objectDef and objects were written as `any` in the new
hook's parameter type. They now name where the component gets each
value: the element of buildViewTabs' result, and
ConsoleObjectViewProps['objects'] and its element. No new
no-explicit-any warning; the moved bodies are unchanged.

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

Slice 7 of the move-only ObjectView split. The environment list's
entitlements, the list actions and their localized toolbar copies, and
the create and import buttons' toolbar-scope predicates move verbatim
into useObjectViewToolbarAffordances in
views/objectViewToolbarAffordances.ts. The resolveAffordance verdict
memo stays in the component (the affordance grant census reads it
there) and is passed in with the other inputs, typed from their
sources. No test edits.

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

Slice 8 of the move-only ObjectView split.

- The header's list_toolbar actions (less the record-scoped ones the
  selection bar carries), the nav runAction deep link and the
  action:bar node move verbatim into useObjectViewHeaderToolbar in
  views/objectViewHeaderToolbar.ts.
- The list's uf_ URL state (seed, write-back, Reset view) moves into
  useObjectViewListUrlState, and the filter[...] equality filters with
  the restored user-filter selections into useObjectViewUrlFilters,
  both in views/objectViewListUrlState.ts.

Hook parameters are typed from their sources. One test edit, path
only: ObjectView.urlFilterSuffix-9196 reads objectViewListUrlState.ts,
where the scanned block now lives.

Claude-Session: https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
Co-authored-by: Claude <noreply@anthropic.com>
…and header chrome into sibling hooks

Move-only split of ObjectViewInner: the view tab items, ViewTabBar and
its own-row face go to objectViewTabBar.tsx; the merged views and the
plugin ObjectViewSchema go to objectViewPluginSchema.ts; the create
button, folded header actions menu and refused-view face go to
objectViewHeaderChrome.tsx. Each hook is called where its block stood.

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

Move-only split of ObjectViewInner's JSX: the PageHeader block goes to
ObjectViewPageHeader in objectViewPageHeader.tsx, and the non-split
NavigationOverlay with its RecordDetailView goes to
ObjectViewRecordOverlay in objectViewRecordOverlay.tsx. Every value is
passed in as a prop; the rendered DOM is unchanged.

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

Move-only split of ObjectViewInner: renderListView, the column-declaration
flags and the held list identity refs only it reads, and the lazy
ObjectChart it mounts go to useObjectViewListRenderer in
objectViewListRenderer.tsx, called where renderListView stood. Six
source-scan tests read the new file (path or file-list changes only).

Claude-Session: https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
Co-authored-by: Claude <noreply@anthropic.com>
…a sibling hook (slice 11b)

The view-config save and create handlers, the draft field update, view
switching, the mobile view switcher registration and the view-tab CRUD
callbacks move verbatim from ObjectViewInner into useObjectViewViewCrud
(objectViewViewCrud.tsx). The panel and dialog state stays in the
component. Six source-scan readers follow the moved handlers (path or
file-list edits only).

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

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · seat domain:ui#1 · 2026-10-27T07:28Z · #25992 slice 11b (closes the card)

Head 00d32b82 (read from the PR object). The slice base is a2e4b760, and it merges main 42ec9519, which touches nothing under views/. Fixes #25992.

  • Shape. 8 files, +526 / −384. B and I move verbatim into one hook, useObjectViewViewCrud, in views/objectViewViewCrud.tsx:

    • B: handleViewConfigSave and handleViewCreate with their persist notes, plus handleViewUpdate;
    • I: view switching, the mobile switcher, and the rename / delete / pin / default / reorder / config / add callbacks.

    It returns 13 handlers under their original names. The five B useStates stay in the component, because activeView reads viewDraft first; the hook gets the setters. ObjectView.tsx goes from 1480 to 1151 lines.

  • Move-only, re-measured by the seat. The multiset of non-blank lines of the base ObjectView.tsx is contained in the new ObjectView.tsx plus the module, except 9 import-list lines that lost moved names.

  • Test edits, re-read by the seat. All 5 are path or file-list changes only, and no assertion changed:

    • writeRefusalCensus-11583: 6 rows file: → objectViewViewCrud.tsx;
    • check-i18n-call-site-keys: the deleteViewConfirm row path;
    • overlayPatchOnly and view-filter-fold.ratchet: the joined lists gain the module;
    • ObjectView.unboundAdapterCall: reads ObjectView.tsx plus the module, so it still reads the handlers it guards.
  • Stated hook-order shift. handleViewConfigSave and handleViewCreate now run after useObjectViewListUrlState. At their old position they would hit the setRefreshKey TDZ, and nothing in between reads them.

  • eslint. 0 errors, net +3 warnings. Ten exhaustive-deps messages now name setters that became hook parameters, with the dependency lists kept verbatim, and seven old ones went away. The body states this.

  • Governed surface. scripts/pm/check-governed-merges.mjs --pr 26542: not governed.

  • Checks pinned to the head. All check runs completed: 0 failing, 0 pending. The seat's landing script checked this before posting.

  • Evidence (dev). Type-check exits 0. 208 files / 3421 tests pass, and the 47 source readers pass after the main merge. The console build exits 0. Gates: 34/34.

Landing: ready + auto-merge through the relay. #25992 closes with this PR: ObjectView.tsx is now 1151 lines across 11 slices.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 27, 2026 07:29
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 27, 2026 07:29
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit f48aa45 Oct 27, 2026
55 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-x004t9 branch October 27, 2026 07:47
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/l tests tooling

Projects

None yet

2 participants