diff --git a/frontend/src/pages/gameView.tsx b/frontend/src/pages/gameView.tsx index 31e813d..d99bb78 100644 --- a/frontend/src/pages/gameView.tsx +++ b/frontend/src/pages/gameView.tsx @@ -33,13 +33,15 @@ import isArray from 'lodash/isArray'; import range from 'lodash/range'; import { DropzoneDialog } from 'material-ui-dropzone'; import * as React from 'react'; +import isEqual from 'react-fast-compare'; import { Controller, useForm } from 'react-hook-form'; +import { DeepReadonly } from 'ts-essentials'; import { isDefined, nameofFactory, noComplete } from '../common'; import { Board } from '../components/board'; import { ClipboardButton } from '../components/clipboard'; import { useServerTime } from '../hooks'; -import { State, StatePlayer, StateTimer, WordPack } from '../protocol'; +import { State, StatePlayer, StateTeams, StateTimer, StateWordList, WordPack } from '../protocol'; import { teamSpecs } from '../teams'; export interface Sender { @@ -58,15 +60,6 @@ export interface Sender { changeHideBomb: (HideBomb: boolean) => void; } -export interface GameViewProps { - roomID: string; - leave: () => void; - send: Sender; - state: State; - pState: StatePlayer; - pTeam: number; -} - const useCenterStyles = makeStyles((_theme: Theme) => createStyles({ blink: { @@ -351,14 +344,29 @@ const useSidebarStyles = makeStyles((_theme: Theme) => }) ); -const Sidebar = ({ send, state, pState, pTeam }: GameViewProps) => { +interface SidebarProps { + send: Sender; + teams: StateTeams; + lists: StateWordList[]; + pTeam: number; + playerID: string; + version: number; + timer: StateTimer | undefined | null; +} + +const Sidebar = React.memo(function Sidebar({ + send, + teams, + lists, + pTeam, + playerID, + version, + timer, +}: DeepReadonly) { const classes = useSidebarStyles(); const theme = useTheme(); const nameShade = theme.palette.type === 'dark' ? 400 : 600; - const teams = state.teams; - const lists = state.lists; - const wordCount = React.useMemo( () => lists.reduce((curr, l) => { @@ -408,7 +416,7 @@ const Sidebar = ({ send, state, pState, pTeam }: GameViewProps) => { gridRow: j + 2, gridColumn: i + 1, color: teamSpecs[i].hue[nameShade], - fontStyle: member.playerID === pState.playerID ? 'italic' : undefined, + fontStyle: member.playerID === playerID ? 'italic' : undefined, }} > {member.spymaster ? `[${member.nickname}]` : member.nickname} @@ -500,14 +508,15 @@ const Sidebar = ({ send, state, pState, pTeam }: GameViewProps) => { )} - {!isDefined(state.timer) ? null : ( + {!isDefined(timer) ? null : (
- +
)} ); -}; +}, +isEqual); const Board2 = ({ send, state, pState, pTeam }: GameViewProps) => { const myTurn = state.turn === pTeam; @@ -523,16 +532,44 @@ const Board2 = ({ send, state, pState, pTeam }: GameViewProps) => { ); }; -const Footer = ({ send, state, pState }: GameViewProps) => { - const end = isDefined(state.winner); +const useFooterStyles = makeStyles((_theme: Theme) => + createStyles({ + root: { + display: 'flex', + justifyContent: 'space-between', + alignContent: 'flex-start', + flexWrap: 'wrap', + }, + left: { + display: 'flex', + alignContent: 'flex-start', + flexWrap: 'wrap', + }, + leftButton: { + marginBottom: '0.5rem', + marginRight: '0.5rem', + }, + }) +); + +interface FooterProps { + send: Sender; + end: boolean; + spymaster: boolean; + hideBomb: boolean; + hasTimer: boolean; +} + +const Footer = React.memo(function Footer({ send, end, spymaster, hideBomb, hasTimer }: FooterProps) { + const classes = useFooterStyles(); return ( -
-
- +
+
+ - + - +
); -}; +}, isEqual); const useStyles = makeStyles((theme: Theme) => createStyles({ @@ -650,8 +687,18 @@ const useStyles = makeStyles((theme: Theme) => }) ); -export const GameView = (props: GameViewProps) => { +export interface GameViewProps { + roomID: string; + leave: () => void; + send: Sender; + state: State; + pState: StatePlayer; + pTeam: number; +} + +export const GameView = (props: DeepReadonly) => { const classes = useStyles(); + const end = isDefined(props.state.winner); return (
@@ -673,10 +720,24 @@ export const GameView = (props: GameViewProps) => {
-
+
- +
diff --git a/frontend/src/protocol/index.ts b/frontend/src/protocol/index.ts index 38e9862..d9e0888 100644 --- a/frontend/src/protocol/index.ts +++ b/frontend/src/protocol/index.ts @@ -123,6 +123,14 @@ const StateTimer = myzod.object({ turnEnd: myzod.date(), }); +export type StateWordList = DeepReadonly>; +const StateWordList = myzod.object({ + name: myzod.string(), + count: myzod.number(), + custom: myzod.boolean(), + enabled: myzod.boolean(), +}); + export type State = DeepReadonly>; export const State = myzod.object({ version: myzod.number(), @@ -131,14 +139,7 @@ export const State = myzod.object({ winner: myzod.number().optional().nullable(), board: StateBoard, wordsLeft: myzod.array(myzod.number()), - lists: myzod.array( - myzod.object({ - name: myzod.string(), - count: myzod.number(), - custom: myzod.boolean(), - enabled: myzod.boolean(), - }) - ), + lists: myzod.array(StateWordList), timer: StateTimer.optional().nullable(), hideBomb: myzod.boolean(), });