Skip to content

Commit

Permalink
WIP progress on world overviews
Browse files Browse the repository at this point in the history
  • Loading branch information
joneugster committed Oct 23, 2023
1 parent b70ac78 commit 4266c09
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 27 deletions.
93 changes: 92 additions & 1 deletion client/src/components/inventory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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[],
Expand Down Expand Up @@ -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>
}
4 changes: 2 additions & 2 deletions client/src/components/level.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -480,7 +480,7 @@ function Introduction({impressum, setImpressum}) {
<Split minSize={0} snapOffset={200} sizes={[25, 50, 25]} className={`app-content level`}>
<IntroductionPanel gameInfo={gameInfo} />
<div className="world-image-container empty"></div>
<InventoryPanel levelInfo={inventory?.data} />
<InventoryOverviewPanel data={inventory?.data} showOverview={false}/>
</Split>
}

Expand Down
6 changes: 3 additions & 3 deletions client/src/components/welcome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { changedOpenedIntro } 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'
Expand Down Expand Up @@ -116,14 +116,14 @@ function Welcome() {
: pageNumber == 1 ?
<WorldTreePanel worlds={gameInfo.data?.worlds} worldSize={gameInfo.data?.worldSize} rulesHelp={rulesHelp} setRulesHelp={setRulesHelp} />
:
<InventoryPanel levelInfo={inventory?.data} />
<InventoryOverviewPanel data={inventory?.data} />
)}
</div>
:
<Split className="welcome" minSize={0} snapOffset={200} sizes={[25, 50, 25]}>
<IntroductionPanel introduction={gameInfo.data?.introduction} />
<WorldTreePanel worlds={gameInfo.data?.worlds} worldSize={gameInfo.data?.worldSize} rulesHelp={rulesHelp} setRulesHelp={setRulesHelp} />
<InventoryPanel levelInfo={inventory?.data} />
<InventoryOverviewPanel data={inventory?.data} />
</Split>
}
</div>
Expand Down
8 changes: 7 additions & 1 deletion client/src/state/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ interface Doc {
category: string,
}

export interface WorldOverview {
world: string
tactics: InventoryTile[]
lemmas: InventoryTile[]
definitions: InventoryTile[]
}

const customBaseQuery = async (
args : {game: string, method: string, params?: any},
Expand Down Expand Up @@ -86,7 +92,7 @@ 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}>({
loadInventoryOverview: builder.query<WorldOverview[], {game: string}>({
query: ({game}) => {return {game, method: "loadInventoryOverview", params: {}}},
}),
loadDoc: builder.query<Doc, {game: string, name: string, type: "lemma"|"tactic"}>({
Expand Down
6 changes: 6 additions & 0 deletions server/GameServer/EnvExtensions.lean
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,12 @@ structure GameLevel where
template: Option String := none
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

/-! ## World -/

Expand Down
64 changes: 44 additions & 20 deletions server/GameServer/Game.lean
Original file line number Diff line number Diff line change
Expand Up @@ -225,26 +225,50 @@ partial def handleServerEvent (ev : ServerEvent) : GameServerM Bool := do
let s ← get
let some game ← getGame? s.game
| return false
-- 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

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

Expand Down

0 comments on commit 4266c09

Please sign in to comment.