add support for multiple games in redux state

pull/54/head
Alexander Bentkamp 2 years ago
parent dac15d84b5
commit cbc9576f98

@ -76,8 +76,9 @@ function PlayableLevel({worldId, levelId}) {
const codeviewRef = useRef<HTMLDivElement>(null) const codeviewRef = useRef<HTMLDivElement>(null)
const introductionPanelRef = useRef<HTMLDivElement>(null) const introductionPanelRef = useRef<HTMLDivElement>(null)
const initialCode = useAppSelector(selectCode(worldId, levelId)) const gameId = React.useContext(GameIdContext)
const initialSelections = useAppSelector(selectSelections(worldId, levelId)) const initialCode = useAppSelector(selectCode(gameId, worldId, levelId))
const initialSelections = useAppSelector(selectSelections(gameId, worldId, levelId))
const [commandLineMode, setCommandLineMode] = useState(true) const [commandLineMode, setCommandLineMode] = useState(true)
const [commandLineInput, setCommandLineInput] = useState("") const [commandLineInput, setCommandLineInput] = useState("")
@ -126,14 +127,13 @@ function PlayableLevel({worldId, levelId}) {
}]); }]);
} }
const gameId = React.useContext(GameIdContext)
const gameInfo = useGetGameInfoQuery({game: gameId}) const gameInfo = useGetGameInfoQuery({game: gameId})
const level = useLoadLevelQuery({game: gameId, world: worldId, level: levelId}) const level = useLoadLevelQuery({game: gameId, world: worldId, level: levelId})
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const onDidChangeContent = (code) => { const onDidChangeContent = (code) => {
dispatch(codeEdited({world: worldId, level: levelId, code})) dispatch(codeEdited({game: gameId, world: worldId, level: levelId, code}))
setCanUndo(code.trim() !== "") setCanUndo(code.trim() !== "")
} }
@ -142,10 +142,10 @@ function PlayableLevel({worldId, levelId}) {
const selections = monacoSelections.map( const selections = monacoSelections.map(
({selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn}) => ({selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn}) =>
{return {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} = const {editor, infoProvider, editorConnection} =
useLevelEditor(worldId, levelId, codeviewRef, initialCode, initialSelections, onDidChangeContent, onDidChangeSelection) useLevelEditor(worldId, levelId, codeviewRef, initialCode, initialSelections, onDidChangeContent, onDidChangeSelection)
@ -408,7 +408,7 @@ function useLoadWorldFiles(worldId) {
if (model) { if (model) {
models.push(model) models.push(model)
} else { } else {
const code = selectCode(worldId, levelId)(store.getState()) const code = selectCode(gameId, worldId, levelId)(store.getState())
models.push(monaco.editor.createModel(code, 'lean4', uri)) models.push(monaco.editor.createModel(code, 'lean4', uri))
} }
} }

@ -19,7 +19,7 @@ import { GameIdContext } from '../App';
function LevelIcon({ worldId, levelId, position }) { function LevelIcon({ worldId, levelId, position }) {
const gameId = React.useContext(GameIdContext) const gameId = React.useContext(GameIdContext)
const completed = useSelector(selectCompleted(worldId,levelId)) const completed = useSelector(selectCompleted(gameId, worldId,levelId))
// TODO: relative positioning? // TODO: relative positioning?
return ( return (
<Link to={`/game/${gameId}/${worldId}/level/${levelId}`} key={`/game/${gameId}/world/${worldId}/level/${levelId}`}> <Link to={`/game/${gameId}/${worldId}/level/${levelId}`} key={`/game/${gameId}/world/${worldId}/level/${levelId}`}>

@ -19,10 +19,12 @@ import { WithRpcSessions } from '../../../../node_modules/lean4-infoview/src/inf
import { ServerVersion } from '../../../../node_modules/lean4-infoview/src/infoview/serverVersion'; import { ServerVersion } from '../../../../node_modules/lean4-infoview/src/infoview/serverVersion';
import { useAppDispatch, useAppSelector } from '../../hooks'; import { useAppDispatch, useAppSelector } from '../../hooks';
import { levelCompleted, selectCompleted } from '../../state/progress'; import { levelCompleted, selectCompleted } from '../../state/progress';
import { GameIdContext } from '../../App';
export function Main(props: {world: string, level: number}) { export function Main(props: {world: string, level: number}) {
const ec = React.useContext(EditorContext); const ec = React.useContext(EditorContext);
const gameId = React.useContext(GameIdContext)
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
@ -33,13 +35,13 @@ export function Main(props: {world: string, level: number}) {
if (ec.events.changedCursorLocation.current && if (ec.events.changedCursorLocation.current &&
ec.events.changedCursorLocation.current.uri === params.uri) { 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. */ /* Set up updates to the global infoview state on editor events. */
const config = useEventResult(ec.events.changedInfoviewConfig) ?? defaultInfoviewConfig; const config = useEventResult(ec.events.changedInfoviewConfig) ?? defaultInfoviewConfig;

@ -1,4 +1,4 @@
const KEY = "progress"; const KEY = "game_progress";
export function loadState() { export function loadState() {
try { try {
const serializedState = localStorage.getItem(KEY); const serializedState = localStorage.getItem(KEY);

@ -3,7 +3,7 @@ import type { PayloadAction } from '@reduxjs/toolkit'
import { loadState } from "./localStorage"; import { loadState } from "./localStorage";
interface ProgressState { interface ProgressState {
level: {[world: string]: {[level: number]: LevelProgressState}} level: {[game: string]: {[world: string]: {[level: number]: LevelProgressState}}}
} }
interface Selection { interface Selection {
selectionStartLineNumber: number, selectionStartLineNumber: number,
@ -20,12 +20,15 @@ interface LevelProgressState {
const initialProgressState = loadState() ?? { level: {} } as ProgressState const initialProgressState = loadState() ?? { level: {} } as ProgressState
const initalLevelProgressState = {code: "", completed: false} as LevelProgressState const initalLevelProgressState = {code: "", completed: false} as LevelProgressState
function addLevelProgress(state, action: PayloadAction<{world: string, level: number}>) { function addLevelProgress(state, action: PayloadAction<{game: string, world: string, level: number}>) {
if (!state.level[action.payload.world]) { if (!state.level[action.payload.game]) {
state.level[action.payload.world] = {} state.level[action.payload.game] = {}
} }
if (!state.level[action.payload.world][action.payload.level]) { if (!state.level[action.payload.game][action.payload.world]) {
state.level[action.payload.world][action.payload.level] = {...initalLevelProgressState} 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', name: 'progress',
initialState: initialProgressState, initialState: initialProgressState,
reducers: { 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) addLevelProgress(state, action)
state.level[action.payload.world][action.payload.level].code = action.payload.code state.level[action.payload.game][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].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) 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) 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) =>{ return (state) =>{
if (!state.progress.level[world]) { return initalLevelProgressState } if (!state.progress.level[game]) { return initalLevelProgressState }
if (!state.progress.level[world][level]) { return initalLevelProgressState } if (!state.progress.level[game][world]) { return initalLevelProgressState }
return state.progress.level[world][level] 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 (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 (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 (state) => {
return selectLevel(world, level)(state).completed return selectLevel(game, world, level)(state).completed
} }
} }

Loading…
Cancel
Save