From 3b33a3efdb5e879f7f5ce8ce63069fb0a89dbd36 Mon Sep 17 00:00:00 2001 From: qorexdev Date: Sat, 28 Mar 2026 04:01:49 +0500 Subject: [PATCH] refactor: move onboarding animation state into event handlers Remove useEffect that watched stepIndex to drive shouldAnimate/contentKey. stepIndex only changes from handleNext/handlePrev, so set shouldAnimate directly in those handlers. Replace contentKey state (always equal to stepIndex) with stepIndex as the animation key. Fixes #1017 --- surfsense_web/components/onboarding-tour.tsx | 26 +++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/surfsense_web/components/onboarding-tour.tsx b/surfsense_web/components/onboarding-tour.tsx index 03fad87b6..e097c212d 100644 --- a/surfsense_web/components/onboarding-tour.tsx +++ b/surfsense_web/components/onboarding-tour.tsx @@ -160,6 +160,8 @@ function TourTooltip({ onPrev, onSkip, isDarkMode, + shouldAnimate, + onAnimationEnd, }: { step: TourStep; stepIndex: number; @@ -170,23 +172,12 @@ function TourTooltip({ onPrev: () => void; onSkip: () => void; isDarkMode: boolean; + shouldAnimate: boolean; + onAnimationEnd: () => void; }) { - const [contentKey, setContentKey] = useState(stepIndex); - const [shouldAnimate, setShouldAnimate] = useState(false); - const prevStepIndexRef = useRef(stepIndex); const isLastStep = stepIndex === totalSteps - 1; const isFirstStep = stepIndex === 0; - // Update content key when step changes to trigger animation - // Only animate if stepIndex actually changes (not on initial mount) - useEffect(() => { - if (prevStepIndexRef.current !== stepIndex) { - setShouldAnimate(true); - setContentKey(stepIndex); - prevStepIndexRef.current = stepIndex; - } - }, [stepIndex]); - const bgColor = isDarkMode ? "#27272a" : "#ffffff"; const textColor = isDarkMode ? "#ffffff" : "#18181b"; const mutedTextColor = isDarkMode ? "#a1a1aa" : "#71717a"; @@ -358,11 +349,11 @@ function TourTooltip({ > {/* Content */}
setShouldAnimate(false)} + onAnimationEnd={onAnimationEnd} >

{step.title} @@ -427,6 +418,7 @@ export function OnboardingTour() { const isMobile = useIsMobile(); const [isActive, setIsActive] = useState(false); const [stepIndex, setStepIndex] = useState(0); + const [shouldAnimate, setShouldAnimate] = useState(false); const [targetEl, setTargetEl] = useState(null); const [spotlightTargetEl, setSpotlightTargetEl] = useState(null); const [spotlightStepTarget, setSpotlightStepTarget] = useState(null); @@ -664,6 +656,7 @@ export function OnboardingTour() { const handleNext = useCallback(() => { if (stepIndex < TOUR_STEPS.length - 1) { retryCountRef.current = 0; + setShouldAnimate(true); setStepIndex(stepIndex + 1); } else { // Tour completed - save to localStorage @@ -678,6 +671,7 @@ export function OnboardingTour() { const handlePrev = useCallback(() => { if (stepIndex > 0) { retryCountRef.current = 0; + setShouldAnimate(true); setStepIndex(stepIndex - 1); } }, [stepIndex]); @@ -772,6 +766,8 @@ export function OnboardingTour() { onPrev={handlePrev} onSkip={handleSkip} isDarkMode={isDarkMode} + shouldAnimate={shouldAnimate} + onAnimationEnd={() => setShouldAnimate(false)} /> )}