cleanup app_bar.tsx
This commit is contained in:
+1
-7
@@ -10,8 +10,6 @@ import './css/reset.css';
|
|||||||
import './css/app.css';
|
import './css/app.css';
|
||||||
import { MobileContext } from './components/infoview/context';
|
import { MobileContext } from './components/infoview/context';
|
||||||
import { useWindowDimensions } from './window_width';
|
import { useWindowDimensions } from './window_width';
|
||||||
import { selectOpenedIntro } from './state/progress';
|
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
|
|
||||||
export const GameIdContext = React.createContext<string>(undefined);
|
export const GameIdContext = React.createContext<string>(undefined);
|
||||||
|
|
||||||
@@ -21,14 +19,10 @@ function App() {
|
|||||||
const {width, height} = useWindowDimensions()
|
const {width, height} = useWindowDimensions()
|
||||||
const [mobile, setMobile] = React.useState(width < 800)
|
const [mobile, setMobile] = React.useState(width < 800)
|
||||||
|
|
||||||
// For mobile only
|
|
||||||
const openedIntro = useSelector(selectOpenedIntro(gameId))
|
|
||||||
const [pageNumber, setPageNumber] = React.useState(openedIntro ? 1 : 0)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="app">
|
<div className="app">
|
||||||
<GameIdContext.Provider value={gameId}>
|
<GameIdContext.Provider value={gameId}>
|
||||||
<MobileContext.Provider value={{mobile, setMobile, pageNumber, setPageNumber}}>
|
<MobileContext.Provider value={{mobile, setMobile}}>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</MobileContext.Provider>
|
</MobileContext.Provider>
|
||||||
</GameIdContext.Provider>
|
</GameIdContext.Provider>
|
||||||
|
|||||||
+167
-336
@@ -1,45 +1,42 @@
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
|
import { faDownload, faUpload, faEraser, faBook, faBookOpen, faGlobe, faHome,
|
||||||
|
faArrowRight, faArrowLeft, faXmark, faBars, faCode,
|
||||||
|
faCircleInfo, faTerminal } from '@fortawesome/free-solid-svg-icons'
|
||||||
import { GameIdContext } from "../app"
|
import { GameIdContext } from "../app"
|
||||||
import { InputModeContext, MobileContext, WorldLevelIdContext } from "./infoview/context"
|
import { InputModeContext, MobileContext, WorldLevelIdContext } from "./infoview/context"
|
||||||
import { GameInfo, useGetGameInfoQuery } from '../state/api'
|
import { GameInfo, useGetGameInfoQuery } from '../state/api'
|
||||||
import { changedOpenedIntro, selectCompleted, selectDifficulty, selectProgress } from '../state/progress'
|
import { changedOpenedIntro, selectCompleted, selectDifficulty, selectProgress } from '../state/progress'
|
||||||
import { useSelector } from 'react-redux'
|
|
||||||
import { useAppDispatch, useAppSelector } from '../hooks'
|
import { useAppDispatch, useAppSelector } from '../hooks'
|
||||||
import { Button } from './button'
|
import { Button } from './button'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { downloadProgress } from './popup/erase'
|
||||||
import { faDownload, faUpload, faEraser, faBook, faBookOpen, faGlobe, faHome, faArrowRight, faArrowLeft, faXmark, faBars, faCode, faCircleInfo, faTerminal } from '@fortawesome/free-solid-svg-icons'
|
|
||||||
import { PrivacyPolicyPopup } from './popup/privacy_policy'
|
|
||||||
import { WorldSelectionMenu, downloadFile } from './world_tree'
|
|
||||||
|
|
||||||
/** navigation to switch between pages on mobile */
|
/** navigation buttons to switch between welcome-pages on mobile */
|
||||||
function MobileNav({pageNumber, setPageNumber}:
|
function MobileNavButtons({pageNumber, setPageNumber}:
|
||||||
{ pageNumber: number,
|
{ pageNumber: number,
|
||||||
setPageNumber: any}) {
|
setPageNumber: any}) {
|
||||||
const gameId = React.useContext(GameIdContext)
|
const gameId = React.useContext(GameIdContext)
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
let prevText = {0 : null, 1: "Intro", 2: null}[pageNumber]
|
// if `prevText` or `prevIcon` is set, show a button to go back
|
||||||
let prevIcon = {0 : null, 1: null, 2: faBookOpen}[pageNumber]
|
let prevText = {0: null, 1: "Intro", 2: null}[pageNumber]
|
||||||
let prevTitle = {
|
let prevIcon = {0: null, 1: null, 2: faBookOpen}[pageNumber]
|
||||||
0: null,
|
let prevTitle = {0: null, 1: "Game Introduction", 2: "World selection"}[pageNumber]
|
||||||
1: "Game Introduction",
|
// if `nextText` or `nextIcon` is set, show a button to go forward
|
||||||
2: "World selection"}[pageNumber]
|
let nextText = {0: "Start", 1: null, 2: null}[pageNumber]
|
||||||
let nextText = {0 : "Start", 1: null, 2: null}[pageNumber]
|
let nextIcon = {0: null, 1: faBook, 2: null}[pageNumber]
|
||||||
let nextIcon = {0 : null, 1: faBook, 2: null}[pageNumber]
|
let nextTitle = {0: "World selection", 1: "Inventory", 2: null}[pageNumber]
|
||||||
let nextTitle = {
|
|
||||||
0: "World selection",
|
|
||||||
1: "Inventory",
|
|
||||||
2: null}[pageNumber]
|
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
{(prevText || prevTitle || prevIcon) &&
|
{(prevText || prevIcon) &&
|
||||||
<Button className="btn btn-inverted toggle-width" to={pageNumber == 0 ? "/" : ""} inverted="true" title={prevTitle}
|
<Button className="btn btn-inverted toggle-width" to={pageNumber == 0 ? "/" : ""}
|
||||||
|
inverted="true" title={prevTitle}
|
||||||
onClick={() => {pageNumber == 0 ? null : setPageNumber(pageNumber - 1)}}>
|
onClick={() => {pageNumber == 0 ? null : setPageNumber(pageNumber - 1)}}>
|
||||||
{prevIcon && <FontAwesomeIcon icon={prevIcon} />}
|
{prevIcon && <FontAwesomeIcon icon={prevIcon} />}
|
||||||
{prevText && `${prevText}`}
|
{prevText && `${prevText}`}
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
{(nextText || nextTitle || nextIcon) &&
|
{(nextText || nextIcon) &&
|
||||||
<Button className="btn btn-inverted toggle-width" to="" inverted="true"
|
<Button className="btn btn-inverted toggle-width" to="" inverted="true"
|
||||||
title={nextTitle} onClick={() => {
|
title={nextTitle} onClick={() => {
|
||||||
console.log(`page number: ${pageNumber}`)
|
console.log(`page number: ${pageNumber}`)
|
||||||
@@ -52,357 +49,191 @@ function MobileNav({pageNumber, setPageNumber}:
|
|||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
||||||
export function WelcomeAppBar({gameInfo, toggleImpressum, openEraseMenu, openUploadMenu, toggleInfo} : {
|
function MenuButton({navOpen, setNavOpen}) {
|
||||||
|
return <Button to="" className="btn toggle-width" id="menu-btn" onClick={(ev) => {setNavOpen(!navOpen)}}>
|
||||||
|
{navOpen ? <FontAwesomeIcon icon={faXmark} /> : <FontAwesomeIcon icon={faBars} />}
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
|
||||||
|
/** the navigation bar on the welcome page */
|
||||||
|
export function WelcomeAppBar({pageNumber, setPageNumber, gameInfo, toggleImpressum, toggleEraseMenu, toggleUploadMenu, toggleInfo} : {
|
||||||
|
pageNumber: number,
|
||||||
|
setPageNumber: any,
|
||||||
gameInfo: GameInfo,
|
gameInfo: GameInfo,
|
||||||
toggleImpressum: any,
|
toggleImpressum: any,
|
||||||
openEraseMenu: any,
|
toggleEraseMenu: any,
|
||||||
openUploadMenu: any,
|
toggleUploadMenu: any,
|
||||||
toggleInfo: any
|
toggleInfo: any
|
||||||
}) {
|
}) {
|
||||||
const gameId = React.useContext(GameIdContext)
|
const gameId = React.useContext(GameIdContext)
|
||||||
const {mobile, pageNumber, setPageNumber} = React.useContext(MobileContext)
|
const gameProgress = useAppSelector(selectProgress(gameId))
|
||||||
|
const {mobile} = React.useContext(MobileContext)
|
||||||
const [navOpen, setNavOpen] = React.useState(false)
|
const [navOpen, setNavOpen] = React.useState(false)
|
||||||
|
|
||||||
|
return <div className="app-bar">
|
||||||
|
<div>
|
||||||
/** Download the current progress (i.e. what's saved in the browser store) */
|
<Button inverted="false" title="back to games selection" to="/">
|
||||||
const gameProgress = useSelector(selectProgress(gameId))
|
<FontAwesomeIcon icon={faArrowLeft} /> <FontAwesomeIcon icon={faGlobe} />
|
||||||
const downloadProgress = (e) => {
|
</Button>
|
||||||
e.preventDefault()
|
<span className="app-bar-title"></span>
|
||||||
downloadFile({
|
</div>
|
||||||
data: JSON.stringify(gameProgress, null, 2),
|
<div>
|
||||||
fileName: `lean4game-${gameId}-${new Date().toLocaleDateString()}.json`,
|
{!mobile && <span className="app-bar-title">{gameInfo?.title}</span>}
|
||||||
fileType: 'text/json',
|
</div>
|
||||||
})
|
<div className="nav-btns">
|
||||||
}
|
{mobile && <MobileNavButtons pageNumber={pageNumber} setPageNumber={setPageNumber} />}
|
||||||
|
<MenuButton navOpen={navOpen} setNavOpen={setNavOpen} />
|
||||||
|
</div>
|
||||||
return <div className="app-bar" >
|
<div className={'menu dropdown' + (navOpen ? '' : ' hidden')}>
|
||||||
<>
|
<Button title="Game Info & Credits" inverted="true" to="" onClick={() => {toggleInfo(); setNavOpen(false)}}>
|
||||||
<div>
|
<FontAwesomeIcon icon={faCircleInfo} /> Game Info
|
||||||
<Button inverted="false" title="back to games selection" to="/">
|
</Button>
|
||||||
<FontAwesomeIcon icon={faArrowLeft} /> <FontAwesomeIcon icon={faGlobe} />
|
<Button title="Clear Progress" inverted="true" to="" onClick={() => {toggleEraseMenu(); setNavOpen(false)}}>
|
||||||
</Button>
|
<FontAwesomeIcon icon={faEraser} /> Erase
|
||||||
<span className="app-bar-title"></span>
|
</Button>
|
||||||
</div>
|
<Button title="Download Progress" inverted="true" to="" onClick={(ev) => {downloadProgress(gameId, gameProgress, ev); setNavOpen(false)}}>
|
||||||
<div>
|
<FontAwesomeIcon icon={faDownload} /> Download
|
||||||
<span className="app-bar-title">
|
</Button>
|
||||||
{mobile ? '' : gameInfo?.title}
|
<Button title="Load Progress from JSON" inverted="true" to="" onClick={() => {toggleUploadMenu(); setNavOpen(false)}}>
|
||||||
</span>
|
<FontAwesomeIcon icon={faUpload} /> Upload
|
||||||
</div>
|
</Button>
|
||||||
<div className="nav-btns">
|
<Button title="Impressum, privacy policy" inverted="true" to="" onClick={() => {toggleImpressum(); setNavOpen(false)}}>
|
||||||
{mobile && <>
|
<FontAwesomeIcon icon={faCircleInfo} /> Impressum
|
||||||
{/* BUTTONS for MOBILE */}
|
</Button>
|
||||||
<MobileNav pageNumber={pageNumber} setPageNumber={setPageNumber} />
|
</div>
|
||||||
|
|
||||||
</>}
|
|
||||||
|
|
||||||
<Button to="" className="btn toggle-width" id="menu-btn" onClick={(ev) => {setNavOpen(!navOpen)}} >
|
|
||||||
{navOpen ? <FontAwesomeIcon icon={faXmark} /> : <FontAwesomeIcon icon={faBars} />}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div className={'menu dropdown' + (navOpen ? '' : ' hidden')}>
|
|
||||||
{/* {levelId < gameInfo.data?.worldSize[worldId] &&
|
|
||||||
<Button inverted="true"
|
|
||||||
to={`/${gameId}/world/${worldId}/level/${levelId + 1}`} title="next level"
|
|
||||||
disabled={difficulty >= 2 && !(completed || levelId == 0)}
|
|
||||||
onClick={() => setNavOpen(false)}>
|
|
||||||
<FontAwesomeIcon icon={faArrowRight} /> {levelId ? "Next" : "Start"}
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
{levelId > 0 && <>
|
|
||||||
<Button disabled={levelId <= 0} inverted="true"
|
|
||||||
to={`/${gameId}/world/${worldId}/level/${levelId - 1}`}
|
|
||||||
title="previous level"
|
|
||||||
onClick={() => setNavOpen(false)}>
|
|
||||||
<FontAwesomeIcon icon={faArrowLeft} /> Previous
|
|
||||||
</Button>
|
|
||||||
</>}
|
|
||||||
<Button to={`/${gameId}`} inverted="true" title="back to world selection">
|
|
||||||
<FontAwesomeIcon icon={faHome} /> Home
|
|
||||||
</Button>
|
|
||||||
<Button disabled={levelId <= 0} inverted="true" to=""
|
|
||||||
onClick={(ev) => { setTypewriterMode(!typewriterMode); setNavOpen(false) }}
|
|
||||||
title="toggle Editor mode">
|
|
||||||
<FontAwesomeIcon icon={faCode} /> Toggle Editor
|
|
||||||
</Button> */}
|
|
||||||
|
|
||||||
<Button title="Game Info & Credits" inverted="true" to="" onClick={(ev) => {toggleInfo(); setNavOpen(false)}}>
|
|
||||||
<FontAwesomeIcon icon={faCircleInfo} /> Game Info
|
|
||||||
</Button>
|
|
||||||
<Button title="Clear Progress" inverted="true" to="" onClick={(ev) => {openEraseMenu(); setNavOpen(false)}}>
|
|
||||||
<FontAwesomeIcon icon={faEraser} /> Erase
|
|
||||||
</Button>
|
|
||||||
<Button title="Download Progress" inverted="true" to="" onClick={(ev) => {downloadProgress(ev); setNavOpen(false)}}>
|
|
||||||
<FontAwesomeIcon icon={faDownload} /> Download
|
|
||||||
</Button>
|
|
||||||
<Button title="Load Progress from JSON" inverted="true" to="" onClick={(ev) => {openUploadMenu(); setNavOpen(false)}}>
|
|
||||||
<FontAwesomeIcon icon={faUpload} /> Upload
|
|
||||||
</Button>
|
|
||||||
<Button title="Impressum, privacy policy" inverted="true" to="" onClick={(ev) => {toggleImpressum(); setNavOpen(false)}}>
|
|
||||||
<FontAwesomeIcon icon={faCircleInfo} /> Impressum
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// /** The menu that is shown next to the world selection graph */
|
function NextButton({worldSize, difficulty, completed, setNavOpen}) {
|
||||||
// function WorldSelectionMenu() {
|
|
||||||
// const [file, setFile] = React.useState<File>();
|
|
||||||
|
|
||||||
// const gameId = React.useContext(GameIdContext)
|
|
||||||
// const store = useStore()
|
|
||||||
// const difficulty = useSelector(selectDifficulty(gameId))
|
|
||||||
|
|
||||||
|
|
||||||
// /* state variables to toggle the pop-up menus */
|
|
||||||
// const [eraseMenu, setEraseMenu] = React.useState(false);
|
|
||||||
// const openEraseMenu = () => setEraseMenu(true);
|
|
||||||
// const closeEraseMenu = () => setEraseMenu(false);
|
|
||||||
// const [uploadMenu, setUploadMenu] = React.useState(false);
|
|
||||||
// const openUploadMenu = () => setUploadMenu(true);
|
|
||||||
// const closeUploadMenu = () => setUploadMenu(false);
|
|
||||||
|
|
||||||
// const gameProgress = useSelector(selectProgress(gameId))
|
|
||||||
// const dispatch = useAppDispatch()
|
|
||||||
|
|
||||||
// /** Download the current progress (i.e. what's saved in the browser store) */
|
|
||||||
// const downloadProgress = (e) => {
|
|
||||||
// e.preventDefault()
|
|
||||||
// downloadFile({
|
|
||||||
// data: JSON.stringify(gameProgress, null, 2),
|
|
||||||
// fileName: `lean4game-${gameId}-${new Date().toLocaleDateString()}.json`,
|
|
||||||
// fileType: 'text/json',
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const handleFileChange = (e) => {
|
|
||||||
// if (e.target.files) {
|
|
||||||
// setFile(e.target.files[0])
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /** Upload progress from a */
|
|
||||||
// const uploadProgress = (e) => {
|
|
||||||
// if (!file) {return}
|
|
||||||
// const fileReader = new FileReader()
|
|
||||||
// fileReader.readAsText(file, "UTF-8")
|
|
||||||
// fileReader.onload = (e) => {
|
|
||||||
// const data = JSON.parse(e.target.result.toString()) as GameProgressState
|
|
||||||
// console.debug("Json Data", data)
|
|
||||||
// dispatch(loadProgress({game: gameId, data: data}))
|
|
||||||
// }
|
|
||||||
// closeUploadMenu()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const eraseProgress = () => {
|
|
||||||
// dispatch(deleteProgress({game: gameId}))
|
|
||||||
// closeEraseMenu()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const downloadAndErase = (e) => {
|
|
||||||
// downloadProgress(e)
|
|
||||||
// eraseProgress()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function label(x : number) {
|
|
||||||
// return x == 0 ? 'none' : x == 1 ? 'lax' : 'regular'
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return <nav className="world-selection-menu">
|
|
||||||
// <Button onClick={downloadProgress} title="Download game progress" to=""><FontAwesomeIcon icon={faDownload} /></Button>
|
|
||||||
// <Button title="Load game progress from JSON" onClick={openUploadMenu} to=""><FontAwesomeIcon icon={faUpload} /></Button>
|
|
||||||
// <Button title="Clear game progress" to="" onClick={openEraseMenu}><FontAwesomeIcon icon={faEraser} /></Button>
|
|
||||||
// <div className="slider-wrap">
|
|
||||||
// <span className="difficulty-label">Game Rules:</span>
|
|
||||||
// <Slider
|
|
||||||
// title="Game Rules: - regular: 🔐 levels, 🔐 tactics - lax: 🔓 levels, 🔐 tactics - none: 🔓 levels, 🔓 tactics"
|
|
||||||
// min={0} max={2}
|
|
||||||
// aria-label="Game Rules"
|
|
||||||
// defaultValue={difficulty}
|
|
||||||
// marks={[
|
|
||||||
// {value: 0, label: label(0)},
|
|
||||||
// {value: 1, label: label(1)},
|
|
||||||
// {value: 2, label: label(2)}
|
|
||||||
// ]}
|
|
||||||
// valueLabelFormat={label}
|
|
||||||
// getAriaValueText={label}
|
|
||||||
// valueLabelDisplay="auto"
|
|
||||||
// onChange={(ev, val: number) => {
|
|
||||||
// dispatch(changedDifficulty({game: gameId, difficulty: val}))
|
|
||||||
// }}
|
|
||||||
// ></Slider>
|
|
||||||
|
|
||||||
// </div>
|
|
||||||
// {eraseMenu?
|
|
||||||
// <div className="modal-wrapper">
|
|
||||||
// <div className="modal-backdrop" onClick={closeEraseMenu} />
|
|
||||||
// <div className="modal">
|
|
||||||
// <div className="codicon codicon-close modal-close" onClick={closeEraseMenu}></div>
|
|
||||||
// <h2>Delete Progress?</h2>
|
|
||||||
|
|
||||||
// <p>Do you want to delete your saved progress irreversibly?</p>
|
|
||||||
// <p>
|
|
||||||
// (This deletes your proofs and your collected inventory.
|
|
||||||
// Saves from other games are not deleted.)
|
|
||||||
// </p>
|
|
||||||
|
|
||||||
// <Button onClick={eraseProgress} to="">Delete</Button>
|
|
||||||
// <Button onClick={downloadAndErase} to="">Download & Delete</Button>
|
|
||||||
// <Button onClick={closeEraseMenu} to="">Cancel</Button>
|
|
||||||
// </div>
|
|
||||||
// </div> : null}
|
|
||||||
// {uploadMenu ?
|
|
||||||
// <div className="modal-wrapper">
|
|
||||||
// <div className="modal-backdrop" onClick={closeUploadMenu} />
|
|
||||||
// <div className="modal">
|
|
||||||
// <div className="codicon codicon-close modal-close" onClick={closeUploadMenu}></div>
|
|
||||||
// <h2>Upload Saved Progress</h2>
|
|
||||||
|
|
||||||
// <p>Select a JSON file with the saved game progress to load your progress.</p>
|
|
||||||
|
|
||||||
// <p><b>Warning:</b> This will delete your current game progress!
|
|
||||||
// Consider <a className="download-link" onClick={downloadProgress} >downloading your current progress</a> first!</p>
|
|
||||||
|
|
||||||
// <input type="file" onChange={handleFileChange}/>
|
|
||||||
|
|
||||||
// <Button to="" onClick={uploadProgress}>Load selected file</Button>
|
|
||||||
// </div>
|
|
||||||
// </div> : null}
|
|
||||||
// </nav>
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
/** The top-navigation bar */
|
|
||||||
export function LevelAppBar({
|
|
||||||
isLoading, levelTitle, impressum, toggleImpressum,
|
|
||||||
pageNumber = undefined, setPageNumber = undefined, lockEditorMode=false}) {
|
|
||||||
const gameId = React.useContext(GameIdContext)
|
const gameId = React.useContext(GameIdContext)
|
||||||
const {worldId, levelId} = React.useContext(WorldLevelIdContext)
|
const {worldId, levelId} = React.useContext(WorldLevelIdContext)
|
||||||
const gameInfo = useGetGameInfoQuery({game: gameId})
|
return (levelId < worldSize ?
|
||||||
|
<Button inverted="true"
|
||||||
|
to={`/${gameId}/world/${worldId}/level/${levelId + 1}`} title="next level"
|
||||||
|
disabled={difficulty >= 2 && !(completed || levelId == 0)}
|
||||||
|
onClick={() => setNavOpen(false)}>
|
||||||
|
<FontAwesomeIcon icon={faArrowRight} /> {levelId ? "Next" : "Start"}
|
||||||
|
</Button>
|
||||||
|
:
|
||||||
|
<Button to={`/${gameId}`} inverted="true" title="back to world selection" id="home-btn">
|
||||||
|
<FontAwesomeIcon icon={faHome} /> Leave World
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const {mobile} = React.useContext(MobileContext)
|
function PreviousButton({setNavOpen}) {
|
||||||
|
const gameId = React.useContext(GameIdContext)
|
||||||
|
const {worldId, levelId} = React.useContext(WorldLevelIdContext)
|
||||||
|
return (levelId > 0 && <>
|
||||||
|
<Button disabled={levelId <= 0} inverted="true"
|
||||||
|
to={`/${gameId}/world/${worldId}/level/${levelId - 1}`}
|
||||||
|
title="previous level"
|
||||||
|
onClick={() => setNavOpen(false)}>
|
||||||
|
<FontAwesomeIcon icon={faArrowLeft} /> Previous
|
||||||
|
</Button>
|
||||||
|
</>)
|
||||||
|
}
|
||||||
|
|
||||||
const difficulty = useSelector(selectDifficulty(gameId))
|
function InputModeButton({setNavOpen, isDropdown}) {
|
||||||
const completed = useAppSelector(selectCompleted(gameId, worldId, levelId))
|
const {levelId} = React.useContext(WorldLevelIdContext)
|
||||||
|
const {typewriterMode, setTypewriterMode, lockInputMode} = React.useContext(InputModeContext)
|
||||||
|
|
||||||
const { typewriterMode, setTypewriterMode } = React.useContext(InputModeContext)
|
/** toggle input mode if allowed */
|
||||||
|
function toggleInputMode(ev: React.MouseEvent) {
|
||||||
const [navOpen, setNavOpen] = React.useState(false)
|
if (!lockInputMode){
|
||||||
|
|
||||||
function toggleEditor(ev) {
|
|
||||||
if (!lockEditorMode){
|
|
||||||
setTypewriterMode(!typewriterMode)
|
setTypewriterMode(!typewriterMode)
|
||||||
setNavOpen(false)
|
setNavOpen(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return <Button
|
||||||
|
className={`btn btn-inverted ${isDropdown? '' : 'toggle-width'}`} disabled={levelId <= 0 || lockInputMode}
|
||||||
|
inverted="true" to=""
|
||||||
|
onClick={(ev) => toggleInputMode(ev)}
|
||||||
|
title={lockInputMode ? "Editor mode is enforced!" : typewriterMode ? "Editor mode" : "Typewriter mode"}>
|
||||||
|
<FontAwesomeIcon icon={typewriterMode ? faCode : faTerminal} />
|
||||||
|
{isDropdown && (typewriterMode ? <> Editor mode</> : <> Typewriter mode</>)}
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
|
||||||
|
function ImpressumButton({setNavOpen, toggleImpressum, isDropdown}) {
|
||||||
|
return <Button className="btn btn-inverted toggle-width"
|
||||||
|
title="information, Impressum, privacy policy" inverted="true" to="" onClick={(ev) => {toggleImpressum(ev); setNavOpen(false)}}>
|
||||||
|
<FontAwesomeIcon icon={faCircleInfo} />
|
||||||
|
{isDropdown && <> Info & Impressum</>}
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
|
||||||
|
function HomeButton({isDropdown}) {
|
||||||
|
const gameId = React.useContext(GameIdContext)
|
||||||
|
return <Button to={`/${gameId}`} inverted="true" title="back to world selection" id="home-btn">
|
||||||
|
<FontAwesomeIcon icon={faHome} />
|
||||||
|
{isDropdown && <> Home</>}
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
|
||||||
|
/** only displays a button if `setPageNumber` is set. */
|
||||||
|
function InventoryButton({pageNumber, setPageNumber}) {
|
||||||
|
return (setPageNumber &&
|
||||||
|
<Button to="" className="btn btn-inverted toggle-width"
|
||||||
|
title={pageNumber ? "close inventory" : "show inventory"}
|
||||||
|
inverted="true" onClick={() => {setPageNumber(pageNumber ? 0 : 1)}}>
|
||||||
|
<FontAwesomeIcon icon={pageNumber ? faBookOpen : faBook} />
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** the navigation bar in a level */
|
||||||
|
export function LevelAppBar({isLoading, levelTitle, toggleImpressum, pageNumber=undefined, setPageNumber=undefined} : {
|
||||||
|
isLoading: boolean,
|
||||||
|
levelTitle: string,
|
||||||
|
toggleImpressum: any,
|
||||||
|
pageNumber?: number,
|
||||||
|
setPageNumber?: any,
|
||||||
|
}) {
|
||||||
|
const gameId = React.useContext(GameIdContext)
|
||||||
|
const {worldId, levelId} = React.useContext(WorldLevelIdContext)
|
||||||
|
const {mobile} = React.useContext(MobileContext)
|
||||||
|
const [navOpen, setNavOpen] = React.useState(false)
|
||||||
|
const gameInfo = useGetGameInfoQuery({game: gameId})
|
||||||
|
const completed = useAppSelector(selectCompleted(gameId, worldId, levelId))
|
||||||
|
const difficulty = useAppSelector(selectDifficulty(gameId))
|
||||||
|
|
||||||
|
let worldTitle = gameInfo.data?.worlds.nodes[worldId].title
|
||||||
|
|
||||||
return <div className="app-bar" style={isLoading ? {display: "none"} : null} >
|
return <div className="app-bar" style={isLoading ? {display: "none"} : null} >
|
||||||
{mobile ?
|
{mobile ?
|
||||||
<>
|
<>
|
||||||
{/* MOBILE VERSION */}
|
{/* MOBILE VERSION */}
|
||||||
<div>
|
<div>
|
||||||
<span className="app-bar-title">
|
<span className="app-bar-title">{levelTitle}</span>
|
||||||
{levelTitle}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="nav-btns">
|
<div className="nav-btns">
|
||||||
{mobile && pageNumber == 0 ?
|
<InventoryButton pageNumber={pageNumber} setPageNumber={setPageNumber}/>
|
||||||
<Button to="" className="btn btn-inverted toggle-width"
|
<MenuButton navOpen={navOpen} setNavOpen={setNavOpen}/>
|
||||||
title="show inventory" inverted="true" onClick={() => {setPageNumber(1)}}>
|
|
||||||
<FontAwesomeIcon icon={faBook}/>
|
|
||||||
</Button>
|
|
||||||
: pageNumber == 1 &&
|
|
||||||
<Button className="btn btn-inverted toggle-width" to=""
|
|
||||||
title="close inventory" inverted="true" onClick={() => {setPageNumber(0)}}>
|
|
||||||
<FontAwesomeIcon icon={faBookOpen}/>
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
<Button to="" className="btn toggle-width" id="menu-btn" onClick={(ev) => {setNavOpen(!navOpen)}} >
|
|
||||||
{navOpen ? <FontAwesomeIcon icon={faXmark} /> : <FontAwesomeIcon icon={faBars} />}
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
<div className={'menu dropdown' + (navOpen ? '' : ' hidden')}>
|
<div className={'menu dropdown' + (navOpen ? '' : ' hidden')}>
|
||||||
{levelId < gameInfo.data?.worldSize[worldId] &&
|
<NextButton worldSize={gameInfo.data?.worldSize[worldId]} difficulty={difficulty} completed={completed} setNavOpen={setNavOpen} />
|
||||||
<Button inverted="true"
|
<PreviousButton setNavOpen={setNavOpen} />
|
||||||
to={`/${gameId}/world/${worldId}/level/${levelId + 1}`} title="next level"
|
<HomeButton isDropdown={true} />
|
||||||
disabled={difficulty >= 2 && !(completed || levelId == 0)}
|
<InputModeButton setNavOpen={setNavOpen} isDropdown={true}/>
|
||||||
onClick={() => setNavOpen(false)}>
|
<ImpressumButton setNavOpen={setNavOpen} toggleImpressum={toggleImpressum} isDropdown={true} />
|
||||||
<FontAwesomeIcon icon={faArrowRight} /> {levelId ? "Next" : "Start"}
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
{levelId > 0 && <>
|
|
||||||
<Button disabled={levelId <= 0} inverted="true"
|
|
||||||
to={`/${gameId}/world/${worldId}/level/${levelId - 1}`}
|
|
||||||
title="previous level"
|
|
||||||
onClick={() => setNavOpen(false)}>
|
|
||||||
<FontAwesomeIcon icon={faArrowLeft} /> Previous
|
|
||||||
</Button>
|
|
||||||
</>}
|
|
||||||
<Button to={`/${gameId}`} inverted="true" title="back to world selection">
|
|
||||||
<FontAwesomeIcon icon={faHome} /> Home
|
|
||||||
</Button>
|
|
||||||
<Button disabled={levelId <= 0} inverted="true" to=""
|
|
||||||
onClick={(ev) => { setTypewriterMode(!typewriterMode); setNavOpen(false) }}
|
|
||||||
title={typewriterMode ? "Editor mode" : "Typewriter mode"}>
|
|
||||||
<FontAwesomeIcon icon={typewriterMode ? faCode : faTerminal} />
|
|
||||||
{typewriterMode ? "Editor mode" : "Typewriter mode"}
|
|
||||||
</Button>
|
|
||||||
<Button title="information, Impressum, privacy policy" inverted="true" to="" onClick={(ev) => {toggleImpressum(ev); setNavOpen(false)}}>
|
|
||||||
<FontAwesomeIcon icon={faCircleInfo} /> Info & Impressum
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</> :
|
||||||
:
|
|
||||||
<>
|
<>
|
||||||
{/* DESKTOP VERSION */}
|
{/* DESKTOP VERSION */}
|
||||||
<div>
|
<div>
|
||||||
<Button to={`/${gameId}`} inverted="true" title="back to world selection" id="home-btn">
|
<HomeButton isDropdown={false} />
|
||||||
<FontAwesomeIcon icon={faHome} />
|
<span className="app-bar-title">{worldTitle && `World: ${worldTitle}`}</span>
|
||||||
</Button>
|
|
||||||
<span className="app-bar-title">
|
|
||||||
{gameInfo.data?.worlds.nodes[worldId].title && `World: ${gameInfo.data?.worlds.nodes[worldId].title}`}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="app-bar-title">
|
<span className="app-bar-title">{levelTitle}</span>
|
||||||
{levelTitle}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="nav-btns">
|
<div className="nav-btns">
|
||||||
{levelId > 0 && <>
|
<PreviousButton setNavOpen={setNavOpen} />
|
||||||
<Button disabled={levelId <= 0} inverted="true"
|
<NextButton worldSize={gameInfo.data?.worldSize[worldId]} difficulty={difficulty} completed={completed} setNavOpen={setNavOpen} />
|
||||||
to={`/${gameId}/world/${worldId}/level/${levelId - 1}`}
|
<InputModeButton setNavOpen={setNavOpen} isDropdown={false}/>
|
||||||
title="previous level"
|
<ImpressumButton setNavOpen={setNavOpen} toggleImpressum={toggleImpressum} isDropdown={false} />
|
||||||
onClick={() => setNavOpen(false)}>
|
|
||||||
<FontAwesomeIcon icon={faArrowLeft} /> Previous
|
|
||||||
</Button>
|
|
||||||
</>}
|
|
||||||
{levelId < gameInfo.data?.worldSize[worldId] ?
|
|
||||||
<Button inverted="true"
|
|
||||||
to={`/${gameId}/world/${worldId}/level/${levelId + 1}`} title="next level"
|
|
||||||
disabled={difficulty >= 2 && !(completed || levelId == 0)}
|
|
||||||
onClick={() => setNavOpen(false)}>
|
|
||||||
<FontAwesomeIcon icon={faArrowRight} /> {levelId ? "Next" : "Start"}
|
|
||||||
</Button>
|
|
||||||
:
|
|
||||||
<Button to={`/${gameId}`} inverted="true" title="back to world selection" id="home-btn">
|
|
||||||
<FontAwesomeIcon icon={faHome} /> Leave World
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
<Button className="btn btn-inverted toggle-width" disabled={levelId <= 0 || lockEditorMode} inverted="true" to=""
|
|
||||||
onClick={(ev) => toggleEditor(ev)}
|
|
||||||
title={lockEditorMode ? "Editor mode is enforced!" : typewriterMode ? "Editor mode" : "Typewriter mode"}>
|
|
||||||
<FontAwesomeIcon icon={typewriterMode ? faCode : faTerminal} />
|
|
||||||
</Button>
|
|
||||||
<Button className="btn btn-inverted toggle-width" title="information, Impressum, privacy policy" inverted="true" to="" onClick={(ev) => {toggleImpressum(ev); setNavOpen(false)}}>
|
|
||||||
<FontAwesomeIcon icon={impressum ? faXmark : faCircleInfo} />
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,13 +65,9 @@ export const ProofStateContext = React.createContext<{
|
|||||||
export const MobileContext = React.createContext<{
|
export const MobileContext = React.createContext<{
|
||||||
mobile : boolean,
|
mobile : boolean,
|
||||||
setMobile: React.Dispatch<React.SetStateAction<Boolean>>,
|
setMobile: React.Dispatch<React.SetStateAction<Boolean>>,
|
||||||
pageNumber: number,
|
|
||||||
setPageNumber: React.Dispatch<React.SetStateAction<Number>>
|
|
||||||
}>({
|
}>({
|
||||||
mobile : false,
|
mobile : false,
|
||||||
setMobile: () => {},
|
setMobile: () => {},
|
||||||
pageNumber: 0,
|
|
||||||
setPageNumber: () => {}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export const WorldLevelIdContext = React.createContext<{
|
export const WorldLevelIdContext = React.createContext<{
|
||||||
@@ -108,10 +104,14 @@ export const InputModeContext = React.createContext<{
|
|||||||
typewriterMode: boolean,
|
typewriterMode: boolean,
|
||||||
setTypewriterMode: React.Dispatch<React.SetStateAction<boolean>>,
|
setTypewriterMode: React.Dispatch<React.SetStateAction<boolean>>,
|
||||||
typewriterInput: string,
|
typewriterInput: string,
|
||||||
setTypewriterInput: React.Dispatch<React.SetStateAction<string>>
|
setTypewriterInput: React.Dispatch<React.SetStateAction<string>>,
|
||||||
|
lockInputMode: boolean,
|
||||||
|
setLockInputMode: React.Dispatch<React.SetStateAction<boolean>>,
|
||||||
}>({
|
}>({
|
||||||
typewriterMode: true,
|
typewriterMode: true,
|
||||||
setTypewriterMode: () => {},
|
setTypewriterMode: () => {},
|
||||||
typewriterInput: "",
|
typewriterInput: "",
|
||||||
setTypewriterInput: () => {},
|
setTypewriterInput: () => {},
|
||||||
|
lockInputMode: false,
|
||||||
|
setLockInputMode: () => {},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useSelector, useStore } from 'react-redux'
|
|||||||
import Split from 'react-split'
|
import Split from 'react-split'
|
||||||
import { useParams } from 'react-router-dom'
|
import { useParams } from 'react-router-dom'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import { faBars, faCode, faXmark, faHome, faCircleInfo, faArrowRight, faArrowLeft, faTerminal } from '@fortawesome/free-solid-svg-icons'
|
import { faHome, faArrowRight } from '@fortawesome/free-solid-svg-icons'
|
||||||
import { CircularProgress } from '@mui/material'
|
import { CircularProgress } from '@mui/material'
|
||||||
import type { Location } from 'vscode-languageserver-protocol'
|
import type { Location } from 'vscode-languageserver-protocol'
|
||||||
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js'
|
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js'
|
||||||
@@ -222,6 +222,8 @@ function PlayableLevel({impressum, setImpressum}) {
|
|||||||
// Only for mobile layout
|
// Only for mobile layout
|
||||||
const [pageNumber, setPageNumber] = useState(0)
|
const [pageNumber, setPageNumber] = useState(0)
|
||||||
const [typewriterMode, setTypewriterMode] = useState(true)
|
const [typewriterMode, setTypewriterMode] = useState(true)
|
||||||
|
// set to true to prevent switching between typewriter and editor
|
||||||
|
const [lockInputMode, setLockInputMode] = useState(false)
|
||||||
const [typewriterInput, setTypewriterInput] = useState("")
|
const [typewriterInput, setTypewriterInput] = useState("")
|
||||||
const lastLevel = levelId >= gameInfo.data?.worldSize[worldId]
|
const lastLevel = levelId >= gameInfo.data?.worldSize[worldId]
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
@@ -392,18 +394,16 @@ function PlayableLevel({impressum, setImpressum}) {
|
|||||||
<div style={level.isLoading ? null : {display: "none"}} className="app-content loading"><CircularProgress /></div>
|
<div style={level.isLoading ? null : {display: "none"}} className="app-content loading"><CircularProgress /></div>
|
||||||
<DeletedChatContext.Provider value={{deletedChat, setDeletedChat, showHelp, setShowHelp}}>
|
<DeletedChatContext.Provider value={{deletedChat, setDeletedChat, showHelp, setShowHelp}}>
|
||||||
<SelectionContext.Provider value={{selectedStep, setSelectedStep}}>
|
<SelectionContext.Provider value={{selectedStep, setSelectedStep}}>
|
||||||
<InputModeContext.Provider value={{typewriterMode, setTypewriterMode, typewriterInput, setTypewriterInput}}>
|
<InputModeContext.Provider value={{typewriterMode, setTypewriterMode, typewriterInput, setTypewriterInput, lockInputMode, setLockInputMode}}>
|
||||||
<ProofContext.Provider value={{proof, setProof}}>
|
<ProofContext.Provider value={{proof, setProof}}>
|
||||||
<EditorContext.Provider value={editorConnection}>
|
<EditorContext.Provider value={editorConnection}>
|
||||||
<MonacoEditorContext.Provider value={editor}>
|
<MonacoEditorContext.Provider value={editor}>
|
||||||
<LevelAppBar
|
<LevelAppBar
|
||||||
|
pageNumber={pageNumber} setPageNumber={setPageNumber}
|
||||||
isLoading={level.isLoading}
|
isLoading={level.isLoading}
|
||||||
lockEditorMode={level.data?.template !== null}
|
|
||||||
levelTitle={`${mobile ? '' : 'Level '}${levelId} / ${gameInfo.data?.worldSize[worldId]}` +
|
levelTitle={`${mobile ? '' : 'Level '}${levelId} / ${gameInfo.data?.worldSize[worldId]}` +
|
||||||
(level?.data?.title && ` : ${level?.data?.title}`)}
|
(level?.data?.title && ` : ${level?.data?.title}`)}
|
||||||
impressum={impressum}
|
toggleImpressum={toggleImpressum} />
|
||||||
toggleImpressum={toggleImpressum}
|
|
||||||
pageNumber={pageNumber} setPageNumber={setPageNumber} />
|
|
||||||
{mobile?
|
{mobile?
|
||||||
// TODO: This is copied from the `Split` component below...
|
// TODO: This is copied from the `Split` component below...
|
||||||
<>
|
<>
|
||||||
@@ -471,7 +471,7 @@ function Introduction({impressum, setImpressum}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<LevelAppBar isLoading={gameInfo.isLoading} levelTitle="Introduction" impressum={impressum} toggleImpressum={toggleImpressum}/>
|
<LevelAppBar isLoading={gameInfo.isLoading} levelTitle="Introduction" toggleImpressum={toggleImpressum}/>
|
||||||
{gameInfo.isLoading ?
|
{gameInfo.isLoading ?
|
||||||
<div className="app-content loading"><CircularProgress /></div>
|
<div className="app-content loading"><CircularProgress /></div>
|
||||||
: mobile ?
|
: mobile ?
|
||||||
|
|||||||
@@ -9,6 +9,15 @@ import { deleteProgress, selectProgress } from '../../state/progress'
|
|||||||
import { downloadFile } from '../world_tree'
|
import { downloadFile } from '../world_tree'
|
||||||
import { Button } from '../button'
|
import { Button } from '../button'
|
||||||
|
|
||||||
|
/** download the current progress (i.e. what's saved in the browser store) */
|
||||||
|
export function downloadProgress(gameId: string, gameProgress: any, ev: React.MouseEvent) {
|
||||||
|
ev.preventDefault()
|
||||||
|
downloadFile({
|
||||||
|
data: JSON.stringify(gameProgress, null, 2),
|
||||||
|
fileName: `lean4game-${gameId}-${new Date().toLocaleDateString()}.json`,
|
||||||
|
fileType: 'text/json',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/** Pop-up to delete game progress.
|
/** Pop-up to delete game progress.
|
||||||
*
|
*
|
||||||
@@ -20,23 +29,13 @@ export function ErasePopup ({handleClose}) {
|
|||||||
const gameProgress = useSelector(selectProgress(gameId))
|
const gameProgress = useSelector(selectProgress(gameId))
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
/** Download the current progress (i.e. what's saved in the browser store) */
|
|
||||||
const downloadProgress = (e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
downloadFile({
|
|
||||||
data: JSON.stringify(gameProgress, null, 2),
|
|
||||||
fileName: `lean4game-${gameId}-${new Date().toLocaleDateString()}.json`,
|
|
||||||
fileType: 'text/json',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const eraseProgress = () => {
|
const eraseProgress = () => {
|
||||||
dispatch(deleteProgress({game: gameId}))
|
dispatch(deleteProgress({game: gameId}))
|
||||||
handleClose()
|
handleClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
const downloadAndErase = (e) => {
|
const downloadAndErase = (ev) => {
|
||||||
downloadProgress(e)
|
downloadProgress(gameId, gameProgress, ev)
|
||||||
eraseProgress()
|
eraseProgress()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { useState, useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import Split from 'react-split'
|
import Split from 'react-split'
|
||||||
import { Box, CircularProgress } from '@mui/material'
|
import { Box, CircularProgress } from '@mui/material'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import { faGlobe, faArrowRight, faArrowLeft } from '@fortawesome/free-solid-svg-icons'
|
import { faArrowRight } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
import { GameIdContext } from '../app'
|
import { GameIdContext } from '../app'
|
||||||
import { useAppDispatch } from '../hooks'
|
import { useAppDispatch, useAppSelector } from '../hooks'
|
||||||
import { changedOpenedIntro } from '../state/progress'
|
import { changedOpenedIntro, selectOpenedIntro } from '../state/progress'
|
||||||
import { useGetGameInfoQuery, useLoadInventoryOverviewQuery } from '../state/api'
|
import { useGetGameInfoQuery, useLoadInventoryOverviewQuery } from '../state/api'
|
||||||
import { Button } from './button'
|
import { Button } from './button'
|
||||||
import { MobileContext } from './infoview/context'
|
import { MobileContext } from './infoview/context'
|
||||||
@@ -24,9 +24,9 @@ import { WelcomeAppBar } from './app_bar'
|
|||||||
import { Hint } from './hints'
|
import { Hint } from './hints'
|
||||||
|
|
||||||
|
|
||||||
/** The panel showing the game's introduction text */
|
/** the panel showing the game's introduction text */
|
||||||
function IntroductionPanel({introduction}: {introduction: string}) {
|
function IntroductionPanel({introduction, setPageNumber}: {introduction: string, setPageNumber}) {
|
||||||
const {mobile, setPageNumber} = React.useContext(MobileContext)
|
const {mobile} = React.useContext(MobileContext)
|
||||||
const gameId = React.useContext(GameIdContext)
|
const gameId = React.useContext(GameIdContext)
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
@@ -46,11 +46,6 @@ function IntroductionPanel({introduction}: {introduction: string}) {
|
|||||||
: <></>
|
: <></>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{/* <Typography variant="body1" component="div" className="welcome-text">
|
|
||||||
<h1>{title}</h1>
|
|
||||||
<Markdown>{introduction}</Markdown>
|
|
||||||
</Typography>
|
|
||||||
*/}
|
|
||||||
{mobile &&
|
{mobile &&
|
||||||
<div className="button-row">
|
<div className="button-row">
|
||||||
<Button className="btn" to=""
|
<Button className="btn" to=""
|
||||||
@@ -65,34 +60,32 @@ function IntroductionPanel({introduction}: {introduction: string}) {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
/** main page of the game showing amoung others the tree of worlds/levels */
|
/** main page of the game showing among others the tree of worlds/levels */
|
||||||
function Welcome() {
|
function Welcome() {
|
||||||
const gameId = React.useContext(GameIdContext)
|
const gameId = React.useContext(GameIdContext)
|
||||||
const {mobile, pageNumber, setPageNumber} = React.useContext(MobileContext)
|
const {mobile} = React.useContext(MobileContext)
|
||||||
const gameInfo = useGetGameInfoQuery({game: gameId})
|
const gameInfo = useGetGameInfoQuery({game: gameId})
|
||||||
const inventory = useLoadInventoryOverviewQuery({game: gameId})
|
const inventory = useLoadInventoryOverviewQuery({game: gameId})
|
||||||
|
|
||||||
// impressum pop-up
|
// For mobile only
|
||||||
|
const openedIntro = useAppSelector(selectOpenedIntro(gameId))
|
||||||
|
const [pageNumber, setPageNumber] = React.useState(openedIntro ? 1 : 0)
|
||||||
|
|
||||||
|
// pop-ups
|
||||||
|
const [eraseMenu, setEraseMenu] = React.useState(false)
|
||||||
const [impressum, setImpressum] = React.useState(false)
|
const [impressum, setImpressum] = React.useState(false)
|
||||||
const [rulesHelp, setRulesHelp] = React.useState(false)
|
|
||||||
|
|
||||||
function closeImpressum() {setImpressum(false)}
|
|
||||||
function toggleImpressum() {setImpressum(!impressum)}
|
|
||||||
function closeRulesHelp() {setRulesHelp(false)}
|
|
||||||
|
|
||||||
const [info, setInfo] = React.useState(false)
|
const [info, setInfo] = React.useState(false)
|
||||||
function closeInfo() {setInfo(false)}
|
const [rulesHelp, setRulesHelp] = React.useState(false)
|
||||||
function toggleInfo() {setInfo(!impressum)}
|
const [uploadMenu, setUploadMenu] = React.useState(false)
|
||||||
|
function closeEraseMenu() {setEraseMenu(false)}
|
||||||
|
function closeImpressum() {setImpressum(false)}
|
||||||
/* state variables to toggle the pop-up menus */
|
function closeInfo() {setInfo(false)}
|
||||||
const [eraseMenu, setEraseMenu] = React.useState(false);
|
function closeRulesHelp() {setRulesHelp(false)}
|
||||||
const openEraseMenu = () => setEraseMenu(true);
|
function closeUploadMenu() {setUploadMenu(false)}
|
||||||
const closeEraseMenu = () => setEraseMenu(false);
|
function toggleEraseMenu() {setEraseMenu(!eraseMenu)}
|
||||||
const [uploadMenu, setUploadMenu] = React.useState(false);
|
function toggleImpressum() {setImpressum(!impressum)}
|
||||||
const openUploadMenu = () => setUploadMenu(true);
|
function toggleInfo() {setInfo(!info)}
|
||||||
const closeUploadMenu = () => setUploadMenu(false);
|
function toggleUploadMenu() {setUploadMenu(!uploadMenu)}
|
||||||
|
|
||||||
|
|
||||||
// set the window title
|
// set the window title
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -106,23 +99,26 @@ function Welcome() {
|
|||||||
<CircularProgress />
|
<CircularProgress />
|
||||||
</Box>
|
</Box>
|
||||||
: <>
|
: <>
|
||||||
<WelcomeAppBar gameInfo={gameInfo.data} toggleImpressum={toggleImpressum} openEraseMenu={openEraseMenu}
|
<WelcomeAppBar pageNumber={pageNumber} setPageNumber={setPageNumber} gameInfo={gameInfo.data} toggleImpressum={toggleImpressum}
|
||||||
openUploadMenu={openUploadMenu} toggleInfo={toggleInfo} />
|
toggleEraseMenu={toggleEraseMenu} toggleUploadMenu={toggleUploadMenu}
|
||||||
|
toggleInfo={toggleInfo} />
|
||||||
<div className="app-content">
|
<div className="app-content">
|
||||||
{ mobile ?
|
{ mobile ?
|
||||||
<div className="welcome mobile">
|
<div className="welcome mobile">
|
||||||
{(pageNumber == 0 ?
|
{(pageNumber == 0 ?
|
||||||
<IntroductionPanel introduction={gameInfo.data?.introduction} />
|
<IntroductionPanel introduction={gameInfo.data?.introduction} setPageNumber={setPageNumber} />
|
||||||
: pageNumber == 1 ?
|
: pageNumber == 1 ?
|
||||||
<WorldTreePanel worlds={gameInfo.data?.worlds} worldSize={gameInfo.data?.worldSize} rulesHelp={rulesHelp} setRulesHelp={setRulesHelp} />
|
<WorldTreePanel worlds={gameInfo.data?.worlds} worldSize={gameInfo.data?.worldSize}
|
||||||
|
rulesHelp={rulesHelp} setRulesHelp={setRulesHelp} />
|
||||||
:
|
:
|
||||||
<InventoryPanel levelInfo={inventory?.data} />
|
<InventoryPanel levelInfo={inventory?.data} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
:
|
:
|
||||||
<Split className="welcome" minSize={0} snapOffset={200} sizes={[25, 50, 25]}>
|
<Split className="welcome" minSize={0} snapOffset={200} sizes={[25, 50, 25]}>
|
||||||
<IntroductionPanel introduction={gameInfo.data?.introduction} />
|
<IntroductionPanel introduction={gameInfo.data?.introduction} setPageNumber={setPageNumber} />
|
||||||
<WorldTreePanel worlds={gameInfo.data?.worlds} worldSize={gameInfo.data?.worldSize} rulesHelp={rulesHelp} setRulesHelp={setRulesHelp} />
|
<WorldTreePanel worlds={gameInfo.data?.worlds} worldSize={gameInfo.data?.worldSize}
|
||||||
|
rulesHelp={rulesHelp} setRulesHelp={setRulesHelp} />
|
||||||
<InventoryPanel levelInfo={inventory?.data} />
|
<InventoryPanel levelInfo={inventory?.data} />
|
||||||
</Split>
|
</Split>
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user