diff --git a/client/src/app.tsx b/client/src/app.tsx index dfa7def..f4ab3f9 100644 --- a/client/src/app.tsx +++ b/client/src/app.tsx @@ -9,14 +9,14 @@ import '@fontsource/roboto/700.css'; import './css/reset.css'; import './css/app.css'; -import { PageContext, PopupContext, PreferencesContext} from './components/infoview/context'; +import { PageContext, PreferencesContext} from './components/infoview/context'; import UsePreferences from "./state/hooks/use_preferences" import i18n from './i18n'; import { Navigation } from './components/navigation'; import { useSelector } from 'react-redux'; -import { changeTypewriterMode, selectTypewriterMode } from './state/progress'; +import { changeTypewriterMode, selectOpenedIntro, selectTypewriterMode } from './state/progress'; import { useAppDispatch } from './hooks'; -import { Popup } from './components/popup/popup'; +import { Popup, PopupContext } from './components/popup/popup'; export const GameIdContext = React.createContext<{ gameId: string, @@ -40,6 +40,16 @@ function App() { const [page, setPage] = useState(0) const [popupContent, setPopupContent] = useState(null) + + const openedIntro = useSelector(selectOpenedIntro(gameId)) + + useEffect(() => { + if (openedIntro && !worldId && page == 0) { + setPage(1) + } + }, [openedIntro]) + + useEffect(() => { i18n.changeLanguage(language) }, [language]) diff --git a/client/src/components/level.tsx b/client/src/components/level.tsx index 92eddc3..8dbd0a2 100644 --- a/client/src/components/level.tsx +++ b/client/src/components/level.tsx @@ -49,7 +49,7 @@ 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 { InfoPopup } from './popup/info' import { PreferencesPopup } from './popup/preferences' import { useTranslation } from 'react-i18next' import i18next from 'i18next' diff --git a/client/src/components/navigation.tsx b/client/src/components/navigation.tsx index 2b9e4f5..ce8fa30 100644 --- a/client/src/components/navigation.tsx +++ b/client/src/components/navigation.tsx @@ -11,6 +11,8 @@ import { downloadProgress } from './popup/erase' import { useTranslation } from 'react-i18next' import '../css/navigation.css' import { PopupContext } from './popup/popup' +import { useSelector } from 'react-redux' +import { selectProgress } from '../state/progress' /** SVG github icon */ function GithubIcon () { @@ -41,15 +43,29 @@ const NavigationContext = createContext<{ setNavOpen: React.Dispatch> }>({navOpen: false, setNavOpen: () => {}}) +/** Content of the navigation during game selection. */ +function NavigationLandingPage () { + return
+
+
+
+
+} /** Content of the navigation on Desktop during world selection. */ function DesktopNavigationOverview () { const { t } = useTranslation() - const {gameId} = useContext(GameIdContext) + const { gameId } = useContext(GameIdContext) + const { setPopupContent } = useContext(PopupContext) const gameInfo = useGetGameInfoQuery({game: gameId}) return
-
+
+ {setPopupContent("rules")}} + inverted={true} /> +
{t(gameInfo.data?.title, {ns: gameId})}
@@ -61,9 +77,15 @@ function DesktopNavigationOverview () { function MobileNavigationOverview () { const { t } = useTranslation() const {page, setPage} = useContext(PageContext) + const { setPopupContent } = useContext(PopupContext) return
-
+
+ {setPopupContent("rules")}} + inverted={true} /> +
@@ -88,15 +110,6 @@ function MobileNavigationOverview () {
} -/** Content of the navigation during game selection. */ -function NavigationLandingPage () { - return
-
-
-
-
-} - /** Content of the navigation on Desktop in a level. */ function DesktopNavigationLevel () { const { t } = useTranslation() @@ -201,6 +214,8 @@ export function Navigation () { const { gameId, worldId } = useContext(GameIdContext) const { mobile } = useContext(PreferencesContext) const { setPopupContent } = useContext(PopupContext) + const gameProgress = useSelector(selectProgress(gameId)) + const [navOpen, setNavOpen] = useState(false) function toggleNav () {setNavOpen(!navOpen)} @@ -246,7 +261,7 @@ export function Navigation () { {downloadProgress(gameId)}} + onClick={() => {downloadProgress(gameId, gameProgress)}} inverted={true} /> { + const eraseProgress = (ev) => { dispatch(deleteProgress({game: gameId})) setPopupContent(null) + ev.preventDefault() // TODO: this is a hack to prevent the buttons below from opening a link } const downloadAndErase = (ev) => { - downloadProgress(gameId) - eraseProgress() + downloadProgress(gameId, gameProgress) + eraseProgress(ev) } return <> @@ -54,6 +54,6 @@ export function ErasePopup () { - + } diff --git a/client/src/components/popup/game_info.tsx b/client/src/components/popup/info.tsx similarity index 98% rename from client/src/components/popup/game_info.tsx rename to client/src/components/popup/info.tsx index 200b564..9c4cefb 100644 --- a/client/src/components/popup/game_info.tsx +++ b/client/src/components/popup/info.tsx @@ -1,6 +1,3 @@ -/** - * @fileOverview -*/ import * as React from 'react' import { Typography } from '@mui/material' import Markdown from '../markdown' diff --git a/client/src/components/popup/popup.tsx b/client/src/components/popup/popup.tsx index c75bcdd..2e7a21a 100644 --- a/client/src/components/popup/popup.tsx +++ b/client/src/components/popup/popup.tsx @@ -1,11 +1,13 @@ import * as React from 'react' import { useContext } from 'react' -import { PrivacyPolicyPopup } from './privacy_policy' +import { PrivacyPolicyPopup } from './privacy' import { ImpressumPopup } from './impressum' -import { InfoPopup } from './game_info' +import { InfoPopup } from './info' import { ErasePopup } from './erase' import { PreferencesPopup } from './preferences' import { UploadPopup } from './upload' +import { RulesPopup } from './rules' +import '../../css/popup.css' /** The context which manages if a popup is shown. * If `popupContent` is `null`, the popup is closed. @@ -30,6 +32,7 @@ export const Popups = { "info": , "preferences": , "privacy": , + "rules": , "upload": , } diff --git a/client/src/components/popup/privacy_policy.tsx b/client/src/components/popup/privacy.tsx similarity index 100% rename from client/src/components/popup/privacy_policy.tsx rename to client/src/components/popup/privacy.tsx diff --git a/client/src/components/popup/rules.tsx b/client/src/components/popup/rules.tsx new file mode 100644 index 0000000..30f9677 --- /dev/null +++ b/client/src/components/popup/rules.tsx @@ -0,0 +1,88 @@ +import { Box, Slider } from '@mui/material' +import * as React from 'react' +import { Trans, useTranslation } from 'react-i18next' +import { GameIdContext } from '../../app' +import { changedDifficulty, selectDifficulty } from '../../state/progress' +import { useSelector } from 'react-redux' +import { useContext } from 'react' +import { useAppDispatch } from '../../hooks' + +/** Pop-up that is displayed when opening the help explaining the game rules. + * + */ +export function RulesPopup () { + const { t } = useTranslation() + const { gameId } = useContext(GameIdContext) + const difficulty = useSelector(selectDifficulty(gameId)) + const dispatch = useAppDispatch() + + function label(x : number) { + return x == 0 ? t("none") : x == 1 ? t("relaxed") : t("regular") + } + + return <> +

{t("Game Rules")}

+ + {/* {t("Rules")} + (setRulesHelp(!rulesHelp))}/> + */} + + { + dispatch(changedDifficulty({game: gameId, difficulty: val})) + }} + /> + + +

+ 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. +

+

+ 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 simp in a level, you can use it henceforth in any level. +

+

The options are:

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
{t("levels")}{t("tactics")}
{t("regular")}🔐🔐
{t("relaxed")}🔓🔐
{t("none")}🔓🔓
+ +} diff --git a/client/src/components/popup/rules_help.tsx b/client/src/components/popup/rules_help.tsx deleted file mode 100644 index cb16af5..0000000 --- a/client/src/components/popup/rules_help.tsx +++ /dev/null @@ -1,60 +0,0 @@ -/** - * @fileOverview -*/ -import * as React from 'react' -import { Trans, useTranslation } from 'react-i18next' - -/** Pop-up that is displayed when opening the help explaining the game rules. - * - * `handleClose` is the function to close it again because it's open/closed state is - * controlled by the containing element. - */ -export function RulesHelpPopup ({handleClose}: {handleClose: () => void}) { - const { t } = useTranslation() - - return
-
-
-
-

{t("Game Rules")}

- -

- 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. -

-

- 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 simp in a level, you can use it henceforth in any level. -

-

The options are:

-
- - - - - - - - - - - - - - - - - - - - - - - - - -
{t("levels")}{t("tactics")}
{t("regular")}🔐🔐
{t("relaxed")}🔓🔐
{t("none")}🔓🔓
-
-
-} diff --git a/client/src/components/welcome.tsx b/client/src/components/welcome.tsx index 4b061b4..17d2b11 100644 --- a/client/src/components/welcome.tsx +++ b/client/src/components/welcome.tsx @@ -13,9 +13,8 @@ import { Button } from './button' import { PageContext, PreferencesContext } from './infoview/context' import { InventoryPanel } from './inventory' import { ErasePopup } from './popup/erase' -import { InfoPopup } from './popup/game_info' -import { PrivacyPolicyPopup } from './popup/privacy_policy' -import { RulesHelpPopup } from './popup/rules_help' +import { InfoPopup } from './popup/info' +import { PrivacyPolicyPopup } from './popup/privacy' import { UploadPopup } from './popup/upload' import { PreferencesPopup} from "./popup/preferences" import { WorldTreePanel } from './world_tree' diff --git a/client/src/components/world_tree.tsx b/client/src/components/world_tree.tsx index 3d1bc92..34be7ac 100644 --- a/client/src/components/world_tree.tsx +++ b/client/src/components/world_tree.tsx @@ -360,7 +360,7 @@ export function WorldTreePanel({worlds, worldSize, rulesHelp, setRulesHelp}: let dx = bounds ? s*(bounds.x2 - bounds.x1) + 2*padding : null return
- + {/* */} { - return state.progress.games[game.toLowerCase()] ?? null + return state.progress.games[game?.toLowerCase()] ?? null } } @@ -198,7 +198,7 @@ export function selectDifficulty(game: string) { /** return whether the intro has been read */ export function selectOpenedIntro(game: string) { return (state) => { - return state.progress.games[game.toLowerCase()]?.openedIntro + return state.progress.games[game?.toLowerCase()]?.openedIntro } }