// MULTIPLIER — main app shell
const { useState, useEffect, useCallback } = React;
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
"theme": "space",
"soundOn": true,
"musicOn": false,
"defaultDifficulty": "auto"
}/*EDITMODE-END*/;
const App = () => {
const t = window.MP_I18N.t;
const [profile, setProfile] = useState(() => window.MP_DATA.loadProfile());
const [screen, setScreen] = useState('home'); // home | ranking | profile | game | resultado | diploma
const [game, setGame] = useState(null);
const [result, setResult] = useState(null);
const [share, setShare] = useState(false);
const [tweaks, setTweaks] = useState(TWEAK_DEFAULTS);
// Load language preference
useEffect(() => {
if (profile?.lang) window.MP_I18N.setLang(profile.lang);
window.MP_SOUND.setEnabled(tweaks.soundOn);
}, [profile?.lang, tweaks.soundOn]);
// Tweaks integration
useEffect(() => {
const onMsg = (e) => {
if (e.data?.type === '__activate_edit_mode') {
window.dispatchEvent(new CustomEvent('mp-tweaks-show'));
} else if (e.data?.type === '__deactivate_edit_mode') {
window.dispatchEvent(new CustomEvent('mp-tweaks-hide'));
}
};
window.addEventListener('message', onMsg);
window.parent.postMessage({ type: '__edit_mode_available' }, '*');
return () => window.removeEventListener('message', onMsg);
}, []);
// Service worker
useEffect(() => {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('./sw.js').catch(() => {});
}
}, []);
// Onboarding
if (!profile || !profile.firstName) {
return (
{
const p = { ...window.MP_DATA.emptyProfile(), ...data };
window.MP_DATA.saveProfile(p);
setProfile(p);
// Register on backend (fire and forget)
window.MP_API.register({
first_name: p.firstName,
last_initial: p.lastInitial,
school_code: p.schoolCode,
school_name: p.schoolName,
grade: p.grade
});
}} />
);
}
const updateProfile = (p) => {
window.MP_DATA.saveProfile(p);
setProfile({ ...p });
};
const playMode = (id) => {
setGame(id);
setScreen('game');
};
const finishGame = (gameResult) => {
const isBestTA = gameResult.mode === 'time_attack' && gameResult.score > (profile.bestTimeAttack || 0);
const isBestES = gameResult.mode === 'escalera' && gameResult.score > (profile.bestEscalera || 0);
const { profile: newProfile, newBadges, newMascots } = window.MP_DATA.applyResult(profile, gameResult);
window.MP_DATA.saveProfile(newProfile);
setProfile(newProfile);
// Submit to server
window.MP_API.submitScore({
first_name: newProfile.firstName,
last_initial: newProfile.lastInitial,
school_code: newProfile.schoolCode,
school_name: newProfile.schoolName,
grade: newProfile.grade,
mode: gameResult.mode,
score: gameResult.score,
correct: gameResult.correct,
total: gameResult.total
});
setResult({ ...gameResult, isBest: isBestTA || isBestES, newBadges, newMascots });
setScreen('resultado');
if (newBadges.length || newMascots.length) {
setTimeout(() => window.MP_SOUND.unlock(), 800);
}
};
const goHome = () => { setScreen('home'); setGame(null); setResult(null); };
// Game router
const renderGame = () => {
const props = { profile, onFinish: finishGame, onQuit: goHome };
switch (game) {
case 'time_attack': return ;
case 'memorama': return ;
case 'escalera': return ;
case 'mision': return ;
case 'tabla': return ;
case 'tf': return ;
case 'blank': return ;
default: return null;
}
};
return (
{screen === 'home' && (
playMode('mision')}
/>
)}
{screen === 'ranking' && }
{screen === 'profile' && (
{ localStorage.removeItem('mp_profile'); setProfile(null); }}
onDiploma={() => setScreen('diploma')}
/>
)}
{screen === 'game' && renderGame()}
{screen === 'resultado' && result && (
{ setScreen('game'); setResult(null); }}
onHome={goHome}
onShare={() => setShare(true)}
onDiploma={() => setScreen('diploma')}
/>
)}
{screen === 'diploma' && (
setScreen(result ? 'resultado' : 'profile')} />
)}
{share && setShare(false)} />}
{/* Bottom navigation */}
{(screen === 'home' || screen === 'ranking' || screen === 'profile') && (
)}
);
};
ReactDOM.createRoot(document.getElementById('root')).render();