use react router, reorganize leanClient connection
This commit is contained in:
+2
-11
@@ -3,6 +3,7 @@ import { useState, useEffect } from 'react';
|
|||||||
import { MathJaxContext } from "better-react-mathjax";
|
import { MathJaxContext } from "better-react-mathjax";
|
||||||
import * as rpc from 'vscode-ws-jsonrpc';
|
import * as rpc from 'vscode-ws-jsonrpc';
|
||||||
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js'
|
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js'
|
||||||
|
import { Outlet } from "react-router-dom";
|
||||||
|
|
||||||
import '@fontsource/roboto/300.css';
|
import '@fontsource/roboto/300.css';
|
||||||
import '@fontsource/roboto/400.css';
|
import '@fontsource/roboto/400.css';
|
||||||
@@ -14,7 +15,6 @@ import { AppBar, CssBaseline, Toolbar, Typography } from '@mui/material';
|
|||||||
import Welcome from './components/Welcome';
|
import Welcome from './components/Welcome';
|
||||||
import Level from './components/Level';
|
import Level from './components/Level';
|
||||||
import GoodBye from './components/GoodBye';
|
import GoodBye from './components/GoodBye';
|
||||||
import { monacoSetup } from 'lean4web/client/src/monacoSetup'
|
|
||||||
import { useAppSelector } from './hooks';
|
import { useAppSelector } from './hooks';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
@@ -40,15 +40,6 @@ function App() {
|
|||||||
setCurLevel(1)
|
setCurLevel(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
let mainComponent;
|
|
||||||
if (finished) {
|
|
||||||
mainComponent = <GoodBye message={conclusion} />
|
|
||||||
} else if (curLevel > 0) {
|
|
||||||
mainComponent = <Level nbLevels={99} level={curLevel} setCurLevel={setCurLevel} setLevelTitle={setLevelTitle} setFinished={setFinished}/>
|
|
||||||
} else {
|
|
||||||
mainComponent = <Welcome setNbLevels={setNbLevels} startGame={startGame} setConclusion={setConclusion}/>
|
|
||||||
}
|
|
||||||
|
|
||||||
const title = useAppSelector(state => state.game.title)
|
const title = useAppSelector(state => state.game.title)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -65,7 +56,7 @@ function App() {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
{mainComponent}
|
<Outlet />
|
||||||
</MathJaxContext>
|
</MathJaxContext>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -11,10 +11,6 @@ import { Button, FormControlLabel, FormGroup, Switch } from '@mui/material';
|
|||||||
import Grid from '@mui/material/Unstable_Grid2';
|
import Grid from '@mui/material/Unstable_Grid2';
|
||||||
|
|
||||||
import LeftPanel from './LeftPanel';
|
import LeftPanel from './LeftPanel';
|
||||||
import InputZone from './InputZone';
|
|
||||||
import Message from './Message';
|
|
||||||
import TacticState from './TacticState';
|
|
||||||
import { LeanClient } from 'lean4web/client/src/editor/leanclient';
|
|
||||||
import { AbbreviationProvider } from 'lean4web/client/src/editor/abbreviation/AbbreviationProvider';
|
import { AbbreviationProvider } from 'lean4web/client/src/editor/abbreviation/AbbreviationProvider';
|
||||||
import { AbbreviationRewriter } from 'lean4web/client/src/editor/abbreviation/rewriter/AbbreviationRewriter';
|
import { AbbreviationRewriter } from 'lean4web/client/src/editor/abbreviation/rewriter/AbbreviationRewriter';
|
||||||
import { InfoProvider } from 'lean4web/client/src/editor/infoview';
|
import { InfoProvider } from 'lean4web/client/src/editor/infoview';
|
||||||
@@ -25,15 +21,8 @@ import './level.css'
|
|||||||
import { ConnectionContext } from '../connection';
|
import { ConnectionContext } from '../connection';
|
||||||
import Infoview from './Infoview';
|
import Infoview from './Infoview';
|
||||||
|
|
||||||
interface LevelProps {
|
function Level() {
|
||||||
nbLevels: any;
|
const level = 1
|
||||||
level: any;
|
|
||||||
setCurLevel: any;
|
|
||||||
setLevelTitle: any;
|
|
||||||
setFinished: any
|
|
||||||
}
|
|
||||||
|
|
||||||
function Level({ nbLevels, level, setCurLevel, setLevelTitle, setFinished }: LevelProps) {
|
|
||||||
const [index, setIndex] = useState(level) // Level number
|
const [index, setIndex] = useState(level) // Level number
|
||||||
const [tacticDocs, setTacticDocs] = useState([])
|
const [tacticDocs, setTacticDocs] = useState([])
|
||||||
const [lemmaDocs, setLemmaDocs] = useState([])
|
const [lemmaDocs, setLemmaDocs] = useState([])
|
||||||
@@ -77,7 +66,7 @@ function Level({ nbLevels, level, setCurLevel, setLevelTitle, setFinished }: Lev
|
|||||||
messagePanelRef.current!.scrollTo(0,0)
|
messagePanelRef.current!.scrollTo(0,0)
|
||||||
}, [level])
|
}, [level])
|
||||||
|
|
||||||
const leanClient = React.useContext(ConnectionContext)
|
const connection = React.useContext(ConnectionContext)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const editor = monaco.editor.create(codeviewRef.current!, {
|
const editor = monaco.editor.create(codeviewRef.current!, {
|
||||||
@@ -97,7 +86,7 @@ function Level({ nbLevels, level, setCurLevel, setLevelTitle, setFinished }: Lev
|
|||||||
theme: 'vs-code-theme-converted'
|
theme: 'vs-code-theme-converted'
|
||||||
})
|
})
|
||||||
|
|
||||||
const infoProvider = new InfoProvider(leanClient)
|
const infoProvider = new InfoProvider(connection.getLeanClient())
|
||||||
const div: HTMLElement = infoviewRef.current!
|
const div: HTMLElement = infoviewRef.current!
|
||||||
const infoviewApi = renderInfoview(infoProvider.getApi(), div)
|
const infoviewApi = renderInfoview(infoProvider.getApi(), div)
|
||||||
|
|
||||||
@@ -112,33 +101,35 @@ function Level({ nbLevels, level, setCurLevel, setLevelTitle, setFinished }: Lev
|
|||||||
|
|
||||||
// The next function will be called when the level changes
|
// The next function will be called when the level changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (editor) {
|
connection.whenLeanClientStarted((leanClient) => {
|
||||||
const model = monaco.editor.createModel('', 'lean4', monaco.Uri.parse(uri))
|
if (editor) {
|
||||||
|
const model = monaco.editor.createModel('', 'lean4', monaco.Uri.parse(uri))
|
||||||
|
|
||||||
editor.setModel(model)
|
editor.setModel(model)
|
||||||
infoviewApi.serverRestarted(leanClient.initializeResult)
|
infoviewApi.serverRestarted(leanClient.initializeResult)
|
||||||
infoProvider.openPreview(editor, infoviewApi)
|
infoProvider.openPreview(editor, infoviewApi)
|
||||||
setReady(true)
|
setReady(true)
|
||||||
|
|
||||||
new AbbreviationRewriter(new AbbreviationProvider(), model, editor)
|
new AbbreviationRewriter(new AbbreviationProvider(), model, editor)
|
||||||
|
|
||||||
leanClient.sendRequest("loadLevel", {world: "TestWorld", level}).then((res) => {
|
leanClient.sendRequest("loadLevel", {world: "TestWorld", level}).then((res) => {
|
||||||
setLevelTitle("Level " + res["index"] + ": " + res["title"])
|
// setLevelTitle("Level " + res["index"] + ": " + res["title"])
|
||||||
setIndex(parseInt(res["index"]))
|
setIndex(parseInt(res["index"]))
|
||||||
setTacticDocs(res["tactics"])
|
setTacticDocs(res["tactics"])
|
||||||
setLemmaDocs(res["lemmas"])
|
setLemmaDocs(res["lemmas"])
|
||||||
setIntroduction(res["introduction"])
|
setIntroduction(res["introduction"])
|
||||||
processResponse(res)
|
processResponse(res)
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => { model.dispose(); setReady(false) }
|
return () => { model.dispose(); setReady(false) }
|
||||||
}
|
}
|
||||||
}, [editor, level, leanClient])
|
})
|
||||||
|
}, [editor, level, connection])
|
||||||
|
|
||||||
function loadLevel(index) {
|
function loadLevel(index) {
|
||||||
setCompleted(false)
|
setCompleted(false)
|
||||||
setHistory([])
|
setHistory([])
|
||||||
setCurLevel(index)
|
// setCurLevel(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -158,10 +149,10 @@ function Level({ nbLevels, level, setCurLevel, setLevelTitle, setFinished }: Lev
|
|||||||
</Grid>
|
</Grid>
|
||||||
<Grid xs={4} className="info-panel">
|
<Grid xs={4} className="info-panel">
|
||||||
<Button disabled={index <= 1} onClick={() => { loadLevel(index - 1) }} sx={{ ml: 3, mt: 2, mb: 2 }} disableFocusRipple>Previous Level</Button>
|
<Button disabled={index <= 1} onClick={() => { loadLevel(index - 1) }} sx={{ ml: 3, mt: 2, mb: 2 }} disableFocusRipple>Previous Level</Button>
|
||||||
<Button disabled={index >= nbLevels} onClick={() => { loadLevel(index + 1) }} sx={{ ml: 3, mt: 2, mb: 2 }} disableFocusRipple>Next Level</Button>
|
<Button disabled={index >= 99} onClick={() => { loadLevel(index + 1) }} sx={{ ml: 3, mt: 2, mb: 2 }} disableFocusRipple>Next Level</Button>
|
||||||
<div style={{display: expertInfoview ? 'block' : 'none' }} ref={infoviewRef} className="infoview vscode-light"></div>
|
<div style={{display: expertInfoview ? 'block' : 'none' }} ref={infoviewRef} className="infoview vscode-light"></div>
|
||||||
<div style={{display: expertInfoview ? 'none' : 'block' }}>
|
<div style={{display: expertInfoview ? 'none' : 'block' }}>
|
||||||
<Infoview leanClient={leanClient} editor={editor} editorApi={infoProvider?.getApi()} />
|
<Infoview leanClient={connection.getLeanClient()} editor={editor} editorApi={infoProvider?.getApi()} />
|
||||||
</div>
|
</div>
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormControlLabel onChange={() => { setExpertInfoview(!expertInfoview) }} control={<Switch />} label="Expert mode" />
|
<FormControlLabel onChange={() => { setExpertInfoview(!expertInfoview) }} control={<Switch />} label="Expert mode" />
|
||||||
|
|||||||
@@ -1,15 +1,37 @@
|
|||||||
|
|
||||||
|
|
||||||
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js'
|
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js'
|
||||||
import { LeanClient } from 'lean4web/client/src/editor/leanclient';
|
import { LeanClient } from 'lean4web/client/src/editor/leanclient';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { monacoSetup } from 'lean4web/client/src/monacoSetup';
|
|
||||||
|
|
||||||
monacoSetup()
|
|
||||||
|
|
||||||
const socketUrl = ((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + '/websocket/'
|
export class Connection {
|
||||||
|
private leanClient = null
|
||||||
|
|
||||||
const uri = monaco.Uri.parse('file:///')
|
getLeanClient() {
|
||||||
export const leanClient = new LeanClient(socketUrl, undefined, uri, () => {})
|
if (this.leanClient === null) {
|
||||||
|
const socketUrl = ((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + '/websocket/'
|
||||||
|
|
||||||
|
const uri = monaco.Uri.parse('file:///')
|
||||||
|
this.leanClient = new LeanClient(socketUrl, undefined, uri, () => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.leanClient
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Call `callback` when the leanClient has started. If not already started, start it. */
|
||||||
|
whenLeanClientStarted = (callback) => {
|
||||||
|
const leanClient = this.getLeanClient()
|
||||||
|
if (leanClient.isRunning()) {
|
||||||
|
callback(leanClient)
|
||||||
|
} else {
|
||||||
|
if (!leanClient.isStarted()) {
|
||||||
|
leanClient.start()
|
||||||
|
}
|
||||||
|
leanClient.restarted(() => { callback(leanClient) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const connection = new Connection()
|
||||||
|
|
||||||
export const ConnectionContext = React.createContext(null);
|
export const ConnectionContext = React.createContext(null);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
||||||
import { LeanClient } from 'lean4web/client/src/editor/leanclient'
|
import { LeanClient } from 'lean4web/client/src/editor/leanclient'
|
||||||
|
import { Connection } from '../connection'
|
||||||
import type { RootState } from '../store'
|
import type { RootState } from '../store'
|
||||||
|
|
||||||
interface GameState {
|
interface GameState {
|
||||||
@@ -34,23 +35,11 @@ export const gameSlice = createSlice({
|
|||||||
|
|
||||||
export const { loadedGame } = gameSlice.actions
|
export const { loadedGame } = gameSlice.actions
|
||||||
|
|
||||||
// TODO: Move this into LeanClient?
|
|
||||||
/** Call `callback` when the leanClient has started. If not already started, start it. */
|
|
||||||
const whenLeanClientStarted = (leanClient, callback) => {
|
|
||||||
if (leanClient.isRunning()) {
|
|
||||||
callback()
|
|
||||||
} else {
|
|
||||||
if (!leanClient.isStarted()) {
|
|
||||||
leanClient.start()
|
|
||||||
}
|
|
||||||
leanClient.restarted(callback)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const fetchGame = (dispatch, getState, extraArgument) => {
|
export const fetchGame = (dispatch, getState, extraArgument) => {
|
||||||
const leanClient : LeanClient = extraArgument.leanClient
|
const connection : Connection = extraArgument.connection
|
||||||
whenLeanClientStarted(leanClient, () => {
|
connection.whenLeanClientStarted(() => {
|
||||||
leanClient.sendRequest("info", {}).then((res) =>{
|
connection.getLeanClient().sendRequest("info", {}).then((res) =>{
|
||||||
dispatch(loadedGame(res))
|
dispatch(loadedGame(res))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
+25
-3
@@ -2,18 +2,40 @@ import * as React from 'react';
|
|||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
|
import Level from './components/Level';
|
||||||
|
import { ConnectionContext, connection } from './connection'
|
||||||
import { store } from './store';
|
import { store } from './store';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import { ConnectionContext, leanClient } from './connection'
|
import {
|
||||||
|
createHashRouter,
|
||||||
|
RouterProvider,
|
||||||
|
Route,
|
||||||
|
} from "react-router-dom";
|
||||||
|
import "./index.css";
|
||||||
|
import { monacoSetup } from 'lean4web/client/src/monacoSetup';
|
||||||
|
|
||||||
|
monacoSetup()
|
||||||
|
|
||||||
|
const router = createHashRouter([
|
||||||
|
{
|
||||||
|
path: "/",
|
||||||
|
element: <App />,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "/",
|
||||||
|
element: <Level />,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
const container = document.getElementById('root');
|
const container = document.getElementById('root');
|
||||||
const root = createRoot(container!);
|
const root = createRoot(container!);
|
||||||
root.render(
|
root.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<ConnectionContext.Provider value={leanClient}>
|
<ConnectionContext.Provider value={connection}>
|
||||||
<App />
|
<RouterProvider router={router} />
|
||||||
</ConnectionContext.Provider>
|
</ConnectionContext.Provider>
|
||||||
</Provider>
|
</Provider>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
|
|||||||
+3
-3
@@ -1,10 +1,10 @@
|
|||||||
import { configureStore } from '@reduxjs/toolkit';
|
import { configureStore } from '@reduxjs/toolkit';
|
||||||
import gameReducer from './game/gameSlice';
|
import gameReducer from './game/gameSlice';
|
||||||
import { leanClient } from './connection'
|
import { connection } from './connection'
|
||||||
import thunkMiddleware from 'redux-thunk'
|
import thunkMiddleware from 'redux-thunk'
|
||||||
|
|
||||||
|
|
||||||
const thunkMiddlewareWithArg = thunkMiddleware.withExtraArgument({ leanClient })
|
const thunkMiddlewareWithArg = thunkMiddleware.withExtraArgument({ connection })
|
||||||
|
|
||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
@@ -13,7 +13,7 @@ export const store = configureStore({
|
|||||||
middleware: getDefaultMiddleware =>
|
middleware: getDefaultMiddleware =>
|
||||||
getDefaultMiddleware({
|
getDefaultMiddleware({
|
||||||
thunk: {
|
thunk: {
|
||||||
extraArgument: { leanClient }
|
extraArgument: { connection }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user