create chat panel

pull/118/head
Jon Eugster 3 years ago
parent 58308f9391
commit a05361022e

@ -42,8 +42,20 @@ import Split from 'react-split'
import { Alert } from '@mui/material'; import { Alert } from '@mui/material';
import { GameIdContext } from '../App'; import { GameIdContext } from '../App';
import { GameHint } from './infoview/rpcApi';
export const MonacoEditorContext = React.createContext<monaco.editor.IStandaloneCodeEditor>(null as any); export const MonacoEditorContext = React.createContext<monaco.editor.IStandaloneCodeEditor>(null as any);
// TODO: Not used yet
export const HintContext = React.createContext<{
hints: Array<GameHint>,
setHints: React.Dispatch<React.SetStateAction<Array<GameHint>>>
}>({
hints: [],
setHints: () => {},
});
export const InputModeContext = React.createContext<{ export const InputModeContext = React.createContext<{
commandLineMode: boolean, commandLineMode: boolean,
setCommandLineMode: React.Dispatch<React.SetStateAction<boolean>>, setCommandLineMode: React.Dispatch<React.SetStateAction<boolean>>,
@ -74,7 +86,7 @@ function Level() {
function PlayableLevel({worldId, levelId}) { function PlayableLevel({worldId, levelId}) {
const codeviewRef = useRef<HTMLDivElement>(null) const codeviewRef = useRef<HTMLDivElement>(null)
const introductionPanelRef = useRef<HTMLDivElement>(null) const chatPanelRef = useRef<HTMLDivElement>(null)
const gameId = React.useContext(GameIdContext) const gameId = React.useContext(GameIdContext)
const initialCode = useAppSelector(selectCode(gameId, worldId, levelId)) const initialCode = useAppSelector(selectCode(gameId, worldId, levelId))
@ -84,12 +96,17 @@ function PlayableLevel({worldId, levelId}) {
const [commandLineInput, setCommandLineInput] = useState("") const [commandLineInput, setCommandLineInput] = useState("")
const [canUndo, setCanUndo] = useState(initialCode.trim() !== "") const [canUndo, setCanUndo] = useState(initialCode.trim() !== "")
// The array of all shown hints. This excludes introduction and conclusion
// TODO: Not used yet
const [hints, setHints] = useState(Array<GameHint>)
const theme = useTheme(); const theme = useTheme();
useEffect(() => { useEffect(() => {
// Scroll to top when loading a new level // Scroll to top when loading a new level
introductionPanelRef.current!.scrollTo(0,0) // TODO: Thats the wrong behaviour probably
chatPanelRef.current!.scrollTo(0,0)
// Reset command line input when loading a new level // Reset command line input when loading a new level
setCommandLineInput("") setCommandLineInput("")
}, [levelId]) }, [levelId])
@ -179,15 +196,35 @@ function PlayableLevel({worldId, levelId}) {
return <> return <>
<div style={level.isLoading ? null : {display: "none"}} className="app-content loading"><CircularProgress /></div> <div style={level.isLoading ? null : {display: "none"}} className="app-content loading"><CircularProgress /></div>
<LevelAppBar isLoading={level.isLoading} levelTitle={levelTitle} worldId={worldId} levelId={levelId} /> <LevelAppBar isLoading={level.isLoading} levelTitle={levelTitle} worldId={worldId} levelId={levelId} />
<Split minSize={0} snapOffset={200} sizes={[50, 25, 25]} className={`app-content level ${level.isLoading ? 'hidden' : ''}`}> <Split minSize={0} snapOffset={200} sizes={[25, 50, 25]} className={`app-content level ${level.isLoading ? 'hidden' : ''}`}>
<div className="exercise-panel"> <div ref={chatPanelRef} className="chat-panel">
<div ref={introductionPanelRef} className="introduction-panel"> {level?.data?.introduction &&
{level?.data?.introduction && <div className="message info">
<Markdown>{level?.data?.introduction}</Markdown>
</div>
}
{hints.map(hint =>
<div className="message info"><Markdown>{hint.text}</Markdown></div>)
}
{completed &&
<>
<div className="message info"> <div className="message info">
<Markdown>{level?.data?.introduction}</Markdown> Level completed! 🎉
</div> </div>
} {level?.data?.conclusion?.trim() &&
</div> <div className="message info">
<Markdown>{level?.data?.conclusion}</Markdown>
</div>
}
{ hints.map(hint => <div className="message info"><Markdown>{hint.text}</Markdown></div>) }
{levelId >= gameInfo.data?.worldSize[worldId] ?
<Button to={`/${gameId}`}><FontAwesomeIcon icon={faHome} /></Button> :
<Button to={`/${gameId}/world/${worldId}/level/${levelId + 1}`}>
Next&nbsp;<FontAwesomeIcon icon={faArrowRight} /></Button>}
</>
}
</div>
<div className="exercise-panel">
<div className="exercise"> <div className="exercise">
<Markdown> <Markdown>
{(level?.data?.statementName ? {(level?.data?.statementName ?
@ -207,34 +244,23 @@ function PlayableLevel({worldId, levelId}) {
</FormGroup> </FormGroup>
</div> </div>
<HintContext.Provider value={{hints, setHints}}>
<EditorContext.Provider value={editorConnection}> <EditorContext.Provider value={editorConnection}>
<MonacoEditorContext.Provider value={editor}> <MonacoEditorContext.Provider value={editor}>
<InputModeContext.Provider value={{commandLineMode, setCommandLineMode, commandLineInput, setCommandLineInput}}> <InputModeContext.Provider value={{commandLineMode, setCommandLineMode, commandLineInput, setCommandLineInput}}>
{editorConnection && <Main key={`${worldId}/${levelId}`} world={worldId} level={levelId} />} {editorConnection && <Main key={`${worldId}/${levelId}`} world={worldId} level={levelId} />}
</InputModeContext.Provider> </InputModeContext.Provider>
</MonacoEditorContext.Provider> </MonacoEditorContext.Provider>
</EditorContext.Provider> </EditorContext.Provider>
</HintContext.Provider>
{completed && <div className="conclusion">
{level?.data?.conclusion?.trim() &&
<div className="message info">
<Markdown>{level?.data?.conclusion}</Markdown>
</div>
}
{levelId >= gameInfo.data?.worldSize[worldId] ?
<Button to={`/${gameId}`}><FontAwesomeIcon icon={faHome} /></Button> :
<Button to={`/${gameId}/world/${worldId}/level/${levelId + 1}`}>
Next&nbsp;<FontAwesomeIcon icon={faArrowRight} /></Button>}
</div>}
</div> </div>
<div className="inventory-panel"> <div className="inventory-panel">
{!level.isLoading && {!level.isLoading &&
<Inventory levelInfo={level?.data} setInventoryDoc={setInventoryDoc} />} <Inventory levelInfo={level?.data} setInventoryDoc={setInventoryDoc} />}
</div> </div>
<div className="doc-panel"> {/* <div className="doc-panel">
{inventoryDoc && <Documentation name={inventoryDoc.name} type={inventoryDoc.type} />} {inventoryDoc && <Documentation name={inventoryDoc.name} type={inventoryDoc.type} />}
</div> </div> */}
</Split> </Split>
</> </>
} }

@ -3,7 +3,7 @@ import * as React from 'react';
import { Alert, FormControlLabel, Switch } from '@mui/material'; import { Alert, FormControlLabel, Switch } from '@mui/material';
import Markdown from '../Markdown'; import Markdown from '../Markdown';
function Hint({hint} : {hint: GameHint}) { export function Hint({hint} : {hint: GameHint}) {
return <div className="message info"><Markdown>{hint.text}</Markdown></div> return <div className="message info"><Markdown>{hint.text}</Markdown></div>
} }

@ -17,7 +17,7 @@ import { RpcContext, useRpcSessionAtPos } from '../../../../node_modules/lean4-i
import { GoalsLocation, Locations, LocationsContext } from '../../../../node_modules/lean4-infoview/src/infoview/goalLocation'; import { GoalsLocation, Locations, LocationsContext } from '../../../../node_modules/lean4-infoview/src/infoview/goalLocation';
import { InteractiveCode } from '../../../../node_modules/lean4-infoview/src/infoview/interactiveCode' import { InteractiveCode } from '../../../../node_modules/lean4-infoview/src/infoview/interactiveCode'
import { CircularProgress } from '@mui/material'; import { CircularProgress } from '@mui/material';
import { InputModeContext, MonacoEditorContext } from '../Level' import { InputModeContext, MonacoEditorContext, HintContext } from '../Level'
type InfoStatus = 'updating' | 'error' | 'ready'; type InfoStatus = 'updating' | 'error' | 'ready';

@ -29,7 +29,7 @@
overflow: auto; overflow: auto;
} }
.introduction-panel, .infoview, .exercise { .chat-panel, .infoview, .exercise {
padding-top: 1em; padding-top: 1em;
padding-left: 1em; padding-left: 1em;
padding-right: 1em; padding-right: 1em;
@ -93,12 +93,12 @@ mjx-container[jax="CHTML"][display="true"] {
/* Styling tables for Markdown */ /* Styling tables for Markdown */
.introduction-panel table, .introduction-panel th, .introduction-panel td { .chat-panel table, .chat-panel th, .chat-panel td {
/* border: 1px solid rgb(0, 0, 0, 0.12); */ /* border: 1px solid rgb(0, 0, 0, 0.12); */
border-collapse: collapse; border-collapse: collapse;
} }
.introduction-panel th, .introduction-panel td { .chat-panel th, .chat-panel td {
padding-left: .5em; padding-left: .5em;
padding-right: .5em; padding-right: .5em;
} }
@ -155,7 +155,7 @@ td code {
border: 1px solid rgb(230, 122, 0); border: 1px solid rgb(230, 122, 0);
} }
.introduction-panel { .chat-panel {
border: 1px solid rgb(192, 18, 178); border: 1px solid rgb(192, 18, 178);
} }

Loading…
Cancel
Save