Browse Source

Store socket in state, handle it with reducer.

pull/1/head
Titouan Rigoudy 9 years ago
parent
commit
70da089658
12 changed files with 95 additions and 105 deletions
  1. +0
    -1
      TODO.md
  2. +22
    -0
      src/actions/RoomActions.js
  3. +5
    -4
      src/actions/RoomActionsFactory.js
  4. +29
    -0
      src/actions/SocketActions.js
  5. +0
    -40
      src/actions/SocketActionsFactory.js
  6. +1
    -3
      src/actions/SocketHandlerActions.js
  7. +5
    -3
      src/components/ConnectForm.js
  8. +1
    -2
      src/components/SolsticeApp.js
  9. +5
    -10
      src/containers/App.js
  10. +0
    -1
      src/index.js
  11. +27
    -10
      src/reducers/socket.js
  12. +0
    -31
      src/utils/SocketClient.js

+ 0
- 1
TODO.md View File

@ -1,5 +1,4 @@
Things to do:
-------------
- can the socket be stored in a reducer?
- actually join rooms, display and send messages

+ 22
- 0
src/actions/RoomActions.js View File

@ -0,0 +1,22 @@
import {
ROOM_SELECT,
ROOM_JOIN
} from "../constants/ActionTypes";
import SocketActions from "./SocketActions";
import ControlRequest from "../utils/ControlRequest";
export default ({
getRoomList: () => SocketActions.send(ControlRequest.roomList()),
select: (room) => ({
type: ROOM_SELECT,
payload: room
}),
join: (room) => ({
type: ROOM_JOIN,
paylod: room
})
});

+ 5
- 4
src/actions/RoomActionsFactory.js View File

@ -2,12 +2,13 @@ import {
ROOM_SELECT,
ROOM_JOIN
} from "../constants/ActionTypes";
import SocketActions from "./SocketActions";
import ControlRequest from "../utils/ControlRequest";
export default (socketActions) => ({
getRoomList: () => {
return socketActions.send(ControlRequest.roomList());
},
export default ({
getRoomList: () => SocketActions.send(ControlRequest.roomList()),
select: (room) => ({
type: ROOM_SELECT,


+ 29
- 0
src/actions/SocketActions.js View File

@ -0,0 +1,29 @@
import {
SOCKET_RECEIVE_MESSAGE,
SOCKET_SEND_MESSAGE,
SOCKET_SET_CLOSED,
SOCKET_SET_CLOSING,
SOCKET_SET_ERROR,
SOCKET_SET_OPEN,
SOCKET_SET_OPENING
} from "../constants/ActionTypes";
export default ({
open: (url, handlers) => ({
type: SOCKET_SET_OPENING,
payload: {
url,
onclose: handlers.onclose,
onerror: handlers.onerror,
onopen: handlers.onopen,
onmessage: handlers.onmessage
}
}),
close: () => ({ type: SOCKET_SET_CLOSING }),
send: (message) => ({
type: SOCKET_SEND_MESSAGE,
payload: message
})
});

+ 0
- 40
src/actions/SocketActionsFactory.js View File

@ -1,40 +0,0 @@
import {
SOCKET_SET_OPENING, SOCKET_SET_CLOSING, SOCKET_SEND_MESSAGE
} from "../constants/ActionTypes";
export default (socketClient) => ({
open: url => {
const action = { type: SOCKET_SET_OPENING };
try {
socketClient.open(url);
action.payload = url;
} catch (err) {
action.error = true;
action.payload = err;
}
return action;
},
close: () => {
const action = { type: SOCKET_SET_CLOSING };
try {
socketClient.close();
} catch (err) {
action.error = true;
action.payload = err;
}
return action;
},
send: message => {
const action = { type: SOCKET_SEND_MESSAGE };
try {
socketClient.send(JSON.stringify(message));
action.payload = message;
} catch (err) {
action.error = true;
action.payload = err;
}
return action;
}
});

+ 1
- 3
src/actions/SocketHandlerActions.js View File

@ -8,9 +8,7 @@ import {
export default {
onclose: event => ({
type: SOCKET_SET_CLOSED,
payload: {
code: event.code
}
payload: event.code
}),
onerror: event => ({ type: SOCKET_SET_ERROR }),


+ 5
- 3
src/components/ConnectForm.js View File

@ -6,9 +6,11 @@ import { STATE_CLOSED } from "../constants/socket";
import ControlRequest from "../utils/ControlRequest";
const ConnectForm = (props) => {
const { fields: { url }, handleSubmit, socket, socketOpen } = props;
const { fields: { url }, handleSubmit, socket, actions } = props;
const onSubmit = handleSubmit((values) => socketOpen(values.url));
const onSubmit = handleSubmit((values) => {
return actions.socket.open(values.url, actions.socketHandlers);
});
return (
<div id="connect-form">
@ -29,7 +31,7 @@ ConnectForm.propTypes = {
fields: PropTypes.object.isRequired,
handleSubmit: PropTypes.func.isRequired,
socket: PropTypes.object.isRequired,
socketOpen: PropTypes.func.isRequired
actions: PropTypes.object.isRequired
};
export default reduxForm({


+ 1
- 2
src/components/SolsticeApp.js View File

@ -15,8 +15,7 @@ const SolsticeApp = (props) => {
if (socket.state !== STATE_OPEN ) {
return (
<div id={ID}>
<ConnectForm socket={socket}
socketOpen={actions.socket.open}/>
<ConnectForm socket={socket} actions={actions} />
</div>
);
}


+ 5
- 10
src/containers/App.js View File

@ -4,14 +4,12 @@ import React, {PropTypes} from "react";
import { bindActionCreators } from "redux";
import { connect } from "react-redux";
import SocketActionsFactory from "../actions/SocketActionsFactory";
import RoomActions from "../actions/RoomActions";
import SocketActions from "../actions/SocketActions";
import SocketHandlerActions from "../actions/SocketHandlerActions";
import RoomActionsFactory from "../actions/RoomActionsFactory";
import SolsticeApp from "../components/SolsticeApp";
import SocketClient from "../utils/SocketClient";
const App = (props) => (<SolsticeApp {...props} />);
App.propTypes = {
@ -22,14 +20,11 @@ App.propTypes = {
const mapStateToProps = ({ socket }) => ({ socket });
function mapDispatchToProps(dispatch) {
const callbacks = bindActionCreators(SocketHandlerActions, dispatch);
const socketClient = new SocketClient(callbacks);
const socketActions = SocketActionsFactory(socketClient);
const roomActions = RoomActionsFactory(socketActions);
return {
actions: {
room: bindActionCreators(roomActions, dispatch),
socket: bindActionCreators(socketActions, dispatch)
room: bindActionCreators(RoomActions, dispatch),
socket: bindActionCreators(SocketActions, dispatch),
socketHandlers: bindActionCreators(SocketHandlerActions, dispatch)
}
};
}


+ 0
- 1
src/index.js View File

@ -4,7 +4,6 @@ import { Provider } from 'react-redux';
import App from './containers/App';
import configureStore from './store/configureStore';
import './styles/styles.scss'; //Yep, that's right. You can import SASS/CSS files too! Webpack will run the associated loader and plug this into the page.
import SocketClient from "./utils/SocketClient";
const store = configureStore();


+ 27
- 10
src/reducers/socket.js View File

@ -4,33 +4,50 @@ import {
} from "../constants/socket.js";
const initialState = {
state: STATE_CLOSED,
url: null
state: STATE_CLOSED
};
export default (state = initialState, action) => {
switch (action.type) {
case types.SOCKET_SET_OPENING:
if (action.error) {
return state;
{
if (state.state !== STATE_CLOSED) {
console.log("Cannot open socket, already open");
}
return { ...state, state: STATE_OPENING, url: action.payload };
const { url, onopen, onclose, onerror, onmessage } = action.payload;
const socket = new WebSocket(url);
socket.onopen = onopen;
socket.onclose = onclose;
socket.onerror = onerror;
socket.onmessage = onmessage;
return {
...state,
state: STATE_OPENING,
socket,
url
};
}
case types.SOCKET_SET_OPEN:
return { ...state, state: STATE_OPEN };
case types.SOCKET_SET_CLOSING:
if (action.error) {
return state;
}
// Ooh bad stateful reducing...
state.socket.close();
return { ...state, state: STATE_CLOSING };
case types.SOCKET_SET_CLOSED:
return { ...state, state: STATE_CLOSED };
case types.SOCKET_SET_ERROR:
if (state.state === STATE_OPENING) {
return { ...state, state: STATE_CLOSED };
console.log("Socket error");
return { ...state, state: state.socket.readyState };
case types.SOCKET_SEND_MESSAGE:
try {
state.socket.send(JSON.stringify(action.payload));
} catch (err) {
console.log(`Socket error: failed to send ${action.payload}`);
}
return state;


+ 0
- 31
src/utils/SocketClient.js View File

@ -1,31 +0,0 @@
import { bindActionCreators } from "redux";
import objectAssign from "object-assign";
const STATE_CONNECTING = 0;
const STATE_OPEN = 1;
const STATE_CLOSING = 2;
const STATE_CLOSED = 3;
class SocketClient {
constructor(callbacks) {
this.callbacks = callbacks;
}
open(url) {
if (this.socket && this.socket.readyState !== STATE_CLOSED) {
throw new Error("SocketClient: socket already open");
}
this.socket = new WebSocket(url);
objectAssign(this.socket, this.callbacks);
}
close() {
this.socket.close();
}
send(message) {
this.socket.send(message);
}
}
export default SocketClient;

Loading…
Cancel
Save