refactor(FR-2977): unify revision-source prefill in DeploymentAddRevisionModal#7602
Conversation
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has required the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
Coverage Report for react-coverage (./react)
File Coverage
|
||||||||||||||||||||||||||||||||||||||||||||||||||
ce82933 to
034f843
Compare
There was a problem hiding this comment.
Pull request overview
Refactors DeploymentAddRevisionModal to a render-as-you-fetch model with a single shared DeploymentAddRevisionModal_revisionSource fragment. The modal now consumes a PreloadedQuery whose variables (sourceRevisionId, hasSourceRevision) can request an id-based revision lookup via revision(id:) @include @since("25.16.0"). Both currentRevision and the optional source revision are read through the same fragment, and a single keyed effect applies the prefill — replacing the previous useLazyLoadQuery + duplicate-state pump pattern and laying the groundwork for the stacked "Duplicate as new revision" PR (#7561).
Changes:
- Introduce
DeploymentAddRevisionModal_revisionSourcefragment + exportedDeploymentAddRevisionQuery; add$sourceRevisionId/$hasSourceRevisionvariables and a conditionalrevision(id:)top-level field. - Switch the modal from
useLazyLoadQuerytousePreloadedQuery+useDeferredValue, deriveeffectiveMode='custom'when the queryRef carrieshasSourceRevision, and reduce prefill to one keyeduseEffect/useEffectEventusingappliedSourceIdRef. - Move query kickoff to
DeploymentDetailPageviauseQueryLoader, drop the local<Suspense>boundary, and gate modal render onaddRevisionQueryRef != null.
Reviewed changes
Copilot reviewed 3 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| react/src/pages/DeploymentDetailPage.tsx | Replaces useToggle with useState+useQueryLoader to preload the modal query on click; removes the local Suspense boundary. |
| react/src/components/DeploymentConfigurationSection.tsx | Updates the Add Revision button comment to reflect the render-as-you-fetch flow. |
| react/src/components/DeploymentAddRevisionModal.tsx | Adds the shared revision-source fragment, exported preloaded query, single apply-prefill effect, and locks mode to Custom when queryRef.variables.hasSourceRevision is true. |
| react/src/generated/DeploymentAddRevisionModalQuery.graphql.ts | Regenerated query artifact for the new variables and conditional revision field. |
| react/src/generated/DeploymentAddRevisionModal_revisionSource.graphql.ts | New generated fragment artifact for the shared revision-source selection. |
Files not reviewed (2)
- react/src/generated/DeploymentAddRevisionModalQuery.graphql.ts: Language not supported
- react/src/generated/DeploymentAddRevisionModal_revisionSource.graphql.ts: Language not supported
034f843 to
1b84a66
Compare
nowgnuesLee
left a comment
There was a problem hiding this comment.
Please resolve the merge conflicts.
…sionModal Lay groundwork for id-based revision-source prefill in the Add Revision modal so a caller (e.g. a future "Duplicate as new revision" action) can pass a `sourceRevisionId` and have the form auto-fill with that revision's full configuration. The existing "Load current revision" alert button continues to work unchanged as the user opt-in path for the deployment's active revision. What changed: - Define a single `DeploymentAddRevisionModal_revisionSource` fragment at module scope. Both prefill paths (current revision + sourceRevisionId) read through it via `useFragment`, sharing one `$data` shape so the newly extracted `applyRevisionPrefill(rev)` helper accepts either without an unsafe cast. - Extend the modal's `useLazyLoadQuery` with `$sourceRevisionId: ID!` and `$hasSourceRevision: Boolean!` variables and a conditional top-level `revision(id:)` field gated by `@include(if:)` and `@since(version: "25.16.0")`. When no source is requested, the field is excluded from the wire payload and the placeholder empty-string id is never evaluated server-side. - Replace the inline `currentRevision` selection with a fragment spread. - Add a `sourceRevisionId?: string | null` prop. When set, `effectiveMode` is derived synchronously as `'custom'` so the form mounts in custom mode immediately — no `setMode` -> mount race, no `pending*Prefill` state pump. The mode segmented control is hidden for that session; the persisted `mode` setting is untouched for the next normal open. - A single `useEffect` keyed on `(open, sourceRevision)` and a `useEffectEvent` body apply the prefill once per source id. The `appliedSourceIdRef` resets on close so the next session starts clean. What did not change: - `presetTransferPrefill` / `customTransferPrefill` (the preset <-> custom mode value transfer mechanism from FR-2862). That path translates between two different form shapes and is a structurally different problem; out of scope here. - Modal UX (alert copy, mode segmented control behavior when not locked, Apply button placement, footer). Pure data-flow refactor. Verified manually against the three flows on the local dev server: 1. Add Revision button (sourceless) -> opens in persisted mode, no prefill, toggle visible. 2. Load current revision alert button -> populates custom form with current revision values (Model Folder, Runtime, image, 4 Core / 8 GiB, Single Node, etc.). 3. Toggle Custom <-> Preset -> existing transfer prefill still moves the shared values across. This base PR is intentionally callsite-neutral: no consumer passes `sourceRevisionId` yet. FR-2957 ("Duplicate as new revision") will stack on top and add the menu items, i18n, icon, and the parent state that drives `sourceRevisionId`.
1b84a66 to
3ba8330
Compare
| )} | ||
| </BAIFlex> | ||
| } | ||
| width={720} |
There was a problem hiding this comment.
[Re: lines +1381 to +1394]
<Suspense fallback={<BAISelect loading />}>
<Form.Item
name="modelFolderId"
noStyle
rules={[{ required: true }]}
>
<BAIVFolderSelect
ref={presetVFolderSelectRef}
currentProjectId={currentProjectId ?? undefined}
disabled={!currentProjectId}
excludeDeleted
filter='usage_mode == "model"'
style={{ flex: 1 }}
/>
</Form.Item>
</Suspense>
See this comment inline on Graphite.

Resolves #7601 (FR-2977)
Summary
Base refactor for
DeploymentAddRevisionModalso any caller (e.g. a future "Duplicate as new revision" action — see the stacked PR #7561) can request an id-based revision prefill cleanly, replacing the prop-drilled fragment ref + dual-useEffectEventstate pump that surfaced during review of FR-2957:DeploymentAddRevisionModal_revisionSourceat module scope, spread on bothcurrentRevisionand the optional id-basedrevision(id:)field. Both are read viauseFragment, sharing one$datashape so the extractedapplyRevisionPrefill(rev)helper accepts either without an unsafe cast.$sourceRevisionId: ID!and$hasSourceRevision: Boolean!variables, plus a top-levelrevision(id:) @include(if:) @since(version: "25.16.0")field. When no source is requested, the field is excluded from the wire payload and the placeholder id is never evaluated server-side. The revision-history list query no longer needs to spread the prefill fragment on every row — only the id is required.useLazyLoadQuerytousePreloadedQuerywith a deferred query ref, following the same pattern PR refactor(FR-2964): switch route and deployment scheduling history modals to render-as-you-fetch #7575 applied to the scheduling-history modals. The opener (DeploymentDetailPage) drives the modal viauseQueryLoader: clicking "Add Revision" callsloadAddRevisionQuery({ deploymentId, sourceRevisionId, hasSourceRevision }, { fetchPolicy: 'store-and-network' })and then flips the open flag. The page-level<Suspense>boundary around the modal is removed since the query is no longer started inside render.queryRef-based source-revision control (nosourceRevisionIdprop) — instead of a separatesourceRevisionIdprop on the modal, the source id andhasSourceRevisionflag ride on the preloaded query's variables. The modal readsqueryRef.variables.hasSourceRevisionto lockeffectiveModeto'custom'synchronously, andqueryRef.variables.sourceRevisionIdis the source id the query already resolved. Callers like feat(FR-2957): add duplicate-revision action in revision detail drawer #7561 pass the source throughloadQueryvariables rather than a separate prop. This consolidates "what data the modal needs" + "what mode the modal opens in" into one decision the opener makes vialoadQuery, matching howRouteSchedulingHistoryModal/DeploymentSchedulingHistoryModalconsume their preloaded queries.useEffectkeyed on(open, sourceRevision), body wrapped inuseEffectEventper project convention, applying the prefill once per source id via anappliedSourceIdRef. The ref resets on close so the next session starts clean.Notes
loadAddRevisionQuerywith the source id. Land bottom-up — this PR first, then feat(FR-2957): add duplicate-revision action in revision detail drawer #7561.DeploymentAddRevisionModalcallsloadAddRevisionQuerywithhasSourceRevision: trueyet.DeploymentDetailPagealways passessourceRevisionId: ''/hasSourceRevision: falsefor the existing "Add Revision" button.mode+sourceRevisionIdas props" sketch — the project standardized on render-as-you-fetch for modals in PR refactor(FR-2964): switch route and deployment scheduling history modals to render-as-you-fetch #7575 (FR-2964), and this PR aligns with that convention. Net effect for callers is the same: the opener decides the mode + source in the same tick, just by constructing the preloaded query's variables instead of passing two separate props.presetTransferPrefill/customTransferPrefill(the preset ↔ custom value transfer from FR-2862) — same anti-shape, but translates between two different form shapes and is a structurally different problem.Test plan
Screenshots