Compare commits
2
Commits
stats
...
world_overviews
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb214f426b | ||
|
|
4266c090db |
@@ -5,7 +5,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faLock, faBan } from '@fortawesome/free-solid-svg-icons'
|
||||
import { GameIdContext } from '../app';
|
||||
import Markdown from './markdown';
|
||||
import { useLoadDocQuery, InventoryTile, LevelInfo, InventoryOverview, useLoadInventoryOverviewQuery } from '../state/api';
|
||||
import { useLoadDocQuery, InventoryTile, LevelInfo, InventoryOverview, useLoadInventoryOverviewQuery, WorldOverview } from '../state/api';
|
||||
import { selectDifficulty, selectInventory } from '../state/progress';
|
||||
import { store } from '../state/store';
|
||||
import { useSelector } from 'react-redux';
|
||||
@@ -37,6 +37,79 @@ export function Inventory({levelInfo, openDoc, enableAll=false} :
|
||||
)
|
||||
}
|
||||
|
||||
export function OverviewInventory({data, openDoc, enableAll=false, showOverview=true} :
|
||||
{
|
||||
data: WorldOverview[],
|
||||
openDoc: (props: {name: string, type: string}) => void,
|
||||
enableAll?: boolean,
|
||||
showOverview?: boolean
|
||||
}) {
|
||||
|
||||
const gameId = React.useContext(GameIdContext)
|
||||
const difficulty = useSelector(selectDifficulty(gameId))
|
||||
const [tab, setTab] = useState<string>()
|
||||
let inv: string[] = selectInventory(gameId)(store.getState())
|
||||
|
||||
let levelInfo : InventoryOverview = {
|
||||
tactics : data ? data.map(world => (world.tactics.map(tile => inv.includes(tile.name) ? {...tile, locked: false} : tile))).flat() : [],
|
||||
lemmas : data ? data.map(world => (world.lemmas.map(tile => inv.includes(tile.name) ? {...tile, locked: false} : tile))).flat() : [],
|
||||
definitions : data ? data.map(world => (world.definitions.map(tile => inv.includes(tile.name) ? {...tile, locked: false} : tile))).flat() : [],
|
||||
lemmaTab: null,
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Inventory levelInfo={levelInfo} openDoc={openDoc} enableAll={enableAll}/>
|
||||
{showOverview &&
|
||||
<div className="inventory">
|
||||
{data && <>
|
||||
<h2>Overviews</h2>
|
||||
<div className="tab-bar">
|
||||
{data.map(world => {
|
||||
return <div key={`category-${world.world}`} className={`tab ${world.world == tab ? "active": ""}`} onClick={() => { setTab((tab == world.world) ? null : world.world) }}>{world.world}</div>
|
||||
})}
|
||||
</div>
|
||||
<div className="inventory-list">
|
||||
{/* TODO: a bit hacky, do we need to redesign inventory completely and provide it in a better order? */}
|
||||
{data.map(world => {
|
||||
if (world.world == tab) {
|
||||
return [
|
||||
...world.tactics.map(item => {
|
||||
return <InventoryItem
|
||||
key={`${world.world}-${item.name}`}
|
||||
showDoc={() => {openDoc({name: item.name, type: 'Tactic'})}}
|
||||
name={item.name} displayName={item.displayName}
|
||||
locked={difficulty > 0 ? !inv.includes(item.name) : false}
|
||||
disabled={item.disabled} newly={false} enableAll={enableAll} />
|
||||
}),
|
||||
...world.lemmas.map(item => {
|
||||
return <InventoryItem
|
||||
key={`${world.world}-${item.name}`}
|
||||
showDoc={() => {openDoc({name: item.name, type: 'Lemma'})}}
|
||||
name={item.name} displayName={item.displayName}
|
||||
locked={difficulty > 0 ? item.locked : false}
|
||||
disabled={item.disabled} newly={false} enableAll={enableAll} />
|
||||
}),
|
||||
...world.definitions.map(item => {
|
||||
return <InventoryItem
|
||||
key={`${world.world}-${item.name}`}
|
||||
showDoc={() => {openDoc({name: item.name, type: 'Definition'})}}
|
||||
name={item.name} displayName={item.displayName}
|
||||
locked={difficulty > 0 ? item.locked : false}
|
||||
disabled={item.disabled} newly={false} enableAll={enableAll} />
|
||||
})
|
||||
]
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
</>}
|
||||
</div>
|
||||
}
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
function InventoryList({items, docType, openDoc, defaultTab=null, level=undefined, enableAll=false} :
|
||||
{
|
||||
items: InventoryTile[],
|
||||
@@ -144,3 +217,21 @@ export function InventoryPanel({levelInfo, visible = true}) {
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
/** The panel (on the welcome page) showing the user's inventory with tactics, definitions, and lemmas */
|
||||
export function InventoryOverviewPanel({data, visible = true, showOverview=true} : {data : WorldOverview[], visible?: boolean, showOverview?: boolean}) {
|
||||
const gameId = React.useContext(GameIdContext)
|
||||
|
||||
// The inventory is overlayed by the doc entry of a clicked item
|
||||
const [inventoryDoc, setInventoryDoc] = useState<{name: string, type: string}>(null)
|
||||
// Set `inventoryDoc` to `null` to close the doc
|
||||
function closeInventoryDoc() {setInventoryDoc(null)}
|
||||
|
||||
return <div className={`column inventory-panel ${visible ? '' : 'hidden'}`}>
|
||||
{inventoryDoc ?
|
||||
<Documentation name={inventoryDoc.name} type={inventoryDoc.type} handleClose={closeInventoryDoc}/>
|
||||
:
|
||||
<OverviewInventory data={data} openDoc={setInventoryDoc} enableAll={true} showOverview={showOverview}/>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import { changedSelection, codeEdited, selectCode, selectSelections, selectCompl
|
||||
import { store } from '../state/store'
|
||||
import { Button } from './button'
|
||||
import Markdown from './markdown'
|
||||
import {InventoryPanel} from './inventory'
|
||||
import {InventoryOverviewPanel, InventoryPanel} from './inventory'
|
||||
import { hasInteractiveErrors } from './infoview/typewriter'
|
||||
import { DeletedChatContext, InputModeContext, MobileContext, MonacoEditorContext,
|
||||
ProofContext, ProofStep, SelectionContext, WorldLevelIdContext } from './infoview/context'
|
||||
@@ -498,7 +498,7 @@ function Introduction({impressum, setImpressum}) {
|
||||
}
|
||||
|
||||
</div>
|
||||
<InventoryPanel levelInfo={inventory?.data} />
|
||||
<InventoryOverviewPanel data={inventory?.data} />
|
||||
</Split>
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import { changedOpenedIntro, selectOpenedIntro } from '../state/progress'
|
||||
import { useGetGameInfoQuery, useLoadInventoryOverviewQuery } from '../state/api'
|
||||
import { Button } from './button'
|
||||
import { MobileContext } from './infoview/context'
|
||||
import { InventoryPanel } from './inventory'
|
||||
import { InventoryOverviewPanel, InventoryPanel } from './inventory'
|
||||
import { ErasePopup } from './popup/erase'
|
||||
import { InfoPopup } from './popup/game_info'
|
||||
import { PrivacyPolicyPopup } from './popup/privacy_policy'
|
||||
@@ -111,7 +111,7 @@ function Welcome() {
|
||||
<WorldTreePanel worlds={gameInfo.data?.worlds} worldSize={gameInfo.data?.worldSize}
|
||||
rulesHelp={rulesHelp} setRulesHelp={setRulesHelp} />
|
||||
:
|
||||
<InventoryPanel levelInfo={inventory?.data} />
|
||||
<InventoryOverviewPanel data={inventory?.data} />
|
||||
)}
|
||||
</div>
|
||||
:
|
||||
@@ -119,7 +119,7 @@ function Welcome() {
|
||||
<IntroductionPanel introduction={gameInfo.data?.introduction} setPageNumber={setPageNumber} />
|
||||
<WorldTreePanel worlds={gameInfo.data?.worlds} worldSize={gameInfo.data?.worldSize}
|
||||
rulesHelp={rulesHelp} setRulesHelp={setRulesHelp} />
|
||||
<InventoryPanel levelInfo={inventory?.data} />
|
||||
<InventoryOverviewPanel data={inventory?.data} />
|
||||
</Split>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -71,6 +71,13 @@ interface Doc {
|
||||
category: string,
|
||||
}
|
||||
|
||||
export interface WorldOverview {
|
||||
world: string
|
||||
tactics: InventoryTile[]
|
||||
lemmas: InventoryTile[]
|
||||
definitions: InventoryTile[]
|
||||
}
|
||||
|
||||
// Define a service using a base URL and expected endpoints
|
||||
export const apiSlice = createApi({
|
||||
reducerPath: 'gameApi',
|
||||
@@ -82,7 +89,7 @@ export const apiSlice = createApi({
|
||||
loadLevel: builder.query<LevelInfo, {game: string, world: string, level: number}>({
|
||||
query: ({game, world, level}) => `${game}/level__${world}__${level}.json`,
|
||||
}),
|
||||
loadInventoryOverview: builder.query<InventoryOverview, {game: string}>({
|
||||
loadInventoryOverview: builder.query<WorldOverview[], {game: string}>({
|
||||
query: ({game}) => `${game}/inventory.json`,
|
||||
}),
|
||||
loadDoc: builder.query<Doc, {game: string, name: string, type: "lemma"|"tactic"}>({
|
||||
|
||||
@@ -269,6 +269,13 @@ structure GameLevel where
|
||||
image : String := default
|
||||
deriving Inhabited, Repr
|
||||
|
||||
structure WorldOverview where
|
||||
world: Name
|
||||
tactics: Array InventoryTile := default
|
||||
definitions: Array InventoryTile := default
|
||||
lemmas: Array InventoryTile := default
|
||||
deriving FromJson, ToJson, Inhabited, Repr
|
||||
|
||||
/-- Json-encodable version of `GameLevel`
|
||||
Fields:
|
||||
- description: Lemma in mathematical language.
|
||||
|
||||
@@ -93,6 +93,54 @@ partial def handleServerEvent (ev : ServerEvent) : GameServerM Bool := do
|
||||
fw.stdin.writeLspMessage msg
|
||||
|
||||
return true
|
||||
| Message.request id "loadInventoryOverview" _ =>
|
||||
let s ← get
|
||||
let some game ← getGame? s.game
|
||||
| return false
|
||||
|
||||
let mut overview : Array WorldOverview := #[]
|
||||
for ⟨worldId, world⟩ in game.worlds.nodes.toList do
|
||||
let mut tactics : Array InventoryTile := #[]
|
||||
let mut theorems : Array InventoryTile := #[]
|
||||
let mut definitions : Array InventoryTile := #[]
|
||||
for ⟨_levelId, lvl⟩ in world.levels.toList do
|
||||
tactics := tactics ++ lvl.tactics.tiles.filterMap (fun tile => match tile.new with
|
||||
| true => some {tile with new := false, locked := true, disabled := false}
|
||||
| false => none)
|
||||
theorems := theorems ++ lvl.lemmas.tiles.filterMap (fun tile => match tile.new with
|
||||
| true => some {tile with new := false, locked := true, disabled := false}
|
||||
| false => none)
|
||||
definitions := definitions ++ lvl.definitions.tiles.filterMap (fun tile => match tile.new with
|
||||
| true => some {tile with new := false, locked := true, disabled := false}
|
||||
| false => none)
|
||||
overview := overview.push {
|
||||
world := worldId,
|
||||
tactics := tactics,
|
||||
lemmas := theorems,
|
||||
definitions := definitions }
|
||||
let c ← read
|
||||
c.hOut.writeLspResponse ⟨id, ToJson.toJson overview⟩
|
||||
return true
|
||||
-- -- All Levels have the same tiles, so we just load them from level 1 of an arbitrary world
|
||||
-- -- and reset `new`, `disabled` and `unlocked`.
|
||||
-- -- Note: as we allow worlds without any levels (for developing), we might need
|
||||
-- -- to try until we find the first world with levels.
|
||||
-- for ⟨worldId, _⟩ in game.worlds.nodes.toList do
|
||||
-- let some lvl ← getLevel? {game := s.game, world := worldId, level := 1}
|
||||
-- | do continue
|
||||
-- let inventory : InventoryOverview := {
|
||||
-- tactics := lvl.tactics.tiles.map
|
||||
-- ({ · with locked := true, disabled := false, new := false }),
|
||||
-- lemmas := lvl.lemmas.tiles.map
|
||||
-- ({ · with locked := true, disabled := false, new := false }),
|
||||
-- definitions := lvl.definitions.tiles.map
|
||||
-- ({ · with locked := true, disabled := false, new := false }),
|
||||
-- lemmaTab := none
|
||||
-- }
|
||||
-- let c ← read
|
||||
-- c.hOut.writeLspResponse ⟨id, ToJson.toJson inventory⟩
|
||||
-- return true
|
||||
-- return false
|
||||
| _ => return false
|
||||
| _ => return false
|
||||
|
||||
|
||||
Reference in New Issue
Block a user