summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJoffrey Bion <joffrey.bion@gmail.com>2016-12-20 19:39:09 +0100
committerGitHub <noreply@github.com>2016-12-20 19:39:09 +0100
commita68ca8acf77ef6c629f02eeaad4c8de3dff5321a (patch)
treecbc81fe22f3b695e10007717bfbe9a570e93335b /src
parentImprove test page for custom subscriptions and msg testing (diff)
parentFix create game with undefined value (diff)
downloadseven-wonders-a68ca8acf77ef6c629f02eeaad4c8de3dff5321a.tar.gz
seven-wonders-a68ca8acf77ef6c629f02eeaad4c8de3dff5321a.tar.bz2
seven-wonders-a68ca8acf77ef6c629f02eeaad4c8de3dff5321a.zip
Merge pull request #1 from luxons/feature/websockets-sagas
Feature/websockets sagas
Diffstat (limited to 'src')
-rw-r--r--src/main/js/package.json8
-rw-r--r--src/main/js/src/components/modals/username.js40
-rw-r--r--src/main/js/src/containers/App/actions.js5
-rw-r--r--src/main/js/src/containers/App/constants.js1
-rw-r--r--src/main/js/src/containers/App/index.js92
-rw-r--r--src/main/js/src/containers/App/saga.js28
-rw-r--r--src/main/js/src/containers/GameBrowser/actions.js16
-rw-r--r--src/main/js/src/containers/GameBrowser/constants.js3
-rw-r--r--src/main/js/src/containers/GameBrowser/index.js33
-rw-r--r--src/main/js/src/containers/GameBrowser/reducer.js16
-rw-r--r--src/main/js/src/containers/GameBrowser/saga.js70
-rw-r--r--src/main/js/src/reducers.js7
-rw-r--r--src/main/js/src/sagas.js24
-rw-r--r--src/main/js/src/utils/createWebSocketConnection.js14
-rw-r--r--src/main/js/yarn.lock21
15 files changed, 361 insertions, 17 deletions
diff --git a/src/main/js/package.json b/src/main/js/package.json
index 75b33982..b3ad0282 100644
--- a/src/main/js/package.json
+++ b/src/main/js/package.json
@@ -12,9 +12,12 @@
"react-redux": "^5.0.1",
"react-router": "4.0.0-alpha.6",
"rebass": "^0.3.3",
+ "reflexbox": "^2.2.3",
"redux": "^3.6.0",
"redux-saga": "^0.13.0",
- "reflexbox": "^2.2.3"
+ "sockjs-client": "latest",
+ "webstomp-client": "^1.0.3",
+ "redux-saga": "^0.13.0"
},
"scripts": {
"start": "react-scripts start",
@@ -24,5 +27,6 @@
},
"eslintConfig": {
"extends": "react-app"
- }
+ },
+ "proxy": "ws://localhost:8080"
}
diff --git a/src/main/js/src/components/modals/username.js b/src/main/js/src/components/modals/username.js
new file mode 100644
index 00000000..61b52114
--- /dev/null
+++ b/src/main/js/src/components/modals/username.js
@@ -0,0 +1,40 @@
+import React from 'react'
+import {
+ Overlay,
+ Panel,
+ PanelHeader,
+ PanelFooter,
+ Button,
+ Input,
+ Close,
+ Space
+} from 'rebass'
+
+const Modal = ({ modalOpen, toggleModal }) => (
+ <Overlay open={modalOpen} onDismiss={toggleModal('usernameModal')}>
+ <Panel theme="info">
+ <PanelHeader>
+ What's your username ?
+ <Space auto />
+ <Close onClick={toggleModal('usernameModal')} />
+ </PanelHeader>
+ <Input
+ label="Username"
+ name="username"
+ placeholder="Cesar92"
+ rounded
+ type="text"
+ />
+ <PanelFooter>
+ <Space auto />
+ <Button
+ theme="success"
+ onClick={toggleModal('usernameModal')}
+ children="Ok"
+ />
+ </PanelFooter>
+ </Panel>
+ </Overlay>
+)
+
+export default Modal
diff --git a/src/main/js/src/containers/App/actions.js b/src/main/js/src/containers/App/actions.js
new file mode 100644
index 00000000..cfb617d5
--- /dev/null
+++ b/src/main/js/src/containers/App/actions.js
@@ -0,0 +1,5 @@
+import { INITIALIZE_WS } from "./constants"
+
+export const initializeWs = () => ({
+ type: INITIALIZE_WS
+})
diff --git a/src/main/js/src/containers/App/constants.js b/src/main/js/src/containers/App/constants.js
new file mode 100644
index 00000000..be31f8cc
--- /dev/null
+++ b/src/main/js/src/containers/App/constants.js
@@ -0,0 +1 @@
+export const INITIALIZE_WS = 'app/INITIALIZE_WS'
diff --git a/src/main/js/src/containers/App/index.js b/src/main/js/src/containers/App/index.js
index e7eef332..70f99b6b 100644
--- a/src/main/js/src/containers/App/index.js
+++ b/src/main/js/src/containers/App/index.js
@@ -1,9 +1,83 @@
-import React from 'react'
-import { Link } from 'react-router'
-
-export default () => <div>
- <h1>Hello World</h1>
- <Link to="/counter">Go to counter</Link>
- <br></br>
- <Link to="/404">Go to 404</Link>
- </div> \ No newline at end of file
+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/src/main/js/src/containers/App/saga.js b/src/main/js/src/containers/App/saga.js
new file mode 100644
index 00000000..f1f72dfd
--- /dev/null
+++ b/src/main/js/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))
+
+ 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/src/main/js/src/containers/GameBrowser/actions.js b/src/main/js/src/containers/GameBrowser/actions.js
new file mode 100644
index 00000000..376973b4
--- /dev/null
+++ b/src/main/js/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/src/main/js/src/containers/GameBrowser/constants.js b/src/main/js/src/containers/GameBrowser/constants.js
new file mode 100644
index 00000000..36f701b7
--- /dev/null
+++ b/src/main/js/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/src/main/js/src/containers/GameBrowser/index.js b/src/main/js/src/containers/GameBrowser/index.js
new file mode 100644
index 00000000..f9b3b29c
--- /dev/null
+++ b/src/main/js/src/containers/GameBrowser/index.js
@@ -0,0 +1,33 @@
+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 Object.keys(games).map(key => {
+ const game = games[key]
+ console.log('game', game, key)
+ return (<Flex key={key}>
+ <Text>{game.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/src/main/js/src/containers/GameBrowser/reducer.js b/src/main/js/src/containers/GameBrowser/reducer.js
new file mode 100644
index 00000000..5f98bfad
--- /dev/null
+++ b/src/main/js/src/containers/GameBrowser/reducer.js
@@ -0,0 +1,16 @@
+import { NEW_GAME } from './constants'
+
+const initialState = {
+}
+
+export default function reducer(state = initialState, action) {
+ switch (action.type) {
+ case NEW_GAME:
+ return {
+ ...state,
+ [action.game.id]: action.game
+ }
+ default:
+ return state
+ }
+}
diff --git a/src/main/js/src/containers/GameBrowser/saga.js b/src/main/js/src/containers/GameBrowser/saga.js
new file mode 100644
index 00000000..8f947683
--- /dev/null
+++ b/src/main/js/src/containers/GameBrowser/saga.js
@@ -0,0 +1,70 @@
+import { call, put, take } from 'redux-saga/effects'
+import { eventChannel } from 'redux-saga'
+
+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 = 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
+ console.log(socket)
+ socket.send("/app/lobby/create-game", JSON.stringify({
+ 'gameName': name,
+ 'playerName': 'Cesar92'
+ }), {})
+}
+
+export function* gameBrowserSaga(socketConnection) {
+ yield [
+ call(watchGames, socketConnection),
+ call(createGame, socketConnection)
+ ]
+}
+
+export default gameBrowserSaga
diff --git a/src/main/js/src/reducers.js b/src/main/js/src/reducers.js
index 79014615..4bfaf7c1 100644
--- a/src/main/js/src/reducers.js
+++ b/src/main/js/src/reducers.js
@@ -1,8 +1,11 @@
import { combineReducers } from 'redux'
import counterReducer from './containers/Counter/reducer'
+import gamesReducer from './containers/GameBrowser/reducer'
+
export default function createReducer() {
return combineReducers({
- counter: counterReducer
+ counter: counterReducer,
+ games: gamesReducer,
})
-} \ No newline at end of file
+}
diff --git a/src/main/js/src/sagas.js b/src/main/js/src/sagas.js
index 24776bf2..92235434 100644
--- a/src/main/js/src/sagas.js
+++ b/src/main/js/src/sagas.js
@@ -1,7 +1,27 @@
-import { fork } from 'redux-saga/effects'
+import { fork, call } from 'redux-saga/effects'
+
+import createWsConnection from './utils/createWebSocketConnection'
import counterSaga from './containers/Counter/saga'
+import errorSaga from './containers/App/saga'
+import newGamesSaga from './containers/GameBrowser/saga'
+
+function* wsAwareSagas() {
+ let wsConnection
+ try {
+ wsConnection = yield call(createWsConnection)
+ } catch (error) {
+ console.error('Could not connect to socket')
+ return
+ }
+
+ yield fork(errorSaga, wsConnection)
+ yield fork(newGamesSaga, wsConnection)
+}
export default function* rootSaga() {
- yield fork(counterSaga)
+ yield [
+ call(counterSaga),
+ call(wsAwareSagas)
+ ]
}
diff --git a/src/main/js/src/utils/createWebSocketConnection.js b/src/main/js/src/utils/createWebSocketConnection.js
new file mode 100644
index 00000000..b0924976
--- /dev/null
+++ b/src/main/js/src/utils/createWebSocketConnection.js
@@ -0,0 +1,14 @@
+import SockJS from 'sockjs-client'
+import Stomp from 'webstomp-client'
+const wsURL = 'http://localhost:8080/seven-wonders-websocket'
+
+const createConnection = (headers = {}) => new Promise((resolve, reject) => {
+ let socket = Stomp.over(new SockJS(wsURL), {
+ debug: process.env.NODE_ENV !== "production"
+ })
+ socket.connect(headers, (frame) => {
+ return resolve({ frame, socket })
+ }, reject)
+})
+
+export default createConnection
diff --git a/src/main/js/yarn.lock b/src/main/js/yarn.lock
index 486bb724..5eeb79b4 100644
--- a/src/main/js/yarn.lock
+++ b/src/main/js/yarn.lock
@@ -1944,7 +1944,7 @@ events@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924"
-eventsource@^0.1.3:
+eventsource@^0.1.3, eventsource@~0.1.6:
version "0.1.6"
resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-0.1.6.tgz#0acede849ed7dd1ccc32c811bb11b944d4f29232"
dependencies:
@@ -2039,6 +2039,12 @@ faye-websocket@^0.10.0:
dependencies:
websocket-driver ">=0.5.1"
+faye-websocket@~0.11.0:
+ version "0.11.0"
+ resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.0.tgz#d9ccf0e789e7db725d74bc4877d23aa42972ac50"
+ dependencies:
+ websocket-driver ">=0.5.1"
+
faye-websocket@~0.7.3:
version "0.7.3"
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.7.3.tgz#cc4074c7f4a4dfd03af54dd65c354b135132ce11"
@@ -4711,6 +4717,17 @@ sockjs-client@^1.0.3, sockjs-client@1.0.3:
json3 "^3.3.2"
url-parse "^1.0.1"
+sockjs-client@latest:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.1.tgz#284843e9a9784d7c474b1571b3240fca9dda4bb0"
+ dependencies:
+ debug "^2.2.0"
+ eventsource "~0.1.6"
+ faye-websocket "~0.11.0"
+ inherits "^2.0.1"
+ json3 "^3.3.2"
+ url-parse "^1.1.1"
+
sockjs@^0.3.15:
version "0.3.18"
resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.18.tgz#d9b289316ca7df77595ef299e075f0f937eb4207"
@@ -5078,7 +5095,7 @@ url-loader@0.5.7:
loader-utils "0.2.x"
mime "1.2.x"
-url-parse@^1.0.1:
+url-parse@^1.0.1, url-parse@^1.1.1:
version "1.1.7"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.1.7.tgz#025cff999653a459ab34232147d89514cc87d74a"
dependencies:
bgstack15