Compare commits
2 Commits
9439186b8a
...
6dea7ae63f
Author | SHA1 | Date | |
---|---|---|---|
6dea7ae63f | |||
d8d3406fe1 |
13
webapp/src/components/Counter.css
Normal file
13
webapp/src/components/Counter.css
Normal 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;
|
||||||
|
}
|
7
webapp/src/components/Counter.jsx
Normal file
7
webapp/src/components/Counter.jsx
Normal 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>
|
||||||
|
}
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -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);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user