make preferences work #179
This commit is contained in:
@@ -36,7 +36,7 @@ module.exports = {
|
||||
},
|
||||
lngs: ['en','de'],
|
||||
ns: [],
|
||||
defaultLng: 'en',
|
||||
defaultLng: 'en-GB',
|
||||
defaultNs: 'translation',
|
||||
defaultValue: (lng, ns, key) => {
|
||||
if (lng === 'en') {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Tactics": "Taktiken",
|
||||
"Lean Game Server": "Lean-Spieleserver",
|
||||
"<p>Game rules determine if it is allowed to skip levels and if the games runs checks to only allow unlocked tactics and theorems in proofs.</p><1>Note: \"Unlocked\" tactics (or theorems) are determined by two things: The set of minimal tactics needed to solve a level, plus any tactics you unlocked in another level. That means if you unlock <1>simp</1> in a level, you can use it henceforth in any level.</1><p>The options are:</p>": "<p>Die Spielregeln bestimmen ob es erlaubt ist, Levels zu überspringen und ob das Spiel überprüft welche Taktiken und Theoreme freigeschaltet sind und nur diese im Beweis akzeptiert.</p><1>Bemerkung: \"Freigeschaltete\" Taktiken (und Theoreme) werden durch zwei Faktoren bestimmt: The Menge der Taktiken die minimal notwending sind um den Level zu lösen und dazu die Menge aller Taktiken, die in einem anderen Level freigeschaltet wurden. Das bedeutet wenn <1>simp</1> in einem Level freigeschaltet wird, kann diese Taktik danach in jeglichen Levels verwendet werden.",
|
||||
"Game Rules": "Spielregeln",
|
||||
@@ -7,5 +8,5 @@
|
||||
"regular": "regulär",
|
||||
"relaxed": "relaxed",
|
||||
"none": "keine",
|
||||
"Rules": "Regend"
|
||||
"Rules": "Regeln"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Tactics": "Tactics",
|
||||
"Lean Game Server": "Lean Game Server",
|
||||
"<p>Game rules determine if it is allowed to skip levels and if the games runs checks to only allow unlocked tactics and theorems in proofs.</p><1>Note: \"Unlocked\" tactics (or theorems) are determined by two things: The set of minimal tactics needed to solve a level, plus any tactics you unlocked in another level. That means if you unlock <1>simp</1> in a level, you can use it henceforth in any level.</1><p>The options are:</p>": "<p>Game rules determine if it is allowed to skip levels and if the games runs checks to only allow unlocked tactics and theorems in proofs.</p><1>Note: \"Unlocked\" tactics (or theorems) are determined by two things: The set of minimal tactics needed to solve a level, plus any tactics you unlocked in another level. That means if you unlock <1>simp</1> in a level, you can use it henceforth in any level.</1><p>The options are:</p>",
|
||||
"Game Rules": "Game Rules",
|
||||
|
||||
+7
-2
@@ -10,6 +10,7 @@ import './css/reset.css';
|
||||
import './css/app.css';
|
||||
import { PreferencesContext} from './components/infoview/context';
|
||||
import UsePreferences from "./state/hooks/use_preferences"
|
||||
import i18n from './i18n';
|
||||
|
||||
export const GameIdContext = React.createContext<string>(undefined);
|
||||
|
||||
@@ -18,12 +19,16 @@ function App() {
|
||||
const params = useParams()
|
||||
const gameId = "g/" + params.owner + "/" + params.repo
|
||||
|
||||
const {mobile, layout, isSavePreferences, setLayout, setIsSavePreferences} = UsePreferences()
|
||||
const {mobile, layout, isSavePreferences, language, setLayout, setIsSavePreferences, setLanguage} = UsePreferences()
|
||||
|
||||
React.useEffect(() => {
|
||||
i18n.changeLanguage(language)
|
||||
}, [language])
|
||||
|
||||
return (
|
||||
<div className="app">
|
||||
<GameIdContext.Provider value={gameId}>
|
||||
<PreferencesContext.Provider value={{mobile, layout, isSavePreferences, setLayout, setIsSavePreferences}}>
|
||||
<PreferencesContext.Provider value={{mobile, layout, isSavePreferences, language, setLayout, setIsSavePreferences, setLanguage}}>
|
||||
<React.Suspense>
|
||||
<Outlet />
|
||||
</React.Suspense>
|
||||
|
||||
@@ -14,6 +14,7 @@ import { useAppDispatch, useAppSelector } from '../hooks'
|
||||
import { Button } from './button'
|
||||
import { downloadProgress } from './popup/erase'
|
||||
import ReactCountryFlag from "react-country-flag"
|
||||
import { t } from 'i18next'
|
||||
|
||||
/** navigation buttons for mobile welcome page to switch between intro/tree/inventory. */
|
||||
function MobileNavButtons({pageNumber, setPageNumber}:
|
||||
@@ -119,28 +120,46 @@ function InputModeButton({setNavOpen, isDropdown}) {
|
||||
</Button>
|
||||
}
|
||||
|
||||
/** button to toggle iimpressum popup */
|
||||
/** button to toggle iimpressum popup
|
||||
*
|
||||
* Note: Do not translate "Impressum"!
|
||||
*/
|
||||
function ImpressumButton({setNavOpen, toggleImpressum, isDropdown}) {
|
||||
return <Button className="btn btn-inverted toggle-width"
|
||||
return <Button className="btn btn-inverted"
|
||||
title="information, Impressum, privacy policy" inverted="true" to="" onClick={(ev) => {toggleImpressum(ev); setNavOpen(false)}}>
|
||||
<FontAwesomeIcon icon={faCircleInfo} />
|
||||
{isDropdown && <> Info & Impressum</>}
|
||||
{isDropdown && <> Impressum</>}
|
||||
</Button>
|
||||
}
|
||||
|
||||
/** button to toggle iimpressum popup */
|
||||
function LanguageButton({setNavOpen, toggleLangNav, isDropdown}) {
|
||||
return <Button className="btn btn-inverted language-btn"
|
||||
title="language" inverted="true" to="" onClick={(ev) => {toggleLangNav(ev); setNavOpen(false)}}>
|
||||
<ReactCountryFlag countryCode="GB"
|
||||
className="emojiFlag"
|
||||
style={{
|
||||
height: '1.3em',
|
||||
width: '1.6em',
|
||||
}}
|
||||
title="English"
|
||||
/>
|
||||
{isDropdown && <> Language</>}
|
||||
function PreferencesButton({setNavOpen, togglePreferencesPopup}) {
|
||||
return <Button title="Preferences" inverted="true" to="" onClick={() => {togglePreferencesPopup(); setNavOpen(false)}}>
|
||||
<FontAwesomeIcon icon={faGear} /> {t("Preferences")}
|
||||
</Button>
|
||||
}
|
||||
|
||||
function GameInfoButton({setNavOpen, toggleInfo}) {
|
||||
return <Button className="btn btn-inverted"
|
||||
title="Game Info & Credits" inverted="true" to="" onClick={() => {toggleInfo(); setNavOpen(false)}}>
|
||||
<FontAwesomeIcon icon={faCircleInfo} /> Game Info
|
||||
</Button>
|
||||
}
|
||||
|
||||
function EraseButton ({setNavOpen, toggleEraseMenu}) {
|
||||
return <Button title="Clear Progress" inverted="true" to="" onClick={() => {toggleEraseMenu(); setNavOpen(false)}}>
|
||||
<FontAwesomeIcon icon={faEraser} /> Erase
|
||||
</Button>
|
||||
}
|
||||
|
||||
function DownloadButton ({setNavOpen, gameId, gameProgress}) {
|
||||
return <Button title="Download Progress" inverted="true" to="" onClick={(ev) => {downloadProgress(gameId, gameProgress, ev); setNavOpen(false)}}>
|
||||
<FontAwesomeIcon icon={faDownload} /> Download
|
||||
</Button>
|
||||
}
|
||||
|
||||
function UploadButton ({setNavOpen, toggleUploadMenu}) {
|
||||
return <Button title="Load Progress from JSON" inverted="true" to="" onClick={() => {toggleUploadMenu(); setNavOpen(false)}}>
|
||||
<FontAwesomeIcon icon={faUpload} /> Upload
|
||||
</Button>
|
||||
}
|
||||
|
||||
@@ -153,6 +172,12 @@ function HomeButton({isDropdown}) {
|
||||
</Button>
|
||||
}
|
||||
|
||||
function LandingPageButton() {
|
||||
return <Button inverted="false" title="back to games selection" to="/">
|
||||
<FontAwesomeIcon icon={faArrowLeft} /> <FontAwesomeIcon icon={faGlobe} />
|
||||
</Button>
|
||||
}
|
||||
|
||||
/** button in mobile level to toggle inventory.
|
||||
* only displays a button if `setPageNumber` is set.
|
||||
*/
|
||||
@@ -184,9 +209,7 @@ export function WelcomeAppBar({pageNumber, setPageNumber, gameInfo, toggleImpres
|
||||
|
||||
return <div className="app-bar">
|
||||
<div className='app-bar-left'>
|
||||
<Button inverted="false" title="back to games selection" to="/">
|
||||
<FontAwesomeIcon icon={faArrowLeft} /> <FontAwesomeIcon icon={faGlobe} />
|
||||
</Button>
|
||||
<LandingPageButton />
|
||||
<span className="app-bar-title"></span>
|
||||
</div>
|
||||
<div>
|
||||
@@ -197,33 +220,23 @@ export function WelcomeAppBar({pageNumber, setPageNumber, gameInfo, toggleImpres
|
||||
<MenuButton navOpen={navOpen} setNavOpen={setNavOpen} />
|
||||
</div>
|
||||
<div className={'menu dropdown' + (navOpen ? '' : ' hidden')}>
|
||||
<Button title="Game Info & Credits" inverted="true" to="" onClick={() => {toggleInfo(); setNavOpen(false)}}>
|
||||
<FontAwesomeIcon icon={faCircleInfo} /> Game Info
|
||||
</Button>
|
||||
<Button title="Clear Progress" inverted="true" to="" onClick={() => {toggleEraseMenu(); setNavOpen(false)}}>
|
||||
<FontAwesomeIcon icon={faEraser} /> Erase
|
||||
</Button>
|
||||
<Button title="Download Progress" inverted="true" to="" onClick={(ev) => {downloadProgress(gameId, gameProgress, ev); setNavOpen(false)}}>
|
||||
<FontAwesomeIcon icon={faDownload} /> Download
|
||||
</Button>
|
||||
<Button title="Load Progress from JSON" inverted="true" to="" onClick={() => {toggleUploadMenu(); setNavOpen(false)}}>
|
||||
<FontAwesomeIcon icon={faUpload} /> Upload
|
||||
</Button>
|
||||
<Button title="Impressum, privacy policy" inverted="true" to="" onClick={() => {toggleImpressum(); setNavOpen(false)}}>
|
||||
<FontAwesomeIcon icon={faCircleInfo} /> Impressum
|
||||
</Button>
|
||||
<Button title="Preferences" inverted="true" to="" onClick={() => {togglePreferencesPopup(); setNavOpen(false)}}>
|
||||
<FontAwesomeIcon icon={faGear} /> Preferences
|
||||
</Button>
|
||||
<GameInfoButton setNavOpen={setNavOpen} toggleInfo={toggleInfo}/>
|
||||
<EraseButton setNavOpen={setNavOpen} toggleEraseMenu={toggleEraseMenu}/>
|
||||
<DownloadButton setNavOpen={setNavOpen} gameId={gameId} gameProgress={gameProgress}/>
|
||||
<UploadButton setNavOpen={setNavOpen} toggleUploadMenu={toggleUploadMenu}/>
|
||||
<ImpressumButton setNavOpen={setNavOpen} toggleImpressum={toggleImpressum} isDropdown={true} />
|
||||
<PreferencesButton setNavOpen={setNavOpen} togglePreferencesPopup={togglePreferencesPopup}/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
/** the navigation bar in a level */
|
||||
export function LevelAppBar({isLoading, levelTitle, toggleImpressum, pageNumber=undefined, setPageNumber=undefined} : {
|
||||
export function LevelAppBar({isLoading, levelTitle, toggleImpressum, toggleInfo, togglePreferencesPopup, pageNumber=undefined, setPageNumber=undefined} : {
|
||||
isLoading: boolean,
|
||||
levelTitle: string,
|
||||
toggleImpressum: any,
|
||||
toggleInfo: any,
|
||||
togglePreferencesPopup: any,
|
||||
pageNumber?: number,
|
||||
setPageNumber?: any,
|
||||
}) {
|
||||
@@ -253,15 +266,16 @@ export function LevelAppBar({isLoading, levelTitle, toggleImpressum, pageNumber=
|
||||
<PreviousButton setNavOpen={setNavOpen} />
|
||||
<HomeButton isDropdown={true} />
|
||||
<InputModeButton setNavOpen={setNavOpen} isDropdown={true}/>
|
||||
<GameInfoButton setNavOpen={setNavOpen} toggleInfo={toggleInfo}/>
|
||||
<ImpressumButton setNavOpen={setNavOpen} toggleImpressum={toggleImpressum} isDropdown={true} />
|
||||
<LanguageButton setNavOpen={setNavOpen} toggleLangNav={() => {}} isDropdown={true} />
|
||||
<PreferencesButton setNavOpen={setNavOpen} togglePreferencesPopup={togglePreferencesPopup}/>
|
||||
</div>
|
||||
</> :
|
||||
<>
|
||||
{/* DESKTOP VERSION */}
|
||||
<div className='app-bar-left'>
|
||||
<HomeButton isDropdown={false} />
|
||||
<span className="app-bar-title">{worldTitle && `World: ${worldTitle}`}</span>
|
||||
<span className="app-bar-title">{worldTitle && `${t("World")}: ${worldTitle}`}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="app-bar-title">{levelTitle}</span>
|
||||
@@ -270,8 +284,12 @@ export function LevelAppBar({isLoading, levelTitle, toggleImpressum, pageNumber=
|
||||
<PreviousButton setNavOpen={setNavOpen} />
|
||||
<NextButton worldSize={gameInfo.data?.worldSize[worldId]} difficulty={difficulty} completed={completed} setNavOpen={setNavOpen} />
|
||||
<InputModeButton setNavOpen={setNavOpen} isDropdown={false}/>
|
||||
<ImpressumButton setNavOpen={setNavOpen} toggleImpressum={toggleImpressum} isDropdown={false} />
|
||||
<LanguageButton setNavOpen={setNavOpen} toggleLangNav={() => {}} isDropdown={false} />
|
||||
<MenuButton navOpen={navOpen} setNavOpen={setNavOpen}/>
|
||||
</div>
|
||||
<div className={'menu dropdown' + (navOpen ? '' : ' hidden')}>
|
||||
<GameInfoButton setNavOpen={setNavOpen} toggleInfo={toggleInfo}/>
|
||||
<ImpressumButton setNavOpen={setNavOpen} toggleImpressum={toggleImpressum} isDropdown={true} />
|
||||
<PreferencesButton setNavOpen={setNavOpen} togglePreferencesPopup={togglePreferencesPopup}/>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -79,15 +79,18 @@ export interface ProofStateProps {
|
||||
export interface IPreferencesContext extends PreferencesState{
|
||||
mobile: boolean, // The variables that actually control the page 'layout' can only be changed through layout.
|
||||
setLayout: React.Dispatch<React.SetStateAction<PreferencesState["layout"]>>;
|
||||
setIsSavePreferences: React.Dispatch<React.SetStateAction<Boolean>>;
|
||||
setIsSavePreferences: React.Dispatch<React.SetStateAction<PreferencesState["isSavePreferences"]>>;
|
||||
setLanguage: React.Dispatch<React.SetStateAction<PreferencesState["language"]>>;
|
||||
}
|
||||
|
||||
export const PreferencesContext = React.createContext<IPreferencesContext>({
|
||||
mobile: false,
|
||||
layout: "auto",
|
||||
isSavePreferences: false,
|
||||
language: "en",
|
||||
setLayout: () => {},
|
||||
setIsSavePreferences: () => {}
|
||||
setIsSavePreferences: () => {},
|
||||
setLanguage: () => {},
|
||||
})
|
||||
|
||||
export const WorldLevelIdContext = React.createContext<{
|
||||
|
||||
@@ -10,6 +10,7 @@ import { useLoadDocQuery, InventoryTile, LevelInfo, InventoryOverview, useLoadIn
|
||||
import { selectDifficulty, selectInventory } from '../state/progress';
|
||||
import { store } from '../state/store';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { t } from 'i18next';
|
||||
|
||||
export function Inventory({levelInfo, openDoc, lemmaTab, setLemmaTab, enableAll=false} :
|
||||
{
|
||||
@@ -24,7 +25,7 @@ export function Inventory({levelInfo, openDoc, lemmaTab, setLemmaTab, enableAll=
|
||||
<div className="inventory">
|
||||
{/* TODO: Click on Tactic: show info
|
||||
TODO: click on paste icon -> paste into command line */}
|
||||
<h2>Tactics</h2>
|
||||
<h2>{t("Tactics")}</h2>
|
||||
{levelInfo?.tactics &&
|
||||
<InventoryList items={levelInfo?.tactics} docType="Tactic" openDoc={openDoc} enableAll={enableAll}/>
|
||||
}
|
||||
|
||||
@@ -51,6 +51,8 @@ import { IConnectionProvider } from 'monaco-languageclient'
|
||||
import { monacoSetup } from 'lean4web/client/src/monacoSetup'
|
||||
import { onigasmH } from 'onigasm/lib/onigasmH'
|
||||
import { isLastStepWithErrors, lastStepHasErrors } from './infoview/goals'
|
||||
import { InfoPopup } from './popup/game_info'
|
||||
import { PreferencesPopup } from './popup/preferences'
|
||||
|
||||
|
||||
monacoSetup()
|
||||
@@ -60,17 +62,29 @@ function Level() {
|
||||
const levelId = parseInt(params.levelId)
|
||||
const worldId = params.worldId
|
||||
|
||||
const [impressum, setImpressum] = React.useState(false)
|
||||
const {layout, isSavePreferences, language, setLayout, setIsSavePreferences, setLanguage} = React.useContext(PreferencesContext)
|
||||
const gameId = React.useContext(GameIdContext)
|
||||
const gameInfo = useGetGameInfoQuery({game: gameId})
|
||||
|
||||
const closeImpressum = () => {
|
||||
setImpressum(false)
|
||||
}
|
||||
// pop-ups
|
||||
const [impressum, setImpressum] = React.useState(false)
|
||||
const [info, setInfo] = React.useState(false)
|
||||
const [preferencesPopup, setPreferencesPopup] = React.useState(false)
|
||||
|
||||
function closeImpressum() {setImpressum(false)}
|
||||
function closeInfo() {setInfo(false)}
|
||||
function closePreferencesPopup() {setPreferencesPopup(false)}
|
||||
function toggleImpressum() {setImpressum(!impressum)}
|
||||
function toggleInfo() {setInfo(!info)}
|
||||
function togglePreferencesPopup() {setPreferencesPopup(!preferencesPopup)}
|
||||
|
||||
return <WorldLevelIdContext.Provider value={{worldId, levelId}}>
|
||||
{levelId == 0 ?
|
||||
<Introduction impressum={impressum} setImpressum={setImpressum} /> :
|
||||
<PlayableLevel key={`${worldId}/${levelId}`} impressum={impressum} setImpressum={setImpressum} />}
|
||||
<Introduction impressum={impressum} setImpressum={setImpressum} toggleInfo={toggleInfo} togglePreferencesPopup={togglePreferencesPopup} /> :
|
||||
<PlayableLevel key={`${worldId}/${levelId}`} impressum={impressum} setImpressum={setImpressum} toggleInfo={toggleInfo} togglePreferencesPopup={togglePreferencesPopup}/>}
|
||||
{impressum ? <PrivacyPolicyPopup handleClose={closeImpressum} /> : null}
|
||||
{info ? <InfoPopup info={gameInfo.data?.info} handleClose={closeInfo}/> : null}
|
||||
{preferencesPopup ? <PreferencesPopup layout={layout} isSavePreferences={isSavePreferences} setLayout={setLayout} setIsSavePreferences={setIsSavePreferences} handleClose={closePreferencesPopup} language={language} setLanguage={setLanguage}/> : null}
|
||||
</WorldLevelIdContext.Provider>
|
||||
}
|
||||
|
||||
@@ -190,7 +204,7 @@ function ExercisePanel({codeviewRef, visible=true}: {codeviewRef: React.MutableR
|
||||
</div>
|
||||
}
|
||||
|
||||
function PlayableLevel({impressum, setImpressum}) {
|
||||
function PlayableLevel({impressum, setImpressum, toggleInfo, togglePreferencesPopup}) {
|
||||
const codeviewRef = useRef<HTMLDivElement>(null)
|
||||
const gameId = React.useContext(GameIdContext)
|
||||
const {worldId, levelId} = useContext(WorldLevelIdContext)
|
||||
@@ -396,7 +410,10 @@ function PlayableLevel({impressum, setImpressum}) {
|
||||
isLoading={level.isLoading}
|
||||
levelTitle={`${mobile ? '' : 'Level '}${levelId} / ${gameInfo.data?.worldSize[worldId]}` +
|
||||
(level?.data?.title && ` : ${level?.data?.title}`)}
|
||||
toggleImpressum={toggleImpressum} />
|
||||
toggleImpressum={toggleImpressum}
|
||||
toggleInfo={toggleInfo}
|
||||
togglePreferencesPopup={togglePreferencesPopup}
|
||||
/>
|
||||
{mobile?
|
||||
// TODO: This is copied from the `Split` component below...
|
||||
<>
|
||||
@@ -452,7 +469,7 @@ function IntroductionPanel({gameInfo}) {
|
||||
export default Level
|
||||
|
||||
/** The site with the introduction text of a world */
|
||||
function Introduction({impressum, setImpressum}) {
|
||||
function Introduction({impressum, setImpressum, toggleInfo, togglePreferencesPopup}) {
|
||||
const gameId = React.useContext(GameIdContext)
|
||||
const {mobile} = useContext(PreferencesContext)
|
||||
|
||||
@@ -470,7 +487,7 @@ function Introduction({impressum, setImpressum}) {
|
||||
}
|
||||
|
||||
return <>
|
||||
<LevelAppBar isLoading={gameInfo.isLoading} levelTitle="Introduction" toggleImpressum={toggleImpressum}/>
|
||||
<LevelAppBar isLoading={gameInfo.isLoading} levelTitle="Introduction" toggleImpressum={toggleImpressum} toggleInfo={toggleInfo} togglePreferencesPopup={togglePreferencesPopup}/>
|
||||
{gameInfo.isLoading ?
|
||||
<div className="app-content loading"><CircularProgress /></div>
|
||||
: mobile ?
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"languages": [
|
||||
{
|
||||
"iso": "en",
|
||||
"flag": "GB",
|
||||
"name": "English"
|
||||
},
|
||||
{
|
||||
"iso": "de",
|
||||
"flag": "DE",
|
||||
"name": "Deutsch"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
import * as React from 'react'
|
||||
import { Input, MenuItem, Select, Typography } from '@mui/material'
|
||||
import { Input, MenuItem, Select, SelectChangeEvent, Typography } from '@mui/material'
|
||||
import Markdown from '../markdown'
|
||||
import { Switch, Button, ButtonGroup } from '@mui/material';
|
||||
import Box from '@mui/material/Box';
|
||||
import Slider from '@mui/material/Slider';
|
||||
import supportedLanguages from './language_config.json'
|
||||
|
||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||
|
||||
@@ -11,102 +12,105 @@ import { IPreferencesContext } from "../infoview/context"
|
||||
import ReactCountryFlag from 'react-country-flag';
|
||||
|
||||
interface PreferencesPopupProps extends Omit<IPreferencesContext, 'mobile'> {
|
||||
handleClose: () => void
|
||||
handleClose: () => void
|
||||
}
|
||||
|
||||
export function PreferencesPopup({ layout, setLayout, isSavePreferences, setIsSavePreferences, handleClose }: PreferencesPopupProps) {
|
||||
export function PreferencesPopup({ layout, setLayout, isSavePreferences, language, setIsSavePreferences, handleClose, setLanguage }: PreferencesPopupProps) {
|
||||
|
||||
const marks = [
|
||||
{
|
||||
value: 0,
|
||||
label: 'Mobile',
|
||||
key: "mobile"
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
label: 'Auto',
|
||||
key: "auto"
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: 'Desktop',
|
||||
key: "desktop"
|
||||
},
|
||||
];
|
||||
const marks = [
|
||||
{
|
||||
value: 0,
|
||||
label: 'Mobile',
|
||||
key: "mobile"
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
label: 'Auto',
|
||||
key: "auto"
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: 'Desktop',
|
||||
key: "desktop"
|
||||
},
|
||||
];
|
||||
|
||||
const handlerChangeLayout = (_: Event, value: number) => {
|
||||
setLayout(marks[value].key as IPreferencesContext["layout"])
|
||||
}
|
||||
const handlerChangeLayout = (_: Event, value: number) => {
|
||||
setLayout(marks[value].key as IPreferencesContext["layout"])
|
||||
}
|
||||
|
||||
return <div className="modal-wrapper">
|
||||
<div className="modal-backdrop" onClick={handleClose} />
|
||||
<div className="modal">
|
||||
<div className="codicon codicon-close modal-close" onClick={handleClose}></div>
|
||||
<Typography variant="body1" component="div" className="settings">
|
||||
<div className='preferences-category'>
|
||||
<div className='category-title'>
|
||||
<h3>Language</h3>
|
||||
</div>
|
||||
<div className='preferences-item first leave-left-gap'>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Box sx={{ width: 300 }}>
|
||||
<Select
|
||||
value={'GB'}
|
||||
label={"Language"}>
|
||||
<MenuItem value={'GB'}>
|
||||
<ReactCountryFlag countryCode="GB"/> English
|
||||
</MenuItem>
|
||||
</Select>
|
||||
</Box>
|
||||
}
|
||||
label=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='preferences-category'>
|
||||
<div className='category-title'>
|
||||
<h3>Layout</h3>
|
||||
</div>
|
||||
<div className='preferences-item first leave-left-gap'>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Box sx={{ width: 300 }}>
|
||||
<Slider
|
||||
aria-label="Always visible"
|
||||
value={marks.find(item => item.key === layout).value}
|
||||
step={1}
|
||||
marks={marks}
|
||||
max={2}
|
||||
sx={{
|
||||
'& .MuiSlider-track': { display: 'none', },
|
||||
}}
|
||||
onChange={handlerChangeLayout}
|
||||
/>
|
||||
</Box>
|
||||
}
|
||||
label=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
const handlerChangeLanguage = (ev: SelectChangeEvent<string>) => {
|
||||
setLanguage(ev.target.value as IPreferencesContext["language"])
|
||||
}
|
||||
|
||||
<div className='preferences-category tail-category'>
|
||||
<div className='preferences-item'>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={isSavePreferences}
|
||||
onChange={() => setIsSavePreferences(!isSavePreferences)}
|
||||
name="checked"
|
||||
color="primary"
|
||||
/>
|
||||
}
|
||||
label="Save my settings (in the browser store)"
|
||||
labelPlacement="end"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Typography>
|
||||
return <div className="modal-wrapper">
|
||||
<div className="modal-backdrop" onClick={handleClose} />
|
||||
<div className="modal">
|
||||
<div className="codicon codicon-close modal-close" onClick={handleClose}></div>
|
||||
<Typography variant="body1" component="div" className="settings">
|
||||
<div className='preferences-category'>
|
||||
<div className='category-title'>
|
||||
<h3>Language</h3>
|
||||
</div>
|
||||
<div className='preferences-item first leave-left-gap'>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Box sx={{ width: 300 }}>
|
||||
<Select
|
||||
value={language}
|
||||
label={"Language"}
|
||||
onChange={handlerChangeLanguage}>
|
||||
{supportedLanguages.languages.map(lang => {return <MenuItem key={`menu-item-lang-${lang.iso}`} value={lang.iso}><ReactCountryFlag countryCode={lang.flag}/> {lang.name}</MenuItem>})}
|
||||
</Select>
|
||||
</Box>
|
||||
}
|
||||
label=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='preferences-category'>
|
||||
<div className='category-title'>
|
||||
<h3>Layout</h3>
|
||||
</div>
|
||||
<div className='preferences-item first leave-left-gap'>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Box sx={{ width: 300 }}>
|
||||
<Slider
|
||||
aria-label="Always visible"
|
||||
value={marks.find(item => item.key === layout).value}
|
||||
step={1}
|
||||
marks={marks}
|
||||
max={2}
|
||||
sx={{
|
||||
'& .MuiSlider-track': { display: 'none', },
|
||||
}}
|
||||
onChange={handlerChangeLayout}
|
||||
/>
|
||||
</Box>
|
||||
}
|
||||
label=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='preferences-category tail-category'>
|
||||
<div className='preferences-item'>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={isSavePreferences}
|
||||
onChange={() => setIsSavePreferences(!isSavePreferences)}
|
||||
name="checked"
|
||||
color="primary"
|
||||
/>
|
||||
}
|
||||
label="Save my settings (in the browser store)"
|
||||
labelPlacement="end"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ function IntroductionPanel({introduction, setPageNumber}: {introduction: string,
|
||||
function Welcome() {
|
||||
const gameId = React.useContext(GameIdContext)
|
||||
const {mobile} = React.useContext(PreferencesContext)
|
||||
const {layout, isSavePreferences, setLayout, setIsSavePreferences} = React.useContext(PreferencesContext)
|
||||
const {layout, isSavePreferences, language, setLayout, setIsSavePreferences, setLanguage} = React.useContext(PreferencesContext)
|
||||
|
||||
const gameInfo = useGetGameInfoQuery({game: gameId})
|
||||
const inventory = useLoadInventoryOverviewQuery({game: gameId})
|
||||
@@ -135,7 +135,7 @@ function Welcome() {
|
||||
{eraseMenu? <ErasePopup handleClose={closeEraseMenu}/> : null}
|
||||
{uploadMenu? <UploadPopup handleClose={closeUploadMenu}/> : null}
|
||||
{info ? <InfoPopup info={gameInfo.data?.info} handleClose={closeInfo}/> : null}
|
||||
{preferencesPopup ? <PreferencesPopup layout={layout} isSavePreferences={isSavePreferences} setLayout={setLayout} setIsSavePreferences={setIsSavePreferences} handleClose={closePreferencesPopup}/> : null}
|
||||
{preferencesPopup ? <PreferencesPopup layout={layout} isSavePreferences={isSavePreferences} setLayout={setLayout} setIsSavePreferences={setIsSavePreferences} handleClose={closePreferencesPopup} language={language} setLanguage={setLanguage}/> : null}
|
||||
</>
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import React, { useState } from "react";
|
||||
import { useAppDispatch, useAppSelector } from "../../hooks";
|
||||
import {
|
||||
PreferencesState,
|
||||
setLayout as setPreferencesLayout,
|
||||
import {
|
||||
PreferencesState,
|
||||
setLayout as setPreferencesLayout,
|
||||
setIsSavePreferences as setPreferencesIsSavePreferences,
|
||||
setLanguage as setLanguagePreferences,
|
||||
getWindowDimensions,
|
||||
AUTO_SWITCH_THRESHOLD
|
||||
} from "../preferences";
|
||||
@@ -19,6 +20,9 @@ const UsePreferences = () => {
|
||||
const isSavePreferences = useAppSelector((state) => state.preferences.isSavePreferences);
|
||||
const setIsSavePreferences = (isSave: boolean) => dispatch(setPreferencesIsSavePreferences(isSave))
|
||||
|
||||
const language = useAppSelector((state) => state.preferences.language);
|
||||
const setLanguage = (lang: string) => dispatch(setLanguagePreferences(lang))
|
||||
|
||||
const automaticallyAdjustLayout = () => {
|
||||
const {width} = getWindowDimensions()
|
||||
setMobile(width < AUTO_SWITCH_THRESHOLD)
|
||||
@@ -28,14 +32,14 @@ const UsePreferences = () => {
|
||||
if (layout === "auto"){
|
||||
void automaticallyAdjustLayout()
|
||||
window.addEventListener('resize', automaticallyAdjustLayout)
|
||||
|
||||
|
||||
return () => window.removeEventListener('resize', automaticallyAdjustLayout)
|
||||
} else {
|
||||
setMobile(layout === "mobile")
|
||||
}
|
||||
}, [layout])
|
||||
|
||||
return {mobile, layout, isSavePreferences, setLayout, setIsSavePreferences}
|
||||
}
|
||||
return {mobile, layout, isSavePreferences, language, setLayout, setIsSavePreferences, setLanguage}
|
||||
}
|
||||
|
||||
export default UsePreferences;
|
||||
export default UsePreferences;
|
||||
|
||||
@@ -5,6 +5,7 @@ import { loadPreferences, removePreferences, savePreferences } from "./local_sto
|
||||
export interface PreferencesState {
|
||||
layout: "mobile" | "auto" | "desktop";
|
||||
isSavePreferences: boolean;
|
||||
language: string;
|
||||
}
|
||||
|
||||
export function getWindowDimensions() {
|
||||
@@ -16,7 +17,8 @@ export const AUTO_SWITCH_THRESHOLD = 800
|
||||
|
||||
const initialState: PreferencesState = loadPreferences() ??{
|
||||
layout: "auto",
|
||||
isSavePreferences: false
|
||||
isSavePreferences: false,
|
||||
language: "en",
|
||||
}
|
||||
|
||||
export const preferencesSlice = createSlice({
|
||||
@@ -29,7 +31,10 @@ export const preferencesSlice = createSlice({
|
||||
setIsSavePreferences: (state, action) => {
|
||||
state.isSavePreferences = action.payload;
|
||||
},
|
||||
setLanguage: (state, action) => {
|
||||
state.language = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { setLayout, setIsSavePreferences } = preferencesSlice.actions;
|
||||
export const { setLayout, setIsSavePreferences, setLanguage } = preferencesSlice.actions;
|
||||
|
||||
Reference in New Issue
Block a user