Browse Source

Big changes everywhere...

pull/1/head
Titouan Rigoudy 9 years ago
parent
commit
1964220ada
20 changed files with 217 additions and 128 deletions
  1. +4
    -1
      package.json
  2. +21
    -0
      src/actions/RoomActionsFactory.js
  3. +0
    -0
      src/actions/SocketActionsFactory.js
  4. +0
    -0
      src/actions/SocketHandlerActions.js
  5. +0
    -8
      src/actions/roomActions.js
  6. +12
    -7
      src/components/ConnectForm.js
  7. +0
    -21
      src/components/Footer.js
  8. +1
    -3
      src/components/LoginStatusPane.js
  9. +9
    -2
      src/components/Room.js
  10. +39
    -22
      src/components/RoomList.js
  11. +9
    -10
      src/components/SolsticeApp.js
  12. +7
    -6
      src/constants/ActionTypes.js
  13. +11
    -10
      src/containers/App.js
  14. +26
    -0
      src/containers/Footer.js
  15. +1
    -1
      src/containers/RoomChat.js
  16. +6
    -21
      src/containers/RoomsPane.js
  17. +36
    -10
      src/reducers/rooms.js
  18. +9
    -1
      src/store/configureStore.js
  19. +21
    -5
      src/styles/styles.scss
  20. +5
    -0
      src/utils/ControlRequest.js

+ 4
- 1
package.json View File

@ -25,7 +25,10 @@
"react-redux": "4.4.0",
"redux": "3.3.1",
"redux-form": "~4.2.0",
"redux-thunk": "~2.0.1"
"redux-thunk": "~2.0.1",
"redux-logger": "~2.6.1",
"redux-promise": "~0.5.1",
"immutable": "~3.7.6"
},
"devDependencies": {
"babel-cli": "6.5.1",


+ 21
- 0
src/actions/RoomActionsFactory.js View File

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

src/actions/socketActionsFactory.js → src/actions/SocketActionsFactory.js View File


src/actions/socketHandlerActions.js → src/actions/SocketHandlerActions.js View File


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

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

+ 12
- 7
src/components/ConnectForm.js View File

@ -1,6 +1,7 @@
import React, {PropTypes} from "react";
import {reduxForm} from "redux-form";
import SocketStatusPane from "./SocketStatusPane";
import { STATE_CLOSED } from "../constants/socket";
import ControlRequest from "../utils/ControlRequest";
@ -10,13 +11,17 @@ const ConnectForm = (props) => {
const onSubmit = handleSubmit((values) => socketOpen(values.url));
return (
<form onSubmit={onSubmit}>
<input type="url" defaultValue="ws://localhost:2244" {...url}
required pattern="wss?://.+"/>
<button type="submit" disabled={socket.state !== STATE_CLOSED}>
Connect
</button>
</form>
<div id="connect-form">
<h2>Connect to a solstice client</h2>
<form onSubmit={onSubmit}>
<input type="url" defaultValue="ws://localhost:2244" {...url}
required pattern="wss?://.+"/>
<button type="submit" disabled={socket.state !== STATE_CLOSED}>
Connect
</button>
</form>
<SocketStatusPane {...socket} />
</div>
);
};


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

@ -1,21 +0,0 @@
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;

src/containers/LoginStatusPane.js → src/components/LoginStatusPane.js View File

@ -88,6 +88,4 @@ LoginStatusPane.propTypes = {
socketSend: PropTypes.func.isRequired
};
export default connect(
state => state.login
)(LoginStatusPane);
export default LoginStatusPane;

+ 9
- 2
src/components/Room.js View File

@ -1,14 +1,21 @@
import React, { PropTypes } from "react";
const Room = ({ name, onClick }) => {
const Room = ({ isSelected, name, onClick }) => {
let className;
if (isSelected) {
className = "room room-selected";
} else {
className = "room";
}
return (
<a className="room" onClick={onClick} href="#">
<a className={className} onClick={onClick} href="#">
{name}
</a>
);
};
Room.propTypes = {
isSelected: PropTypes.bool.isRequired,
name: PropTypes.string.isRequired,
onClick: PropTypes.func.isRequired
};


+ 39
- 22
src/components/RoomList.js View File

@ -3,33 +3,50 @@ import React, { PropTypes } from "react";
import Room from "./Room";
import RoomListHeader from "./RoomListHeader";
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 onClick={onClick} name={room_name} {...room_data} />
</li>
);
class RoomList extends React.Component {
constructor(props) {
super(props);
}
return (
<div id="room-list">
<RoomListHeader refresh={refresh}/>
<ul> {children} </ul>
</div>
);
};
componentDidMount() {
this.props.roomActions.getRoomList();
}
render() {
const { selected, rooms, roomActions } = this.props;
const children = [];
console.log(`Selected: "${selected}"`);
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(
<li key={room_name}>
<Room onClick={onClick} name={room_name} {...room_data}
isSelected={selected == room_name} />
</li>
);
}
return (
<div id="room-list">
<RoomListHeader refresh={this.props.roomActions.getRoomList}/>
<ul> {children} </ul>
</div>
);
}
}
RoomList.propTypes = {
refresh: PropTypes.func.isRequired,
rooms: PropTypes.object.isRequired,
roomActions: PropTypes.object.isRequired
roomActions: PropTypes.object.isRequired,
selected: PropTypes.string
};
export default RoomList;

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

@ -2,32 +2,31 @@ import React, {PropTypes} from "react";
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";
import Footer from "../containers/Footer";
import { STATE_OPEN } from "../constants/socket";
const ID = "solstice-app";
const SolsticeApp = (props) => {
const { actions, socket } = props;
if (socket.state !== STATE_OPEN ) {
return (
<main>
<div id={ID}>
<ConnectForm socket={socket}
socketOpen={actions.socketActions.open}/>
<SocketStatusPane {...socket} />
</main>
socketOpen={actions.socket.open}/>
</div>
);
}
return (
<div id="solstice-app">
<div id={ID}>
<Header />
<main>
<RoomsPane socketSend={actions.socketActions.send}/>
<RoomsPane actions={actions}/>
</main>
<Footer socket={socket} socketActions={actions.socketActions} />
<Footer actions={actions} />
</div>
);
};


+ 7
- 6
src/constants/ActionTypes.js View File

@ -1,11 +1,12 @@
// 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");
export const SOCKET_SET_CLOSING = Symbol("SOCKET_SET_CLOSING");
export const SOCKET_SET_ERROR = Symbol("SOCKET_SET_ERROR");
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");
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");
export const SOCKET_SEND_MESSAGE = Symbol("SOCKET_SEND_MESSAGE");
// Room actions
export const ROOM_SELECT = Symbol("ROOM_SELECT");
export const ROOM_JOIN = Symbol("ROOM_JOIN");

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

@ -4,9 +4,12 @@ import React, {PropTypes} from "react";
import { bindActionCreators } from "redux";
import { connect } from "react-redux";
import SocketActionsFactory from "../actions/SocketActionsFactory";
import SocketHandlerActions from "../actions/SocketHandlerActions";
import RoomActionsFactory from "../actions/RoomActionsFactory";
import SolsticeApp from "../components/SolsticeApp";
import socketActionsFactory from "../actions/socketActionsFactory";
import socketHandlerActions from "../actions/socketHandlerActions";
import SocketClient from "../utils/SocketClient";
const App = (props) => (<SolsticeApp {...props} />);
@ -16,19 +19,17 @@ App.propTypes = {
socket: PropTypes.object.isRequired
};
function mapStateToProps(state) {
return {
socket: state.socket
};
}
const mapStateToProps = ({ socket }) => ({ socket });
function mapDispatchToProps(dispatch) {
const callbacks = bindActionCreators(socketHandlerActions, dispatch);
const callbacks = bindActionCreators(SocketHandlerActions, dispatch);
const socketClient = new SocketClient(callbacks);
const socketActions = socketActionsFactory(socketClient);
const socketActions = SocketActionsFactory(socketClient);
const roomActions = RoomActionsFactory(socketActions);
return {
actions: {
socketActions: bindActionCreators(socketActions, dispatch)
room: bindActionCreators(roomActions, dispatch),
socket: bindActionCreators(socketActions, dispatch)
}
};
}


+ 26
- 0
src/containers/Footer.js View File

@ -0,0 +1,26 @@
import React, { PropTypes } from "react";
import { connect } from "react-redux";
import LoginStatusPane from "../components/LoginStatusPane";
import SocketStatusPane from "../components/SocketStatusPane";
const Footer = ({ actions, login, socket }) => {
return (
<footer>
<SocketStatusPane {...socket} />
<LoginStatusPane {...login} socketSend={actions.socket.send} />
</footer>
);
};
Footer.propTypes = {
actions: PropTypes.object.isRequired,
login: PropTypes.object.isRequired,
socket: PropTypes.object.isRequired
};
const mapStateToProps = ({ socket, login }) => ({ socket, login });
export default connect(
mapStateToProps
)(Footer);

+ 1
- 1
src/containers/RoomChat.js View File

@ -6,7 +6,7 @@ const RoomChat = ({ name }) => {
};
RoomChat.propTypes = {
name: PropTypes.string.isRequired
name: PropTypes.string
};
export default connect(


+ 6
- 21
src/containers/RoomsPane.js View File

@ -6,8 +6,6 @@ import RoomList from "../components/RoomList";
import RoomChat from "../containers/RoomChat";
import roomActions from "../actions/roomActions";
import ControlRequest from "../utils/ControlRequest";
class RoomsPane extends React.Component {
@ -15,40 +13,27 @@ class RoomsPane extends React.Component {
super(props);
}
componentDidMount() {
this.props.socketSend(ControlRequest.roomList());
}
render() {
const refresh = () => {
this.props.socketSend(ControlRequest.roomList());
};
return (
<div id="rooms-pane">
<RoomChat />
<RoomList
rooms={this.props.rooms}
refresh={refresh}
roomActions={this.props.roomActions}/>
roomActions={this.props.actions.room}
selected={this.props.selected}
/>
<RoomChat />
</div>
);
}
}
RoomsPane.propTypes = {
rooms: PropTypes.object.isRequired,
roomActions: PropTypes.object.isRequired,
socketSend: PropTypes.func.isRequired
actions: PropTypes.object.isRequired,
rooms: PropTypes.object.isRequired
};
const mapStateToProps = (state) => state.rooms;
const mapDispatchToProps = (dispatch) => ({
roomActions: bindActionCreators(roomActions, dispatch)
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(RoomsPane);

+ 36
- 10
src/reducers/rooms.js View File

@ -1,7 +1,13 @@
import { ROOM_SELECT, SOCKET_RECEIVE_MESSAGE } from "../constants/ActionTypes";
import Immutable from "immutable";
import {
ROOM_JOIN,
ROOM_SELECT,
SOCKET_RECEIVE_MESSAGE
} from "../constants/ActionTypes";
const initialState = {
rooms: new Map(),
rooms: Immutable.OrderedMap(),
selected: null
};
@ -19,13 +25,16 @@ const reduceRoomList = (old_rooms, room_list) => {
});
// Then build the new rooms map
const new_rooms = new Map();
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.set(room_name, { ...old_data, ...room_data });
new_rooms = new_rooms.set(room_name, {
...old_data,
...room_data
});
} else {
new_rooms.set(room_name, room_data);
new_rooms = new_rooms.set(room_name, room_data);
}
}
return new_rooms;
@ -34,10 +43,11 @@ const reduceRoomList = (old_rooms, room_list) => {
const reduceReceiveMessage = (state, payload) => {
switch (payload.variant) {
case "RoomListResponse":
{
const rooms = reduceRoomList(state.rooms, payload.data.rooms);
return { ...state, rooms };
}
return {
...state,
rooms: reduceRoomList(state.rooms, payload.data.rooms)
};
default:
return state;
}
@ -50,7 +60,23 @@ export default (state = initialState, action) => {
return reduceReceiveMessage(state, payload);
case ROOM_SELECT:
return { ...state, selected: payload };
return {
...state,
selected: payload
};
case ROOM_JOIN:
{
const rooms = state.rooms.merge({
[payload]: {
joined: true
}
});
return {
...state,
rooms
};
}
default:
return state;


+ 9
- 1
src/store/configureStore.js View File

@ -1,5 +1,7 @@
import { applyMiddleware } from "redux";
import thunk from "redux-thunk";
import promise from "redux-promise";
import createLogger from "redux-logger";
let configureStore;
if (process.env.NODE_ENV === 'production') {
@ -8,4 +10,10 @@ if (process.env.NODE_ENV === 'production') {
configureStore = require('./configureStore.dev').default;
}
export default () => configureStore(undefined, applyMiddleware(thunk));
export default () => {
const logger = createLogger();
return configureStore(
undefined,
applyMiddleware(thunk, promise, logger)
);
};

+ 21
- 5
src/styles/styles.scss View File

@ -26,6 +26,8 @@ html {
#solstice-app {
display: flex;
flex-flow: column;
justify-content: center;
align-items: center;
}
header {
@ -42,6 +44,7 @@ footer {
}
main {
width: 100%;
height: 85%;
margin: 0;
padding: 0;
@ -64,7 +67,6 @@ main {
flex-flow: column;
border: solid grey 0.1em;
height: 100%;
overflow: auto;
}
#room-chat {
@ -72,23 +74,27 @@ main {
}
#room-list-header {
flex: 1;
display: flex;
flex-flow: row;
border: solid grey 0.1em;
}
#room-list-header div {
#room-list-header > div {
flex: 1;
border: solid grey 0.1em;
}
#room-list ul {
display: block;
list-style: none;
padding: 0;
margin: 0;
height: 84%;
overflow-y: auto;
}
.room {
a.room {
display: flex;
color: inherit;
text-decoration: inherit;
@ -96,14 +102,24 @@ main {
border: solid grey 0.1em;
}
.room:hover {
a.room:hover {
background: lightgrey;
}
.room:active {
a.room:active {
background: grey;
}
a.room-selected {
background: orange;
}
#login-status-pane, #socket-status-pane {
flex: 1;
}
#connect-form {
display: flex;
flex-flow: column;
align-items: center;
}

+ 5
- 0
src/utils/ControlRequest.js View File

@ -7,5 +7,10 @@ export default {
roomList: () => ({
"variant": "RoomListRequest",
"fields": []
}),
joinRoom: (room) => ({
"variant": "JoinRoomRequest",
"fields": [{ room }]
})
};

Loading…
Cancel
Save