add inventory to start page

This commit is contained in:
Jon Eugster
2023-07-20 20:24:59 +02:00
parent e8c3b7bce1
commit 803e261a49
5 changed files with 114 additions and 31 deletions
+12 -9
View File
@@ -5,11 +5,11 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faLock, faLockOpen, faBook, faHammer, faBan } from '@fortawesome/free-solid-svg-icons'
import { GameIdContext } from '../app';
import Markdown from './markdown';
import { useLoadDocQuery, InventoryTile, LevelInfo } from '../state/api';
import { useLoadDocQuery, InventoryTile, LevelInfo, InventoryOverview } from '../state/api';
export function Inventory({levelInfo, openDoc } :
{
levelInfo: LevelInfo,
levelInfo: LevelInfo|InventoryOverview,
openDoc: (name: string, type: string) => void,
}) {
@@ -18,14 +18,17 @@ export function Inventory({levelInfo, openDoc } :
{/* TODO: Click on Tactic: show info
TODO: click on paste icon -> paste into command line */}
<h2>Tactics</h2>
<InventoryList items={levelInfo?.tactics} docType="Tactic" openDoc={openDoc} />
{levelInfo?.tactics &&
<InventoryList items={levelInfo?.tactics} docType="Tactic" openDoc={openDoc} />
}
<h2>Definitions</h2>
<InventoryList items={levelInfo?.definitions} docType="Definition" openDoc={openDoc} />
{levelInfo?.definitions &&
<InventoryList items={levelInfo?.definitions} docType="Definition" openDoc={openDoc} />
}
<h2>Lemmas</h2>
<InventoryList items={levelInfo?.lemmas} docType="Lemma" openDoc={openDoc}
defaultTab={levelInfo?.lemmaTab} level={levelInfo}/>
{levelInfo?.lemmas &&
<InventoryList items={levelInfo?.lemmas} docType="Lemma" openDoc={openDoc} defaultTab={levelInfo?.lemmaTab} level={levelInfo}/>
}
</div>
)
}
@@ -36,7 +39,7 @@ function InventoryList({items, docType, openDoc, defaultTab=null, level=undefine
docType: string,
openDoc(name: string, type: string): void,
defaultTab? : string,
level? : LevelInfo,
level? : LevelInfo|InventoryOverview,
}) {
// TODO: `level` is only used in the `useEffect` below to check if a new level has
// been loaded. Is there a better way to observe this?
+33 -18
View File
@@ -26,7 +26,7 @@ import { EditorConnection, EditorEvents } from '../../../node_modules/lean4-info
import { EventEmitter } from '../../../node_modules/lean4-infoview/src/infoview/event';
import type { Location } from 'vscode-languageserver-protocol';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faHome, faCircleInfo, faArrowRight, faArrowLeft, faShield, faRotateLeft } from '@fortawesome/free-solid-svg-icons'
import { faBars, faHome, faCircleInfo, faArrowRight, faArrowLeft, faShield, faRotateLeft } from '@fortawesome/free-solid-svg-icons'
import { styled, useTheme, Theme, CSSObject } from '@mui/material/styles';
import { GameIdContext } from '../app';
@@ -235,7 +235,8 @@ function PlayableLevel({worldId, levelId}) {
// Set `inventoryDoc` to `null` to close the doc
const closeInventoryDoc = () => setInventoryDoc(null);
const levelTitle = <>{levelId && `Level ${levelId}`}{level?.data?.title && `: ${level?.data?.title}`}</>
const levelTitle = <>
{levelId && `Level ${levelId} / ${gameInfo.data?.worldSize[worldId]}`}{level?.data?.title && `: ${level?.data?.title}`}</>
// TODO: with the new design, there is no difference between the introduction and
// a hint at the beginning of the proof...
@@ -261,7 +262,7 @@ function PlayableLevel({worldId, levelId}) {
<SelectionContext.Provider value={{selectedStep, setSelectedStep}}>
<InputModeContext.Provider value={{commandLineMode, setCommandLineMode, commandLineInput, setCommandLineInput}}>
<ProofContext.Provider value={{proof, setProof}}>
<LevelAppBar isLoading={level.isLoading} levelTitle={levelTitle} worldId={worldId} levelId={levelId} toggleImpressum={toggleImpressum}/>
<LevelAppBar isLoading={level.isLoading} levelTitle={`Level ${levelId} / ${gameInfo.data?.worldSize[worldId]}` + (level?.data?.title && ` : ${level?.data?.title}`)} worldId={worldId} levelId={levelId} toggleImpressum={toggleImpressum}/>
<Split minSize={0} snapOffset={200} sizes={[25, 50, 25]} className={`app-content level ${level.isLoading ? 'hidden' : ''}`}>
<div className="chat-panel">
<div ref={chatRef} className="chat">
@@ -292,7 +293,7 @@ function PlayableLevel({worldId, levelId}) {
</div>
}
{levelId >= gameInfo.data?.worldSize[worldId] ?
<Button to={`/${gameId}`}><FontAwesomeIcon icon={faHome} /></Button> :
<Button to={`/${gameId}`}><FontAwesomeIcon icon={faHome} /> Leave World</Button> :
<Button to={`/${gameId}/world/${worldId}/level/${levelId + 1}`}>
Next&nbsp;<FontAwesomeIcon icon={faArrowRight} /></Button>}
</>
@@ -392,26 +393,40 @@ function LevelAppBar({isLoading, levelId, worldId, levelTitle, toggleImpressum})
return <div className="app-bar" style={isLoading ? {display: "none"} : null} >
<div>
<Button to={`/${gameId}`}><FontAwesomeIcon icon={faHome} /></Button>
<span className="app-bar-title">
{gameInfo.data?.worlds.nodes[worldId].title && `World: ${gameInfo.data?.worlds.nodes[worldId].title}`}
</span>
</div>
<span className="app-bar-title">
{gameInfo.data?.worlds.nodes[worldId].title && `World: ${gameInfo.data?.worlds.nodes[worldId].title}`}
</span>
</div>
<div>
<span className="app-bar-title">
{levelTitle}
</span>
<Button disabled={levelId <= 0} inverted="true" to="" onClick={(ev) => { setCommandLineMode(!commandLineMode) }}>
Editor
{levelId > 0 && <>
<Button disabled={levelId <= 0} inverted="true" to=""
onClick={(ev) => { setCommandLineMode(!commandLineMode) }}
title="toggle Editor mode">
Editor
</Button>
<Button disabled={levelId <= 0} inverted="true"
to={`/${gameId}/world/${worldId}/level/${levelId - 1}`}
title="previous level">
<FontAwesomeIcon icon={faArrowLeft} />&nbsp;Previous
</Button>
</>}
{levelId < gameInfo.data?.worldSize[worldId] &&
<Button inverted="true" to={`/${gameId}/world/${worldId}/level/${levelId + 1}`} title="next level">
Next&nbsp;<FontAwesomeIcon icon={faArrowRight} />
</Button>
}
<Button title="information, Impressum, privacy policy" inverted="true" to="" onClick={toggleImpressum}>
<FontAwesomeIcon icon={faCircleInfo} /> Info &amp; Impressum
</Button>
<Button disabled={levelId <= 0} inverted="true" to={`/${gameId}/world/${worldId}/level/${levelId - 1}`}>
<FontAwesomeIcon icon={faArrowLeft} />&nbsp;Previous
<Button to={`/${gameId}`} inverted="true" title="back to world selection">
<FontAwesomeIcon icon={faHome} /> Home
</Button>
<Button disabled={levelId >= gameInfo.data?.worldSize[worldId]} inverted="true" to={`/${gameId}/world/${worldId}/level/${levelId + 1}`}>
Next&nbsp;<FontAwesomeIcon icon={faArrowRight} />
</Button>
<Button title="Information, Impressum, Privacy Policy" inverted="true" to="" onClick={toggleImpressum}>
<FontAwesomeIcon icon={faCircleInfo} />
<Button to="" inverted="true">
<FontAwesomeIcon icon={faBars} />
</Button>
</div>
+26 -2
View File
@@ -12,11 +12,12 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faGlobe, faHome, faCircleInfo, faArrowRight, faArrowLeft, faShield, faRotateLeft } from '@fortawesome/free-solid-svg-icons'
import { GameIdContext } from '../app';
import { selectCompleted } from '../state/progress';
import { useGetGameInfoQuery } from '../state/api';
import { useGetGameInfoQuery, useLoadInventoryOverviewQuery } from '../state/api';
import Markdown from './markdown';
import WorldSelectionMenu from './world_selection_menu';
import {PrivacyPolicy} from './privacy_policy';
import { Button } from './button';
import { Documentation, Inventory } from './inventory';
cytoscape.use( klay );
@@ -47,6 +48,20 @@ function Welcome() {
const gameId = React.useContext(GameIdContext)
const gameInfo = useGetGameInfoQuery({game: gameId})
const inventory = useLoadInventoryOverviewQuery({game: gameId})
// When clicking on an inventory item, the inventory is overlayed by the item's doc.
// If this state is set to a pair `(name, type)` then the according doc will be open.
const [inventoryDoc, setInventoryDoc] = useState<{name: string, type: string}>(null)
// Open the doc of the clicked inventory item
function openInventoryDoc(name, type) {
setInventoryDoc({name, type})
}
// Set `inventoryDoc` to `null` to close the doc
const closeInventoryDoc = () => setInventoryDoc(null);
const { nodes, bounds }: any = gameInfo.data ? computeWorldLayout(gameInfo.data?.worlds) : {nodes: []}
useEffect(() => {
@@ -100,7 +115,7 @@ function Welcome() {
<CircularProgress />
</Box>
:
<Split className="welcome" minSize={200} sizes={[70, 30]}>
<Split className="welcome" minSize={200} sizes={[40, 35, 25]}>
<div className="column">
<Typography variant="body1" component="div" className="welcome-text">
<Button inverted="false" title="back to games selection" to="/">
@@ -118,6 +133,15 @@ function Welcome() {
</svg>
</Box>
</div>
<div className="inventory-panel">
{<>
{inventoryDoc ?
<Documentation name={inventoryDoc.name} type={inventoryDoc.type} handleClose={closeInventoryDoc}/>
:
<Inventory levelInfo={inventory.data} openDoc={openInventoryDoc} />
}
</>}
</div>
</Split>
}
<PrivacyPolicy />
+12 -1
View File
@@ -36,6 +36,14 @@ export interface LevelInfo {
statementName: null|string
}
/** Used to display the inventory on the welcome page */
export interface InventoryOverview {
tactics: InventoryTile[],
lemmas: InventoryTile[],
definitions: InventoryTile[],
lemmaTab: null,
}
interface Doc {
name: string,
displayName: string,
@@ -74,6 +82,9 @@ export const apiSlice = createApi({
loadLevel: builder.query<LevelInfo, {game: string, world: string, level: number}>({
query: ({game, world, level}) => {return {game, method: "loadLevel", params: {world, level}}},
}),
loadInventoryOverview: builder.query<InventoryOverview, {game: string}>({
query: ({game}) => {return {game, method: "loadInventoryOverview", params: {}}},
}),
loadDoc: builder.query<Doc, {game: string, name: string, type: "lemma"|"tactic"}>({
query: ({game, name, type}) => {return {game, method: "loadDoc", params: {name, type}}},
}),
@@ -82,4 +93,4 @@ export const apiSlice = createApi({
// Export hooks for usage in functional components, which are
// auto-generated based on the defined endpoints
export const { useGetGameInfoQuery, useLoadLevelQuery, useLoadDocQuery } = apiSlice
export const { useGetGameInfoQuery, useLoadLevelQuery, useLoadDocQuery, useLoadInventoryOverviewQuery } = apiSlice