import { useNavigate, Routes, Route } from 'react-router-dom'; import PropTypes from 'prop-types'; import { useClientIdentity } from './client.js'; import { Page } from './page.js'; import { Heading } from './widgets/heading.js'; import { ButtonBar, Button } from './widgets/buttonBar.js'; import { Tutorial } from './features/tutorial/tutorial.js'; import { RULES_TUTORIAL } from './tutorials/rules.js'; import { NotificationHandler } from './features/lobby/notificationHandler.js'; import { Account } from './features/lobby/account.js'; import { Invitation } from './features/lobby/invitation.js'; import { GameMenu } from './features/lobby/gameMenu.js'; import { SettingsMenu } from './features/lobby/settingsMenu.js'; import { Player } from './features/play/player.js'; import { Editor } from './features/play/editor.js'; import { AbruptShutdownRecovery } from './features/play/abruptShutdownRecovery.js'; import { Network } from './features/play/network.js'; import { OneGameMonitor } from './features/play/monitor.js'; import './app.css'; import homeIcon from './icons/home.svg'; import rulesIcon from './icons/rules.svg'; import localIcon from './icons/local.svg'; import editIcon from './icons/edit.svg'; import analysisIcon from './icons/analysis.svg'; import remoteIcon from './icons/remote.svg'; import GAME_LIBRARY from './gameLibrary.js'; const GAME = GAME_LIBRARY.get('boost-9-9-2-8-2'); const DRAGON_COUNT = 7; const ANALYSIS_DEPTH = 5; function PageWithNavigationBar(props) { const { homeAltText, homeTo, rulesAltText, rulesTo, localAltText, localTo, editAltText, editTo, analysisAltText, analysisTo, remoteAltText, remoteTo, } = { homeAltText: 'Home', homeTo: '/', rulesAltText: 'Learn the Rules', rulesTo: '/rules', localAltText: 'Play a Game on this Device', localTo: '/local/gameMenu', editAltText: 'Edit a Position', editTo: '/edit/gameMenu', analysisAltText: 'Analyze a Game', analysisTo: '/analysis/gameMenu', remoteAltText: 'Play a Network Game', remoteTo: '/remote/gameMenu', ...props, }; const navigate = useNavigate(); return ( <Page> <ButtonBar> <Button image={homeIcon} altText={homeAltText} disabled={homeTo === undefined} onClick={homeTo !== undefined ? () => navigate(homeTo) : undefined} /> <Button image={rulesIcon} altText={rulesAltText} disabled={rulesTo === undefined} onClick={rulesTo !== undefined ? () => navigate(rulesTo) : undefined} /> <Button image={localIcon} altText={localAltText} disabled={localTo === undefined} onClick={localTo !== undefined ? () => navigate(localTo) : undefined} /> <Button image={editIcon} altText={editAltText} disabled={editTo === undefined} onClick={editTo !== undefined ? () => navigate(editTo) : undefined} /> <Button image={analysisIcon} altText={analysisAltText} disabled={analysisTo === undefined} onClick={analysisTo !== undefined ? () => navigate(analysisTo) : undefined} /> <Button image={remoteIcon} altText={remoteAltText} disabled={remoteTo === undefined} onClick={remoteTo !== undefined ? () => navigate(remoteTo) : undefined} /> </ButtonBar> {props.children} </Page> ); } PageWithNavigationBar.propTypes = { homeAltText: PropTypes.string, homeTo: PropTypes.string, rulesAltText: PropTypes.string, rulesTo: PropTypes.string, localAltText: PropTypes.string, localTo: PropTypes.string, editAltText: PropTypes.string, editTo: PropTypes.string, analysisAltText: PropTypes.string, analysisTo: PropTypes.string, remoteAltText: PropTypes.string, remoteTo: PropTypes.string, }; export function App() { const clientIdentity = useClientIdentity(); const invitationTarget = clientIdentity !== undefined ? '/remote/invitation' : '/remote/invitationLogin'; const navigate = useNavigate(); const home = <PageWithNavigationBar homeTo={undefined}> <nav> <Heading text={'Boost Board Game'} /> <ButtonBar> <Button text={'Learn the Rules'} autofocus={true} onClick={() => navigate('/rules')} /> </ButtonBar> <ButtonBar> <Button text={'Play a Game on this Device'} onClick={() => navigate('/local/gameMenu')} /> </ButtonBar> <ButtonBar> <Button text={'Play a Network Game'} onClick={() => navigate('/remote/gameMenu')} /> </ButtonBar> <ButtonBar> <Button text={'Accept a Game Invitation'} onClick={() => navigate(invitationTarget)} /> </ButtonBar> <div /> </nav> </PageWithNavigationBar>; const rulesTutorial = <PageWithNavigationBar rulesTo={undefined}> <Heading text={RULES_TUTORIAL.title} /> <Tutorial slot={'rules'} tutorial={RULES_TUTORIAL} /> </PageWithNavigationBar>; const localGameMenu = <PageWithNavigationBar localTo={undefined}> <GameMenu slot={'local'} gameIdentifier={GAME.identifier} dragons={DRAGON_COUNT} remote={false} newGameTo={'/local/settingsMenu'} loadGameTo={'/local'} /> </PageWithNavigationBar>; const localSettingsMenu = <PageWithNavigationBar> <Heading text={'Choose Settings'} /> <SettingsMenu slot={'local'} remote={false} backTo={'/local/gameMenu'} to={'/local'} /> </PageWithNavigationBar>; const localBoard = <PageWithNavigationBar localAltText={'Play another Game on this Device'} editAltText={'Edit this Position'} editTo={'/edit'} analysisAltText={'Analyze from this Position'} analysisTo={'/analysis'}> <Heading text={'Local Game'} /> <Player slot={'local'} remote={false} analysisOnly={false} analysisDepth={ANALYSIS_DEPTH} settingsTo={'/local/settingsMenu'} /> </PageWithNavigationBar>; const editGameMenu = <PageWithNavigationBar editTo={undefined}> <GameMenu slot={'local'} gameIdentifier={GAME.identifier} dragons={DRAGON_COUNT} remote={false} newGameTo={'/edit'} loadGameTo={'/edit'} /> </PageWithNavigationBar>; const editBoard = <PageWithNavigationBar localAltText={'Play from this Position'} localTo={'/local'} editAltText={'Edit another Position'} analysisAltText={'Analyze from this Position'} analysisTo={'/analysis'}> <Heading text={'Editor'} /> <Editor slot={'local'} /> </PageWithNavigationBar>; const analyzeGameMenu = <PageWithNavigationBar analysisTo={undefined}> <GameMenu slot={'local'} gameIdentifier={GAME.identifier} dragons={DRAGON_COUNT} remote={false} newGameTo={'/analysis'} loadGameTo={'/analysis'} /> </PageWithNavigationBar>; const analyzeBoard = <PageWithNavigationBar localAltText={'Play from this Position'} localTo={'/local'} editAltText={'Edit this Position'} editTo={'/edit'} analysisAltText={'Analyze another Game'}> <Heading text={'Analysis'} /> <Player slot={'local'} remote={false} analysisOnly={true} analysisDepth={ANALYSIS_DEPTH} /> </PageWithNavigationBar>; const accountPage = <PageWithNavigationBar> <Heading text={'Account'} /> <Account listTo={'/remote/gameMenu'} /> </PageWithNavigationBar>; const remoteGameMenu = <PageWithNavigationBar remoteTo={undefined}> <GameMenu slot={'remote'} gameIdentifier={GAME.identifier} dragons={DRAGON_COUNT} remote={true} accountTo={'/remote/account'} acceptTo={'/remote/invitation'} newGameTo={'/remote/settingsMenu'} loadGameTo={'/remote'} /> </PageWithNavigationBar>; const invitationLoginPage = <PageWithNavigationBar> <Heading text={'Account'} /> <Account listTo={'/remote/invitation'} /> </PageWithNavigationBar>; const invitationAcceptancePage = <PageWithNavigationBar> <Heading text={'Accept Invitation'} /> <Invitation slot={'remote'} to={'/remote'} backTo={'/remote/gameMenu'} /> </PageWithNavigationBar>; const remoteSettingsMenu = <PageWithNavigationBar> <Heading text={'Choose Settings'} /> <SettingsMenu slot={'remote'} remote={true} backTo={'/remote/gameMenu'} to={'/remote'} /> </PageWithNavigationBar>; const remoteBoard = <PageWithNavigationBar> <Heading text={'Network Game'} /> <Player slot={'remote'} remote={true} analysisOnly={false} analysisDepth={ANALYSIS_DEPTH} backTo={'/remote/gameMenu'} /> <OneGameMonitor slot={'remote'} /> </PageWithNavigationBar>; const notification = <PageWithNavigationBar> <NotificationHandler slot={'remote'} backTo={'/'} accountTo={'/notificationLogin'} to={'/remote'} /> </PageWithNavigationBar>; const notificationLoginPage = <PageWithNavigationBar> <Heading text={'Account'} /> <Account listTo={'/remote'} /> </PageWithNavigationBar>; return ( <> <Routes> <Route path={'/'} element={home} /> <Route path={'/rules/*'} element={rulesTutorial} /> <Route path={'/local/gameMenu/*'} element={localGameMenu} /> <Route path={'/local/settingsMenu/*'} element={localSettingsMenu} /> <Route path={'/local/*'} element={localBoard} /> <Route path={'/edit/gameMenu/*'} element={editGameMenu} /> <Route path={'/edit/*'} element={editBoard} /> <Route path={'/analysis/gameMenu/*'} element={analyzeGameMenu} /> <Route path={'/analysis/*'} element={analyzeBoard} /> <Route path={'/remote/account/*'} element={accountPage} /> <Route path={'/remote/gameMenu/*'} element={remoteGameMenu} /> <Route path={'/remote/invitationLogin/*'} element={invitationLoginPage} /> <Route path={'/remote/invitation/*'} element={invitationAcceptancePage} /> <Route path={'/remote/settingsMenu/*'} element={remoteSettingsMenu} /> <Route path={'/remote/*'} element={remoteBoard} /> <Route path={'/notification/*'} element={notification} /> <Route path={'/notificationLogin/*'} element={notificationLoginPage} /> </Routes> <AbruptShutdownRecovery /> <Network slot={'remote'} /> </> ); } if (navigator.serviceWorker) { console.log('Listening to service worker messages…'); navigator.serviceWorker.addEventListener('message', (message) => { if (typeof message.data.hash === 'string') { globalThis.location.hash = message.data.hash; } }); }