Fix: first screen's looping animations (mascot bob, island ring, start button) never ran until a tab switch; AnimatePresence initial={false} leaked into children

This commit is contained in:
2026-10-08 18:12:35 +03:30
parent 6930c83e07
commit 8879b42b1c

View File

@@ -1,4 +1,4 @@
import { lazy, Suspense, useEffect, useState, type ComponentType } from 'react'; import { lazy, Suspense, useEffect, useRef, useState, type ComponentType } from 'react';
import { AnimatePresence, motion } from 'motion/react'; import { AnimatePresence, motion } from 'motion/react';
import { CheckCircle2, XCircle } from 'lucide-react'; import { CheckCircle2, XCircle } from 'lucide-react';
import { api, type Grade, type UserProfile } from '@/api'; import { api, type Grade, type UserProfile } from '@/api';
@@ -71,15 +71,22 @@ function Screens({ onSignedOut }: Pick<Props, 'onSignedOut'>) {
const nav = useNav(); const nav = useNav();
const { route } = nav; const { route } = nav;
const showNav = isTab(route.name) && nav.tab === route.name; const showNav = isTab(route.name) && nav.tab === route.name;
// صفحه‌ی اولِ بعد از ورود بی‌انیمیشنِ ورود می‌آید، ولی این را روی خودِ motion.div می‌گذاریم نه
// `<AnimatePresence initial={false}>`: آن یکی به همه‌ی فرزندهای motionِ صفحه‌ی اول هم سرایت می‌کند و
// انیمیشن‌های تکرارشونده‌شان (ماسکوتِ بالا‌پایین، حلقه‌ی جزیره، دکمه‌ی «شروع») تا اولین تعویضِ تب شروع نمی‌شدند.
const firstRender = useRef(true);
useEffect(() => {
firstRender.current = false;
}, []);
return ( return (
<> <>
<div className="relative flex-1 overflow-hidden bg-page"> <div className="relative flex-1 overflow-hidden bg-page">
<AnimatePresence mode="popLayout" initial={false}> <AnimatePresence mode="popLayout">
<motion.div <motion.div
key={JSON.stringify(route)} key={JSON.stringify(route)}
className="absolute inset-0 flex flex-col bg-page" className="absolute inset-0 flex flex-col bg-page"
initial={{ opacity: 0, x: -14 }} initial={firstRender.current ? false : { opacity: 0, x: -14 }}
animate={{ opacity: 1, x: 0 }} animate={{ opacity: 1, x: 0 }}
// صفحه‌ی قبلی نمی‌پرد و محو نمی‌شود، زیرِ صفحه‌ی تازه می‌ماند تا کامل بیاید. با محوشدنِ هم‌زمانِ // صفحه‌ی قبلی نمی‌پرد و محو نمی‌شود، زیرِ صفحه‌ی تازه می‌ماند تا کامل بیاید. با محوشدنِ هم‌زمانِ
// هر دو، پس‌زمینه‌ی تیره‌ی body از وسط دیده می‌شد و تعویضِ تب «گلیچ» می‌زد (از روی کد؛ با // هر دو، پس‌زمینه‌ی تیره‌ی body از وسط دیده می‌شد و تعویضِ تب «گلیچ» می‌زد (از روی کد؛ با