summaryrefslogtreecommitdiff
path: root/frontend
diff options
context:
space:
mode:
Diffstat (limited to 'frontend')
-rw-r--r--frontend/src/api/model.js16
-rw-r--r--frontend/src/components/game/GameScene.css13
-rw-r--r--frontend/src/components/game/GameScene.jsx38
-rw-r--r--frontend/src/components/game/Hand.css26
-rw-r--r--frontend/src/components/game/Hand.jsx34
-rw-r--r--frontend/src/models/currentGame.js3
-rw-r--r--frontend/src/reducers.js4
-rw-r--r--frontend/src/redux/currentGame.js55
8 files changed, 147 insertions, 42 deletions
diff --git a/frontend/src/api/model.js b/frontend/src/api/model.js
index 3408d5a2..f5c3481b 100644
--- a/frontend/src/api/model.js
+++ b/frontend/src/api/model.js
@@ -47,9 +47,21 @@ export type ApiTable = {
export type ApiAction = {};
-export type ApiHandCard = {};
+export type ApiCard = {
+ name: string,
+ image: string
+};
+
+export type ApiHandCard = ApiCard & {
+ playability: ApiPlayability
+};
+
+export type ApiPlayability = {
+ playable: boolean
+};
-export type ApiTableCard = {};
+export type ApiTableCard = ApiCard & {
+};
export type ApiCardBack = {
image: string
diff --git a/frontend/src/components/game/GameScene.css b/frontend/src/components/game/GameScene.css
new file mode 100644
index 00000000..525e68fa
--- /dev/null
+++ b/frontend/src/components/game/GameScene.css
@@ -0,0 +1,13 @@
+.gameSceneRoot {
+ background: url('background-papyrus.jpg') center no-repeat;
+ background-size: cover;
+}
+
+.fullscreen {
+ position: fixed;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+ overflow: auto;
+}
diff --git a/frontend/src/components/game/GameScene.jsx b/frontend/src/components/game/GameScene.jsx
index fb5763db..cf3c91f8 100644
--- a/frontend/src/components/game/GameScene.jsx
+++ b/frontend/src/components/game/GameScene.jsx
@@ -2,7 +2,7 @@ import { Button, Classes, Intent } from '@blueprintjs/core';
import { List } from 'immutable';
import React, { Component } from 'react';
import { connect } from 'react-redux';
-import type { ApiPlayerTurnInfo } from '../../api/model';
+import type { ApiHandCard, ApiPlayerTurnInfo } from '../../api/model';
import { Game } from '../../models/games';
import { Player } from '../../models/players';
import { actions } from '../../redux/actions/game';
@@ -10,7 +10,9 @@ import { getCurrentTurnInfo } from '../../redux/currentGame';
import { getCurrentGame } from '../../redux/games';
import { getCurrentPlayer, getPlayers } from '../../redux/players';
-import { PlayerList } from '../lobby/PlayerList';
+import { Hand } from './Hand';
+
+import './GameScene.css'
type GameSceneProps = {
game: Game,
@@ -20,21 +22,31 @@ type GameSceneProps = {
sayReady: () => void
}
-class GameScenePresenter extends Component<GameSceneProps> {
- getTitle() {
- if (this.props.game) {
- return this.props.game.name + ' — Game';
- } else {
- return 'What are you doing here? You haven\'t joined a game yet!';
- }
+type GameSceneState = {
+ selectedCard: ApiHandCard | void
+}
+
+class GameScenePresenter extends Component<GameSceneProps, GameSceneState> {
+
+ state = {
+ selectedCard: null
+ };
+
+ selectCard(c: ApiHandCard) {
+ this.setState({selectedCard: c})
}
render() {
return (
- <div>
- <h2>{this.getTitle()}</h2>
- <PlayerList players={this.props.players} currentPlayer={this.props.currentPlayer} owner={this.props.game.owner}/>
- <Button text="READY" className={Classes.LARGE} intent={Intent.PRIMARY} icon='play' onClick={this.props.sayReady} />
+ <div className='gameSceneRoot fullscreen'>
+ <h2>Now playing!</h2>
+ <p>{this.props.turnInfo ? this.props.turnInfo.message : 'Click "ready" when you are'}</p>
+
+ {this.props.turnInfo && <Hand cards={this.props.turnInfo.hand}
+ selectedCard={this.state.selectedCard}
+ onClick={(c) => this.selectCard(c)}/>}
+
+ {!this.props.turnInfo && <Button text="READY" className={Classes.LARGE} intent={Intent.PRIMARY} icon='play' onClick={this.props.sayReady} />}
<h3>Turn Info</h3>
<div>
diff --git a/frontend/src/components/game/Hand.css b/frontend/src/components/game/Hand.css
new file mode 100644
index 00000000..de7b556d
--- /dev/null
+++ b/frontend/src/components/game/Hand.css
@@ -0,0 +1,26 @@
+.hand {
+ display: flex;
+}
+
+.hand-card {
+ margin: 0.2rem;
+}
+
+.hand-card-unplayable {
+ opacity: 0.7;
+}
+
+.hand-card-img-selected {
+ border: 2px solid #0054ff;
+}
+
+.hand-card-img {
+ border-radius: 0.5rem;
+ box-shadow: 0 0 10px black;
+ width: 11rem;
+}
+
+.hand-card-img:hover {
+ box-shadow: 0 10px 40px black;
+ width: 14rem;
+}
diff --git a/frontend/src/components/game/Hand.jsx b/frontend/src/components/game/Hand.jsx
new file mode 100644
index 00000000..7dea23de
--- /dev/null
+++ b/frontend/src/components/game/Hand.jsx
@@ -0,0 +1,34 @@
+import React from 'react';
+import type { ApiHandCard } from '../../api/model';
+import './Hand.css'
+
+type HandCardProps = {
+ card: ApiHandCard,
+ isSelected: boolean,
+ onClick: () => void
+}
+
+const HandCard = ({card, isSelected, onClick}: HandCardProps) => {
+ let playableClass = card.playability.playable ? '' : 'hand-card-unplayable';
+ let selectedClass = isSelected ? 'hand-card-img-selected' : '';
+ return <div className={`hand-card ${playableClass}`}
+ onClick={() => card.playability.playable && onClick()}
+ aria-disabled={!card.playability.playable}>
+ <img src={`/images/cards/${card.image}`}
+ title={card.name}
+ alt={'Card ' + card.name}
+ className={`hand-card-img ${selectedClass}`}/>
+ </div>
+};
+
+type HandProps = {
+ cards: ApiHandCard[],
+ selectedCard: ApiHandCard,
+ onClick: (card: ApiHandCard) => void
+}
+
+export const Hand = ({cards, selectedCard, onClick}: HandProps) => {
+ return <div className='hand'>{cards.map((c, i) => <HandCard key={i} card={c}
+ isSelected={selectedCard === c}
+ onClick={() => onClick(c)}/>)}</div>;
+}
diff --git a/frontend/src/models/currentGame.js b/frontend/src/models/currentGame.js
index fcabe875..6ff00858 100644
--- a/frontend/src/models/currentGame.js
+++ b/frontend/src/models/currentGame.js
@@ -1,6 +1,7 @@
+import { List } from 'immutable';
import type { ApiPlayerTurnInfo } from '../api/model';
export class CurrentGameState {
- playersReadiness: Map<string, boolean> = new Map();
+ readyUsernames: List<string> = new List();
turnInfo: ApiPlayerTurnInfo | null = null
}
diff --git a/frontend/src/reducers.js b/frontend/src/reducers.js
index f5e7b18d..5e066d28 100644
--- a/frontend/src/reducers.js
+++ b/frontend/src/reducers.js
@@ -1,13 +1,13 @@
// @flow
import { routerReducer } from 'react-router-redux';
import { combineReducers } from 'redux-immutable';
-import { currentGameReducer } from './redux/currentGame';
+import { createCurrentGameReducer } from './redux/currentGame';
import { gamesReducer } from './redux/games';
import { playersReducer } from './redux/players';
export function createReducer() {
return combineReducers({
- currentGame: currentGameReducer,
+ currentGame: createCurrentGameReducer(),
games: gamesReducer,
players: playersReducer,
routing: routerReducer,
diff --git a/frontend/src/redux/currentGame.js b/frontend/src/redux/currentGame.js
index cefabb6f..f4174eca 100644
--- a/frontend/src/redux/currentGame.js
+++ b/frontend/src/redux/currentGame.js
@@ -1,32 +1,39 @@
// @flow
-import type { ApiPlayerTurnInfo } from '../api/model';
+import { List } from 'immutable';
+import { combineReducers } from 'redux';
+import type { ApiPlayerTurnInfo, ApiTable } from '../api/model';
import { CurrentGameState } from '../models/currentGame';
import type { Action } from './actions/all';
import { types } from './actions/game';
-export const currentGameReducer = (state: CurrentGameState = new CurrentGameState(), action: Action) => {
- switch (action.type) {
- case types.REQUEST_SAY_READY:
- // TODO handle end of feedback between say ready and ready event received
- return state;
- case types.PLAYER_READY_RECEIVED:
- // const newReadiness = state.playersReadiness.set(action.username, true);
- // return { playersReadiness: newReadiness, ...state };
- return state;
- case types.TABLE_UPDATE_RECEIVED:
- // TODO
- return state;
- case types.PREPARED_CARD_RECEIVED:
- // TODO
- return state;
- case types.TURN_INFO_RECEIVED:
- // TODO find a better way to just update what's needed
- const newState = new CurrentGameState();
- newState.turnInfo = action.turnInfo;
- newState.playersReadiness = state.playersReadiness;
- return newState;
- default:
- return state;
+export function createCurrentGameReducer() {
+ return combineReducers({
+ readyUsernames: readyUsernamesReducer,
+ turnInfo: turnInfoReducer,
+ });
+}
+
+const readyUsernamesReducer = (state: List<string> = new List(), action: Action) => {
+ if (action.type === types.PLAYER_READY_RECEIVED) {
+ return state.push(action.username);
+ } else {
+ return state;
+ }
+};
+
+const turnInfoReducer = (state: ApiPlayerTurnInfo | null = null, action: Action) => {
+ if (action.type === types.TURN_INFO_RECEIVED) {
+ return action.turnInfo;
+ } else {
+ return state;
+ }
+};
+
+const tableUpdatesReducer = (state: ApiTable, action: Action) => {
+ if (action.type === types.TABLE_UPDATE_RECEIVED) {
+ return action.table;
+ } else {
+ return state;
}
};
bgstack15