Compare commits

...

2 Commits

Author SHA1 Message Date
6dea7ae63f GameSekector: isSelected, onSelect 2023-11-15 08:25:37 +01:00
d8d3406fe1 Counter - for watong for you games 2023-11-14 18:44:06 +01:00
7 changed files with 110 additions and 28 deletions

View File

@ -0,0 +1,13 @@
.Counter {
display: inline-block;
line-height: 0px;
border-radius: 50%;
}
.Counter span {
display: inline-block;
padding-top: 50%;
padding-bottom: 50%;
margin-left: 2px;
margin-right: 2px;
}

View File

@ -0,0 +1,7 @@
import React from 'react';
import './Counter.css';
export default function Counter({ number }) {
if (number !== 0)
return <span className="Counter"><span>{number}</span></span>
}

View File

@ -59,7 +59,12 @@
box-shadow: 0 1.5px 0 0 currentColor; box-shadow: 0 1.5px 0 0 currentColor;
} }
.ViewSelector .Counter {
background-color:palevioletred;
margin-right: 1px;
font-size: 60%;
vertical-align: 7px;
}
.ViewProvider { .ViewProvider {
display: flex; display: flex;

View File

@ -17,6 +17,7 @@ import Forward from './games/action/Forward';
import GameBoard from './games/GameBoard'; import GameBoard from './games/GameBoard';
import Message2Opponent from './games/Message2Opponent'; import Message2Opponent from './games/Message2Opponent';
import Counter from '../components/Counter';
import './Games.css'; import './Games.css';
@ -27,7 +28,7 @@ export default function Games({ context: { gamesReducer, gamesApi }, players })
<GamesContext.Provider value={games} > <GamesContext.Provider value={games} >
<div className='Games'> <div className='Games'>
<div className='left-side'> <div className='left-side'>
<ViewSelector /> <ViewSelector games={games} />
<ViewProvider gamesReducer={gamesReducer} players={players} /> <ViewProvider gamesReducer={gamesReducer} players={players} />
</div> </div>
<div className='right-side'> <div className='right-side'>
@ -43,21 +44,21 @@ export default function Games({ context: { gamesReducer, gamesApi }, players })
) )
}; };
function ViewSelector() { function ViewSelector({ games }) {
// TODO: counter Wating for YOU const awaiting = countGames(games.gamesList);
return ( return (
<nav className='ViewSelector'> <nav className='ViewSelector' >
<NavLink to='new'>New</NavLink> <NavLink to='new'>New</NavLink>
<NavLink to='proposal'>Proposal</NavLink> <NavLink to='proposal'>Proposal<Counter number={awaiting.proposals} /></NavLink>
<NavLink to='active'>Active</NavLink> <NavLink to='active' >Active<Counter number={awaiting.active} /></NavLink>
<NavLink to='archive'>Archive</NavLink> <NavLink to='archive' >Archive</NavLink>
</nav> </nav>
) )
} }
function ViewProvider({ gamesReducer, players }) { function ViewProvider({ gamesReducer, players }) {
const [/*games*/, dispatchGames] = gamesReducer; const [games, dispatchGames] = gamesReducer;
return ( return (
<div className='ViewProvider'> <div className='ViewProvider'>
@ -65,9 +66,7 @@ function ViewProvider({ gamesReducer, players }) {
<Route path='new' element={ <Route path='new' element={
<NewGame <NewGame
onSelectPlayer={(whitePlayer, blackPlayer) => { onSelectPlayer={(whitePlayer, blackPlayer) => dispatchGames({ type: 'nextNewGame', whitePlayer, blackPlayer })}
dispatchGames({ type: 'nextNewGame', whitePlayer, blackPlayer })
}}
players={players} players={players}
/> />
} /> } />
@ -76,7 +75,8 @@ function ViewProvider({ gamesReducer, players }) {
<GameSelector <GameSelector
yours='GAME_PROPOSAL_WAIT_FOR_YOU' yours='GAME_PROPOSAL_WAIT_FOR_YOU'
opponents='GAME_PROPOSAL_WAIT_FOR_OPPONENT' opponents='GAME_PROPOSAL_WAIT_FOR_OPPONENT'
onClick={(uuid) => console.log("GameProposal", uuid)} isSelected={(uuid) => uuid === games.proposal.selectedUUID}
onSelect={(selectedUUID) => dispatchGames({ type: 'nextProposal', selectedUUID })}
/> />
} /> } />
@ -103,3 +103,30 @@ function ActionPanel({ players, gamesApi }) {
</div> </div>
) )
} }
function countGames(gamesList) {
var awaiting = {
proposals: 0,
active: 0
};
if (!gamesList)
return awaiting;
for (const game of gamesList) {
switch (game.status) {
case 'GAME_PROPOSAL_WAIT_FOR_YOU':
awaiting.proposals++;
break;
case 'GAME_BOARD_WAIT_FOR_YOU':
case 'DRAW_REQUEST_WAIT_FOR_YO':
awaiting.active++;
break;
default:
break;
}
}
return awaiting;
}

View File

@ -13,6 +13,13 @@ hr {
margin-top: 3px; margin-top: 3px;
} }
.Selectable .Title {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.Selectable q { .Selectable q {
color: gray; color: gray;
font-size: 70%; font-size: 70%;
@ -33,9 +40,14 @@ hr {
.Selectable:hover { .Selectable:hover {
background-color: #d3d3d360; background-color: #d3d3d360;
border-radius: 2px; border-radius: 2px;
} }
.Selectable.selected {
background-color: #d3d3d3f0;
border-radius: 2px;
}
.Separator { .Separator {
/* width: 20%; */ /* width: 20%; */
/* height: 20px; */ /* height: 20px; */
@ -46,9 +58,6 @@ hr {
margin-bottom: 7px; margin-bottom: 7px;
} }
.Selectable .Title { .Separator .Counter {
display: flex; background-color: darkgray;
flex-direction: row;
align-items: center;
justify-content: center;
} }

View File

@ -4,21 +4,30 @@ import { GamesContext } from '../../../context/games';
import { Color, Player } from '../../../components/Checkers'; import { Color, Player } from '../../../components/Checkers';
import Loading from '../../../components/Loading'; import Loading from '../../../components/Loading';
import Counter from '../../../components/Counter';
export default function GameSelector({ yours, opponents, onClick }) { export default function GameSelector({ yours, opponents, isSelected, onSelect }) {
const games = useContext(GamesContext);
const gamesList = useContext(GamesContext).gamesList; const gamesList = useContext(GamesContext).gamesList;
if (gamesList === null) if (gamesList === null)
return <Loading /> return <Loading />
const onClick = (uuid) => {
if (isSelected(uuid))
onSelect(null); // deselect previously selected game
else
onSelect(uuid);
}
const yoursList = gamesList.filter(game => game.status === yours) const yoursList = gamesList.filter(game => game.status === yours)
.map(game => <Selectable game={game} key={game.uuid} onClick={onClick} />) .map(game => <Selectable game={game} key={game.uuid} selected={isSelected(game.uuid)} onClick={onClick} />)
const opponentsList = gamesList.filter(game => game.status === opponents) const opponentsList = gamesList.filter(game => game.status === opponents)
.map(game => <Selectable game={game} key={game.uuid} onClick={onClick} />) .map(game => <Selectable game={game} key={game.uuid} selected={isSelected(game.uuid)} onClick={onClick} />)
return ( return (
<div className="GameSelector"> <div className='GameSelector'>
{yoursList} {yoursList}
{opponentsList.length > 0 && <Separator counter={opponentsList.length} />} {opponentsList.length > 0 && <Separator counter={opponentsList.length} />}
{opponentsList} {opponentsList}
@ -26,14 +35,15 @@ export default function GameSelector({ yours, opponents, onClick }) {
) )
} }
function Selectable({ game, onClick }) { function Selectable({ game, selected, onClick }) {
const myColor = game.myColor; const myColor = game.myColor;
const opponentColor = Color.opposite(myColor); const opponentColor = Color.opposite(myColor);
const opponentName = game.opponentName; const opponentName = game.opponentName;
return ( return (
<div > <div >
<div className='Selectable' onClick={() => onClick(game.uuid)}> <div className={'Selectable' + (selected ? ' selected' : '')}
onClick={() => onClick(game.uuid)}>
<div className='Title'> <div className='Title'>
<Player color={myColor} /> <Player color={myColor} />
<i>vs</i> <i>vs</i>
@ -48,8 +58,9 @@ function Selectable({ game, onClick }) {
function Separator({ counter }) { function Separator({ counter }) {
return ( return (
<div className="Separator"> <div className='Separator'>
waiting for opponent ({counter}) waiting for opponent
<Counter number={counter} />
</div> </div>
) )
} }

View File

@ -10,6 +10,10 @@ const initialState = {
message2opponent: '', message2opponent: '',
}, },
proposal: {
selectedUUID: null,
},
// Network // Network
isPollingGamesList: false, isPollingGamesList: false,
isPushingNewGame: false, isPushingNewGame: false,
@ -27,6 +31,12 @@ function reducer(state, action) {
newGame: nextState(state.newGame, action) newGame: nextState(state.newGame, action)
}; };
case 'nextProposal':
return {
...state,
proposal: nextState(state.proposal, action)
};
default: default:
throw Error('GamesReducer: unknown action.type', action.type); throw Error('GamesReducer: unknown action.type', action.type);
} }