diff options
Diffstat (limited to 'frontend/src/containers')
-rw-r--r-- | frontend/src/containers/App/actions.js | 5 | ||||
-rw-r--r-- | frontend/src/containers/App/constants.js | 1 | ||||
-rw-r--r-- | frontend/src/containers/App/index.js | 83 | ||||
-rw-r--r-- | frontend/src/containers/App/saga.js | 28 | ||||
-rw-r--r-- | frontend/src/containers/GameBrowser/actions.js | 16 | ||||
-rw-r--r-- | frontend/src/containers/GameBrowser/constants.js | 3 | ||||
-rw-r--r-- | frontend/src/containers/GameBrowser/index.js | 31 | ||||
-rw-r--r-- | frontend/src/containers/GameBrowser/reducer.js | 13 | ||||
-rw-r--r-- | frontend/src/containers/GameBrowser/saga.js | 75 | ||||
-rw-r--r-- | frontend/src/containers/HomePage/actions.js | 6 | ||||
-rw-r--r-- | frontend/src/containers/HomePage/index.js | 39 | ||||
-rw-r--r-- | frontend/src/containers/HomePage/saga.js | 57 | ||||
-rw-r--r-- | frontend/src/containers/UserRepo/actions.js | 8 | ||||
-rw-r--r-- | frontend/src/containers/UserRepo/reducer.js | 18 |
14 files changed, 383 insertions, 0 deletions
diff --git a/frontend/src/containers/App/actions.js b/frontend/src/containers/App/actions.js new file mode 100644 index 00000000..cfb617d5 --- /dev/null +++ b/frontend/src/containers/App/actions.js @@ -0,0 +1,5 @@ +import { INITIALIZE_WS } from "./constants" + +export const initializeWs = () => ({ + type: INITIALIZE_WS +}) diff --git a/frontend/src/containers/App/constants.js b/frontend/src/containers/App/constants.js new file mode 100644 index 00000000..be31f8cc --- /dev/null +++ b/frontend/src/containers/App/constants.js @@ -0,0 +1 @@ +export const INITIALIZE_WS = 'app/INITIALIZE_WS' diff --git a/frontend/src/containers/App/index.js b/frontend/src/containers/App/index.js new file mode 100644 index 00000000..70f99b6b --- /dev/null +++ b/frontend/src/containers/App/index.js @@ -0,0 +1,83 @@ +import React, { Component } from 'react' +import { connect } from 'react-redux' +import { + Banner, + Heading, + Space, + Button, + InlineForm, + Text +} from 'rebass' +import { Flex } from 'reflexbox' +import Modal from '../../components/modals/username' +import GameBrowser from '../GameBrowser' + +class App extends Component { + state = { + usernameModal: false, + } + + componentDidMount() { + + } + + toggleModal = (key) => { + return (e) => { + const val = !this.state[key] + this.setState({ [key]: val }) + } + } + + createGame = (e) => { + e.preventDefault() + if (this._gameName !== undefined) { + this.props.createGame(this._gameName) + } + } + + render() { + return ( + <div> + <Banner + align="center" + style={{minHeight: '30vh'}} + backgroundImage="https://images.unsplash.com/photo-1431207446535-a9296cf995b1?dpr=1&auto=format&fit=crop&w=1199&h=799&q=80&cs=tinysrgb&crop=" + > + <Heading level={1}>Seven Wonders</Heading> + </Banner> + <Flex align="center" p={1}> + <InlineForm + buttonLabel="Create Game" + label="Game name" + name="game_name" + onChange={(e) => this._gameName = e.target.value} + onClick={this.createGame} + > + + </InlineForm> + <Space auto /> + <Text><b>Username:</b> Cesar92</Text> + <Space x={1} /> + <Button + onClick={this.toggleModal('usernameModal')} + children="Change"/> + </Flex> + <GameBrowser /> + <Modal toggleModal={this.toggleModal} modalOpen={this.state.usernameModal} /> + </div> + ) + } +} + +const mapStateToProps = (state) => ({ + +}) + +import { initializeWs } from "./actions"; +import { createGame } from '../GameBrowser/actions' +const mapDispatchToProps = { + initializeWs, + createGame +} + +export default connect(mapStateToProps, mapDispatchToProps)(App) diff --git a/frontend/src/containers/App/saga.js b/frontend/src/containers/App/saga.js new file mode 100644 index 00000000..0c212142 --- /dev/null +++ b/frontend/src/containers/App/saga.js @@ -0,0 +1,28 @@ +import { put, take } from 'redux-saga/effects' +import { eventChannel } from 'redux-saga' + +function createSocketChannel(socket) { + return eventChannel(emit => { + const errorHandler = event => emit(JSON.parse(event.body)) + + const userErrors = socket.subscribe('/user/queue/errors', errorHandler) + + const unsubscribe = () => { + userErrors.unsubscribe() + } + + return unsubscribe + }) +} + +export function* watchOnErrors(socketConnection) { + const { socket } = socketConnection + const socketChannel = createSocketChannel(socket) + + while (true) { + const payload = yield take(socketChannel) + yield put({ type: 'USER_ERROR', payload }) + } +} + +export default watchOnErrors diff --git a/frontend/src/containers/GameBrowser/actions.js b/frontend/src/containers/GameBrowser/actions.js new file mode 100644 index 00000000..376973b4 --- /dev/null +++ b/frontend/src/containers/GameBrowser/actions.js @@ -0,0 +1,16 @@ +import { NEW_GAME, JOIN_GAME, CREATE_GAME } from './constants' + +export const newGame = (game) => ({ + type: NEW_GAME, + game +}) + +export const joinGame = (id) => ({ + type: JOIN_GAME, + id +}) + +export const createGame = (name) => ({ + type: CREATE_GAME, + name +}) diff --git a/frontend/src/containers/GameBrowser/constants.js b/frontend/src/containers/GameBrowser/constants.js new file mode 100644 index 00000000..36f701b7 --- /dev/null +++ b/frontend/src/containers/GameBrowser/constants.js @@ -0,0 +1,3 @@ +export const NEW_GAME = 'gameBrowser/NEW_GAME' +export const JOIN_GAME = 'gameBrowser/JOIN_GAME' +export const CREATE_GAME = 'gameBrowser/CREATE_GAME' diff --git a/frontend/src/containers/GameBrowser/index.js b/frontend/src/containers/GameBrowser/index.js new file mode 100644 index 00000000..9deb720b --- /dev/null +++ b/frontend/src/containers/GameBrowser/index.js @@ -0,0 +1,31 @@ +import React, { Component } from 'react' +import { connect } from 'react-redux' +import { Flex } from 'reflexbox' +import { Text, Space } from 'rebass' + +class GameBrowser extends Component { + + listGames = (games) => { + return games.valueSeq().map((game, index) => { + return (<Flex key={index}> + <Text>{game.get('name')}</Text> + <Space auto /> + <a href="#">Join</a> + </Flex>) + }) + } + + render() { + return ( + <div> + {this.listGames(this.props.games)} + </div> + ) + } +} + +const mapStateToProps = (state) => ({ + games: state.games +}) + +export default connect(mapStateToProps, {})(GameBrowser) diff --git a/frontend/src/containers/GameBrowser/reducer.js b/frontend/src/containers/GameBrowser/reducer.js new file mode 100644 index 00000000..4fb3390a --- /dev/null +++ b/frontend/src/containers/GameBrowser/reducer.js @@ -0,0 +1,13 @@ +import { Map } from 'immutable' +import { NEW_GAME } from './constants' + +const initialState = Map({}) + +export default function reducer(state = initialState, action) { + switch (action.type) { + case NEW_GAME: + return state.set(action.game.get('id'), action.game) + default: + return state + } +} diff --git a/frontend/src/containers/GameBrowser/saga.js b/frontend/src/containers/GameBrowser/saga.js new file mode 100644 index 00000000..4cd3d207 --- /dev/null +++ b/frontend/src/containers/GameBrowser/saga.js @@ -0,0 +1,75 @@ +import { call, put, take } from 'redux-saga/effects' +import { eventChannel } from 'redux-saga' +import { fromJS } from 'immutable' +import { push } from 'react-router-redux' + +import { NEW_GAME, JOIN_GAME, CREATE_GAME } from './constants' +import { newGame, joinGame } from './actions' + +function createSocketChannel(socket) { + return eventChannel(emit => { + const makeHandler = (type) => (event) => { + const response = fromJS(JSON.parse(event.body)) + + emit({ + type, + response + }) + } + + const newGameHandler = makeHandler(NEW_GAME) + const joinGameHandler = makeHandler(JOIN_GAME) + + const newGame = socket.subscribe('/topic/games', newGameHandler) + const joinGame = socket.subscribe('/user/queue/join-game', joinGameHandler) + + const unsubscribe = () => { + newGame.unsubscribe() + joinGame.unsubscribe() + } + + return unsubscribe + }) +} + +export function* watchGames(socketConnection) { + + const { socket } = socketConnection + const socketChannel = createSocketChannel(socket) + + while (true) { + const { type, response } = yield take(socketChannel) + + switch (type) { + case NEW_GAME: + yield put(newGame(response)) + break; + case JOIN_GAME: + yield put(joinGame(response)) + break; + default: + console.error('Unknown type') + } + } +} + +export function* createGame(socketConnection) { + const { name } = yield take(CREATE_GAME) + const { socket } = socketConnection + + socket.send("/app/lobby/create-game", JSON.stringify({ + 'gameName': name, + 'playerName': 'Cesar92' + }), {}) +} + +export function* gameBrowserSaga(socketConnection) { + yield put(push('/lobby')) + + yield [ + call(watchGames, socketConnection), + call(createGame, socketConnection) + ] +} + +export default gameBrowserSaga diff --git a/frontend/src/containers/HomePage/actions.js b/frontend/src/containers/HomePage/actions.js new file mode 100644 index 00000000..e06d6fa2 --- /dev/null +++ b/frontend/src/containers/HomePage/actions.js @@ -0,0 +1,6 @@ +export const ENTER_GAME = 'homePage/ENTER_GAME' + +export const enterGame = (username) => ({ + type: ENTER_GAME, + username +}) diff --git a/frontend/src/containers/HomePage/index.js b/frontend/src/containers/HomePage/index.js new file mode 100644 index 00000000..c8e33239 --- /dev/null +++ b/frontend/src/containers/HomePage/index.js @@ -0,0 +1,39 @@ +import React, { Component } from 'react' +import { connect } from 'react-redux' +import { Heading, InlineForm } from 'rebass' + +class HomePage extends Component { + + play = (e) => { + e.preventDefault() + if (this._username !== undefined) { + this.props.enterGame(this._username) + } + } + + render() { + return ( + <div> + <Heading>Enter your username to start playing!</Heading> + <InlineForm + buttonLabel="Play now!" + label="Username" + name="username" + onChange={(e) => this._username = e.target.value} + onClick={this.play} + /> + </div> + ) + } +} + +const mapStateToProps = (state) => ({ + +}) + +import { enterGame } from './actions' +const mapDispatchToProps = { + enterGame +} + +export default connect(mapStateToProps, mapDispatchToProps)(HomePage) diff --git a/frontend/src/containers/HomePage/saga.js b/frontend/src/containers/HomePage/saga.js new file mode 100644 index 00000000..0fbe8a45 --- /dev/null +++ b/frontend/src/containers/HomePage/saga.js @@ -0,0 +1,57 @@ +import { call, put, take } from 'redux-saga/effects' +import { eventChannel } from 'redux-saga' +import { ENTER_GAME } from './actions' +import { setUsername } from '../UserRepo/actions' + +import gameBrowserSaga from '../GameBrowser/saga' + +function* sendUsername(socketConnection) { + const { username: playerName } = yield take(ENTER_GAME) + const { socket } = socketConnection + + socket.send("/app/chooseName", JSON.stringify({ + playerName + }), {}) +} + +function createSocketChannel(socket) { + return eventChannel(emit => { + const receiveUsername = socket.subscribe('/user/queue/nameChoice', event => { + emit(JSON.parse(event.body)) + }) + + const unsubscribe = () => { + receiveUsername.unsubscribe() + } + + return unsubscribe + }) +} + +function* validateUsername(socketConnection) { + const { socket } = socketConnection + const socketChannel = createSocketChannel(socket) + + const response = yield take(socketChannel) + + if (response.error) { + return false + } + + yield put(setUsername(response.userName, response.displayName, response.index)) + yield call(gameBrowserSaga, socketConnection) + return true +} + +function* homeSaga(socketConnection) { + let validated = false + do { + const [, usernameValid] = yield [ + call(sendUsername, socketConnection), + call(validateUsername, socketConnection) + ] + validated = usernameValid + } while (!validated) +} + +export default homeSaga diff --git a/frontend/src/containers/UserRepo/actions.js b/frontend/src/containers/UserRepo/actions.js new file mode 100644 index 00000000..dc06035b --- /dev/null +++ b/frontend/src/containers/UserRepo/actions.js @@ -0,0 +1,8 @@ +export const SET_USERNAME = 'homePage/SET_USERNAME' + +export const setUsername = (userName, displayName, index) => ({ + type: SET_USERNAME, + userName, + index, + displayName +}) diff --git a/frontend/src/containers/UserRepo/reducer.js b/frontend/src/containers/UserRepo/reducer.js new file mode 100644 index 00000000..82960a58 --- /dev/null +++ b/frontend/src/containers/UserRepo/reducer.js @@ -0,0 +1,18 @@ +import { SET_USERNAME } from './actions' +import { fromJS } from 'immutable' +const initialState = fromJS({ + username: '', + displayName: '', + id: null +}) + +export default (state = initialState, action) => { + switch (action.type) { + case SET_USERNAME: + return state.set('username', action.userName) + .set('displayName', action.displayName) + .set('id', action.index) + default: + return state + } +} |