Skip to content

Commit

Permalink
Improved stability, added restart option.
Browse files Browse the repository at this point in the history
  • Loading branch information
przemyslaw-zan committed Jan 9, 2025
1 parent a7df505 commit 288e746
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 98 deletions.
20 changes: 16 additions & 4 deletions app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { QRCodeSVG } from 'qrcode.react';
import { type ReactNode, useState } from 'react';
import type { LobbyStatusResponse } from '../server/middlewares/PutLobbyStatus.ts';
import { GameGuestPage } from './components/GameGuestPage.tsx';
import { GameSetupPage } from './components/GameSetupPage.tsx';
import { InfoPage } from './components/InfoPage.tsx';
Expand All @@ -9,10 +10,21 @@ import { clearGameIdInUrl } from './utils/gameId.ts';

export type PageType = 'signature' | 'gameSetup' | 'gameGuest' | 'info';

export type GameStatus = LobbyStatusResponse | null;

export type PageProps = {
gameStatus: GameStatus;
setGameStatus: ( arg: GameStatus ) => void;
setCurrentPage: ( arg: PageType ) => void;
};

export function App(): ReactNode {
const [ gameStatus, setGameStatus ] = useState<GameStatus>( null );
const [ currentPage, setCurrentPage ] = useState<PageType>( 'signature' );
const [ optionsOpen, setOptionsOpen ] = useState( false );

const pageProps: PageProps = { gameStatus, setGameStatus, setCurrentPage };

return (
<>
<div
Expand All @@ -32,10 +44,10 @@ export function App(): ReactNode {
⚙️ Options
</button>
</div>
{ currentPage === 'signature' && <SignaturePage setCurrentPage={ setCurrentPage }/> }
{ currentPage === 'gameSetup' && <GameSetupPage setCurrentPage={ setCurrentPage }/> }
{ currentPage === 'gameGuest' && <GameGuestPage setCurrentPage={ setCurrentPage }/> }
{ currentPage === 'info' && <InfoPage/> }
{ currentPage === 'signature' && <SignaturePage { ...pageProps }/> }
{ currentPage === 'gameSetup' && <GameSetupPage { ...pageProps }/> }
{ currentPage === 'gameGuest' && <GameGuestPage { ...pageProps }/> }
{ currentPage === 'info' && <InfoPage { ...pageProps }/> }
</div>

<div
Expand Down
30 changes: 23 additions & 7 deletions app/components/GameGuestPage.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { type ReactNode, useEffect } from 'react';
import type { GameGuestRequest } from '../../server/middlewares/PutGameGuest.ts';
import type { PageType } from '../App.tsx';
import type { PageProps } from '../App.tsx';
import { getCookie } from '../utils/cookies.ts';
import { getGameIdFromUrl } from '../utils/gameId.ts';
import { getServerUrl } from '../utils/getServerUrl.ts';
import { LobbyStatus } from './LobbyStatus.tsx';

export function GameGuestPage( {
setCurrentPage
}: {
setCurrentPage: ( arg: PageType ) => void;
} ): ReactNode {
export function GameGuestPage( pageProps: PageProps ): ReactNode {
const { gameStatus, setCurrentPage } = pageProps;

useEffect( () => {
const intervalId = setInterval( () => {
const requestBody: GameGuestRequest = {
Expand All @@ -28,5 +26,23 @@ export function GameGuestPage( {
return (): void => clearInterval( intervalId );
}, [] );

return <LobbyStatus setCurrentPage={ setCurrentPage }/>;
useEffect( () => {
if ( !gameStatus ) {
return;
}

if ( !gameStatus.game.hasBegun ) {
return;
}

const playerId = getCookie();

if ( !gameStatus.game.players.find( player => player.id === playerId ) ) {
return;
}

setCurrentPage( 'info' );
}, [ gameStatus ] );

return <LobbyStatus { ...pageProps }/>;
}
54 changes: 35 additions & 19 deletions app/components/GameSetupPage.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,19 @@
import { type ReactNode, useEffect, useState } from 'react';
import type { SelectedCards } from '../../server/main.ts';
import type { GameSetupRequest } from '../../server/middlewares/PutGameSetup.ts';
import type { PageType } from '../App.tsx';
import type { GameStatus, PageProps } from '../App.tsx';
import { type Card, cards } from '../utils/cards.ts';
import { getCookie } from '../utils/cookies.ts';
import { getGameIdFromUrl, setGameIdInUrl } from '../utils/gameId.ts';
import { getServerUrl } from '../utils/getServerUrl.ts';
import { LobbyStatus } from './LobbyStatus.tsx';

export function GameSetupPage( {
setCurrentPage
}: {
setCurrentPage: ( arg: PageType ) => void;
} ): ReactNode {
const [ playerCount, setPlayerCount ] = useState( 0 );
const [ gameBegun, setGameBegun ] = useState( false );
const [ selectedCards, setSelectedCards ] = useState<SelectedCards>(
cards.reduce( ( output, card ) => {
output[card.id] = card.alwaysSelected ? 1 : 0;

return output;
}, {} as SelectedCards )
);
export function GameSetupPage( pageProps: PageProps ): ReactNode {
const { gameStatus, setCurrentPage } = pageProps;

const equalPlayersAndCardCount = playerCount === Object.values( selectedCards ).reduce( ( a, b ) => a + b );
const [ gameBegun, setGameBegun ] = useState( false );
const [ selectedCards, setSelectedCards ] = useState<SelectedCards>( getInitialSelectedCards( gameStatus ) );
const [ canStartGame, setCanStartGame ] = useState( false );

setGameIdInUrl();

Expand All @@ -40,9 +30,23 @@ export function GameSetupPage( {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify( requestBody )
} ).then( () => {
if ( gameBegun ) {
setCurrentPage( 'info' );
}
} );
}, [ gameBegun, selectedCards ] );

useEffect( () => {
if ( !gameStatus ) {
return;
}

const equalPlayersAndCardCount = gameStatus.game.players.length === Object.values( selectedCards ).reduce( ( a, b ) => a + b );

setCanStartGame( equalPlayersAndCardCount );
}, [ gameStatus ] );

function updateSelectedCards( card: Card, eventTarget: HTMLInputElement ): void {
let newValue: number;

Expand Down Expand Up @@ -105,15 +109,27 @@ export function GameSetupPage( {
);
} ) }
</div>
<LobbyStatus setCurrentPage={ setCurrentPage } setPlayerCount={ setPlayerCount }/>
<button disabled={ !equalPlayersAndCardCount } onClick={ () => setGameBegun( true ) }>
<LobbyStatus { ...pageProps }/>
<button disabled={ !canStartGame } onClick={ () => setGameBegun( true ) }>
Start the game
</button>
{ !equalPlayersAndCardCount && (
{ !canStartGame && (
<span style={ { color: 'red' } }>
Uneven amount of players and selected cards!
</span>
) }
</>
);
}

function getInitialSelectedCards( gameStatus: GameStatus ): SelectedCards {
if ( gameStatus ) {
return gameStatus.game.selectedCards;
}

return cards.reduce( ( output, card ) => {
output[card.id] = card.alwaysSelected ? 1 : 0;

return output;
}, {} as SelectedCards );
}
113 changes: 89 additions & 24 deletions app/components/InfoPage.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
import { type ReactNode, useEffect, useState } from 'react';
import type { LobbyStatusResponse } from '../../server/middlewares/PutLobbyStatus.ts';
import { type ReactNode, useEffect } from 'react';
import type { GameStatus, PageProps } from '../App.tsx';
import { cards } from '../utils/cards.ts';
import { getCookie } from '../utils/cookies.ts';
import { fetchLobbyStatus } from '../utils/fetchLobbyStatus.ts';

export function InfoPage(): ReactNode {
const [ status, setStatus ] = useState<null | LobbyStatusResponse>( null );

export function InfoPage( { gameStatus, setGameStatus, setCurrentPage }: PageProps ): ReactNode {
useEffect( () => {
const intervalId = setInterval( () => {
fetchLobbyStatus().then( res => setStatus( res ) );
fetchLobbyStatus().then( res => setGameStatus( res ) );
}, 500 );

return (): void => clearInterval( intervalId );
}, [] );

if ( !status ) {
useEffect( () => {
if ( !gameStatus ) {
return;
}

if ( !gameStatus.game.hasBegun ) {
setCurrentPage( 'gameGuest' );
}
}, [ gameStatus ] );

if ( !gameStatus ) {
return (
<p>
Loading game status...
Expand All @@ -24,23 +32,58 @@ export function InfoPage(): ReactNode {
}

const playerId = getCookie();
const playerName = status.knownPlayers.find( p => p.id === playerId )!.name;
const playerCardId = status.game.players.find( p => p.id === playerId )!.cardId!;
const playerName = gameStatus.knownPlayers.find( p => p.id === playerId )!.name;
const playerGameData = gameStatus.game.players.find( p => p.id === playerId )!;

if ( !playerGameData?.cardId ) {
return (
<>
<b style={ { fontSize: '1.5rem' } }>
{ playerName }
</b>
<span>
Game in progress, please wait...
</span>
</>
);
}

const playerCardId = playerGameData.cardId;
const playerCard = cards.find( c => c.id === playerCardId )!;
const playerMap = status.game.players.map( player => {
return {
const playerMap = gameStatus.game.players
.filter( player => player.cardId )
.map( player => ( {
...player,
name: status.knownPlayers.find( p => p.id === player.id )!.name,
name: gameStatus.knownPlayers.find( p => p.id === player.id )!.name,
card: cards.find( c => c.id === player.cardId )!
};
} );
} ) );

const otherEvilPlayers = playerMap
.filter( player => {
if ( player.card.affiliation !== 'evil' ) {
return false;
}

if ( player.card.id === 'oberon' ) {
return false;
}

if ( player.id === playerId ) {
return false;
}

return true;
} );

return (
<>
<i>
Game begun at: { getDate( gameStatus ) }
</i>
<b style={ { fontSize: '1.5rem' } }>
{ playerName }
</b>
<b style={ { fontSize: '2rem' } }>
<b style={ { fontSize: '2rem', color: playerCard.affiliation === 'good' ? 'blue' : 'red' } }>
{ playerCard.name }
</b>

Expand Down Expand Up @@ -79,20 +122,42 @@ export function InfoPage(): ReactNode {
{ [ 'mordred', 'morgana', 'assassin', 'minion' ].includes( playerCardId ) && (
<>
<span>
Other evil players are:
{ otherEvilPlayers.length ? 'Other evil players are:' : 'You are the only evil player.' }
</span>
<ul>
{ playerMap
.filter( player => player.card.affiliation === 'evil' && player.card.id !== 'oberon' )
.map( player => (
<li key={ player.id }>
{ player.name }
{ player.cardId === 'evilLancelot' && ` (${player.card.name})` }
</li>
) ) }
{ otherEvilPlayers.map( player => (
<li key={ player.id }>
{ player.name }
{ player.cardId === 'evilLancelot' && ` (${player.card.name})` }
</li>
) ) }
</ul>
</>
) }
{ playerGameData.isHost && (
<button
onClick={ () => {
setGameStatus( null );
setCurrentPage( 'gameSetup' );
} }
>
Restart the game
</button>
) }
</>
);
}

function getDate( gameStatus: GameStatus ): string {
if ( !gameStatus ) {
return '?';
}

if ( !gameStatus.game.beginTimestamp ) {
return '?';
}

const date = new Date( gameStatus.game.beginTimestamp );

return date.toLocaleString();
}
Loading

0 comments on commit 288e746

Please sign in to comment.