Remove MobileContext and use PreferencesContext instead
This commit is contained in:
+4
-29
@@ -8,49 +8,24 @@ import '@fontsource/roboto/700.css';
|
||||
|
||||
import './css/reset.css';
|
||||
import './css/app.css';
|
||||
import { MobileContext, PreferencesContext} from './components/infoview/context';
|
||||
import { AUTO_SWITCH_THRESHOLD, getWindowDimensions, setLayout, setisSavePreferences, PreferencesState} from './state/preferences';
|
||||
import { useAppDispatch, useAppSelector } from './hooks';
|
||||
import { PreferencesContext} from './components/infoview/context';
|
||||
import UsePreferences from "./state/hooks/use_preferences"
|
||||
|
||||
export const GameIdContext = React.createContext<string>(undefined);
|
||||
|
||||
function App() {
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
const params = useParams()
|
||||
const gameId = "g/" + params.owner + "/" + params.repo
|
||||
|
||||
// TODO: Modifying setMobile will not change 'layout', and the setMobile function may not exist in the future.
|
||||
const [mobile, setMobile] = React.useState<boolean>()
|
||||
const layout = useAppSelector((state) => state.preferences.layout);
|
||||
const changeLayout = (layout: PreferencesState["layout"]) => dispatch(setLayout(layout))
|
||||
const isSavePreferences = useAppSelector((state) => state.preferences.isSavePreferences);
|
||||
const changeIsSavePreferences = (isSave: boolean) => dispatch(setisSavePreferences(isSave))
|
||||
|
||||
const automaticallyAdjustLayout = () => {
|
||||
const {width} = getWindowDimensions()
|
||||
setMobile(width < AUTO_SWITCH_THRESHOLD)
|
||||
}
|
||||
|
||||
React.useEffect(()=>{
|
||||
if (layout === "auto"){
|
||||
void automaticallyAdjustLayout()
|
||||
window.addEventListener('resize', automaticallyAdjustLayout)
|
||||
|
||||
return () => window.removeEventListener('resize', automaticallyAdjustLayout)
|
||||
} else {
|
||||
setMobile(layout === "mobile")
|
||||
}
|
||||
}, [layout])
|
||||
const {mobile, layout, isSavePreferences, setLayout, setIsSavePreferences} = UsePreferences()
|
||||
|
||||
return (
|
||||
<div className="app">
|
||||
<GameIdContext.Provider value={gameId}>
|
||||
<MobileContext.Provider value={{mobile, setMobile}}>
|
||||
<PreferencesContext.Provider value={{layout, isSavePreferences, setLayout: changeLayout, setIsSavePreferences: changeIsSavePreferences}}>
|
||||
<PreferencesContext.Provider value={{mobile, layout, isSavePreferences, setLayout, setIsSavePreferences}}>
|
||||
<Outlet />
|
||||
</PreferencesContext.Provider>
|
||||
</MobileContext.Provider>
|
||||
</GameIdContext.Provider>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@ import { faDownload, faUpload, faEraser, faBook, faBookOpen, faGlobe, faHome,
|
||||
faArrowRight, faArrowLeft, faXmark, faBars, faCode,
|
||||
faCircleInfo, faTerminal, faMobileScreenButton, faDesktop, faGear } from '@fortawesome/free-solid-svg-icons'
|
||||
import { GameIdContext } from "../app"
|
||||
import { InputModeContext, MobileContext, WorldLevelIdContext } from "./infoview/context"
|
||||
import { InputModeContext, PreferencesContext, WorldLevelIdContext } from "./infoview/context"
|
||||
import { GameInfo, useGetGameInfoQuery } from '../state/api'
|
||||
import { changedOpenedIntro, selectCompleted, selectDifficulty, selectProgress } from '../state/progress'
|
||||
import { useAppDispatch, useAppSelector } from '../hooks'
|
||||
@@ -162,7 +162,7 @@ export function WelcomeAppBar({pageNumber, setPageNumber, gameInfo, toggleImpres
|
||||
}) {
|
||||
const gameId = React.useContext(GameIdContext)
|
||||
const gameProgress = useAppSelector(selectProgress(gameId))
|
||||
const {mobile, setMobile} = React.useContext(MobileContext)
|
||||
const {mobile} = React.useContext(PreferencesContext)
|
||||
const [navOpen, setNavOpen] = React.useState(false)
|
||||
|
||||
return <div className="app-bar">
|
||||
@@ -212,7 +212,7 @@ export function LevelAppBar({isLoading, levelTitle, toggleImpressum, pageNumber=
|
||||
}) {
|
||||
const gameId = React.useContext(GameIdContext)
|
||||
const {worldId, levelId} = React.useContext(WorldLevelIdContext)
|
||||
const {mobile} = React.useContext(MobileContext)
|
||||
const {mobile} = React.useContext(PreferencesContext)
|
||||
const [navOpen, setNavOpen] = React.useState(false)
|
||||
const gameInfo = useGetGameInfoQuery({game: gameId})
|
||||
const completed = useAppSelector(selectCompleted(gameId, worldId, levelId))
|
||||
|
||||
@@ -64,27 +64,19 @@ export const ProofStateContext = React.createContext<{
|
||||
})
|
||||
|
||||
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>>;
|
||||
}
|
||||
|
||||
export const PreferencesContext = React.createContext<IPreferencesContext>({
|
||||
mobile: false,
|
||||
layout: "auto",
|
||||
isSavePreferences: false,
|
||||
setLayout: () => {},
|
||||
setIsSavePreferences: () => {}
|
||||
})
|
||||
|
||||
export interface IMobileContext {
|
||||
mobile : boolean,
|
||||
setMobile: React.Dispatch<React.SetStateAction<Boolean>>,
|
||||
}
|
||||
|
||||
export const MobileContext = React.createContext<IMobileContext>({
|
||||
mobile: false,
|
||||
setMobile: () => {},
|
||||
})
|
||||
|
||||
export const WorldLevelIdContext = React.createContext<{
|
||||
worldId : string,
|
||||
levelId: number
|
||||
|
||||
@@ -27,7 +27,7 @@ import Markdown from '../markdown';
|
||||
import { Infos } from './infos';
|
||||
import { AllMessages, Errors, WithLspDiagnosticsContext } from './messages';
|
||||
import { Goal } from './goals';
|
||||
import { DeletedChatContext, InputModeContext, MobileContext, MonacoEditorContext, ProofContext, ProofStep, SelectionContext, WorldLevelIdContext } from './context';
|
||||
import { DeletedChatContext, InputModeContext, PreferencesContext, MonacoEditorContext, ProofContext, ProofStep, SelectionContext, WorldLevelIdContext } from './context';
|
||||
import { Typewriter, hasErrors, hasInteractiveErrors } from './typewriter';
|
||||
import { InteractiveDiagnostic } from '@leanprover/infoview/*';
|
||||
import { Button } from '../button';
|
||||
@@ -349,7 +349,7 @@ export function TypewriterInterface({props}) {
|
||||
const [disableInput, setDisableInput] = React.useState<boolean>(false)
|
||||
const [loadingProgress, setLoadingProgress] = React.useState<number>(0)
|
||||
const { setDeletedChat, showHelp, setShowHelp } = React.useContext(DeletedChatContext)
|
||||
const {mobile} = React.useContext(MobileContext)
|
||||
const {mobile} = React.useContext(PreferencesContext)
|
||||
const { proof } = React.useContext(ProofContext)
|
||||
const { setTypewriterInput } = React.useContext(InputModeContext)
|
||||
const { selectedStep, setSelectedStep } = React.useContext(SelectionContext)
|
||||
|
||||
@@ -27,7 +27,7 @@ import { Button } from './button'
|
||||
import Markdown from './markdown'
|
||||
import {InventoryPanel} from './inventory'
|
||||
import { hasInteractiveErrors } from './infoview/typewriter'
|
||||
import { DeletedChatContext, InputModeContext, MobileContext, MonacoEditorContext,
|
||||
import { DeletedChatContext, InputModeContext, PreferencesContext, MonacoEditorContext,
|
||||
ProofContext, ProofStep, SelectionContext, WorldLevelIdContext } from './infoview/context'
|
||||
import { DualEditor } from './infoview/main'
|
||||
import { GameHint } from './infoview/rpc_api'
|
||||
@@ -74,7 +74,7 @@ function Level() {
|
||||
|
||||
function ChatPanel({lastLevel}) {
|
||||
const chatRef = useRef<HTMLDivElement>(null)
|
||||
const {mobile} = useContext(MobileContext)
|
||||
const {mobile} = useContext(PreferencesContext)
|
||||
const gameId = useContext(GameIdContext)
|
||||
const {worldId, levelId} = useContext(WorldLevelIdContext)
|
||||
const level = useLoadLevelQuery({game: gameId, world: worldId, level: levelId})
|
||||
@@ -215,7 +215,7 @@ function PlayableLevel({impressum, setImpressum}) {
|
||||
const codeviewRef = useRef<HTMLDivElement>(null)
|
||||
const gameId = React.useContext(GameIdContext)
|
||||
const {worldId, levelId} = useContext(WorldLevelIdContext)
|
||||
const {mobile} = React.useContext(MobileContext)
|
||||
const {mobile} = React.useContext(PreferencesContext)
|
||||
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
@@ -441,7 +441,7 @@ function PlayableLevel({impressum, setImpressum}) {
|
||||
function IntroductionPanel({gameInfo}) {
|
||||
const gameId = React.useContext(GameIdContext)
|
||||
const {worldId} = useContext(WorldLevelIdContext)
|
||||
const {mobile} = React.useContext(MobileContext)
|
||||
const {mobile} = React.useContext(PreferencesContext)
|
||||
|
||||
let text: Array<string> = gameInfo.data?.worlds.nodes[worldId].introduction.split(/\n(\s*\n)+/)
|
||||
|
||||
@@ -468,7 +468,7 @@ export default Level
|
||||
/** The site with the introduction text of a world */
|
||||
function Introduction({impressum, setImpressum}) {
|
||||
const gameId = React.useContext(GameIdContext)
|
||||
const {mobile} = useContext(MobileContext)
|
||||
const {mobile} = useContext(PreferencesContext)
|
||||
|
||||
const inventory = useLoadInventoryOverviewQuery({game: gameId})
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import FormControlLabel from '@mui/material/FormControlLabel';
|
||||
|
||||
import { IPreferencesContext } from "../infoview/context"
|
||||
|
||||
interface PreferencesPopupProps extends IPreferencesContext {
|
||||
interface PreferencesPopupProps extends Omit<IPreferencesContext, 'mobile'> {
|
||||
handleClose: () => void
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import { useAppDispatch, useAppSelector } from '../hooks'
|
||||
import { changedOpenedIntro, selectOpenedIntro } from '../state/progress'
|
||||
import { useGetGameInfoQuery, useLoadInventoryOverviewQuery } from '../state/api'
|
||||
import { Button } from './button'
|
||||
import { MobileContext, PreferencesContext } from './infoview/context'
|
||||
import { PreferencesContext } from './infoview/context'
|
||||
import { InventoryPanel } from './inventory'
|
||||
import { ErasePopup } from './popup/erase'
|
||||
import { InfoPopup } from './popup/game_info'
|
||||
@@ -27,7 +27,7 @@ import { Hint } from './hints'
|
||||
|
||||
/** the panel showing the game's introduction text */
|
||||
function IntroductionPanel({introduction, setPageNumber}: {introduction: string, setPageNumber}) {
|
||||
const {mobile} = React.useContext(MobileContext)
|
||||
const {mobile} = React.useContext(PreferencesContext)
|
||||
const gameId = React.useContext(GameIdContext)
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
@@ -64,7 +64,7 @@ function IntroductionPanel({introduction, setPageNumber}: {introduction: string,
|
||||
/** main page of the game showing among others the tree of worlds/levels */
|
||||
function Welcome() {
|
||||
const gameId = React.useContext(GameIdContext)
|
||||
const {mobile, setMobile} = React.useContext(MobileContext)
|
||||
const {mobile} = React.useContext(PreferencesContext)
|
||||
const {layout, isSavePreferences, setLayout, setIsSavePreferences} = React.useContext(PreferencesContext)
|
||||
|
||||
const gameInfo = useGetGameInfoQuery({game: gameId})
|
||||
|
||||
@@ -16,7 +16,7 @@ import { selectDifficulty, changedDifficulty, selectCompleted } from '../state/p
|
||||
import { store } from '../state/store'
|
||||
|
||||
import '../css/world_tree.css'
|
||||
import { MobileContext } from './infoview/context'
|
||||
import { PreferencesContext } from './infoview/context'
|
||||
|
||||
// Settings for the world tree
|
||||
cytoscape.use( klay )
|
||||
@@ -198,7 +198,7 @@ export function WorldSelectionMenu({rulesHelp, setRulesHelp}) {
|
||||
const gameId = React.useContext(GameIdContext)
|
||||
const difficulty = useSelector(selectDifficulty(gameId))
|
||||
const dispatch = useAppDispatch()
|
||||
const { mobile } = React.useContext(MobileContext)
|
||||
const { mobile } = React.useContext(PreferencesContext)
|
||||
|
||||
|
||||
function label(x : number) {
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import React, { useState } from "react";
|
||||
import { useAppDispatch, useAppSelector } from "../../hooks";
|
||||
import {
|
||||
PreferencesState,
|
||||
setLayout as setPreferencesLayout,
|
||||
setIsSavePreferences as setPreferencesIsSavePreferences,
|
||||
getWindowDimensions,
|
||||
AUTO_SWITCH_THRESHOLD
|
||||
} from "../preferences";
|
||||
|
||||
|
||||
const UsePreferences = () => {
|
||||
const dispatch = useAppDispatch()
|
||||
const [mobile, setMobile] = React.useState<boolean>()
|
||||
|
||||
const layout = useAppSelector((state) => state.preferences.layout);
|
||||
const setLayout = (layout: PreferencesState["layout"]) => dispatch(setPreferencesLayout(layout))
|
||||
|
||||
const isSavePreferences = useAppSelector((state) => state.preferences.isSavePreferences);
|
||||
const setIsSavePreferences = (isSave: boolean) => dispatch(setPreferencesIsSavePreferences(isSave))
|
||||
|
||||
const automaticallyAdjustLayout = () => {
|
||||
const {width} = getWindowDimensions()
|
||||
setMobile(width < AUTO_SWITCH_THRESHOLD)
|
||||
}
|
||||
|
||||
React.useEffect(()=>{
|
||||
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}
|
||||
}
|
||||
|
||||
export default UsePreferences;
|
||||
@@ -26,10 +26,10 @@ export const preferencesSlice = createSlice({
|
||||
setLayout: (state, action) => {
|
||||
state.layout = action.payload;
|
||||
},
|
||||
setisSavePreferences: (state, action) => {
|
||||
setIsSavePreferences: (state, action) => {
|
||||
state.isSavePreferences = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { setLayout, setisSavePreferences } = preferencesSlice.actions;
|
||||
export const { setLayout, setIsSavePreferences } = preferencesSlice.actions;
|
||||
|
||||
Reference in New Issue
Block a user