diff --git a/mobile/app/h/[hostId]/session/[worktreeId].tsx b/mobile/app/h/[hostId]/session/[worktreeId].tsx index 6c214f1d6..fb2b78b76 100644 --- a/mobile/app/h/[hostId]/session/[worktreeId].tsx +++ b/mobile/app/h/[hostId]/session/[worktreeId].tsx @@ -1168,17 +1168,6 @@ export default function SessionScreen() { [clearToastHideTimer] ) - useEffect(() => { - return () => { - // Why: toast timers can outlive quick route changes; bump the sequence - // so pending animation callbacks cannot clear a newer/unmounted surface. - toastSeqRef.current += 1 - clearToastHideTimer() - clearDelayedActionTimers() - clearTerminalLiveInputFocusTimer(liveInputFocusTimerRef) - } - }, [clearDelayedActionTimers, clearToastHideTimer]) - const dictation = useMobileDictation({ client, enabled: canSend, @@ -2977,9 +2966,21 @@ export default function SessionScreen() { }, [stopAccessoryRepeat] ) - useEffect(() => { - return () => stopAccessoryRepeat() - }, [stopAccessoryRepeat]) + const setMobileSessionRootRef = useCallback( + (node: View | null): void => { + if (node !== null) { + return + } + // Why: route-level timers can outlive quick session changes; clear them + // from the root lifecycle without passive cleanup-only Effects. + toastSeqRef.current += 1 + clearToastHideTimer() + clearDelayedActionTimers() + clearTerminalLiveInputFocusTimer(liveInputFocusTimerRef) + stopAccessoryRepeat() + }, + [clearDelayedActionTimers, clearToastHideTimer, stopAccessoryRepeat] + ) const handleSelectionMode = useCallback((handle: string, active: boolean) => { if (handle !== activeHandleRef.current) return @@ -3662,7 +3663,7 @@ export default function SessionScreen() { : [] return ( - +