diff --git a/package.json b/package.json index c5db1ea..a1f07e0 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,8 @@ "redux-thunk": "~2.0.1", "redux-logger": "~2.6.1", "redux-promise": "~0.5.1", - "immutable": "~3.7.6" + "immutable": "~3.7.6", + "react-immutable-proptypes": "~1.7.0" }, "devDependencies": { "babel-cli": "6.5.1", diff --git a/src/actions/RoomActions.js b/src/actions/RoomActions.js index a941384..d6ea652 100644 --- a/src/actions/RoomActions.js +++ b/src/actions/RoomActions.js @@ -11,7 +11,13 @@ import ControlRequest from "../utils/ControlRequest"; export default ({ getRoomList: () => SocketActions.send(ControlRequest.roomList()), - join: (room) => SocketActions.send(ControlRequest.joinRoom(room)), + join: (room) => (dispatch) => { + dispatch({ + type: ROOM_JOIN, + payload: room + }); + dispatch(SocketActions.send(ControlRequest.joinRoom(room))); + }, select: (room) => ({ type: ROOM_SELECT, @@ -19,7 +25,6 @@ export default ({ }), say: (room, message) => (dispatch) => { - dispatch(SocketActions.send(ControlRequest.sayRoom(room, message))); dispatch({ type: ROOM_SAY, payload: { @@ -27,5 +32,6 @@ export default ({ message } }); + dispatch(SocketActions.send(ControlRequest.sayRoom(room, message))); } }); diff --git a/src/components/RoomChatMessageList.js b/src/components/RoomChatMessageList.js new file mode 100644 index 0000000..883c9ef --- /dev/null +++ b/src/components/RoomChatMessageList.js @@ -0,0 +1,29 @@ +import React, { PropTypes } from "react"; +import ImmutablePropTypes from "react-immutable-proptypes"; + +const RoomChatMessageList = ({ messages }) => { + // Append all messages in the chat room. + const children = []; + let i = 0; + for (const { user_name, message } of messages) { + children.push( +
  • + {user_name}: {message} +
  • + ); + i++; + } + + return ( +
    + +
    + ); +}; + +RoomChatMessageList.propTypes = { + messages: ImmutablePropTypes.list.isRequired +}; + +export default RoomChatMessageList; + diff --git a/src/components/RoomList.js b/src/components/RoomList.js index 884c3b3..d0e720d 100644 --- a/src/components/RoomList.js +++ b/src/components/RoomList.js @@ -20,9 +20,6 @@ class RoomList extends React.Component { for (const [room_name, room_data] of rooms) { const onClick = (event) => { roomActions.select(room_name); - if (!room_data.joined) { - roomActions.join(room_name); - } }; children.push( diff --git a/src/containers/RoomChat.js b/src/containers/RoomChat.js index 2c70dd1..2662a5e 100644 --- a/src/containers/RoomChat.js +++ b/src/containers/RoomChat.js @@ -1,42 +1,73 @@ import React, { PropTypes } from "react"; -import { connect } from "react-redux"; -import RoomActions from "../actions/RoomActions"; import RoomChatForm from "../components/RoomChatForm"; +import RoomChatMessageList from "../components/RoomChatMessageList"; -const RoomChat = ({ name, data, roomActions }) => { - if (!name) { - return
    Select a room
    ; +const ID = "room-chat"; +const ID_HEADER = "room-chat-header"; + +class RoomChat extends React.Component { + constructor(props) { + super(props); + } + + componentDidMount() { + this.join_if_non_member(this.props); + } + + componentWillReceiveProps(props) { + this.join_if_non_member(props); + } + + join_if_non_member(props) { + const { name, room, roomActions } = props; + if (room && room.membership == "NonMember") { + roomActions.join(name); + } } - // 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} -
  • + render_only_header(string) { + return ( +
    +
    + {string} +
    +
    ); - i++; } - return ( -
    -
    {name}
    -
    - + render() { + const { name, room, roomActions } = this.props; + + // If no room is selected, just tell the user to select one. + if (!name || !room) { + return this.render_only_header("Select a room"); + } + + switch (room.membership) { + case "NonMember": + return this.render_only_header(`Not a member of ${name}`); + + case "Joining": + return this.render_only_header(`Joining ${name}`); + + case "Leaving": + return this.render_only_header(`Leaving ${name}`); + } + + // room.membership == "Member" + return ( +
    +
    {name}
    + +
    - -
    - ); -}; + ); + } +} RoomChat.propTypes = { - data: PropTypes.object, + room: PropTypes.object, name: PropTypes.string, roomActions: PropTypes.object.isRequired }; diff --git a/src/containers/RoomsPane.js b/src/containers/RoomsPane.js index f384a97..a4b0ed3 100644 --- a/src/containers/RoomsPane.js +++ b/src/containers/RoomsPane.js @@ -14,17 +14,18 @@ class RoomsPane extends React.Component { } render() { + const { actions, rooms, selected } = this.props; return (
    ); diff --git a/src/reducers/rooms.js b/src/reducers/rooms.js index 71a45b6..5a30646 100644 --- a/src/reducers/rooms.js +++ b/src/reducers/rooms.js @@ -43,14 +43,24 @@ const reduceRoomList = (old_rooms, room_list) => { return new_rooms; }; -const reduceReceiveMessage = (rooms, payload) => { - switch (payload.variant) { +const reduceReceiveMessage = (rooms, { variant, data }) => { + switch (variant) { + case "JoinRoomResponse": + { + const { room_name } = data; + const room = rooms.get(room_name); + return rooms.set(room_name, { + ...room, + membership: "Member" + }); + } + case "RoomListResponse": - return reduceRoomList(rooms, payload.data.rooms); + return reduceRoomList(rooms, data.rooms); case "SayRoomResponse": { - const { room_name, user_name, message } = payload.data; + const { room_name, user_name, message } = data; const room_data = rooms.get(room_name); if (!room_data) { console.log(`Error: room "${room_name} not found`); @@ -88,10 +98,9 @@ export default (state = initialState, action) => { case ROOM_JOIN: { - const rooms = state.rooms.merge({ - [payload]: { - joined: true - } + const rooms = state.rooms.set(payload, { + ...state.rooms.get(payload), + membership: "Joining" }); return { ...state, diff --git a/src/styles/styles.scss b/src/styles/styles.scss index 806f87f..dc31882 100644 --- a/src/styles/styles.scss +++ b/src/styles/styles.scss @@ -58,7 +58,6 @@ main { #room-chat { border: solid grey 0.1em; - padding: 2em; } #room-list { @@ -71,6 +70,35 @@ main { #room-chat { flex: 3; + display: flex; + flex-flow: column; +} + +#room-chat-header { + text-align: center; + font-size: 1.3em; + padding: 0.8em; + border: solid grey 0.1em; +} + +#room-chat-messages { + flex: 1; +} + +#room-chat-form { + width: 100%; + border: solid grey 0.1em; +} + +#room-chat-form form { + width: 100%; + display: flex; + flex-flow: row; +} + +#room-chat-form input { + flex: 1; + padding: 0.8em; } #room-list-header {