save state of code
This commit is contained in:
@@ -21,7 +21,10 @@ import './level.css'
|
||||
import { ConnectionContext } from '../connection';
|
||||
import Infoview from './Infoview';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useLoadLevelQuery } from '../game/api';
|
||||
import { useLoadLevelQuery } from '../state/api';
|
||||
import { codeEdited, selectCode } from '../state/progress';
|
||||
import { useAppDispatch } from '../hooks';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
|
||||
|
||||
@@ -45,7 +48,17 @@ function Level() {
|
||||
const connection = React.useContext(ConnectionContext)
|
||||
|
||||
const level = useLoadLevelQuery({world: worldId, level: levelId})
|
||||
const {editor, infoProvider} = useLevelEditor(worldId, levelId, codeviewRef, infoviewRef)
|
||||
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
const onDidChangeContent = (code) => {
|
||||
dispatch(codeEdited({world: worldId, level: levelId, code}))
|
||||
}
|
||||
|
||||
const initialCode = useSelector(selectCode(worldId, levelId))
|
||||
|
||||
const {editor, infoProvider} =
|
||||
useLevelEditor(worldId, levelId, codeviewRef, infoviewRef, initialCode, onDidChangeContent)
|
||||
|
||||
return <>
|
||||
<Box style={level.isLoading ? null : {display: "none"}} display="flex" alignItems="center" justifyContent="center" sx={{ height: "calc(100vh - 64px)" }}><CircularProgress /></Box>
|
||||
@@ -84,7 +97,7 @@ function Level() {
|
||||
export default Level
|
||||
|
||||
|
||||
function useLevelEditor(worldId: string, levelId: number, codeviewRef, infoviewRef) {
|
||||
function useLevelEditor(worldId: string, levelId: number, codeviewRef, infoviewRef, initialCode, onDidChangeContent) {
|
||||
|
||||
const connection = React.useContext(ConnectionContext)
|
||||
|
||||
@@ -128,8 +141,11 @@ function useLevelEditor(worldId: string, levelId: number, codeviewRef, infoviewR
|
||||
if (editor) {
|
||||
|
||||
const uri = monaco.Uri.parse(`file:///${worldId}/${levelId}`)
|
||||
const model = monaco.editor.getModel(uri) ??
|
||||
monaco.editor.createModel('', 'lean4', uri)
|
||||
let model = monaco.editor.getModel(uri)
|
||||
if (!model) {
|
||||
model = monaco.editor.createModel(initialCode, 'lean4', uri)
|
||||
model.onDidChangeContent(() => onDidChangeContent(model.getValue()))
|
||||
}
|
||||
|
||||
editor.setModel(model)
|
||||
infoviewApi.serverRestarted(leanClient.initializeResult)
|
||||
@@ -139,7 +155,7 @@ function useLevelEditor(worldId: string, levelId: number, codeviewRef, infoviewR
|
||||
new AbbreviationRewriter(new AbbreviationProvider(), model, editor)
|
||||
}
|
||||
})
|
||||
|
||||
// TODO: Properly close the file to stop send "keepAlive" calls to the server
|
||||
}, [editor, levelId, connection])
|
||||
|
||||
return {editor, infoProvider}
|
||||
|
||||
@@ -14,7 +14,7 @@ import { Link as RouterLink, useNavigate } from 'react-router-dom';
|
||||
cytoscape.use( klay );
|
||||
|
||||
import { Box, Typography, Button, CircularProgress, Grid } from '@mui/material';
|
||||
import { useGetGameInfoQuery } from '../game/api';
|
||||
import { useGetGameInfoQuery } from '../state/api';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
|
||||
import type { RootState, AppDispatch } from './store'
|
||||
import type { RootState, AppDispatch } from './state/store'
|
||||
|
||||
// Use throughout your app instead of plain `useDispatch` and `useSelector`
|
||||
export const useAppDispatch: () => AppDispatch = useDispatch
|
||||
|
||||
@@ -3,7 +3,7 @@ import { createRoot } from 'react-dom/client';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import { ConnectionContext, connection } from './connection'
|
||||
import { store } from './store';
|
||||
import { store } from './state/store';
|
||||
import { Provider } from 'react-redux';
|
||||
import {
|
||||
createHashRouter,
|
||||
|
||||
@@ -37,7 +37,7 @@ const customBaseQuery = async (
|
||||
}
|
||||
|
||||
// Define a service using a base URL and expected endpoints
|
||||
export const gameApi = createApi({
|
||||
export const apiSlice = createApi({
|
||||
reducerPath: 'gameApi',
|
||||
baseQuery: customBaseQuery,
|
||||
endpoints: (builder) => ({
|
||||
@@ -52,4 +52,4 @@ export const gameApi = createApi({
|
||||
|
||||
// Export hooks for usage in functional components, which are
|
||||
// auto-generated based on the defined endpoints
|
||||
export const { useGetGameInfoQuery, useLoadLevelQuery } = gameApi
|
||||
export const { useGetGameInfoQuery, useLoadLevelQuery } = apiSlice
|
||||
@@ -0,0 +1,30 @@
|
||||
import { createSlice } from '@reduxjs/toolkit'
|
||||
import type { PayloadAction } from '@reduxjs/toolkit'
|
||||
|
||||
interface ProgressState {
|
||||
code: {[world: string]: {[level: number]: string}}
|
||||
}
|
||||
|
||||
const initialState = { code: {} } as ProgressState
|
||||
|
||||
export const progressSlice = createSlice({
|
||||
name: 'progress',
|
||||
initialState,
|
||||
reducers: {
|
||||
codeEdited(state, action: PayloadAction<{world: string, level: number, code: string}>) {
|
||||
if (!state.code[action.payload.world]) {
|
||||
state.code[action.payload.world] = {}
|
||||
}
|
||||
state.code[action.payload.world][action.payload.level] = action.payload.code
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
export function selectCode(world: string, level: number) {
|
||||
return (state) => {
|
||||
if (!state.progress.code[world]) { return undefined }
|
||||
state.progress.code[world][level];
|
||||
}
|
||||
}
|
||||
|
||||
export const { codeEdited } = progressSlice.actions
|
||||
@@ -1,19 +1,21 @@
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import { connection } from './connection'
|
||||
import { connection } from '../connection'
|
||||
import thunkMiddleware from 'redux-thunk'
|
||||
import { gameApi } from './game/api'
|
||||
import { apiSlice } from './api'
|
||||
import { progressSlice } from './progress'
|
||||
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
[gameApi.reducerPath]: gameApi.reducer,
|
||||
[apiSlice.reducerPath]: apiSlice.reducer,
|
||||
[progressSlice.name]: progressSlice.reducer,
|
||||
},
|
||||
middleware: getDefaultMiddleware =>
|
||||
getDefaultMiddleware({
|
||||
thunk: {
|
||||
extraArgument: { connection }
|
||||
}
|
||||
}).concat(gameApi.middleware),
|
||||
}).concat(apiSlice.middleware),
|
||||
});
|
||||
|
||||
// Infer the `RootState` and `AppDispatch` types from the store itself
|
||||
Reference in New Issue
Block a user