Browse Source

Add RoomChat, lay out everything nicely.

pull/1/head
Titouan Rigoudy 9 years ago
parent
commit
289a35682f
13 changed files with 230 additions and 28 deletions
  1. +8
    -0
      src/actions/roomActions.js
  2. +21
    -0
      src/components/Footer.js
  3. +2
    -2
      src/components/Header.js
  4. +8
    -3
      src/components/Room.js
  5. +13
    -4
      src/components/RoomList.js
  6. +20
    -0
      src/components/RoomListHeader.js
  7. +1
    -1
      src/components/SocketStatusPane.js
  8. +10
    -8
      src/components/SolsticeApp.js
  9. +4
    -0
      src/constants/ActionTypes.js
  10. +14
    -0
      src/containers/RoomChat.js
  11. +22
    -8
      src/containers/RoomsPane.js
  12. +7
    -2
      src/reducers/rooms.js
  13. +100
    -0
      src/styles/styles.scss

+ 8
- 0
src/actions/roomActions.js View File

@ -0,0 +1,8 @@
import { ROOM_SELECT } from "../constants/ActionTypes";
export default {
select: (room_name) => ({
type: ROOM_SELECT,
payload: room_name
})
};

+ 21
- 0
src/components/Footer.js View File

@ -0,0 +1,21 @@
import React, { PropTypes } from "react";
import SocketStatusPane from "./SocketStatusPane";
import LoginStatusPane from "../containers/LoginStatusPane";
const Footer = ({ socket, socketActions }) => {
return (
<footer>
<SocketStatusPane {...socket} />
<LoginStatusPane socketSend={socketActions.send} />
</footer>
);
};
Footer.propTypes = {
socket: PropTypes.object.isRequired,
socketActions: PropTypes.object.isRequired
};
export default Footer;

+ 2
- 2
src/components/Header.js View File

@ -2,9 +2,9 @@ import React, { PropTypes } from "react";
const Header = (props) => {
return (
<div id="header">
<header>
<h1>Solstice web UI</h1>
</div>
</header>
);
};


+ 8
- 3
src/components/Room.js View File

@ -1,11 +1,16 @@
import React, { PropTypes } from "react";
const Room = ({ name }) => {
return <div className="room">{name}</div>;
const Room = ({ name, onClick }) => {
return (
<a className="room" onClick={onClick} href="#">
{name}
</a>
);
};
Room.propTypes = {
name: PropTypes.string.isRequired
name: PropTypes.string.isRequired,
onClick: PropTypes.func.isRequired
};
export default Room;

+ 13
- 4
src/components/RoomList.js View File

@ -1,26 +1,35 @@
import React, { PropTypes } from "react";
import Room from "./Room";
import RoomListHeader from "./RoomListHeader";
const RoomList = ({ rooms }) => {
const RoomList = ({ refresh, rooms, roomActions }) => {
const children = [];
for (const [room_name, room_data] of rooms) {
const onClick = (event) => {
roomActions.select(room_name);
};
children.push(
<li key={room_name}>
<Room name={room_name} {...room_data} />
<Room onClick={onClick} name={room_name} {...room_data} />
</li>
);
}
return (
<div id="room-list">
<div id="room-list-header">Room List</div>
<RoomListHeader refresh={refresh}/>
<ul> {children} </ul>
</div>
);
};
RoomList.propTypes = {
rooms: PropTypes.object.isRequired
refresh: PropTypes.func.isRequired,
rooms: PropTypes.object.isRequired,
roomActions: PropTypes.object.isRequired
};
export default RoomList;

+ 20
- 0
src/components/RoomListHeader.js View File

@ -0,0 +1,20 @@
import React, { PropTypes } from "react";
const RoomListHeader = ({ refresh }) => {
return (
<div id="room-list-header">
<div>
<h2>Room List</h2>
</div>
<div>
<button onClick={refresh}>Refresh</button>
</div>
</div>
);
};
RoomListHeader.propTypes = {
refresh: PropTypes.func.isRequired
};
export default RoomListHeader;

+ 1
- 1
src/components/SocketStatusPane.js View File

@ -21,7 +21,7 @@ const SocketStatusPane = (props) => {
string = `disconnected`;
break;
}
return <div>Connection status: {string}</div>;
return <div id="socket-status-pane">Connection status: {string}</div>;
};
SocketStatusPane.propTypes = {


+ 10
- 8
src/components/SolsticeApp.js View File

@ -1,8 +1,9 @@
import React, {PropTypes} from "react";
import ConnectForm from "../components/ConnectForm";
import Header from "../components/Header";
import SocketStatusPane from "../components/SocketStatusPane";
import ConnectForm from "./ConnectForm";
import Header from "./Header";
import Footer from "./Footer";
import SocketStatusPane from "./SocketStatusPane";
import LoginStatusPane from "../containers/LoginStatusPane";
import RoomsPane from "../containers/RoomsPane";
@ -21,12 +22,13 @@ const SolsticeApp = (props) => {
);
}
return (
<main>
<div id="solstice-app">
<Header />
<SocketStatusPane {...socket} />
<LoginStatusPane socketSend={actions.socketActions.send}/>
<RoomsPane socketSend={actions.socketActions.send}/>
</main>
<main>
<RoomsPane socketSend={actions.socketActions.send}/>
</main>
<Footer socket={socket} socketActions={actions.socketActions} />
</div>
);
};


+ 4
- 0
src/constants/ActionTypes.js View File

@ -1,3 +1,4 @@
// Socket actions
export const SOCKET_SET_OPEN = Symbol("SOCKET_SET_OPEN");
export const SOCKET_SET_OPENING = Symbol("SOCKET_SET_OPENING");
export const SOCKET_SET_CLOSED = Symbol("SOCKET_SET_CLOSED");
@ -5,3 +6,6 @@ export const SOCKET_SET_CLOSING = Symbol("SOCKET_SET_CLOSING");
export const SOCKET_SET_ERROR = Symbol("SOCKET_SET_ERROR");
export const SOCKET_RECEIVE_MESSAGE = Symbol("SOCKET_RECEIVE_MESSAGE");
export const SOCKET_SEND_MESSAGE = Symbol("SOCKET_SEND_MESSAGE");
// Room actions
export const ROOM_SELECT = Symbol("ROOM_SELECT");

+ 14
- 0
src/containers/RoomChat.js View File

@ -0,0 +1,14 @@
import React, { PropTypes } from "react";
import { connect } from "react-redux";
const RoomChat = ({ name }) => {
return <div id="room-chat">{name}</div>;
};
RoomChat.propTypes = {
name: PropTypes.string.isRequired
};
export default connect(
(state) => ({ name: state.rooms.selected })
)(RoomChat);

+ 22
- 8
src/containers/RoomsPane.js View File

@ -1,10 +1,14 @@
import React, { PropTypes } from "react";
import { connect } from "react-redux";
import { bindActionCreators } from "redux";
import RoomList from "../components/RoomList";
import ControlRequest from "../utils/ControlRequest";
const ID = "rooms-pane";
import RoomChat from "../containers/RoomChat";
import roomActions from "../actions/roomActions";
import ControlRequest from "../utils/ControlRequest";
class RoomsPane extends React.Component {
constructor(props) {
@ -16,15 +20,17 @@ class RoomsPane extends React.Component {
}
render() {
const onClick = (event) => {
const refresh = () => {
this.props.socketSend(ControlRequest.roomList());
event.preventDefault();
};
return (
<div id={ID}>
<button onClick={onClick}>Refresh</button>
<RoomList rooms={this.props.rooms} />
<div id="rooms-pane">
<RoomChat />
<RoomList
rooms={this.props.rooms}
refresh={refresh}
roomActions={this.props.roomActions}/>
</div>
);
}
@ -32,9 +38,17 @@ class RoomsPane extends React.Component {
RoomsPane.propTypes = {
rooms: PropTypes.object.isRequired,
roomActions: PropTypes.object.isRequired,
socketSend: PropTypes.func.isRequired
};
const mapStateToProps = (state) => state.rooms;
const mapDispatchToProps = (dispatch) => ({
roomActions: bindActionCreators(roomActions, dispatch)
});
export default connect(
state => state.rooms
mapStateToProps,
mapDispatchToProps
)(RoomsPane);

+ 7
- 2
src/reducers/rooms.js View File

@ -1,7 +1,8 @@
import { SOCKET_RECEIVE_MESSAGE } from "../constants/ActionTypes";
import { ROOM_SELECT, SOCKET_RECEIVE_MESSAGE } from "../constants/ActionTypes";
const initialState = {
rooms: new Map()
rooms: new Map(),
selected: null
};
const reduceRoomList = (old_rooms, room_list) => {
@ -47,6 +48,10 @@ export default (state = initialState, action) => {
switch (type) {
case SOCKET_RECEIVE_MESSAGE:
return reduceReceiveMessage(state, payload);
case ROOM_SELECT:
return { ...state, selected: payload };
default:
return state;
}


+ 100
- 0
src/styles/styles.scss View File

@ -6,4 +6,104 @@ body {
-moz-font-smoothing: antialiased;
font-smoothing: antialiased;
font-weight: 300;
padding: 0 !important;
margin: 0 !important;
height: 100%;
}
html {
padding: 0;
margin: 0;
height: 100vh;
}
#app, #solstice-app {
height: 100%;
margin: 0;
padding: 0;
}
#solstice-app {
display: flex;
flex-flow: column;
}
header {
margin: 0;
padding: 1.5em 2em;
}
header h1 {
margin: 0;
}
footer {
display: flex;
}
main {
height: 85%;
margin: 0;
padding: 0;
}
#rooms-pane {
display: flex;
border: solid grey 0.1em;
height: 100%;
}
#room-chat {
border: solid grey 0.1em;
padding: 2em;
}
#room-list {
flex: 1;
display: flex;
flex-flow: column;
border: solid grey 0.1em;
height: 100%;
overflow: auto;
}
#room-chat {
flex: 3;
}
#room-list-header {
display: flex;
flex-flow: row;
border: solid grey 0.1em;
}
#room-list-header div {
flex: 1;
border: solid grey 0.1em;
}
#room-list ul {
list-style: none;
padding: 0;
margin: 0;
}
.room {
display: flex;
color: inherit;
text-decoration: inherit;
padding: .5em 1em;
border: solid grey 0.1em;
}
.room:hover {
background: lightgrey;
}
.room:active {
background: grey;
}
#login-status-pane, #socket-status-pane {
flex: 1;
}

Loading…
Cancel
Save