209 lines
7.2 KiB
TypeScript
209 lines
7.2 KiB
TypeScript
import { useState, useEffect, lazy, Suspense } from 'react';
|
|
import { useLocation } from 'react-router-dom';
|
|
import MatrixRain from '@/components/MatrixRain';
|
|
import LoadingScreen from '@/components/LoadingScreen';
|
|
import MainLayout from '@/components/MainLayout';
|
|
import MatrixCursor from '@/components/MatrixCursor';
|
|
import { VERIFIED_KEY, BYPASS_KEY } from '@/components/HumanVerification';
|
|
import { useSettings } from '@/contexts/SettingsContext';
|
|
import { useAchievements } from '@/contexts/AchievementsContext';
|
|
import { useKonamiCode } from '@/hooks/useKonamiCode';
|
|
import { useZenMode } from '@/hooks/useZenMode';
|
|
import { toast } from '@/hooks/use-toast';
|
|
import { AudioBlockedOverlay } from '@/components/AudioBlockedOverlay';
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
|
|
// Lazy load conditionally rendered components to reduce initial bundle
|
|
const HumanVerification = lazy(() => import('@/components/HumanVerification'));
|
|
const MusicPlayer = lazy(() => import('@/components/MusicPlayer'));
|
|
const SettingsPanel = lazy(() => import('@/components/SettingsPanel'));
|
|
const CryptoConsentModal = lazy(() => import('@/components/CryptoConsentModal'));
|
|
const TerminalCommand = lazy(() => import('@/components/TerminalCommand'));
|
|
|
|
const Index = () => {
|
|
const [isVerified, setIsVerified] = useState(() => {
|
|
// Check localStorage or bypass URL
|
|
if (localStorage.getItem(VERIFIED_KEY) === 'true') return true;
|
|
const params = new URLSearchParams(window.location.search);
|
|
if (params.has(BYPASS_KEY) || window.location.pathname.includes(BYPASS_KEY)) return true;
|
|
return false;
|
|
});
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [isRedTheme, setIsRedTheme] = useState(() => {
|
|
const saved = localStorage.getItem('themeColor');
|
|
// Default to red theme unless explicitly set to green
|
|
return saved !== 'green';
|
|
});
|
|
const [showConsentModal, setShowConsentModal] = useState(false);
|
|
const [konamiActive, setKonamiActive] = useState(false);
|
|
|
|
const {
|
|
crtEnabled,
|
|
matrixEnabled,
|
|
playSound,
|
|
cryptoConsent,
|
|
showAudioOverlay,
|
|
enableAudio,
|
|
disableAudio,
|
|
soundEnabled,
|
|
setSoundEnabled
|
|
} = useSettings();
|
|
const { unlockAchievement } = useAchievements();
|
|
const { activated: konamiActivated, reset: resetKonami } = useKonamiCode();
|
|
const { isZenMode, toggleZenMode } = useZenMode();
|
|
const location = useLocation();
|
|
|
|
// Hide mini music player when on the full music page (to avoid UI duplication)
|
|
// but audio continues playing via MusicContext
|
|
const showMiniPlayer = location.pathname !== '/music';
|
|
|
|
// Handle Konami code activation
|
|
useEffect(() => {
|
|
if (konamiActivated) {
|
|
setKonamiActive(true);
|
|
|
|
// Unlock achievement
|
|
unlockAchievement('konami_master');
|
|
|
|
// Play special sound sequence
|
|
playSound('success');
|
|
setTimeout(() => playSound('boot'), 200);
|
|
setTimeout(() => playSound('success'), 400);
|
|
|
|
// Show secret toast
|
|
toast({
|
|
title: "🎮 KONAMI CODE ACTIVATED",
|
|
description: "You found the secret! You are a true hacker.",
|
|
});
|
|
|
|
// Reset after animation
|
|
setTimeout(() => {
|
|
setKonamiActive(false);
|
|
resetKonami();
|
|
}, 3000);
|
|
}
|
|
}, [konamiActivated, playSound, resetKonami, unlockAchievement]);
|
|
|
|
// Persist theme to localStorage
|
|
useEffect(() => {
|
|
localStorage.setItem('themeColor', isRedTheme ? 'red' : 'green');
|
|
}, [isRedTheme]);
|
|
|
|
useEffect(() => {
|
|
const timer = setTimeout(() => {
|
|
setIsLoading(false);
|
|
// Show consent modal after loading if user hasn't accepted yet (null = needs prompt)
|
|
if (cryptoConsent === null) {
|
|
setShowConsentModal(true);
|
|
}
|
|
}, 3000); // Extended to 3s for boot sequence
|
|
|
|
return () => clearTimeout(timer);
|
|
}, [cryptoConsent]);
|
|
|
|
useEffect(() => {
|
|
if (isRedTheme) {
|
|
document.documentElement.classList.add('red-theme');
|
|
document.body.classList.add('red-theme');
|
|
} else {
|
|
document.documentElement.classList.remove('red-theme');
|
|
document.body.classList.remove('red-theme');
|
|
}
|
|
}, [isRedTheme]);
|
|
|
|
const toggleTheme = () => {
|
|
setIsRedTheme(!isRedTheme);
|
|
playSound('click');
|
|
unlockAchievement('theme_switcher');
|
|
// Notify other components of theme change
|
|
window.dispatchEvent(new CustomEvent('themeChange', { detail: { isRedTheme: !isRedTheme } }));
|
|
};
|
|
|
|
const handleConsentClose = () => {
|
|
setShowConsentModal(false);
|
|
};
|
|
|
|
const handleVerified = () => {
|
|
setIsVerified(true);
|
|
unlockAchievement('verified_human');
|
|
};
|
|
|
|
// Show verification gate if not verified
|
|
if (!isVerified) {
|
|
return (
|
|
<div className={`min-h-screen overflow-x-hidden ${crtEnabled ? 'crt' : ''}`}>
|
|
{matrixEnabled && <MatrixRain color={isRedTheme ? '#FF0000' : '#00FF00'} />}
|
|
<Suspense fallback={null}>
|
|
<HumanVerification onVerified={handleVerified} />
|
|
</Suspense>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className={`min-h-screen overflow-x-hidden ${crtEnabled ? 'crt' : ''} ${konamiActive ? 'konami-active' : ''}`}>
|
|
<MatrixCursor />
|
|
{matrixEnabled && <MatrixRain color={isRedTheme ? '#FF0000' : '#00FF00'} />}
|
|
<LoadingScreen isLoading={isLoading} />
|
|
|
|
{/* Audio blocked overlay */}
|
|
{showAudioOverlay && soundEnabled && (
|
|
<AudioBlockedOverlay
|
|
onEnableAudio={enableAudio}
|
|
onDisableAudio={disableAudio}
|
|
/>
|
|
)}
|
|
|
|
{/* Moving scanline - only visible when CRT is enabled */}
|
|
<div className="moving-scanline" />
|
|
|
|
{/* Zen Mode indicator */}
|
|
<AnimatePresence>
|
|
{isZenMode && (
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
exit={{ opacity: 0, y: 20 }}
|
|
transition={{ duration: 0.3 }}
|
|
className="fixed bottom-4 left-1/2 -translate-x-1/2 z-50 bg-background/80 border border-primary/50 px-4 py-2 rounded-lg backdrop-blur-sm"
|
|
>
|
|
<p className="text-primary text-sm font-minecraft">
|
|
ZEN MODE • Press <kbd className="bg-primary/20 px-1.5 py-0.5 rounded mx-1">Z</kbd> or <kbd className="bg-primary/20 px-1.5 py-0.5 rounded mx-1">ESC</kbd> to exit
|
|
</p>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
|
|
{!isLoading && (
|
|
<Suspense fallback={null}>
|
|
<CryptoConsentModal isOpen={showConsentModal} onClose={handleConsentClose} />
|
|
|
|
{/* Hide UI elements in Zen Mode */}
|
|
<AnimatePresence>
|
|
{!isZenMode && (
|
|
<motion.div
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
exit={{ opacity: 0 }}
|
|
transition={{ duration: 0.3 }}
|
|
>
|
|
<SettingsPanel onToggleTheme={toggleTheme} isRedTheme={isRedTheme} />
|
|
<TerminalCommand />
|
|
|
|
<div className="relative z-10 flex flex-col items-center min-h-screen pb-16">
|
|
<MainLayout />
|
|
</div>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
|
|
{/* Music player stays visible in Zen Mode */}
|
|
{showMiniPlayer && <MusicPlayer />}
|
|
</Suspense>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Index;
|