import { useState, useEffect, useCallback, useRef } from 'react'; import { useNavigate } from 'react-router-dom'; import { useAuthStore } from '../store/auth'; import { AuroraCanvas } from '../components/AuroraCanvas'; import { LoginModal } from '../components/LoginModal'; import { ForceChangePasswordModal } from '../components/ForceChangePasswordModal'; import logoImg from '../assets/logo_512.png'; import styles from './LandingPage.module.css'; interface Props { autoLogin?: boolean; } export function LandingPage({ autoLogin }: Props) { const navigate = useNavigate(); const isAuthenticated = useAuthStore((s) => s.isAuthenticated); const mustChangePassword = useAuthStore((s) => s.mustChangePassword); const [showLogin, setShowLogin] = useState(false); const [showForceChange, setShowForceChange] = useState(false); const [showSpark, setShowSpark] = useState(false); const [playing, setPlaying] = useState(false); const audioRef = useRef(null); const [barHeights, setBarHeights] = useState([0.4, 0.4, 0.4, 0.4]); // Auto-open login modal when rendered via /login route useEffect(() => { if (autoLogin && !isAuthenticated) { setShowLogin(true); } }, [autoLogin, isAuthenticated]); // Auto-show force change password modal if authenticated but must change useEffect(() => { if (isAuthenticated && mustChangePassword) { setShowForceChange(true); } }, [isAuthenticated, mustChangePassword]); const handleAirDrama = () => { if (isAuthenticated) { if (mustChangePassword) { setShowForceChange(true); } else { navigate('/app'); } } else { setShowLogin(true); } }; const handleAirSpark = () => { setShowSpark(true); }; const handleLoginSuccess = () => { setShowLogin(false); if (mustChangePassword) { setShowForceChange(true); } else { navigate('/app', { replace: true }); } }; const handleForceChangeSuccess = () => { setShowForceChange(false); navigate('/app', { replace: true }); }; // Close spark overlay on click const closeSpark = useCallback(() => setShowSpark(false), []); // Animate bar heights when playing useEffect(() => { if (!playing) { setBarHeights([0.4, 0.4, 0.4, 0.4]); return; } const id = setInterval(() => { setBarHeights([ 0.3 + Math.random() * 0.7, 0.3 + Math.random() * 0.7, 0.3 + Math.random() * 0.7, 0.3 + Math.random() * 0.7, ]); }, 150); return () => clearInterval(id); }, [playing]); // Stop music on unmount (e.g. navigating to /app after login) useEffect(() => { return () => { if (audioRef.current) { audioRef.current.pause(); audioRef.current.currentTime = 0; } }; }, []); // Music toggle const toggleMusic = useCallback(() => { if (!audioRef.current) { audioRef.current = new Audio('/bgm.mp3'); audioRef.current.loop = true; } if (playing) { audioRef.current.pause(); } else { audioRef.current.play(); } setPlaying((p) => !p); }, [playing]); return (
{/* Layer 1-4: Aurora background */} {/* Layer 5: Content */}
AirDrama

AIRFLOW STUDIO

AI VISUAL NARRATIVE

逐帧造梦
灵感自燃
{/* Easter egg */}

Every frame was once just air.

{/* Air Spark full-screen overlay */} {showSpark && (

别急,土豆🥔正在 coding 中

灵感自燃功能即将上线

)} {/* Music easter egg — bottom right */} {/* Login modal */} setShowLogin(false)} onSuccess={handleLoginSuccess} /> {/* Force change password modal (unclosable) */} {showForceChange && ( )}
); }