summaryrefslogtreecommitdiff
path: root/sw-ui/src/components/game-browser
diff options
context:
space:
mode:
Diffstat (limited to 'sw-ui/src/components/game-browser')
-rw-r--r--sw-ui/src/components/game-browser/GameBrowser.tsx56
-rw-r--r--sw-ui/src/components/game-browser/GameList.css3
-rw-r--r--sw-ui/src/components/game-browser/GameList.tsx85
-rw-r--r--sw-ui/src/components/game-browser/GameStatus.tsx17
-rw-r--r--sw-ui/src/components/game-browser/PlayerCount.css3
-rw-r--r--sw-ui/src/components/game-browser/PlayerCount.tsx12
-rw-r--r--sw-ui/src/components/game-browser/PlayerInfo.tsx27
7 files changed, 203 insertions, 0 deletions
diff --git a/sw-ui/src/components/game-browser/GameBrowser.tsx b/sw-ui/src/components/game-browser/GameBrowser.tsx
new file mode 100644
index 00000000..a6367d5e
--- /dev/null
+++ b/sw-ui/src/components/game-browser/GameBrowser.tsx
@@ -0,0 +1,56 @@
+import { Button, Classes, InputGroup, Intent } from '@blueprintjs/core';
+import React, { ChangeEvent, Component, SyntheticEvent } from 'react';
+import { connect } from 'react-redux';
+import { Flex } from 'reflexbox';
+import { actions } from '../../redux/actions/lobby';
+import { GameList } from './GameList';
+import { PlayerInfo } from './PlayerInfo';
+
+type GameBrowserProps = {
+ createGame: (gameName: string) => void,
+}
+
+class GameBrowserPresenter extends Component<GameBrowserProps> {
+
+ _gameName: string | void = undefined;
+
+ createGame = (e: SyntheticEvent<any>): void => {
+ e.preventDefault();
+ if (this._gameName !== undefined) {
+ this.props.createGame(this._gameName);
+ }
+ };
+
+ render() {
+ return (
+ <div>
+ <Flex align="center" justify='space-between' p={1}>
+ <form onSubmit={this.createGame}>
+ <InputGroup
+ placeholder="Game name"
+ name="game_name"
+ onChange={(e: ChangeEvent<HTMLInputElement>) => (this._gameName = e.target.value)}
+ rightElement={<CreateGameButton createGame={this.createGame}/>}
+ />
+ </form>
+ <PlayerInfo />
+ </Flex>
+ <GameList />
+ </div>
+ );
+ }
+}
+
+type CreateGameButtonProps = {
+ createGame: (e: SyntheticEvent<any>) => void
+}
+
+const CreateGameButton = ({createGame}: CreateGameButtonProps) => (
+ <Button className={Classes.MINIMAL} intent={Intent.PRIMARY} icon='add' onClick={createGame} />
+);
+
+const mapDispatchToProps = {
+ createGame: actions.requestCreateGame,
+};
+
+export const GameBrowser = connect(null, mapDispatchToProps)(GameBrowserPresenter);
diff --git a/sw-ui/src/components/game-browser/GameList.css b/sw-ui/src/components/game-browser/GameList.css
new file mode 100644
index 00000000..a04e126c
--- /dev/null
+++ b/sw-ui/src/components/game-browser/GameList.css
@@ -0,0 +1,3 @@
+tr.gameListRow td {
+ vertical-align: middle;
+}
diff --git a/sw-ui/src/components/game-browser/GameList.tsx b/sw-ui/src/components/game-browser/GameList.tsx
new file mode 100644
index 00000000..1b136940
--- /dev/null
+++ b/sw-ui/src/components/game-browser/GameList.tsx
@@ -0,0 +1,85 @@
+import { Button, Classes } from '@blueprintjs/core'
+import { List } from 'immutable';
+import React from 'react';
+import { connect } from 'react-redux';
+import { ApiLobby } from '../../api/model';
+import { GlobalState } from '../../reducers';
+import { actions } from '../../redux/actions/lobby';
+import { getAllGames } from '../../redux/games';
+import './GameList.css';
+import { GameStatus } from './GameStatus';
+import { PlayerCount } from './PlayerCount';
+
+type GameListStateProps = {
+ games: List<ApiLobby>,
+};
+
+type GameListDispatchProps = {
+ joinGame: (gameId: number) => void,
+};
+
+type GameListProps = GameListStateProps & GameListDispatchProps
+
+const GameListPresenter = ({ games, joinGame }: GameListProps) => (
+ <table className={Classes.HTML_TABLE}>
+ <thead>
+ <GameListHeaderRow />
+ </thead>
+ <tbody>
+ {games.map((game: ApiLobby) => <GameListItemRow key={game.id} game={game} joinGame={joinGame}/>)}
+ </tbody>
+ </table>
+);
+
+const GameListHeaderRow = () => (
+ <tr>
+ <th>Name</th>
+ <th>Status</th>
+ <th>Nb Players</th>
+ <th>Join</th>
+ </tr>
+);
+
+type GameListItemRowProps = {
+ game: ApiLobby,
+ joinGame: (gameId: number) => void,
+};
+
+const GameListItemRow = ({game, joinGame}: GameListItemRowProps) => (
+ <tr className="gameListRow">
+ <td>{game.name}</td>
+ <td>
+ <GameStatus state={game.state} />
+ </td>
+ <td>
+ <PlayerCount nbPlayers={game.players.length} />
+ </td>
+ <td>
+ <JoinButton game={game} joinGame={joinGame}/>
+ </td>
+ </tr>
+);
+
+type JoinButtonProps = {
+ game: ApiLobby,
+ joinGame: (gameId: number) => void,
+};
+
+const JoinButton = ({game, joinGame}: JoinButtonProps) => {
+ const disabled = game.state !== 'LOBBY';
+ const onClick = () => joinGame(game.id);
+ return <Button minimal disabled={disabled} icon='arrow-right' title='Join Game' onClick={onClick}/>;
+};
+
+function mapStateToProps(state: GlobalState): GameListStateProps {
+ return {
+ games: getAllGames(state),
+ };
+}
+
+const mapDispatchToProps: GameListDispatchProps = {
+ joinGame: actions.requestJoinGame,
+};
+
+export const GameList = connect(mapStateToProps, mapDispatchToProps)(GameListPresenter);
+
diff --git a/sw-ui/src/components/game-browser/GameStatus.tsx b/sw-ui/src/components/game-browser/GameStatus.tsx
new file mode 100644
index 00000000..5f237258
--- /dev/null
+++ b/sw-ui/src/components/game-browser/GameStatus.tsx
@@ -0,0 +1,17 @@
+import { Tag } from '@blueprintjs/core';
+import { Intent } from '@blueprintjs/core';
+import * as React from 'react';
+import { ApiGameState } from '../../api/model';
+
+type GameStatusProps = {
+ state: ApiGameState,
+}
+
+export const GameStatus = ({state}: GameStatusProps) => (
+ <Tag minimal intent={statusIntents[state]}>{state}</Tag>
+);
+
+const statusIntents = {
+ 'LOBBY': Intent.SUCCESS,
+ 'PLAYING': Intent.WARNING,
+};
diff --git a/sw-ui/src/components/game-browser/PlayerCount.css b/sw-ui/src/components/game-browser/PlayerCount.css
new file mode 100644
index 00000000..d2f18e50
--- /dev/null
+++ b/sw-ui/src/components/game-browser/PlayerCount.css
@@ -0,0 +1,3 @@
+.playerCountIcon, .playerCount {
+ vertical-align: middle;
+}
diff --git a/sw-ui/src/components/game-browser/PlayerCount.tsx b/sw-ui/src/components/game-browser/PlayerCount.tsx
new file mode 100644
index 00000000..64028f68
--- /dev/null
+++ b/sw-ui/src/components/game-browser/PlayerCount.tsx
@@ -0,0 +1,12 @@
+import { Icon } from '@blueprintjs/core';
+import * as React from 'react';
+import './PlayerCount.css';
+
+type PlayerCountProps = {
+ nbPlayers: number,
+}
+
+export const PlayerCount = ({nbPlayers}: PlayerCountProps) => <div title='Number of players'>
+ <Icon className="playerCountIcon" icon="people" title={false} />
+ <span className="playerCount"> {nbPlayers}</span>
+</div>;
diff --git a/sw-ui/src/components/game-browser/PlayerInfo.tsx b/sw-ui/src/components/game-browser/PlayerInfo.tsx
new file mode 100644
index 00000000..4afed671
--- /dev/null
+++ b/sw-ui/src/components/game-browser/PlayerInfo.tsx
@@ -0,0 +1,27 @@
+import { Text } from '@blueprintjs/core';
+import React from 'react';
+import { connect } from 'react-redux';
+import { GlobalState } from '../../reducers';
+import { User } from '../../redux/user';
+import { getCurrentUser } from '../../redux/user';
+
+type PlayerInfoProps = {
+ user: User | null,
+}
+
+const PlayerInfoPresenter = ({user}: PlayerInfoProps) => (
+ <Text>
+ <b>Username:</b>
+ {' '}
+ {user && user.displayName}
+ </Text>
+);
+
+const mapStateToProps = (state: GlobalState): PlayerInfoProps => ({
+ user: getCurrentUser(state),
+});
+
+const mapDispatchToProps = {
+};
+
+export const PlayerInfo = connect(mapStateToProps, mapDispatchToProps)(PlayerInfoPresenter);
bgstack15