/* ============================================================ INDIC LEAGUE NEWS — App shell Top-level routing + persistence. Routes: splash -> login -> onboarding -> quiz -> feed ============================================================ */ const ROUTES = { SPLASH: 'splash', LOGIN: 'login', ONBOARD: 'onboard', QUIZ: 'quiz', FEED: 'feed', }; function App() { const Storage = window.IL_Util.Storage; const KEYS = window.IL_KEYS; // Initial route inference. If user already onboarded, go straight to feed. const computeInitial = () => { const topics = Storage.get(KEYS.topics, null); if (Array.isArray(topics) && topics.length >= 3) return ROUTES.FEED; return ROUTES.SPLASH; }; const [route, setRoute] = React.useState(computeInitial); const [user, setUser] = React.useState(() => Storage.get(KEYS.user, null)); const [theme, setTheme] = React.useState(() => { const stored = Storage.get(KEYS.theme, null); if (stored === 'dark' || stored === 'light') return stored; if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) return 'dark'; return 'light'; }); // Quiz-result-on-return-to-onboarding stash const [pendingPolitical, setPendingPolitical] = React.useState(null); // Apply theme React.useEffect(() => { window.IL_Util.applyTheme(theme); Storage.set(KEYS.theme, theme); }, [theme]); // Remove pre-react boot splash once mounted React.useEffect(() => { const boot = document.querySelector('.boot'); if (boot) { boot.style.transition = 'opacity 400ms'; boot.style.opacity = '0'; setTimeout(() => boot.remove(), 420); } }, []); // Browser back-button support React.useEffect(() => { const onPop = (e) => { // Always send user to feed if onboarded, else splash if (Storage.get(KEYS.topics, []).length >= 3) { setRoute(ROUTES.FEED); } else { setRoute(ROUTES.SPLASH); } }; window.addEventListener('popstate', onPop); return () => window.removeEventListener('popstate', onPop); }, []); // Track desktop layout class const [wide, setWide] = React.useState(window.matchMedia('(min-width: 1024px)').matches); React.useEffect(() => { const mq = window.matchMedia('(min-width: 1024px)'); const handler = (e) => setWide(e.matches); if (mq.addEventListener) mq.addEventListener('change', handler); else mq.addListener(handler); return () => { if (mq.removeEventListener) mq.removeEventListener('change', handler); else mq.removeListener(handler); }; }, []); // -------- handlers -------- const onSplashContinue = (where) => { if (where === 'login') setRoute(ROUTES.LOGIN); else setRoute(ROUTES.ONBOARD); }; const onLoggedIn = (u) => { setUser(u); const topics = Storage.get(KEYS.topics, []); if (Array.isArray(topics) && topics.length >= 3) { setRoute(ROUTES.FEED); } else { setRoute(ROUTES.ONBOARD); } }; const onOnboardComplete = () => { setPendingPolitical(null); setRoute(ROUTES.FEED); }; const onStartQuiz = () => setRoute(ROUTES.QUIZ); const onQuizComplete = (result) => { // result: { position, label, optionId, method } setPendingPolitical(result); setRoute(ROUTES.ONBOARD); }; const onQuizCancel = () => { setPendingPolitical(null); // If user came from onboarding, back to onboarding. Otherwise feed. const topics = Storage.get(KEYS.topics, []); if (Array.isArray(topics) && topics.length >= 3) setRoute(ROUTES.FEED); else setRoute(ROUTES.ONBOARD); }; const onLogout = () => { Storage.remove(KEYS.user); Storage.remove(KEYS.authed); setUser(null); // Keep topics so they don't have to re-onboard. Just dump to splash. setRoute(ROUTES.SPLASH); }; const toggleTheme = () => { setTheme(t => (t === 'dark' ? 'light' : 'dark')); window.IL_Sounds && window.IL_Sounds.tap(); window.IL_Util && window.IL_Util.Haptics.light(); }; // -------- render -------- let body; switch (route) { case ROUTES.SPLASH: body = ; break; case ROUTES.LOGIN: body = setRoute(ROUTES.SPLASH)} />; break; case ROUTES.ONBOARD: body = ( ); break; case ROUTES.QUIZ: body = ; break; case ROUTES.FEED: default: body = ( ); break; } return (
9:41
{body}
); } Object.assign(window, { App });