Skip to content

Commit

Permalink
Add basic frontend representation of secrets
Browse files Browse the repository at this point in the history
  • Loading branch information
iamlogand committed Jan 15, 2024
1 parent 41316ae commit b424eba
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 6 deletions.
5 changes: 5 additions & 0 deletions frontend/classes/Collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ class Collection<T extends Identifiable> {
return new Collection(this.asArray)
}

update(instance: T) {
this.remove(instance.id)
return this.add(instance)
}

get asArray(): T[] {
return this.allIds.map((id) => this.byId[id])
}
Expand Down
22 changes: 22 additions & 0 deletions frontend/classes/Secret.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
interface SecretData {
id: number
name: string | null
type: string | null
faction: number
}

class Secret {
public id: number
public name: string | null
public type: string | null
public faction: number

constructor(data: SecretData) {
this.id = data.id
this.name = data.name
this.type = data.type
this.faction = data.faction
}
}

export default Secret
53 changes: 50 additions & 3 deletions frontend/components/GamePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import ProgressSection from "@/components/ProgressSection"
import ActionLog from "@/classes/ActionLog"
import refreshAccessToken from "@/functions/tokens"
import SenatorActionLog from "@/classes/SenatorActionLog"
import Secret from "@/classes/Secret"

const webSocketURL: string = process.env.NEXT_PUBLIC_WS_URL ?? ""

Expand Down Expand Up @@ -84,6 +85,7 @@ const GamePage = (props: GamePageProps) => {
setActionLogs,
setSenatorActionLogs,
setNotifications,
setAllSecrets,
} = useGameContext()
const [latestActions, setLatestActions] = useState<Collection<Action>>(
new Collection<Action>()
Expand Down Expand Up @@ -294,6 +296,52 @@ const GamePage = (props: GamePageProps) => {
[setLatestActions, fetchData]
)

const fetchSecrets = useCallback(async () => {
fetchData(
`secrets-private/?game=${props.gameId}`,
(data: any) => {
const deserializedInstances = deserializeToInstances<Secret>(
Secret,
data
)
setAllSecrets((instances) => {
// Merge the new notifications with the existing ones
// Loop over each new notification and add it to the collection if it doesn't already exist
for (const newInstance of deserializedInstances) {
if (!instances.allIds.includes(newInstance.id)) {
instances = instances.add(newInstance)
} else if (instances.allIds.includes(newInstance.id)) {
// If the instance already exists, update it
instances = instances.update(newInstance)
}
}
return instances
})
},
() => {}
)
fetchData(
`secrets-public/?game=${props.gameId}`,
(data: any) => {
const deserializedInstances = deserializeToInstances<Secret>(
Secret,
data
)
setAllSecrets((instances) => {
// Merge the new instances with the existing ones
// Loop over each new instance and add it to the collection if it doesn't already exist
for (const newInstance of deserializedInstances) {
if (!instances.allIds.includes(newInstance.id)) {
instances = instances.add(newInstance)
}
}
return instances
})
},
() => {}
)
}, [props.gameId, setNotifications, fetchData])

const fetchNotifications = useCallback(async () => {
const minIndex = -10 // Fetch the last 10 notifications
const maxIndex = -1
Expand All @@ -316,9 +364,7 @@ const GamePage = (props: GamePageProps) => {
return notifications
})
},
() => {
setNotifications(new Collection<ActionLog>())
}
() => {}
)
}, [props.gameId, setNotifications, fetchData])

Expand Down Expand Up @@ -349,6 +395,7 @@ const GamePage = (props: GamePageProps) => {
fetchLatestTurn(),
fetchLatestPhase(),
fetchNotifications(),
fetchSecrets(),
]
const results = await Promise.all(requestsBatch1)
const updatedLatestStep: Step | null = results[0] as Step | null
Expand Down
24 changes: 24 additions & 0 deletions frontend/components/SecretList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import Faction from "@/classes/Faction"
import Secret from "@/classes/Secret"
import { useGameContext } from "@/contexts/GameContext"
import { capitalize } from "@mui/material/utils"

const SecretList = ({ faction }: { faction: Faction }) => {
const { allSecrets } = useGameContext()

const secrets = allSecrets.asArray.filter(
(secret: Secret) => secret.faction === faction.id
)

return (
<div className="flex flex-col gap-2">
{secrets.map((secret: Secret) => (
<p>
<b>{capitalize(secret.type as string)}</b>: {secret.name}
</p>
))}
</div>
)
}

export default SecretList
46 changes: 43 additions & 3 deletions frontend/components/detailSections/DetailSection_Faction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@ import AttributeGrid, { Attribute } from "@/components/AttributeGrid"
import SenatorPortrait from "@/components/SenatorPortrait"
import Title from "@/classes/Title"
import SenatorLink from "@/components/SenatorLink"
import TermLink from "../TermLink"
import TermLink from "@/components/TermLink"
import SecretList from "@/components/SecretList"
import { useAuthContext } from "@/contexts/AuthContext"

// Detail section content for a faction
const FactionDetails = () => {
const { user } = useAuthContext()
const {
allPlayers,
allFactions,
Expand All @@ -26,6 +29,7 @@ const FactionDetails = () => {
selectedDetail,
factionDetailTab,
setFactionDetailTab,
allSecrets,
} = useGameContext()

// Get faction-specific data
Expand Down Expand Up @@ -56,6 +60,15 @@ const FactionDetails = () => {
(total, senator) => total + senator.votes,
0
)
const secrets = allSecrets.asArray.filter((s) => s.faction === faction?.id)

// Get faction-specific data for the current user
const currentPlayer: Player | null = user?.id
? allPlayers.asArray.find((p) => p.user?.id === user?.id) ?? null
: null
const currentFaction: Faction | null = currentPlayer?.id
? allFactions.asArray.find((f) => f.player === currentPlayer?.id) ?? null
: null

// Get faction leader
const factionLeaderTitle: Title | null =
Expand Down Expand Up @@ -88,6 +101,16 @@ const FactionDetails = () => {
setFactionDetailTab(newValue)
}

// Switch to the overview tab if secret tab is selected and the current user is not in this faction
if (
factionDetailTab === 2 &&
currentFaction &&
faction &&
currentFaction.id !== faction.id
) {
setFactionDetailTab(0)
}

// Attribute data
const attributes: Attribute[] = [
{ name: "Senators", value: senators.allIds.length, icon: SenatorsIcon },
Expand All @@ -98,6 +121,7 @@ const FactionDetails = () => {
},
{ name: "Talents", value: totalTalents, icon: TalentsIcon },
{ name: "Votes", value: totalVotes, icon: VotesIcon },
{ name: "Secrets", value: secrets.length, icon: VotesIcon },
]

// If there is no faction selected, render nothing
Expand All @@ -122,7 +146,12 @@ const FactionDetails = () => {
name="HRAO"
tooltipTitle="Highest Ranking Available Official"
/>
{majorOffices.length == 0 && <span>{": "}<SenatorLink senator={hraoSenator} /></span>}
{majorOffices.length == 0 && (
<span>
{": "}
<SenatorLink senator={hraoSenator} />
</span>
)}
</span>
)}
{majorOffices.length > 0 &&
Expand All @@ -139,7 +168,8 @@ const FactionDetails = () => {
: office.name
}
displayName={office.name}
/>{": "}
/>
{": "}
<SenatorLink senator={senator} />
</span>
)
Expand Down Expand Up @@ -171,6 +201,9 @@ const FactionDetails = () => {
>
<Tab label="Overview" />
<Tab label="Members" />
{currentFaction && currentFaction.id === faction.id && (
<Tab label="Secrets" />
)}
</Tabs>
</div>
<div className="grow overflow-y-auto bg-stone-50 shadow-inner">
Expand All @@ -197,6 +230,13 @@ const FactionDetails = () => {
<SenatorList faction={faction} selectable border />
</div>
)}
{factionDetailTab === 2 &&
currentFaction &&
currentFaction.id === faction.id && (
<div className="h-full p-4 box-border">
<SecretList faction={faction} />
</div>
)}
</div>
</div>
)
Expand Down
8 changes: 8 additions & 0 deletions frontend/contexts/GameContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import ActionLog from "@/classes/ActionLog"
import SenatorActionLog from "@/classes/SenatorActionLog"
import Phase from "@/classes/Phase"
import Turn from "@/classes/Turn"
import Secret from "@/classes/Secret"

interface GameContextType {
game: Game | null
Expand Down Expand Up @@ -50,6 +51,8 @@ interface GameContextType {
setDebugShowEntityIds: Dispatch<SetStateAction<boolean>>
notifications: Collection<ActionLog>
setNotifications: Dispatch<SetStateAction<Collection<ActionLog>>>
allSecrets: Collection<Secret>
setAllSecrets: Dispatch<SetStateAction<Collection<Secret>>>
}

const GameContext = createContext<GameContextType | null>(null)
Expand Down Expand Up @@ -99,6 +102,9 @@ export const GameProvider = (props: GameProviderProps): JSX.Element => {
const [notifications, setNotifications] = useState<Collection<ActionLog>>(
new Collection<ActionLog>()
)
const [allSecrets, setAllSecrets] = useState<Collection<Secret>>(
new Collection<Secret>()
)

return (
<GameContext.Provider
Expand Down Expand Up @@ -133,6 +139,8 @@ export const GameProvider = (props: GameProviderProps): JSX.Element => {
setDebugShowEntityIds,
notifications,
setNotifications,
allSecrets,
setAllSecrets,
}}
>
{props.children}
Expand Down

0 comments on commit b424eba

Please sign in to comment.