import { useEffect, useState } from 'react'; import { motion, AnimatePresence } from 'motion/react'; import { Loader2, Coins, ShoppingCart, ShieldCheck } from 'lucide-react'; import Sidebar from './components/Sidebar'; import CodingSection from './components/CodingSection'; import Compiler from './components/Compiler'; import AptitudeSection from './components/AptitudeSection'; import AptitudeCategoryPage from './components/AptitudeCategoryPage'; import AptitudeContentPage from './components/AptitudeContentPage'; import SolutionView from './components/SolutionView'; import CodingSheet from './components/CodingSheet'; import AuthPage from './components/AuthPage'; import LandingPage, { type LandingAuthMode } from './components/LandingPage'; import SystemDesignSection from './components/SystemDesignSection'; import CSFundamentalsSection from './components/CSFundamentalsSection'; import LearningPathsPage from './components/LearningPathsPage'; // SubTopicDetailsPage removed import AITeacherPage from './components/AITeacherPage'; import ArenaHubSection from './components/ArenaHubSection'; import ArenaLobby from './components/ArenaLobby'; import ArenaBattle from './components/ArenaBattle'; import ProfileSection from './components/ProfileSectionNew'; import SettingsPage from './components/SettingsPage'; import Dashboard from './components/Dashboard'; import Playground from './components/Playground'; import ShoppingPage from './components/ShoppingPage'; import WeeklyContestPage from './components/WeeklyContestPage'; import DailyContestPage from './components/DailyContestPage'; import CompilerPage from './pages/CompilerPage'; import FullLeaderboardPage from './components/FullLeaderboardPage'; import DBMSPage from './components/DBMSPage'; import DBMSContentPage from './components/DBMSContentPage'; import CNPage from './components/CNPage'; import CNContentPage from './components/CNContentPage'; import OSPage from './components/OSPage'; import OSContentPage from './components/OSContentPage'; import CoinHistoryPage from './components/CoinHistoryPage'; import QuestsPage from './components/QuestsPage'; import QuestDetailPage from './components/QuestDetailPage'; import { ALL_QUESTS } from './data/questData'; import { AUTH_TOKEN_KEY, addCoins, fetchSessionUser, fetchUserProgress, importUserProgress, recordSolvedQuestion, spendCoins, type AuthUser, type ProgressLeaderboardEntry, type UserProgressStats, } from './lib/authClient'; import { clearAnalyticsEvents, createDefaultPreferences, fontSizeToRootRem, loadUserPreferences, playUiSound, saveUserPreferences, sendBrowserNotification, trackAnalyticsEvent, type UserPreferences, userScopedStorageKey, } from './lib/preferences'; import { type CodingQuestion, codingQuestions } from './data/codingQuestions'; const ROUTE_TO_SECTION: Record = { '/compiler': 'compiler', }; const SECTION_TO_ROUTE: Record = { compiler: '/compiler', }; function getInitialSectionFromPath() { if (typeof window === 'undefined') { return 'dashboard'; } return ROUTE_TO_SECTION[window.location.pathname] ?? 'dashboard'; } export default function App() { return ; } function AppContent() { const [user, setUser] = useState(null); const [isAuthReady, setIsAuthReady] = useState(false); const [isProgressReady, setIsProgressReady] = useState(false); const [showLanding, setShowLanding] = useState(true); const [authMode, setAuthMode] = useState('login'); const [activeSection, setActiveSection] = useState(getInitialSectionFromPath); const [activeAptitudeCategory, setActiveAptitudeCategory] = useState(null); const [activeAptitudeTopicNo, setActiveAptitudeTopicNo] = useState(null); const [activeAptitudeMode, setActiveAptitudeMode] = useState<'study' | 'concept' | 'revision'>('study'); const [selectedQuestion, setSelectedQuestion] = useState(null); const [viewMode, setViewMode] = useState<'code' | 'solution'>('code'); const [solvedQuestionIds, setSolvedQuestionIds] = useState([]); const [dailyActivity, setDailyActivity] = useState>({}); const [dailyActivityBreakdown, setDailyActivityBreakdown] = useState>>({}); const [leaderboard, setLeaderboard] = useState([]); const [codingLeaderboard, setCodingLeaderboard] = useState([]); const [codingSolvedCount, setCodingSolvedCount] = useState(0); const [arenaQuestion, setArenaQuestion] = useState(null); const [arenaDifficulty, setArenaDifficulty] = useState<'Easy' | 'Medium' | 'Hard'>('Medium'); const [preferences, setPreferences] = useState(() => { if (typeof window !== 'undefined' && localStorage.getItem('theme_mode') === 'light') { return createDefaultPreferences('light'); } return createDefaultPreferences('dark'); }); const [isTwoFactorVerified, setIsTwoFactorVerified] = useState(true); const [twoFactorCode, setTwoFactorCode] = useState(''); const [twoFactorError, setTwoFactorError] = useState(''); const [selectedDBMSTopicNo, setSelectedDBMSTopicNo] = useState(null); const [selectedCNTopicNo, setSelectedCNTopicNo] = useState(null); const [selectedOSSectionNo, setSelectedOSSectionNo] = useState(null); const [activeQuestId, setActiveQuestId] = useState(null); useEffect(() => { if (typeof window === 'undefined') { return; } if (preferences.themeMode === 'light') { document.documentElement.classList.add('day-mode'); localStorage.setItem('theme_mode', 'light'); } else { document.documentElement.classList.remove('day-mode'); localStorage.setItem('theme_mode', 'dark'); } document.documentElement.style.fontSize = fontSizeToRootRem(preferences.fontSize); document.documentElement.lang = interfaceLanguageToHtmlLang(preferences.language); }, [preferences.fontSize, preferences.language, preferences.themeMode]); useEffect(() => { if (typeof window === 'undefined') { return; } const route = SECTION_TO_ROUTE[activeSection]; if (route && window.location.pathname !== route) { window.history.pushState({}, '', route); return; } if (!route && ROUTE_TO_SECTION[window.location.pathname]) { window.history.pushState({}, '', '/'); } }, [activeSection]); useEffect(() => { if (typeof window === 'undefined') { return; } const handlePopState = () => { setActiveSection(ROUTE_TO_SECTION[window.location.pathname] ?? 'dashboard'); }; window.addEventListener('popstate', handlePopState); return () => window.removeEventListener('popstate', handlePopState); }, []); // Hide scrollbars after 3s of mouse inactivity, show on mouse move useEffect(() => { let timer: ReturnType; const showScrollbars = () => { document.body.classList.remove('scrollbar-inactive'); clearTimeout(timer); timer = setTimeout(() => { document.body.classList.add('scrollbar-inactive'); }, 1500); }; // Start the timer immediately on mount timer = setTimeout(() => { document.body.classList.add('scrollbar-inactive'); }, 1500); window.addEventListener('mousemove', showScrollbars); window.addEventListener('mousedown', showScrollbars); window.addEventListener('touchstart', showScrollbars); window.addEventListener('keydown', showScrollbars); window.addEventListener('scroll', showScrollbars, true); return () => { clearTimeout(timer); window.removeEventListener('mousemove', showScrollbars); window.removeEventListener('mousedown', showScrollbars); window.removeEventListener('touchstart', showScrollbars); window.removeEventListener('keydown', showScrollbars); window.removeEventListener('scroll', showScrollbars, true); }; }, []); useEffect(() => { if (!user) { setPreferences((current) => current.themeMode === 'light' ? createDefaultPreferences('light') : createDefaultPreferences('dark'), ); setIsTwoFactorVerified(true); setTwoFactorCode(''); setTwoFactorError(''); return; } const loaded = loadUserPreferences(user.id, preferences.themeMode); setPreferences(loaded); if (!loaded.dataAnalytics) { clearAnalyticsEvents(user.id); } }, [user?.id]); useEffect(() => { if (!user) { return; } const sessionKey = userScopedStorageKey('two_factor_verified', user.id); const alreadyVerified = window.sessionStorage.getItem(sessionKey) === 'true'; const requiresTwoFactor = preferences.twoFactorEnabled && preferences.twoFactorCode.length >= 6; setIsTwoFactorVerified(!requiresTwoFactor || alreadyVerified); setTwoFactorCode(''); setTwoFactorError(''); }, [preferences.twoFactorCode, preferences.twoFactorEnabled, user?.id]); useEffect(() => { let cancelled = false; (async () => { // Handle Google OAuth redirect callback (/auth/fallback/?token=...) if (typeof window !== 'undefined') { const params = new URLSearchParams(window.location.search); const oauthToken = params.get('token'); const oauthError = params.get('error'); if (oauthError) { // Clean up URL so the error param doesn't persist window.history.replaceState({}, '', window.location.pathname); // Fall through to show AuthPage with an error (handled below) } else if (oauthToken) { localStorage.setItem(AUTH_TOKEN_KEY, oauthToken); // Clean up the URL so the token isn't visible window.history.replaceState({}, '', '/'); try { const sessionUser = await fetchSessionUser(oauthToken); if (!cancelled) { if (sessionUser) { setUser(sessionUser); setShowLanding(false); } else { localStorage.removeItem(AUTH_TOKEN_KEY); } setIsAuthReady(true); } } catch { if (!cancelled) { setIsAuthReady(true); } } return; } } const token = localStorage.getItem(AUTH_TOKEN_KEY); if (!token) { if (!cancelled) { setUser(null); setIsAuthReady(true); } return; } try { const sessionUser = await fetchSessionUser(token); if (cancelled) { return; } if (!sessionUser) { localStorage.removeItem(AUTH_TOKEN_KEY); setUser(null); } else { setUser(sessionUser); } } catch { if (!cancelled) { setUser(null); } } if (!cancelled) { setIsAuthReady(true); } })(); return () => { cancelled = true; }; }, []); useEffect(() => { if (!user) { return; } const token = localStorage.getItem(AUTH_TOKEN_KEY); if (!token) { return; } let cancelled = false; let midnightTimer: number | undefined; const syncSession = async () => { try { const sessionUser = await fetchSessionUser(token); if (cancelled) { return; } if (!sessionUser) { localStorage.removeItem(AUTH_TOKEN_KEY); setSolvedQuestionIds([]); setDailyActivity({}); setDailyActivityBreakdown({}); setLeaderboard([]); setIsProgressReady(true); setUser(null); return; } setUser((currentUser) => { if (!currentUser || currentUser.id !== sessionUser.id) { return sessionUser; } return { ...currentUser, ...sessionUser, }; }); scheduleMidnightSync(); } catch { if (!cancelled) { scheduleMidnightSync(); } } }; const scheduleMidnightSync = () => { if (midnightTimer) { window.clearTimeout(midnightTimer); } midnightTimer = window.setTimeout(() => { void syncSession(); }, msUntilNextLocalMidnight()); }; const handleWindowFocus = () => { void syncSession(); }; const handleVisibilityChange = () => { if (document.visibilityState === 'visible') { void syncSession(); } }; scheduleMidnightSync(); window.addEventListener('focus', handleWindowFocus); document.addEventListener('visibilitychange', handleVisibilityChange); return () => { cancelled = true; if (midnightTimer) { window.clearTimeout(midnightTimer); } window.removeEventListener('focus', handleWindowFocus); document.removeEventListener('visibilitychange', handleVisibilityChange); }; }, [user?.id]); useEffect(() => { const handleUserUpdate = (event: Event) => { const customEvent = event as CustomEvent; setUser(customEvent.detail); }; window.addEventListener('user-updated', handleUserUpdate); return () => { window.removeEventListener('user-updated', handleUserUpdate); }; }, []); useEffect(() => { if (!user || activeSection !== 'coding') return; let mounted = true; const pollInterval = setInterval(async () => { try { const token = localStorage.getItem(AUTH_TOKEN_KEY); if (!token) return; const stats = await fetchUserProgress(token); if (mounted) { setLeaderboard(stats.leaderboard); setCodingLeaderboard(stats.codingLeaderboard || []); setCodingSolvedCount(stats.codingSolvedCount || 0); } } catch (err) { // Silently fail polling } }, 5000); return () => { mounted = false; clearInterval(pollInterval); }; }, [user, activeSection]); useEffect(() => { if (!user) { setSolvedQuestionIds([]); setDailyActivity({}); setDailyActivityBreakdown({}); setLeaderboard([]); setIsProgressReady(true); return; } const token = localStorage.getItem(AUTH_TOKEN_KEY); if (!token) { setSolvedQuestionIds([]); setDailyActivity({}); setDailyActivityBreakdown({}); setLeaderboard([]); setIsProgressReady(true); return; } let cancelled = false; setIsProgressReady(false); const loadProgress = async () => { try { let stats = await fetchUserProgress(token); const legacySolvedQuestionIds = getLegacySolvedQuestionIds(user.id); const legacyDailyActivity = getLegacyDailyActivity(user.id); if (needsLegacyProgressImport(stats, legacySolvedQuestionIds, legacyDailyActivity)) { const imported = await importUserProgress(token, { solvedQuestionIds: legacySolvedQuestionIds, dailyActivity: legacyDailyActivity, }); stats = imported.stats; if (!cancelled) { setUser(imported.user); } } if (cancelled) { return; } setSolvedQuestionIds(stats.solvedQuestionIds); setDailyActivity(stats.dailyActivity); setDailyActivityBreakdown(stats.dailyActivityBreakdown || {}); setLeaderboard(stats.leaderboard); setCodingLeaderboard(stats.codingLeaderboard || []); setCodingSolvedCount(stats.codingSolvedCount || 0); } catch { if (!cancelled) { setSolvedQuestionIds(getLegacySolvedQuestionIds(user.id)); setDailyActivity(getLegacyDailyActivity(user.id)); setDailyActivityBreakdown({}); setLeaderboard([]); setCodingLeaderboard([]); setCodingSolvedCount(0); } } finally { if (!cancelled) { setIsProgressReady(true); } } }; void loadProgress(); return () => { cancelled = true; }; }, [user?.id]); useEffect(() => { if (!user) { return; } if (preferences.autoSave) { localStorage.setItem(`solved_questions_${user.id}`, JSON.stringify(solvedQuestionIds)); localStorage.setItem(`daily_activity_${user.id}`, JSON.stringify(dailyActivity)); return; } localStorage.removeItem(`solved_questions_${user.id}`); localStorage.removeItem(`daily_activity_${user.id}`); }, [dailyActivity, preferences.autoSave, solvedQuestionIds, user?.id]); useEffect(() => { if (!user) { return; } trackAnalyticsEvent(user.id, preferences.dataAnalytics, 'section.opened', { section: activeSection, }); }, [activeSection, preferences.dataAnalytics, user?.id]); const updatePreferences = (nextPreferences: UserPreferences) => { if (!user) { setPreferences(nextPreferences); return nextPreferences; } const saved = saveUserPreferences(user.id, nextPreferences); setPreferences(saved); const twoFactorSessionKey = userScopedStorageKey('two_factor_verified', user.id); if (saved.twoFactorEnabled && !preferences.twoFactorEnabled) { window.sessionStorage.setItem(twoFactorSessionKey, 'true'); setIsTwoFactorVerified(true); } if (!saved.twoFactorEnabled) { window.sessionStorage.removeItem(twoFactorSessionKey); setIsTwoFactorVerified(true); } if (!saved.dataAnalytics) { clearAnalyticsEvents(user.id); } return saved; }; const handleSelectQuestion = (question: CodingQuestion, mode: 'code' | 'solution') => { setSelectedQuestion(question); setViewMode(mode); }; const handleQuestionSolved = async (item: { id: string; title: string; difficulty: string }, language?: string) => { if (!user) { return; } const token = localStorage.getItem(AUTH_TOKEN_KEY); if (!token) { return; } try { const result = await recordSolvedQuestion(token, { questionId: item.id, difficulty: item.difficulty, language, }); setUser(result.user); setSolvedQuestionIds(result.stats.solvedQuestionIds); setDailyActivity(result.stats.dailyActivity); setDailyActivityBreakdown(result.stats.dailyActivityBreakdown || {}); setLeaderboard(result.stats.leaderboard); setCodingLeaderboard(result.stats.codingLeaderboard || []); setCodingSolvedCount(result.stats.codingSolvedCount || 0); trackAnalyticsEvent(user.id, preferences.dataAnalytics, 'question.solved', { questionId: item.id, difficulty: item.difficulty, }); playUiSound(preferences.soundEnabled, 'success'); sendBrowserNotification(preferences.pushNotifications, 'Problem solved', { body: `${item.title} was added to your completed list.`, icon: '/favicon.ico', }); } catch (error) { console.error('[App] Failed to record solved question:', error); playUiSound(preferences.soundEnabled, 'error'); } }; const handlePurchase = async (price: number, itemName?: string) => { const token = localStorage.getItem(AUTH_TOKEN_KEY); if (!token) { return false; } try { const result = await spendCoins(token, price); setUser(result.user); setSolvedQuestionIds(result.stats.solvedQuestionIds); setDailyActivity(result.stats.dailyActivity); setDailyActivityBreakdown(result.stats.dailyActivityBreakdown || {}); setLeaderboard(result.stats.leaderboard); setCodingLeaderboard(result.stats.codingLeaderboard || []); setCodingSolvedCount(result.stats.codingSolvedCount || 0); if (user) { trackAnalyticsEvent(user.id, preferences.dataAnalytics, 'reward.claimed', { price, itemName: itemName ?? null, }); } playUiSound(preferences.soundEnabled, 'success'); sendBrowserNotification(preferences.pushNotifications, 'Reward claimed', { body: itemName ? `${itemName} has been reserved from the swag store.` : 'Your swag store purchase was completed.', icon: '/favicon.ico', }); return true; } catch (error) { console.error('[App] Failed to spend coins:', error); playUiSound(preferences.soundEnabled, 'error'); return false; } }; const handleContestPrizeClaim = async (amount: number, contestId: string, rank: number) => { if (!user) { return false; } const token = localStorage.getItem(AUTH_TOKEN_KEY); if (!token) { return false; } try { const result = await addCoins(token, amount); setUser(result.user); try { const stats = await fetchUserProgress(token); setSolvedQuestionIds(stats.solvedQuestionIds); setDailyActivity(stats.dailyActivity); setLeaderboard(stats.leaderboard); setCodingLeaderboard(stats.codingLeaderboard || []); setCodingSolvedCount(stats.codingSolvedCount || 0); } catch { // Leave the rest of the UI as-is if the leaderboard refresh fails. } trackAnalyticsEvent(user.id, preferences.dataAnalytics, 'contest.prize.claimed', { contestId, rank, amount, }); playUiSound(preferences.soundEnabled, 'success'); sendBrowserNotification(preferences.pushNotifications, 'Contest reward claimed', { body: `${amount} coins were added to your account for finishing rank #${rank}.`, icon: '/favicon.ico', }); return true; } catch (error) { console.error('[App] Failed to claim contest prize:', error); playUiSound(preferences.soundEnabled, 'error'); return false; } }; const openAuthPage = (mode: LandingAuthMode = 'login') => { setAuthMode(mode); setShowLanding(false); window.scrollTo(0, 0); }; if (!isAuthReady || (user && !isProgressReady)) { return (
); } if (!user) { if (showLanding) { return ; } return ( setShowLanding(true)} /> ); } const handleLogout = async () => { if (user) { window.sessionStorage.removeItem(userScopedStorageKey('two_factor_verified', user.id)); } localStorage.removeItem(AUTH_TOKEN_KEY); setSolvedQuestionIds([]); setDailyActivity({}); setLeaderboard([]); setAuthMode('login'); setShowLanding(true); setUser(null); }; const handleVerifyTwoFactor = () => { if (!user) { return; } if (twoFactorCode.trim() !== preferences.twoFactorCode) { setTwoFactorError('That verification code does not match your saved security code.'); playUiSound(preferences.soundEnabled, 'error'); return; } window.sessionStorage.setItem(userScopedStorageKey('two_factor_verified', user.id), 'true'); setIsTwoFactorVerified(true); setTwoFactorCode(''); setTwoFactorError(''); playUiSound(preferences.soundEnabled, 'success'); }; if (user && !isTwoFactorVerified) { return (

Verify your sign-in

Two-step verification is enabled for this account. Enter the 6-digit security code from your settings page to unlock RYP.

{ setTwoFactorCode(event.target.value.replace(/\D/g, '').slice(0, 6)); if (twoFactorError) { setTwoFactorError(''); } }} onKeyDown={(event) => { if (event.key === 'Enter') { event.preventDefault(); handleVerifyTwoFactor(); } }} placeholder="Enter 6 digits" className="w-full rounded-2xl border border-zinc-800 bg-zinc-900/80 px-4 py-3 text-lg font-bold tracking-[0.35em] text-white outline-none transition-all focus:border-emerald-500/50 focus:ring-1 focus:ring-emerald-500/30" /> {twoFactorError &&

{twoFactorError}

}
); } if (selectedQuestion) { if (viewMode === 'solution') { return ( setSelectedQuestion(null)} onGoToCode={() => setViewMode('code')} fontSize={preferences.fontSize} /> ); } return ( setSelectedQuestion(null)} onSolved={(lang) => handleQuestionSolved(selectedQuestion, lang)} fontSize={preferences.fontSize} /> ); } const isViewportSection = activeSection === 'dashboard' || activeSection === 'weekly-contest' || activeSection === 'daily-contest'; const isFullscreenAppSection = activeSection === 'ryp-quests' || activeSection === 'ryp-quest-detail'; const usesFullscreenSectionLayout = activeSection === 'ai-teacher' || activeSection === 'settings' || activeSection === 'compiler' || isFullscreenAppSection; return (
{activeSection === 'dashboard' && (
setActiveSection('coin-history')} whileHover={{ scale: 1.05 }} className="flex items-center gap-1.5 sm:gap-2 bg-[#0c0c0c]/80 backdrop-blur-xl border border-yellow-500/30 hover:border-yellow-500/50 hover:bg-yellow-500/10 transition-colors px-2.5 sm:px-4 py-1.5 sm:py-2 rounded-xl sm:rounded-2xl shadow-2xl cursor-pointer" > {user.coins || 0}
)}
{activeSection === 'dashboard' && ( setActiveSection('leaderboard')} /> )} {activeSection === 'coin-history' && ( setActiveSection('dashboard')} currentCoins={user.coins || 0} /> )} {activeSection === 'weekly-contest' && ( )} {activeSection === 'daily-contest' && ( )} {activeSection === 'leaderboard' && ( setActiveSection('dashboard')} /> )} {activeSection === 'ryp-quests' && ( { setActiveQuestId(questId); setActiveSection('ryp-quest-detail'); }} /> )} {activeSection === 'ryp-quest-detail' && activeQuestId && (() => { const quest = ALL_QUESTS.find((q) => q.id === activeQuestId); const token = localStorage.getItem(AUTH_TOKEN_KEY) ?? ''; return quest ? ( setActiveSection('ryp-quests')} onNavigate={(section, itemId) => { if (section === 'coding' && itemId) { const q = codingQuestions.find((cq) => cq.id === itemId); if (q) { handleSelectQuestion(q, 'code'); return; } } setActiveSection(section); }} onAddCoins={async (amount, _source) => { const result = await addCoins(token, amount); setUser(result.user); }} /> ) : null; })()} {activeSection === 'ai-teacher' && } {activeSection === 'coding-leaderboard' && ( setActiveSection('coding')} isCodingLeaderboard={true} /> )} {activeSection === 'coding' && ( setActiveSection('coding-leaderboard')} /> )} {activeSection === 'compiler' && } {activeSection === 'learning' && } {activeSection === 'system-design' && handleQuestionSolved({ id: `sd-${id}`, title, difficulty: 'medium' })} />} {activeSection === 'cs-fundamentals' && setActiveSection('dbms')} onNavigateToCN={() => setActiveSection('cn')} onNavigateToOS={() => setActiveSection('os')} onSolve={(id, title) => handleQuestionSolved({ id: `cs-${id}`, title, difficulty: 'easy' })} />} {activeSection === 'aptitude' && { setActiveAptitudeCategory(cat.replace('-', '_')); setActiveSection('aptitude-category'); }} />} {activeSection === 'aptitude-category' && activeAptitudeCategory && ( w.charAt(0).toUpperCase() + w.slice(1)).join(' ')} description={`Master the concepts of ${activeAptitudeCategory.replace('_', ' ')}.`} onBack={() => setActiveSection('aptitude')} onSelectTopic={(topicNo, mode) => { setActiveAptitudeTopicNo(topicNo); setActiveAptitudeMode(mode); setActiveSection('aptitude-content'); }} /> )} {activeSection === 'aptitude-content' && activeAptitudeCategory && activeAptitudeTopicNo && ( setActiveSection('aptitude-category')} /> )} {activeSection === 'arena' && setActiveSection('arena-lobby')} />} {activeSection === 'arena-lobby' && ( setActiveSection('arena')} onMatchFound={(question, difficulty) => { setArenaQuestion(question); setArenaDifficulty(difficulty); setActiveSection('arena-battle'); }} /> )} {activeSection === 'arena-battle' && arenaQuestion && ( setActiveSection('arena')} onSolved={(lang) => handleQuestionSolved(arenaQuestion, lang)} fontSize={preferences.fontSize} /> )} {activeSection === 'playground' && ( )} {activeSection === 'shopping' && ( setActiveSection('dashboard')} onOpenSettings={() => setActiveSection('settings')} userCoins={user.coins || 0} userId={user.id} address={preferences.address} onPurchase={handlePurchase} /> )} {activeSection === 'sheets' && ( )} {activeSection === 'profile' && ( )} {activeSection === 'settings' && ( )} {activeSection === 'dbms' && ( { setSelectedDBMSTopicNo(topicNo); setActiveSection('dbms-content'); }} onBack={() => setActiveSection('cs-fundamentals')} onSolve={(id, title) => handleQuestionSolved({ id: `cs-dbms-${id}`, title, difficulty: 'easy' })} /> )} {activeSection === 'dbms-content' && selectedDBMSTopicNo && ( setActiveSection('dbms')} /> )} {activeSection === 'cn' && ( { setSelectedCNTopicNo(topicNo); setActiveSection('cn-content'); }} onBack={() => setActiveSection('cs-fundamentals')} onSolve={(id, title) => handleQuestionSolved({ id: `cs-cn-${id}`, title, difficulty: 'easy' })} /> )} {activeSection === 'cn-content' && selectedCNTopicNo && ( setActiveSection('cn')} /> )} {activeSection === 'os' && ( { setSelectedOSSectionNo(sectionNo); setActiveSection('os-content'); }} onBack={() => setActiveSection('cs-fundamentals')} onSolve={(id, title) => handleQuestionSolved({ id: `cs-os-${id}`, title, difficulty: 'easy' })} /> )} {activeSection === 'os-content' && selectedOSSectionNo && ( setActiveSection('os')} /> )}
); } function getLegacySolvedQuestionIds(userId: string) { const solvedFromApp = readLegacySolvedArray(`solved_questions_${userId}`); const solvedFromCodingSection = readLegacyCompletedQuestions(); return Array.from(new Set([...solvedFromApp, ...solvedFromCodingSection])); } function getLegacyDailyActivity(userId: string) { const raw = localStorage.getItem(`daily_activity_${userId}`); if (!raw) { return {}; } try { const parsed = JSON.parse(raw); return typeof parsed === 'object' && parsed !== null ? parsed : {}; } catch { return {}; } } function readLegacySolvedArray(key: string) { const raw = localStorage.getItem(key); if (!raw) { return [] as string[]; } try { const parsed = JSON.parse(raw); return Array.isArray(parsed) ? parsed.filter((value): value is string => typeof value === 'string') : []; } catch { return []; } } function readLegacyCompletedQuestions() { const raw = localStorage.getItem('completed_coding_questions'); if (!raw) { return [] as string[]; } try { const parsed = JSON.parse(raw); if (!parsed || typeof parsed !== 'object') { return []; } return Object.entries(parsed) .filter(([, solved]) => Boolean(solved)) .map(([questionId]) => questionId); } catch { return []; } } function needsLegacyProgressImport( stats: UserProgressStats, legacySolvedQuestionIds: string[], legacyDailyActivity: Record, ) { if (legacySolvedQuestionIds.length > stats.solvedQuestionIds.length) { return true; } for (const [key, count] of Object.entries(legacyDailyActivity)) { if ((stats.dailyActivity[key] || 0) < count) { return true; } } return false; } function msUntilNextLocalMidnight() { const now = new Date(); const nextMidnight = new Date(now); nextMidnight.setHours(24, 0, 5, 0); return Math.max(60_000, nextMidnight.getTime() - now.getTime()); } function interfaceLanguageToHtmlLang(language: UserPreferences['language']) { if (language === 'spanish') { return 'es'; } if (language === 'french') { return 'fr'; } if (language === 'german') { return 'de'; } return 'en'; }