diff --git a/src/actions/RoomActions.js b/src/actions/RoomActions.js index a0e5e0d..a941384 100644 --- a/src/actions/RoomActions.js +++ b/src/actions/RoomActions.js @@ -1,6 +1,7 @@ import { - ROOM_SELECT, - ROOM_JOIN + ROOM_JOIN, + ROOM_SAY, + ROOM_SELECT } from "../constants/ActionTypes"; import SocketActions from "./SocketActions"; @@ -10,13 +11,21 @@ import ControlRequest from "../utils/ControlRequest"; export default ({ getRoomList: () => SocketActions.send(ControlRequest.roomList()), + join: (room) => SocketActions.send(ControlRequest.joinRoom(room)), + select: (room) => ({ type: ROOM_SELECT, payload: room }), - join: (room) => ({ - type: ROOM_JOIN, - paylod: room - }) + say: (room, message) => (dispatch) => { + dispatch(SocketActions.send(ControlRequest.sayRoom(room, message))); + dispatch({ + type: ROOM_SAY, + payload: { + room_name: room, + message + } + }); + } }); diff --git a/src/actions/RoomActionsFactory.js b/src/actions/RoomActionsFactory.js deleted file mode 100644 index a0e5e0d..0000000 --- a/src/actions/RoomActionsFactory.js +++ /dev/null @@ -1,22 +0,0 @@ -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 - }) -}); diff --git a/src/components/ConnectForm.js b/src/components/ConnectForm.js index 98b6d73..1fae4b1 100644 --- a/src/components/ConnectForm.js +++ b/src/components/ConnectForm.js @@ -3,7 +3,6 @@ import {reduxForm} from "redux-form"; import SocketStatusPane from "./SocketStatusPane"; import { STATE_CLOSED } from "../constants/socket"; -import ControlRequest from "../utils/ControlRequest"; const ConnectForm = (props) => { const { fields: { url }, handleSubmit, socket, actions } = props; diff --git a/src/components/RoomChatForm.js b/src/components/RoomChatForm.js new file mode 100644 index 0000000..d36f51c --- /dev/null +++ b/src/components/RoomChatForm.js @@ -0,0 +1,33 @@ +import React, {PropTypes} from "react"; +import {reduxForm} from "redux-form"; + +const RoomChatForm = (props) => { + const { fields: { message }, handleSubmit, name, say } = props; + + const onSubmit = handleSubmit((values) => { + say(name, values.message); + }); + + return ( +
+
+ + +
+
+ ); +}; + +RoomChatForm.propTypes = { + fields: PropTypes.object.isRequired, + handleSubmit: PropTypes.func.isRequired, + name: PropTypes.string, + say: PropTypes.func.isRequired +}; + +export default reduxForm({ + form: "chat", + fields: ["message"] +})(RoomChatForm); + diff --git a/src/components/RoomList.js b/src/components/RoomList.js index 65ca86a..884c3b3 100644 --- a/src/components/RoomList.js +++ b/src/components/RoomList.js @@ -17,7 +17,6 @@ class RoomList extends React.Component { const children = []; - console.log(`Selected: "${selected}"`); for (const [room_name, room_data] of rooms) { const onClick = (event) => { roomActions.select(room_name); diff --git a/src/containers/RoomChat.js b/src/containers/RoomChat.js index b1d0206..2c70dd1 100644 --- a/src/containers/RoomChat.js +++ b/src/containers/RoomChat.js @@ -1,14 +1,44 @@ import React, { PropTypes } from "react"; import { connect } from "react-redux"; -const RoomChat = ({ name }) => { - return
{name}
; +import RoomActions from "../actions/RoomActions"; +import RoomChatForm from "../components/RoomChatForm"; + +const RoomChat = ({ name, data, roomActions }) => { + if (!name) { + return
Select a room
; + } + + // Append all messages in the chat room. + const children = []; + let i = 0; + for (const { user_name, message } of data.messages) { + children.push( +
  • + {user_name}: {message} +
  • + ); + i++; + } + + return ( +
    +
    {name}
    +
    + +
    + +
    + ); }; RoomChat.propTypes = { - name: PropTypes.string + data: PropTypes.object, + name: PropTypes.string, + roomActions: PropTypes.object.isRequired }; -export default connect( - (state) => ({ name: state.rooms.selected }) -)(RoomChat); +export default RoomChat; diff --git a/src/containers/RoomsPane.js b/src/containers/RoomsPane.js index ab1fee1..f384a97 100644 --- a/src/containers/RoomsPane.js +++ b/src/containers/RoomsPane.js @@ -21,7 +21,11 @@ class RoomsPane extends React.Component { roomActions={this.props.actions.room} selected={this.props.selected} /> - + ); } @@ -29,7 +33,8 @@ class RoomsPane extends React.Component { RoomsPane.propTypes = { actions: PropTypes.object.isRequired, - rooms: PropTypes.object.isRequired + rooms: PropTypes.object.isRequired, + selected: PropTypes.string }; const mapStateToProps = (state) => state.rooms; diff --git a/src/reducers/rooms.js b/src/reducers/rooms.js index ade3b60..ac96987 100644 --- a/src/reducers/rooms.js +++ b/src/reducers/rooms.js @@ -2,6 +2,7 @@ import Immutable from "immutable"; import { ROOM_JOIN, + ROOM_SAY, ROOM_SELECT, SOCKET_RECEIVE_MESSAGE } from "../constants/ActionTypes"; @@ -11,6 +12,19 @@ const initialState = { selected: null }; +const reduceRoom = (old_room, new_room) => { + if (!old_room) { + return { + messages: Immutable.List(), + ...new_room + }; + } + return { + ...old_room, + ...new_room + }; +}; + const reduceRoomList = (old_rooms, room_list) => { // First sort the room list by room name room_list.sort((room_pair_1, room_pair_2) => { @@ -28,28 +42,34 @@ const reduceRoomList = (old_rooms, room_list) => { let new_rooms = Immutable.OrderedMap(); for (const [ room_name, room_data ] of room_list) { const old_data = old_rooms.get(room_name); - if (old_data) { - new_rooms = new_rooms.set(room_name, { - ...old_data, - ...room_data - }); - } else { - new_rooms = new_rooms.set(room_name, room_data); - } + const new_data = reduceRoom(old_data, room_data); + new_rooms = new_rooms.set(room_name, new_data); } return new_rooms; }; -const reduceReceiveMessage = (state, payload) => { +const reduceReceiveMessage = (rooms, payload) => { switch (payload.variant) { case "RoomListResponse": - return { - ...state, - rooms: reduceRoomList(state.rooms, payload.data.rooms) + return reduceRoomList(rooms, payload.data.rooms); + + case "SayRoomResponse": + { + const { room_name, user_name, message } = payload.data; + const room_data = rooms.get(room_name); + if (!room_data) { + console.log(`Error: room "${room_name} not found`); + return rooms; + } + const new_room_data = { + ...room_data, + messages: room_data.messages.push({ user_name, message }) }; + return rooms.set(room_name, new_room_data); + } default: - return state; + return rooms; } }; @@ -57,7 +77,13 @@ export default (state = initialState, action) => { const { type, payload } = action; switch (type) { case SOCKET_RECEIVE_MESSAGE: - return reduceReceiveMessage(state, payload); + return { + ...state, + rooms: reduceReceiveMessage(state.rooms, payload) + }; + + case ROOM_SAY: + return state; case ROOM_SELECT: return {