Skip to content

Commit 150d896

Browse files
committed
fix(settings): keep press prefetch, guard fork direction switch, tighten docs
- Restore the sidebar link's press-time route prefetch. Measured with a realistic ~90ms press, the prefetch mousedown starts completes before the click commits, so removing it slowed every navigation (commit ~105ms -> ~190ms after mousedown). - Route the fork sync direction switch through the unsaved-changes guard: switching Push/Pull drops the in-session mapping choices, so unsaved edits confirm first. - Give SettingsNavigationProvider a props interface, and scope the section layout's 404/307 note to section authorization, which is what an unknown or legacy segment skips.
1 parent bc9a85f commit 150d896

4 files changed

Lines changed: 17 additions & 24 deletions

File tree

‎apps/sim/app/workspace/[workspaceId]/settings/[section]/layout.tsx‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ const TOP_LEVEL_REDIRECTS: Readonly<Record<string, (workspaceId: string) => stri
2626
* the shell instead of waiting on the body's lazily-loaded chunk.
2727
*
2828
* Whether a segment names a section at all is decided here too, before the shell renders, so a
29-
* bad or legacy URL loaded directly answers 404 or 307 without touching the database. Whether
30-
* the *viewer* may open a section is a different question and stays in the page, where it
29+
* bad or legacy URL loaded directly answers 404 or 307 without running section authorization.
30+
* Whether the *viewer* may open a section is a different question and stays in the page, where it
3131
* belongs; those checks need the database and are reached almost entirely by client navigation.
3232
*
3333
* There is deliberately no sibling `loading.tsx`, and the page renders its body under no Suspense
@@ -36,9 +36,9 @@ const TOP_LEVEL_REDIRECTS: Readonly<Record<string, (workspaceId: string) => stri
3636
* `<Suspense>` around the code-split body — put that floor under every section switch. Without
3737
* one, a switch is a transition: the sidebar moves its selection and the settings layout paints
3838
* the incoming heading over the outgoing section, which stays mounted but invisible until the
39-
* incoming section and its chunk are ready. A boundary above
40-
* this layout (a `settings/loading.tsx`, or a `<Suspense>` in the settings layout) would also
41-
* swallow the 404 and 307 above.
39+
* incoming section and its chunk are ready. A boundary above this layout (a
40+
* `settings/loading.tsx`, or a `<Suspense>` in the settings layout) would also swallow the 404
41+
* and 307 above.
4242
*
4343
* Authentication is already enforced by the ancestor workspace layout, so this runs only for a
4444
* signed-in viewer.

‎apps/sim/components/settings/settings-intent-link.tsx‎

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -80,24 +80,12 @@ function IntentAwareSettingsLink({
8080
prefetchTimerRef.current = null
8181
}, [])
8282

83-
/** Runs the caller's intent warmer; the route payload is left to the navigation. */
84-
const warmForIntent = () => {
83+
const prefetchForIntent = () => {
8584
cancelScheduledPrefetch()
8685
if (isCurrentRoute || navigationIntentRef.current) return
8786
navigationIntentRef.current = true
88-
onIntent?.()
89-
}
90-
91-
/**
92-
* Hover and focus land well ahead of a click, so they also prefetch the route payload. A press
93-
* does not: the navigation follows within a frame or two, and a prefetch started then is a
94-
* second request the navigation ends up waiting on (tree, then data) instead of its own one.
95-
*/
96-
const prefetchForIntent = () => {
97-
cancelScheduledPrefetch()
98-
if (isCurrentRoute) return
99-
warmForIntent()
10087
setShouldPrefetchRoute(true)
88+
onIntent?.()
10189
}
10290

10391
const schedulePrefetch = () => {
@@ -139,13 +127,13 @@ function IntentAwareSettingsLink({
139127
onPointerDown={(event) => {
140128
onPointerDown?.(event)
141129
if (event.pointerType === 'mouse' && isUnmodifiedPrimaryPointer(event)) {
142-
warmForIntent()
130+
prefetchForIntent()
143131
}
144132
}}
145133
onPointerUp={(event) => {
146134
onPointerUp?.(event)
147135
if (event.pointerType !== 'mouse' && isUnmodifiedPrimaryPointer(event)) {
148-
warmForIntent()
136+
prefetchForIntent()
149137
}
150138
}}
151139
onPointerCancel={(event) => {
@@ -164,7 +152,7 @@ function IntentAwareSettingsLink({
164152
!isCurrentRoute &&
165153
!navigationIntentRef.current
166154
) {
167-
warmForIntent()
155+
prefetchForIntent()
168156
}
169157
}}
170158
/>

‎apps/sim/components/settings/settings-navigation-provider.tsx‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ interface SettingsNavigationState {
3030

3131
const SettingsNavigationContext = createContext<SettingsNavigationState | null>(null)
3232

33+
interface SettingsNavigationProviderProps {
34+
children: ReactNode
35+
}
36+
3337
/**
3438
* Shares an in-flight settings section navigation between the sidebar that starts it and the
3539
* content area that previews it.
@@ -40,7 +44,7 @@ const SettingsNavigationContext = createContext<SettingsNavigationState | null>(
4044
* settles — on commit, and equally when the server redirects back or the navigation fails — and
4145
* because it is state rather than a Suspense fallback, the incoming section is never held back.
4246
*/
43-
export function SettingsNavigationProvider({ children }: { children: ReactNode }) {
47+
export function SettingsNavigationProvider({ children }: SettingsNavigationProviderProps) {
4448
const router = useRouter()
4549
const [pendingSection, setPendingSection] = useOptimistic<string | null>(null)
4650
const [, startTransition] = useTransition()

‎apps/sim/ee/workspace-forking/components/fork-sync-detail-view/fork-sync-detail-view.tsx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ export function ForkSyncDetailView({
114114
>
115115
<ForkSyncView
116116
controller={controller}
117-
onDirectionChange={(next) => void setDirection(next)}
117+
// A direction switch drops the in-session mapping choices, so unsaved edits confirm first.
118+
onDirectionChange={(next) => guard.guardBack(() => void setDirection(next))}
118119
/>
119120
</SettingsPanel>
120121

0 commit comments

Comments
 (0)