set up rtk query

pull/43/head
Alexander Bentkamp 4 years ago
parent c26227a631
commit c6d8b35806

@ -35,7 +35,7 @@ function App() {
setCurLevel(1) setCurLevel(1)
} }
const title = useAppSelector(state => state.game.title) const title = ""//useAppSelector(state => state.gameApi.data.title)
return ( return (
<div className="App"> <div className="App">

@ -110,7 +110,7 @@ function Level() {
// The next function will be called when the level changes // The next function will be called when the level changes
useEffect(() => { useEffect(() => {
connection.whenLeanClientStarted((leanClient) => { connection.startLeanClient().then((leanClient) => {
if (editor) { if (editor) {
const model = monaco.editor.createModel('', 'lean4', monaco.Uri.parse(uri)) const model = monaco.editor.createModel('', 'lean4', monaco.Uri.parse(uri))

@ -6,24 +6,18 @@ import '@fontsource/roboto/300.css';
import '@fontsource/roboto/400.css'; import '@fontsource/roboto/400.css';
import '@fontsource/roboto/500.css'; import '@fontsource/roboto/500.css';
import '@fontsource/roboto/700.css'; import '@fontsource/roboto/700.css';
import * as rpc from 'vscode-ws-jsonrpc';
import cytoscape from 'cytoscape' import cytoscape from 'cytoscape'
import klay from 'cytoscape-klay'; import klay from 'cytoscape-klay';
import { useSelector, useDispatch } from 'react-redux'
import { fetchGame } from '../game/gameSlice'
import { Link as RouterLink, useNavigate } from 'react-router-dom'; import { Link as RouterLink, useNavigate } from 'react-router-dom';
cytoscape.use( klay ); cytoscape.use( klay );
import { Box, Typography, Button, CircularProgress, Grid } from '@mui/material'; import { Box, Typography, Button, CircularProgress, Grid } from '@mui/material';
import { LeanClient } from 'lean4web/client/src/editor/leanclient'; import { useGetGameInfoQuery } from '../game/api';
import { ConnectionContext } from '../connection';
import { useAppDispatch, useAppSelector } from '../hooks';
function Welcome() { function Welcome() {
const dispatch = useAppDispatch()
const navigate = useNavigate(); const navigate = useNavigate();
const worldsRef = useRef<HTMLDivElement>(null) const worldsRef = useRef<HTMLDivElement>(null)
@ -75,23 +69,25 @@ function Welcome() {
}); });
} }
useEffect(() => { dispatch(fetchGame); }, []) const gameInfo = useGetGameInfoQuery()
const worlds = useAppSelector(state => state.game.worlds) useEffect(() => {
useEffect(() => { if (worlds) { drawWorlds(worlds); } }, [worlds]) if (gameInfo.data?.worlds) { drawWorlds(gameInfo.data.worlds); }
}, [gameInfo.data?.worlds])
const title = useAppSelector(state => state.game.title) useEffect(() => {
useEffect(() => { window.document.title = title }, [title]) if (gameInfo.data?.title) window.document.title = gameInfo.data.title
}, [gameInfo.data?.title])
const introduction = useAppSelector(state => state.game.introduction)
return <div> return <div>
{ introduction?// TODO: find a better way to mark loading state? { gameInfo.isLoading?
<Box display="flex" alignItems="center" justifyContent="center" sx={{ height: "calc(100vh - 64px)" }}><CircularProgress /></Box>
:
<div> <div>
<Box sx={{ m: 3 }}> <Box sx={{ m: 3 }}>
<Typography variant="body1" component="div"> <Typography variant="body1" component="div">
<MathJax> <MathJax>
<ReactMarkdown>{introduction}</ReactMarkdown> <ReactMarkdown>{gameInfo.data.introduction}</ReactMarkdown>
</MathJax> </MathJax>
</Typography> </Typography>
</Box> </Box>
@ -100,7 +96,6 @@ function Welcome() {
</Box> </Box>
<div ref={worldsRef} style={{"width": "100%","height": "50em"}} /> <div ref={worldsRef} style={{"width": "100%","height": "50em"}} />
</div> </div>
: <Box display="flex" alignItems="center" justifyContent="center" sx={{ height: "calc(100vh - 64px)" }}><CircularProgress /></Box>
} }
</div> </div>

@ -18,17 +18,21 @@ export class Connection {
return this.leanClient return this.leanClient
} }
/** Call `callback` when the leanClient has started. If not already started, start it. */ /** If not already started, starts the Lean client. resolves the returned promise as soon as a
whenLeanClientStarted = (callback) => { * Lean client is running.
const leanClient = this.getLeanClient() */
if (leanClient.isRunning()) { startLeanClient = () => {
callback(leanClient) return new Promise<LeanClient>((resolve) => {
} else { const leanClient = this.getLeanClient()
if (!leanClient.isStarted()) { if (leanClient.isRunning()) {
leanClient.start() resolve(leanClient)
} else {
if (!leanClient.isStarted()) {
leanClient.start()
}
leanClient.restarted(() => { resolve(leanClient) })
} }
leanClient.restarted(() => { callback(leanClient) }) })
}
} }
} }

@ -0,0 +1,38 @@
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { Connection } from '../connection'
interface GameState {
title: null|string,
introduction: null|string,
worlds: null|{nodes: string[], edges: string[][2]},
authors: null|string[],
conclusion: null|string,
}
const customBaseQuery = async (
args : {method: string, params?: any},
{ signal, dispatch, getState, extra },
extraOptions
) => {
const connection : Connection = extra.connection
let leanClient = await connection.startLeanClient()
console.log(`Sending request ${args.method}`)
let res = await leanClient.sendRequest(args.method, args.params)
console.log('Received response', res)
return {'data': res}
}
// Define a service using a base URL and expected endpoints
export const gameApi = createApi({
reducerPath: 'gameApi',
baseQuery: customBaseQuery,
endpoints: (builder) => ({
getGameInfo: builder.query<GameState, void>({
query: () => {return {method: 'info', params: {}}},
}),
}),
})
// Export hooks for usage in functional components, which are
// auto-generated based on the defined endpoints
export const { useGetGameInfoQuery } = gameApi

@ -1,48 +0,0 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import { LeanClient } from 'lean4web/client/src/editor/leanclient'
import { Connection } from '../connection'
import type { RootState } from '../store'
interface GameState {
title: null|string,
introduction: null|string,
worlds: null|{nodes: string[], edges: string[][2]},
authors: null|string[],
conclusion: null|string,
}
const initialState : GameState = {
title: null,
introduction: null,
worlds: null,
authors: null,
conclusion: null,
}
export const gameSlice = createSlice({
name: 'game',
initialState,
reducers: {
loadedGame: (state, action: PayloadAction<any>) => {
state.title = action.payload.title
state.introduction = action.payload.introduction
state.worlds = action.payload.worlds
state.authors = action.payload.authors
state.conclusion = action.payload.conclusion
},
},
})
export const { loadedGame } = gameSlice.actions
export const fetchGame = (dispatch, getState, extraArgument) => {
const connection : Connection = extraArgument.connection
connection.whenLeanClientStarted(() => {
connection.getLeanClient().sendRequest("info", {}).then((res) =>{
dispatch(loadedGame(res))
})
})
}
export default gameSlice.reducer

@ -1,21 +1,19 @@
import { configureStore } from '@reduxjs/toolkit'; import { configureStore } from '@reduxjs/toolkit';
import gameReducer from './game/gameSlice';
import { connection } from './connection' import { connection } from './connection'
import thunkMiddleware from 'redux-thunk' import thunkMiddleware from 'redux-thunk'
import { gameApi } from './game/api'
const thunkMiddlewareWithArg = thunkMiddleware.withExtraArgument({ connection })
export const store = configureStore({ export const store = configureStore({
reducer: { reducer: {
game: gameReducer, [gameApi.reducerPath]: gameApi.reducer,
}, },
middleware: getDefaultMiddleware => middleware: getDefaultMiddleware =>
getDefaultMiddleware({ getDefaultMiddleware({
thunk: { thunk: {
extraArgument: { connection } extraArgument: { connection }
} }
}) }).concat(gameApi.middleware),
}); });
// Infer the `RootState` and `AppDispatch` types from the store itself // Infer the `RootState` and `AppDispatch` types from the store itself

Loading…
Cancel
Save