diff --git a/client/src/components/Level.tsx b/client/src/components/Level.tsx index 9e44489..513a8ed 100644 --- a/client/src/components/Level.tsx +++ b/client/src/components/Level.tsx @@ -76,8 +76,9 @@ function PlayableLevel({worldId, levelId}) { const codeviewRef = useRef(null) const introductionPanelRef = useRef(null) - const initialCode = useAppSelector(selectCode(worldId, levelId)) - const initialSelections = useAppSelector(selectSelections(worldId, levelId)) + const gameId = React.useContext(GameIdContext) + const initialCode = useAppSelector(selectCode(gameId, worldId, levelId)) + const initialSelections = useAppSelector(selectSelections(gameId, worldId, levelId)) const [commandLineMode, setCommandLineMode] = useState(true) const [commandLineInput, setCommandLineInput] = useState("") @@ -126,14 +127,13 @@ function PlayableLevel({worldId, levelId}) { }]); } - const gameId = React.useContext(GameIdContext) const gameInfo = useGetGameInfoQuery({game: gameId}) const level = useLoadLevelQuery({game: gameId, world: worldId, level: levelId}) const dispatch = useAppDispatch() const onDidChangeContent = (code) => { - dispatch(codeEdited({world: worldId, level: levelId, code})) + dispatch(codeEdited({game: gameId, world: worldId, level: levelId, code})) setCanUndo(code.trim() !== "") } @@ -142,10 +142,10 @@ function PlayableLevel({worldId, levelId}) { const selections = monacoSelections.map( ({selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn}) => {return {selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn}}) - dispatch(changedSelection({world: worldId, level: levelId, selections})) + dispatch(changedSelection({game: gameId, world: worldId, level: levelId, selections})) } - const completed = useAppSelector(selectCompleted(worldId, levelId)) + const completed = useAppSelector(selectCompleted(gameId, worldId, levelId)) const {editor, infoProvider, editorConnection} = useLevelEditor(worldId, levelId, codeviewRef, initialCode, initialSelections, onDidChangeContent, onDidChangeSelection) @@ -408,7 +408,7 @@ function useLoadWorldFiles(worldId) { if (model) { models.push(model) } else { - const code = selectCode(worldId, levelId)(store.getState()) + const code = selectCode(gameId, worldId, levelId)(store.getState()) models.push(monaco.editor.createModel(code, 'lean4', uri)) } } diff --git a/client/src/components/Welcome.tsx b/client/src/components/Welcome.tsx index c5fce54..5fb36f3 100644 --- a/client/src/components/Welcome.tsx +++ b/client/src/components/Welcome.tsx @@ -19,7 +19,7 @@ import { GameIdContext } from '../App'; function LevelIcon({ worldId, levelId, position }) { const gameId = React.useContext(GameIdContext) - const completed = useSelector(selectCompleted(worldId,levelId)) + const completed = useSelector(selectCompleted(gameId, worldId,levelId)) // TODO: relative positioning? return ( diff --git a/client/src/components/infoview/main.tsx b/client/src/components/infoview/main.tsx index 369bdf7..39075bd 100644 --- a/client/src/components/infoview/main.tsx +++ b/client/src/components/infoview/main.tsx @@ -19,10 +19,12 @@ import { WithRpcSessions } from '../../../../node_modules/lean4-infoview/src/inf import { ServerVersion } from '../../../../node_modules/lean4-infoview/src/infoview/serverVersion'; import { useAppDispatch, useAppSelector } from '../../hooks'; import { levelCompleted, selectCompleted } from '../../state/progress'; +import { GameIdContext } from '../../App'; export function Main(props: {world: string, level: number}) { const ec = React.useContext(EditorContext); + const gameId = React.useContext(GameIdContext) const dispatch = useAppDispatch() @@ -33,13 +35,13 @@ export function Main(props: {world: string, level: number}) { if (ec.events.changedCursorLocation.current && ec.events.changedCursorLocation.current.uri === params.uri) { - dispatch(levelCompleted({world: props.world, level: props.level})) + dispatch(levelCompleted({game: gameId, world: props.world, level: props.level})) } }, [] ); - const completed = useAppSelector(selectCompleted(props.world, props.level)) + const completed = useAppSelector(selectCompleted(gameId, props.world, props.level)) /* Set up updates to the global infoview state on editor events. */ const config = useEventResult(ec.events.changedInfoviewConfig) ?? defaultInfoviewConfig; diff --git a/client/src/state/localStorage.ts b/client/src/state/localStorage.ts index a290d75..729d84d 100644 --- a/client/src/state/localStorage.ts +++ b/client/src/state/localStorage.ts @@ -1,4 +1,4 @@ -const KEY = "progress"; +const KEY = "game_progress"; export function loadState() { try { const serializedState = localStorage.getItem(KEY); diff --git a/client/src/state/progress.ts b/client/src/state/progress.ts index 6005d4f..3d0a29c 100644 --- a/client/src/state/progress.ts +++ b/client/src/state/progress.ts @@ -3,7 +3,7 @@ import type { PayloadAction } from '@reduxjs/toolkit' import { loadState } from "./localStorage"; interface ProgressState { - level: {[world: string]: {[level: number]: LevelProgressState}} + level: {[game: string]: {[world: string]: {[level: number]: LevelProgressState}}} } interface Selection { selectionStartLineNumber: number, @@ -20,12 +20,15 @@ interface LevelProgressState { const initialProgressState = loadState() ?? { level: {} } as ProgressState const initalLevelProgressState = {code: "", completed: false} as LevelProgressState -function addLevelProgress(state, action: PayloadAction<{world: string, level: number}>) { - if (!state.level[action.payload.world]) { - state.level[action.payload.world] = {} +function addLevelProgress(state, action: PayloadAction<{game: string, world: string, level: number}>) { + if (!state.level[action.payload.game]) { + state.level[action.payload.game] = {} } - if (!state.level[action.payload.world][action.payload.level]) { - state.level[action.payload.world][action.payload.level] = {...initalLevelProgressState} + if (!state.level[action.payload.game][action.payload.world]) { + state.level[action.payload.game][action.payload.world] = {} + } + if (!state.level[action.payload.game][action.payload.world][action.payload.level]) { + state.level[action.payload.game][action.payload.world][action.payload.level] = {...initalLevelProgressState} } } @@ -33,45 +36,46 @@ export const progressSlice = createSlice({ name: 'progress', initialState: initialProgressState, reducers: { - codeEdited(state, action: PayloadAction<{world: string, level: number, code: string}>) { + codeEdited(state, action: PayloadAction<{game: string, world: string, level: number, code: string}>) { addLevelProgress(state, action) - state.level[action.payload.world][action.payload.level].code = action.payload.code - state.level[action.payload.world][action.payload.level].completed = false + state.level[action.payload.game][action.payload.world][action.payload.level].code = action.payload.code + state.level[action.payload.game][action.payload.world][action.payload.level].completed = false }, - changedSelection(state, action: PayloadAction<{world: string, level: number, selections: Selection[]}>) { + changedSelection(state, action: PayloadAction<{game: string, world: string, level: number, selections: Selection[]}>) { addLevelProgress(state, action) - state.level[action.payload.world][action.payload.level].selections = action.payload.selections + state.level[action.payload.game][action.payload.world][action.payload.level].selections = action.payload.selections }, - levelCompleted(state, action: PayloadAction<{world: string, level: number}>) { + levelCompleted(state, action: PayloadAction<{game: string, world: string, level: number}>) { addLevelProgress(state, action) - state.level[action.payload.world][action.payload.level].completed = true + state.level[action.payload.game][action.payload.world][action.payload.level].completed = true }, } }) -export function selectLevel(world: string, level: number) { +export function selectLevel(game: string, world: string, level: number) { return (state) =>{ - if (!state.progress.level[world]) { return initalLevelProgressState } - if (!state.progress.level[world][level]) { return initalLevelProgressState } - return state.progress.level[world][level] + if (!state.progress.level[game]) { return initalLevelProgressState } + if (!state.progress.level[game][world]) { return initalLevelProgressState } + if (!state.progress.level[game][world][level]) { return initalLevelProgressState } + return state.progress.level[game][world][level] } } -export function selectCode(world: string, level: number) { +export function selectCode(game: string, world: string, level: number) { return (state) => { - return selectLevel(world, level)(state).code + return selectLevel(game, world, level)(state).code } } -export function selectSelections(world: string, level: number) { +export function selectSelections(game: string, world: string, level: number) { return (state) => { - return selectLevel(world, level)(state).selections + return selectLevel(game, world, level)(state).selections } } -export function selectCompleted(world: string, level: number) { +export function selectCompleted(game: string, world: string, level: number) { return (state) => { - return selectLevel(world, level)(state).completed + return selectLevel(game, world, level)(state).completed } }