Skip to content

Commit 6d14ace

Browse files
waleedlatif1claude
andcommitted
fix(emcn/toast): let global status toasts survive navigation (persistAcrossRoutes)
The route-clear dismissed every toast on navigation, including the persistent realtime connection/reconnect status toast — which then never re-showed (the provider's id ref short-circuited). Add a persistAcrossRoutes flag: the navigation clear now keeps flagged toasts and only drops route-scoped ones, and the WorkspacePermissionsProvider status toasts set it. Page-scoped toasts (including actionable block errors) still clear on navigation as before. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 04dba48 commit 6d14ace

2 files changed

Lines changed: 35 additions & 4 deletions

File tree

apps/sim/app/workspace/[workspaceId]/providers/workspace-permissions-provider.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ export function WorkspacePermissionsProvider({ children }: WorkspacePermissionsP
9696

9797
clearRealtimeStatusNotification()
9898

99-
const id = toast.error(realtimeStatusMessage, { duration: 0 })
99+
const id = toast.error(realtimeStatusMessage, { duration: 0, persistAcrossRoutes: true })
100100

101101
realtimeStatusNotificationIdRef.current = id
102102
realtimeStatusNotificationMessageRef.current = realtimeStatusMessage
@@ -130,6 +130,7 @@ export function WorkspacePermissionsProvider({ children }: WorkspacePermissionsP
130130
try {
131131
toast.error('Connection unavailable', {
132132
duration: 0,
133+
persistAcrossRoutes: true,
133134
action: { label: 'Refresh', onClick: () => window.location.reload() },
134135
})
135136
setHasShownOfflineNotification(true)

apps/sim/components/emcn/components/toast/toast.tsx

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ interface ToastData {
9292
variant: ToastVariant
9393
action?: ToastAction
9494
duration: number
95+
persistAcrossRoutes: boolean
9596
}
9697

9798
type ToastInput = {
@@ -100,6 +101,13 @@ type ToastInput = {
100101
variant?: ToastVariant
101102
action?: ToastAction
102103
duration?: number
104+
/**
105+
* Keep the toast across navigation. The stack is otherwise cleared on every
106+
* route change (route-scoped notifications shouldn't trail the user); set
107+
* this for global, ongoing-state toasts like a connection/reconnect status.
108+
* @default false
109+
*/
110+
persistAcrossRoutes?: boolean
103111
}
104112

105113
type ToastFn = {
@@ -529,6 +537,7 @@ export function ToastProvider({ children }: { children?: ReactNode }) {
529537
variant: input.variant ?? 'default',
530538
action: input.action,
531539
duration: input.duration ?? (input.action ? 0 : AUTO_DISMISS_MS),
540+
persistAcrossRoutes: input.persistAcrossRoutes ?? false,
532541
}
533542
setToasts((prev) => {
534543
const next = [...prev, data]
@@ -563,6 +572,27 @@ export function ToastProvider({ children }: { children?: ReactNode }) {
563572
setHeights({})
564573
}, [])
565574

575+
/**
576+
* Clear only route-scoped toasts. Toasts flagged `persistAcrossRoutes` —
577+
* global, ongoing-state notifications like the connection status — survive,
578+
* everything else (page-scoped notifications) is cleared on navigation.
579+
*/
580+
const dismissRouteScopedToasts = useCallback(() => {
581+
setToasts((prev) => {
582+
const kept = prev.filter((t) => t.persistAcrossRoutes)
583+
if (kept.length === prev.length) return prev
584+
for (const t of prev) {
585+
if (t.persistAcrossRoutes) continue
586+
const timer = timersRef.current.get(t.id)
587+
if (timer) {
588+
clearTimeout(timer)
589+
timersRef.current.delete(t.id)
590+
}
591+
}
592+
return kept
593+
})
594+
}, [])
595+
566596
const measureToast = useCallback((id: string, height: number) => {
567597
setHeights((prev) => (prev[id] === height ? prev : { ...prev, [id]: height }))
568598
}, [])
@@ -627,10 +657,10 @@ export function ToastProvider({ children }: { children?: ReactNode }) {
627657
}
628658
}, [])
629659

630-
/** Clear the stack on navigation so a toast never trails the user across the platform. */
660+
/** On navigation, clear route-scoped toasts so they don't trail the user; `persistAcrossRoutes` toasts survive. */
631661
useEffect(() => {
632-
dismissAllToasts()
633-
}, [pathname, dismissAllToasts])
662+
dismissRouteScopedToasts()
663+
}, [pathname, dismissRouteScopedToasts])
634664

635665
/** Held in a ref (seeded once from the stable `addToast`) so the module-level `toast` binds to the live provider. */
636666
const toastFn = useRef<ToastFn>(createToastFn(addToast))

0 commit comments

Comments
 (0)