currently its working

pull/118/head
Jon Eugster 3 years ago
parent 13c78ba420
commit 4039fa9467

@ -1,3 +1,6 @@
/**
* @fileOverview
*/
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { Connection } from '../connection' import { Connection } from '../connection'

@ -1,14 +1,28 @@
/**
* @fileOverview Load/save the state to the local browser store
*/
const KEY = "game_progress"; const KEY = "game_progress";
/** Load from browser storage */
export function loadState() { export function loadState() {
try { try {
const serializedState = localStorage.getItem(KEY); const serializedState = localStorage.getItem(KEY);
if (!serializedState) return undefined; if (!serializedState) return undefined;
return JSON.parse(serializedState); let x = JSON.parse(serializedState);
// Complatibilty because `state.level` has been renamed to `x.games`.
// TODO: Does this work?
if (x.level) {
x.games = x.level
x.level = undefined
}
return x
} catch (e) { } catch (e) {
return undefined; return undefined;
} }
} }
/** Save to browser storage */
export async function saveState(state: any) { export async function saveState(state: any) {
try { try {
const serializedState = JSON.stringify(state); const serializedState = JSON.stringify(state);

@ -1,15 +1,10 @@
/**
* @fileOverview Defines the user progress which is loaded from the browser store and kept
*/
import { createSlice } from '@reduxjs/toolkit' import { createSlice } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit' import type { PayloadAction } from '@reduxjs/toolkit'
import { loadState } from "./local_storage"; import { loadState } from "./local_storage";
export interface GameProgressState {
[world: string] : {[level: number]: LevelProgressState}
}
interface ProgressState {
level: {[game: string]: GameProgressState}
}
interface Selection { interface Selection {
selectionStartLineNumber: number, selectionStartLineNumber: number,
selectionStartColumn: number, selectionStartColumn: number,
@ -22,18 +17,29 @@ interface LevelProgressState {
completed: boolean completed: boolean
} }
const initialProgressState = loadState() ?? { level: {} } as ProgressState export interface GameProgressState {
const initalLevelProgressState = {code: "", completed: false} as LevelProgressState [world: string] : {[level: number]: LevelProgressState}
}
/** The progress made on all lean4-games */
interface ProgressState {
games: {[game: string]: GameProgressState}
}
function addLevelProgress(state, action: PayloadAction<{game: string, world: string, level: number}>) { const initialProgressState: ProgressState = loadState() ?? { games: {} }
if (!state.level[action.payload.game]) {
state.level[action.payload.game] = {} const initalLevelProgressState: LevelProgressState = {code: "", completed: false, selections: []}
/** Add an empty skeleton with progress for the current level */
function addLevelProgress(state: ProgressState, action: PayloadAction<{game: string, world: string, level: number}>) {
if (!state.games[action.payload.game]) {
state.games[action.payload.game] = {}
} }
if (!state.level[action.payload.game][action.payload.world]) { if (!state.games[action.payload.game][action.payload.world]) {
state.level[action.payload.game][action.payload.world] = {} state.games[action.payload.game][action.payload.world] = {}
} }
if (!state.level[action.payload.game][action.payload.world][action.payload.level]) { if (!state.games[action.payload.game][action.payload.world][action.payload.level]) {
state.level[action.payload.game][action.payload.world][action.payload.level] = {...initalLevelProgressState} state.games[action.payload.game][action.payload.world][action.payload.level] = {...initalLevelProgressState}
} }
} }
@ -41,60 +47,77 @@ export const progressSlice = createSlice({
name: 'progress', name: 'progress',
initialState: initialProgressState, initialState: initialProgressState,
reducers: { reducers: {
codeEdited(state, action: PayloadAction<{game: string, world: string, level: number, code: string}>) { /** put edited code in the state and set completed to false */
codeEdited(state: ProgressState, action: PayloadAction<{game: string, world: string, level: number, code: string}>) {
addLevelProgress(state, action) addLevelProgress(state, action)
state.level[action.payload.game][action.payload.world][action.payload.level].code = action.payload.code state.games[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 state.games[action.payload.game][action.payload.world][action.payload.level].completed = false
}, },
changedSelection(state, action: PayloadAction<{game: string, world: string, level: number, selections: Selection[]}>) { /** TODO: ? */
changedSelection(state: ProgressState, action: PayloadAction<{game: string, world: string, level: number, selections: Selection[]}>) {
addLevelProgress(state, action) addLevelProgress(state, action)
state.level[action.payload.game][action.payload.world][action.payload.level].selections = action.payload.selections state.games[action.payload.game][action.payload.world][action.payload.level].selections = action.payload.selections
}, },
levelCompleted(state, action: PayloadAction<{game: string, world: string, level: number}>) { /** mark level as completed */
levelCompleted(state: ProgressState, action: PayloadAction<{game: string, world: string, level: number}>) {
addLevelProgress(state, action) addLevelProgress(state, action)
state.level[action.payload.game][action.payload.world][action.payload.level].completed = true state.games[action.payload.game][action.payload.world][action.payload.level].completed = true
}, },
deleteProgress(state, action: PayloadAction<{game: string}>) { /** delete all progress for this game */
state.level[action.payload.game] = {} deleteProgress(state: ProgressState, action: PayloadAction<{game: string}>) {
state.games[action.payload.game] = {}
},
/** delete progress for this level */
deleteLevelProgress(state: ProgressState, action: PayloadAction<{game: string, world: string, level: number}>) {
addLevelProgress(state, action)
state.games[action.payload.game][action.payload.world][action.payload.level] = initalLevelProgressState
}, },
loadProgress(state, action: PayloadAction<{game: string, data:GameProgressState}>) { /** load progress, e.g. from external import */
loadProgress(state: ProgressState, action: PayloadAction<{game: string, data:GameProgressState}>) {
console.debug(`setting data to:\n ${action.payload.data}`) console.debug(`setting data to:\n ${action.payload.data}`)
state.level[action.payload.game] = action.payload.data state.games[action.payload.game] = action.payload.data
}, },
} }
}) })
/** if the level does not exist, return default values */
export function selectLevel(game: string, world: string, level: number) { export function selectLevel(game: string, world: string, level: number) {
return (state) =>{ return (state) =>{
if (!state.progress.level[game]) { return initalLevelProgressState } if (!state.progress.games[game]) { return initalLevelProgressState }
if (!state.progress.level[game][world]) { return initalLevelProgressState } if (!state.progress.games[game][world]) { return initalLevelProgressState }
if (!state.progress.level[game][world][level]) { return initalLevelProgressState } if (!state.progress.games[game][world][level]) { return initalLevelProgressState }
return state.progress.level[game][world][level] return state.progress.games[game][world][level]
} }
} }
/** return the code of the current level */
export function selectCode(game: string, world: string, level: number) { export function selectCode(game: string, world: string, level: number) {
return (state) => { return (state) => {
return selectLevel(game, world, level)(state).code return selectLevel(game, world, level)(state).code
} }
} }
/** return the selections made in the current level */
export function selectSelections(game: string, world: string, level: number) { export function selectSelections(game: string, world: string, level: number) {
return (state) => { return (state) => {
return selectLevel(game, world, level)(state).selections return selectLevel(game, world, level)(state).selections
} }
} }
/** return whether the current level is clompleted */
export function selectCompleted(game: string, world: string, level: number) { export function selectCompleted(game: string, world: string, level: number) {
return (state) => { return (state) => {
return selectLevel(game, world, level)(state).completed return selectLevel(game, world, level)(state).completed
} }
} }
/** return progress for the current game if it exists */
export function selectProgress(game: string) { export function selectProgress(game: string) {
return (state) => { return (state) => {
return state.progress.level[game] ?? null return state.progress.games[game] ?? null
} }
} }
export const { changedSelection, codeEdited, levelCompleted, deleteProgress, loadProgress } = progressSlice.actions /** Export actions to modify the progress */
export const { changedSelection, codeEdited, levelCompleted, deleteProgress,
deleteLevelProgress, loadProgress } = progressSlice.actions

7
package-lock.json generated

@ -52,6 +52,7 @@
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
"@redux-devtools/core": "^3.13.1", "@redux-devtools/core": "^3.13.1",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@types/debounce": "^1.2.1",
"babel-loader": "^8.3.0", "babel-loader": "^8.3.0",
"concurrently": "^7.6.0", "concurrently": "^7.6.0",
"css-loader": "^6.7.3", "css-loader": "^6.7.3",
@ -3016,6 +3017,12 @@
"resolved": "https://registry.npmjs.org/@types/cytoscape/-/cytoscape-3.19.9.tgz", "resolved": "https://registry.npmjs.org/@types/cytoscape/-/cytoscape-3.19.9.tgz",
"integrity": "sha512-oqCx0ZGiBO0UESbjgq052vjDAy2X53lZpMrWqiweMpvVwKw/2IiYDdzPFK6+f4tMfdv9YKEM9raO5bAZc3UYBg==" "integrity": "sha512-oqCx0ZGiBO0UESbjgq052vjDAy2X53lZpMrWqiweMpvVwKw/2IiYDdzPFK6+f4tMfdv9YKEM9raO5bAZc3UYBg=="
}, },
"node_modules/@types/debounce": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@types/debounce/-/debounce-1.2.1.tgz",
"integrity": "sha512-epMsEE85fi4lfmJUH/89/iV/LI+F5CvNIvmgs5g5jYFPfhO2S/ae8WSsLOKWdwtoaZw9Q2IhJ4tQ5tFCcS/4HA==",
"dev": true
},
"node_modules/@types/debug": { "node_modules/@types/debug": {
"version": "4.1.7", "version": "4.1.7",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz",

@ -48,6 +48,7 @@
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
"@redux-devtools/core": "^3.13.1", "@redux-devtools/core": "^3.13.1",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@types/debounce": "^1.2.1",
"babel-loader": "^8.3.0", "babel-loader": "^8.3.0",
"concurrently": "^7.6.0", "concurrently": "^7.6.0",
"css-loader": "^6.7.3", "css-loader": "^6.7.3",

Loading…
Cancel
Save