From e82639f7dfe8d1739fd124bd7f8db97106782011 Mon Sep 17 00:00:00 2001 From: Titouan Rigoudy Date: Tue, 27 Jul 2021 18:38:20 -0400 Subject: [PATCH] Install and run prettier formatter. --- .prettierignore | 4 + .prettierrc.json | 1 + TODO.md | 5 +- src/App.test.tsx | 12 +- src/App.tsx | 8 +- src/actions/LoginActions.js | 6 +- src/actions/RoomActions.js | 78 +++---- src/actions/SocketActions.js | 48 ++-- src/actions/SocketHandlerActions.js | 51 +++-- src/actions/UserActions.js | 6 +- src/app/hooks.ts | 4 +- src/app/store.ts | 10 +- src/components/ConnectForm.js | 14 +- src/components/Header.js | 22 +- src/components/LoginStatusPane.js | 76 +++---- src/components/Room.js | 35 +-- src/components/RoomChat.js | 129 +++++------ src/components/RoomChatForm.js | 62 +++--- src/components/RoomChatHeader.js | 91 ++++---- src/components/RoomChatMessageList.js | 55 +++-- src/components/RoomList.js | 18 +- src/components/RoomListHeader.js | 22 +- src/components/RoomUserList.js | 28 ++- src/components/SearchableList.js | 98 ++++----- src/components/SocketStatusPane.tsx | 4 +- src/components/SolsticeApp.js | 8 +- src/components/User.js | 17 +- src/components/UserList.js | 14 +- src/constants/ActionTypes.js | 22 +- src/containers/ConnectPage.js | 20 +- src/containers/Footer.js | 2 +- src/containers/RoomsPane.js | 82 ++++--- src/containers/UsersPane.js | 21 +- src/features/counter/Counter.module.css | 4 +- src/features/counter/Counter.tsx | 10 +- src/features/counter/counterSlice.spec.ts | 18 +- src/features/counter/counterSlice.ts | 35 ++- src/index.tsx | 10 +- src/modules/login/message.ts | 12 +- src/modules/login/slice.ts | 28 +-- src/modules/websocket/actions.ts | 10 +- src/modules/websocket/message.ts | 14 +- src/modules/websocket/middleware.ts | 60 ++--- src/modules/websocket/slice.ts | 14 +- src/reducers/index.js | 10 +- src/reducers/login.js | 102 ++++----- src/reducers/rooms.js | 187 ++++++++-------- src/reducers/socket.js | 155 ++++++------- src/reducers/users.js | 66 +++--- src/setupTests.ts | 2 +- src/styles/styles.scss | 256 +++++++++++----------- src/utils/ControlRequest.js | 56 ++--- src/utils/OrderedMap.js | 116 +++++----- src/utils/propTypeRequiredWrapper.js | 15 +- src/utils/propTypeSymbol.js | 25 +-- 55 files changed, 1114 insertions(+), 1164 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc.json diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..e6ce93e --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +build +node_modules +package.json +package-lock.json diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1 @@ +{} diff --git a/TODO.md b/TODO.md index 65ff79e..5dcba9a 100644 --- a/TODO.md +++ b/TODO.md @@ -1,4 +1,3 @@ -Things to do: -------------- +## Things to do: - - actually join rooms, display and send messages +- actually join rooms, display and send messages diff --git a/src/App.test.tsx b/src/App.test.tsx index 659cc13..e4874f8 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -1,10 +1,10 @@ -import React from 'react'; -import { render } from '@testing-library/react'; -import { Provider } from 'react-redux'; -import { store } from './app/store'; -import App from './App'; +import React from "react"; +import { render } from "@testing-library/react"; +import { Provider } from "react-redux"; +import { store } from "./app/store"; +import App from "./App"; -test('renders learn react link', () => { +test("renders learn react link", () => { const { getByText } = render( diff --git a/src/App.tsx b/src/App.tsx index e8da502..e28b81e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,7 @@ -import React from 'react'; -import logo from './logo.svg'; -import { Counter } from './features/counter/Counter'; -import './App.css'; +import React from "react"; +import logo from "./logo.svg"; +import { Counter } from "./features/counter/Counter"; +import "./App.css"; function App() { return ( diff --git a/src/actions/LoginActions.js b/src/actions/LoginActions.js index 166114d..ec14a5d 100644 --- a/src/actions/LoginActions.js +++ b/src/actions/LoginActions.js @@ -1,7 +1,7 @@ import { LOGIN_GET_STATUS } from "../constants/ActionTypes"; export default { - getStatus: () => ({ - type: LOGIN_GET_STATUS - }) + getStatus: () => ({ + type: LOGIN_GET_STATUS, + }), }; diff --git a/src/actions/RoomActions.js b/src/actions/RoomActions.js index 87e192e..01f5874 100644 --- a/src/actions/RoomActions.js +++ b/src/actions/RoomActions.js @@ -1,48 +1,48 @@ import { - ROOM_GET_LIST, - ROOM_JOIN, - ROOM_LEAVE, - ROOM_MESSAGE, - ROOM_SELECT, - ROOM_SHOW_USERS, - ROOM_HIDE_USERS + ROOM_GET_LIST, + ROOM_JOIN, + ROOM_LEAVE, + ROOM_MESSAGE, + ROOM_SELECT, + ROOM_SHOW_USERS, + ROOM_HIDE_USERS, } from "../constants/ActionTypes"; -export default ({ - getList: () => ({ - type: ROOM_GET_LIST - }), +export default { + getList: () => ({ + type: ROOM_GET_LIST, + }), - join: (room_name) => ({ - type: ROOM_JOIN, - payload: room_name - }), + join: (room_name) => ({ + type: ROOM_JOIN, + payload: room_name, + }), - leave: (room_name) => ({ - type: ROOM_LEAVE, - payload: room_name - }), + leave: (room_name) => ({ + type: ROOM_LEAVE, + payload: room_name, + }), - select: (room_name) => ({ - type: ROOM_SELECT, - payload: room_name - }), + select: (room_name) => ({ + type: ROOM_SELECT, + payload: room_name, + }), - sendMessage: (room_name, message) => ({ - type: ROOM_MESSAGE, - payload: { - room_name, - message - } - }), + sendMessage: (room_name, message) => ({ + type: ROOM_MESSAGE, + payload: { + room_name, + message, + }, + }), - showUsers: (room_name) => ({ - type: ROOM_SHOW_USERS, - payload: room_name - }), + showUsers: (room_name) => ({ + type: ROOM_SHOW_USERS, + payload: room_name, + }), - hideUsers: (room_name) => ({ - type: ROOM_HIDE_USERS, - payload: room_name - }) -}); + hideUsers: (room_name) => ({ + type: ROOM_HIDE_USERS, + payload: room_name, + }), +}; diff --git a/src/actions/SocketActions.js b/src/actions/SocketActions.js index cbf7e5e..476ac0a 100644 --- a/src/actions/SocketActions.js +++ b/src/actions/SocketActions.js @@ -1,29 +1,29 @@ import { - SOCKET_RECEIVE_MESSAGE, - SOCKET_SEND_MESSAGE, - SOCKET_SET_CLOSED, - SOCKET_SET_CLOSING, - SOCKET_SET_ERROR, - SOCKET_SET_OPEN, - SOCKET_SET_OPENING + 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 - } - }), +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 }), + close: () => ({ type: SOCKET_SET_CLOSING }), - send: (message) => ({ - type: SOCKET_SEND_MESSAGE, - payload: message - }) -}); + send: (message) => ({ + type: SOCKET_SEND_MESSAGE, + payload: message, + }), +}; diff --git a/src/actions/SocketHandlerActions.js b/src/actions/SocketHandlerActions.js index ac79b7a..43feacd 100644 --- a/src/actions/SocketHandlerActions.js +++ b/src/actions/SocketHandlerActions.js @@ -1,33 +1,36 @@ import { - SOCKET_SET_CLOSED, - SOCKET_SET_ERROR, - SOCKET_SET_OPEN, - SOCKET_RECEIVE_MESSAGE + SOCKET_SET_CLOSED, + SOCKET_SET_ERROR, + SOCKET_SET_OPEN, + SOCKET_RECEIVE_MESSAGE, } from "../constants/ActionTypes"; export default { - onclose: event => ({ - type: SOCKET_SET_CLOSED, - payload: event.code - }), + onclose: (event) => ({ + type: SOCKET_SET_CLOSED, + payload: event.code, + }), - onerror: event => ({ type: SOCKET_SET_ERROR }), + onerror: (event) => ({ type: SOCKET_SET_ERROR }), - onopen: event => ({ type: SOCKET_SET_OPEN }), + onopen: (event) => ({ type: SOCKET_SET_OPEN }), - onmessage: event => { - console.log(`Received message: ${event.data}`); - const action = { type: SOCKET_RECEIVE_MESSAGE }; - try { - const { variant, fields: [data] } = JSON.parse(event.data); - if (typeof variant === "undefined") { - throw new Error('Missing "variant" field in control response'); - } - action.payload = { variant, data }; - } catch (err) { - action.error = true; - action.payload = err; - } - return action; + onmessage: (event) => { + console.log(`Received message: ${event.data}`); + const action = { type: SOCKET_RECEIVE_MESSAGE }; + try { + const { + variant, + fields: [data], + } = JSON.parse(event.data); + if (typeof variant === "undefined") { + throw new Error('Missing "variant" field in control response'); + } + action.payload = { variant, data }; + } catch (err) { + action.error = true; + action.payload = err; } + return action; + }, }; diff --git a/src/actions/UserActions.js b/src/actions/UserActions.js index 86765a9..5cc25e5 100644 --- a/src/actions/UserActions.js +++ b/src/actions/UserActions.js @@ -1,9 +1,9 @@ import { USER_GET_LIST } from "../constants/ActionTypes"; const UserActions = { - getList: () => ({ - type: USER_GET_LIST - }) + getList: () => ({ + type: USER_GET_LIST, + }), }; export default UserActions; diff --git a/src/app/hooks.ts b/src/app/hooks.ts index 520e84e..36b5ea2 100644 --- a/src/app/hooks.ts +++ b/src/app/hooks.ts @@ -1,5 +1,5 @@ -import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'; -import type { RootState, AppDispatch } from './store'; +import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"; +import type { RootState, AppDispatch } from "./store"; // Use throughout your app instead of plain `useDispatch` and `useSelector` export const useAppDispatch = () => useDispatch(); diff --git a/src/app/store.ts b/src/app/store.ts index 89d3112..f9fbd62 100644 --- a/src/app/store.ts +++ b/src/app/store.ts @@ -1,4 +1,4 @@ -import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit'; +import { configureStore, ThunkAction, Action } from "@reduxjs/toolkit"; import counterReducer from "../features/counter/counterSlice"; // TODO: Rework these to use a slice, otherwise redux complains about @@ -20,10 +20,10 @@ export const store = configureStore({ socket: socketReducer, //users, }, - middleware: (getDefaultMiddleware) => ( - getDefaultMiddleware().concat(makeSocketMiddleware( - loginSocketMessageHandlers)) - ) + middleware: (getDefaultMiddleware) => + getDefaultMiddleware().concat( + makeSocketMiddleware(loginSocketMessageHandlers) + ), }); export type AppDispatch = typeof store.dispatch; diff --git a/src/components/ConnectForm.js b/src/components/ConnectForm.js index 88d1d60..5216bb0 100644 --- a/src/components/ConnectForm.js +++ b/src/components/ConnectForm.js @@ -5,12 +5,12 @@ import SocketStatusPane from "./SocketStatusPane"; import { SocketSliceState, SocketState, - socketOpen + socketOpen, } from "../modules/websocket/slice"; interface Props { - socket: SocketSliceState, -}; + socket: SocketSliceState; +} const ConnectForm: React.FC = ({ socket }) => { const dispatch = useDispatch(); @@ -24,7 +24,7 @@ const ConnectForm: React.FC = ({ socket }) => { return (

Connect to a solstice client

-
( @@ -36,10 +36,8 @@ const ConnectForm: React.FC = ({ socket }) => { required pattern="wss?://.+" /> - )} diff --git a/src/components/Header.js b/src/components/Header.js index d8fcd74..58883c0 100644 --- a/src/components/Header.js +++ b/src/components/Header.js @@ -2,17 +2,17 @@ import React from "react"; import { Link } from "react-router-dom"; const Header = () => { - return ( -
-

Solstice web UI

- - Rooms - - - Users - -
- ); + return ( +
+

Solstice web UI

+ + Rooms + + + Users + +
+ ); }; export default Header; diff --git a/src/components/LoginStatusPane.js b/src/components/LoginStatusPane.js index 179ab09..33ae5b9 100644 --- a/src/components/LoginStatusPane.js +++ b/src/components/LoginStatusPane.js @@ -1,8 +1,8 @@ import { LoginStatus, LoginSliceState } from "../modules/login/slice"; interface Props { - login: LoginSliceState, -}; + login: LoginSliceState; +} const LoginStatusPane: React.FC = ({ login }) => { let statusText; @@ -10,50 +10,40 @@ const LoginStatusPane: React.FC = ({ login }) => { let reason; switch (login.status) { - case LoginStatus.Unknown: - statusText = "unknown"; - break; - - case LoginStatus.Fetching: - statusText = "fetching"; - break; - - case LoginStatus.Pending: - statusText = `logging in as ${login.username}`; - break; - - case LoginStatus.Success: - statusText = `logged in as ${login.username}`; - motd = ( - - MOTD: {login.motd} - - ); - break; - - case LoginStatus.Failure: - statusText = `failed to log in as ${login.username}`; - reason = ( - - Reason: {login.reason} - - ); - break; - - default: - statusText = `invalid status ${login.status}`; - break; + case LoginStatus.Unknown: + statusText = "unknown"; + break; + + case LoginStatus.Fetching: + statusText = "fetching"; + break; + + case LoginStatus.Pending: + statusText = `logging in as ${login.username}`; + break; + + case LoginStatus.Success: + statusText = `logged in as ${login.username}`; + motd = MOTD: {login.motd}; + break; + + case LoginStatus.Failure: + statusText = `failed to log in as ${login.username}`; + reason = Reason: {login.reason}; + break; + + default: + statusText = `invalid status ${login.status}`; + break; } return ( -
- - Login status: {statusText} - - {motd} - {reason} -
+
+ Login status: {statusText} + {motd} + {reason} +
); -} +}; export default LoginStatusPane; diff --git a/src/components/Room.js b/src/components/Room.js index 4612980..680193e 100644 --- a/src/components/Room.js +++ b/src/components/Room.js @@ -5,29 +5,30 @@ import ImmutablePropTypes from "react-immutable-proptypes"; import md5 from "md5"; const Room = ({ name, data }) => { - const { membership, userCount } = data; + const { membership, userCount } = data; - const classes = ["room"]; - if (membership == "Member") { - classes.push("room-joined"); - } + const classes = ["room"]; + if (membership == "Member") { + classes.push("room-joined"); + } - const path = `/app/rooms/${md5(name)}`; + const path = `/app/rooms/${md5(name)}`; - return ( - - {name} - ({userCount}) - - ); + return ( + + {name} + ({userCount}) + + ); }; Room.propTypes = { - name: PropTypes.string.isRequired, - data: ImmutablePropTypes.map.isRequired + name: PropTypes.string.isRequired, + data: ImmutablePropTypes.map.isRequired, }; export default Room; diff --git a/src/components/RoomChat.js b/src/components/RoomChat.js index e062dcf..74da11a 100644 --- a/src/components/RoomChat.js +++ b/src/components/RoomChat.js @@ -12,88 +12,79 @@ import RoomChatMessageList from "../components/RoomChatMessageList"; const ID = "room-chat"; class RoomChat extends React.Component { - constructor(props) { - super(props); - } + constructor(props) { + super(props); + } - componentDidMount() { - this.join_if_non_member(this.props); - } + componentDidMount() { + this.join_if_non_member(this.props); + } - componentWillReceiveProps(props) { - this.join_if_non_member(props); - } + componentWillReceiveProps(props) { + this.join_if_non_member(props); + } - join_if_non_member(props) { - const { room, roomActions } = props; - if (room && room.membership == "NonMember") { - roomActions.join(room.name); - } + join_if_non_member(props) { + const { room, roomActions } = props; + if (room && room.membership == "NonMember") { + roomActions.join(room.name); } + } - render() { - const { loginUserName, room, roomActions } = this.props; + render() { + const { loginUserName, room, roomActions } = this.props; - if (!room) { - return ( -
- -
- ); - } - - const { name, membership, messages, showUsers } = room; + if (!room) { + return ( +
+ +
+ ); + } - const header = ( - - ); + const { name, membership, messages, showUsers } = room; - if (membership != "Member") { - return ( -
- {header} -
- ); - } + const header = ( + + ); - // room.membership == "Member" - return ( -
- {header} - - -
- ); + if (membership != "Member") { + return
{header}
; } + + // room.membership == "Member" + return ( +
+ {header} + + +
+ ); + } } RoomChat.propTypes = { - loginUserName: PropTypes.string, - room: PropTypes.shape({ - name: PropTypes.string.isRequired, - membership: PropTypes.string.isRequired, - messages: ImmutablePropTypes.list.isRequired, - showUsers: PropTypes.bool - }), - roomActions: PropTypes.shape({ - join: PropTypes.func.isRequired, - sendMessage: PropTypes.func.isRequired - }).isRequired + loginUserName: PropTypes.string, + room: PropTypes.shape({ + name: PropTypes.string.isRequired, + membership: PropTypes.string.isRequired, + messages: ImmutablePropTypes.list.isRequired, + showUsers: PropTypes.bool, + }), + roomActions: PropTypes.shape({ + join: PropTypes.func.isRequired, + sendMessage: PropTypes.func.isRequired, + }).isRequired, }; export default RoomChat; diff --git a/src/components/RoomChatForm.js b/src/components/RoomChatForm.js index 9b67b80..9e427eb 100644 --- a/src/components/RoomChatForm.js +++ b/src/components/RoomChatForm.js @@ -1,43 +1,41 @@ -import React, {PropTypes} from "react"; -import {reduxForm} from "redux-form"; +import React, { PropTypes } from "react"; +import { reduxForm } from "redux-form"; const RoomChatForm = (props) => { - const { - fields: { message }, - handleSubmit, - resetForm, - roomName, - sendMessage - } = props; + const { + fields: { message }, + handleSubmit, + resetForm, + roomName, + sendMessage, + } = props; - const onSubmit = handleSubmit((values) => { - sendMessage(roomName, values.message); - resetForm(); - }); + const onSubmit = handleSubmit((values) => { + sendMessage(roomName, values.message); + resetForm(); + }); - return ( -
-
- - -
-
- ); + return ( +
+
+ + +
+
+ ); }; RoomChatForm.propTypes = { - fields: PropTypes.shape({ - message: PropTypes.object.isRequired - }).isRequired, - handleSubmit: PropTypes.func.isRequired, - resetForm: PropTypes.func.isRequired, - roomName: PropTypes.string, - sendMessage: PropTypes.func.isRequired + fields: PropTypes.shape({ + message: PropTypes.object.isRequired, + }).isRequired, + handleSubmit: PropTypes.func.isRequired, + resetForm: PropTypes.func.isRequired, + roomName: PropTypes.string, + sendMessage: PropTypes.func.isRequired, }; export default reduxForm({ - form: "chat", - fields: ["message"] + form: "chat", + fields: ["message"], })(RoomChatForm); - diff --git a/src/components/RoomChatHeader.js b/src/components/RoomChatHeader.js index 7cdfa46..149cf5d 100644 --- a/src/components/RoomChatHeader.js +++ b/src/components/RoomChatHeader.js @@ -3,64 +3,59 @@ import { withRouter } from "react-router"; import ImmutablePropTypes from "react-immutable-proptypes"; const make_header = (title, showUsersButton, leaveButton) => ( -
-
{title}
- {showUsersButton} - {leaveButton} -
+
+
{title}
+ {showUsersButton} + {leaveButton} +
); const RoomChatHeader = ({ room, roomActions, router }) => { - if (!room) { - return make_header("Select a room"); + if (!room) { + return make_header("Select a room"); + } + + switch (room.membership) { + case "Member": { + const onClickLeave = (event) => { + router.push("/app/rooms"); + roomActions.leave(room.name); + }; + const leaveButton = ; + + let toggleUsersButton; + if (room.showUsers) { + const onClick = (event) => roomActions.hideUsers(room.name); + toggleUsersButton = ; + } else { + const onClick = (event) => roomActions.showUsers(room.name); + toggleUsersButton = ; + } + + return make_header(room.name, toggleUsersButton, leaveButton); } - switch (room.membership) { - case "Member": - { - const onClickLeave = (event) => { - router.push("/app/rooms"); - roomActions.leave(room.name); - }; - const leaveButton = ; + case "NonMember": + return make_header(`Not a member of ${room.name}`); - let toggleUsersButton; - if (room.showUsers) { - const onClick = (event) => roomActions.hideUsers(room.name); - toggleUsersButton = ( - - ); - } else { - const onClick = (event) => roomActions.showUsers(room.name); - toggleUsersButton = ( - - ); - } + case "Joining": + return make_header(`Joining ${room.name}`); - return make_header(room.name, toggleUsersButton, leaveButton); - } - - case "NonMember": - return make_header(`Not a member of ${room.name}`); - - case "Joining": - return make_header(`Joining ${room.name}`); - - case "Leaving": - return make_header(`Leaving ${room.name}`); - } + case "Leaving": + return make_header(`Leaving ${room.name}`); + } }; RoomChatHeader.propTypes = { - room: PropTypes.shape({ - membership: PropTypes.string.isRequired, - name: PropTypes.string.isRequired, - showUsers: PropTypes.bool - }), - roomActions: PropTypes.shape({ - leave: PropTypes.func.isRequired - }).isRequired, - router: PropTypes.object.isRequired + room: PropTypes.shape({ + membership: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, + showUsers: PropTypes.bool, + }), + roomActions: PropTypes.shape({ + leave: PropTypes.func.isRequired, + }).isRequired, + router: PropTypes.object.isRequired, }; export default withRouter(RoomChatHeader); diff --git a/src/components/RoomChatMessageList.js b/src/components/RoomChatMessageList.js index 97e730d..dca61b8 100644 --- a/src/components/RoomChatMessageList.js +++ b/src/components/RoomChatMessageList.js @@ -2,39 +2,38 @@ import React, { PropTypes } from "react"; import ImmutablePropTypes from "react-immutable-proptypes"; const RoomChatMessageList = ({ loginUserName, messages }) => { - // Append all messages in the chat room. - const children = []; - let i = 0; - for (const { user_name, message } of messages) { - if (user_name == loginUserName) { - children.push( -
  • -
    {message}
    -
  • - ); - } else { - children.push( -
  • -
    {user_name}
    -
    {message}
    -
  • - ); - } - i++; + // Append all messages in the chat room. + const children = []; + let i = 0; + for (const { user_name, message } of messages) { + if (user_name == loginUserName) { + children.push( +
  • +
    {message}
    +
  • + ); + } else { + children.push( +
  • +
    {user_name}
    +
    {message}
    +
  • + ); } + i++; + } - return
      {children}
    ; + return
      {children}
    ; }; RoomChatMessageList.propTypes = { - loginUserName: PropTypes.string.isRequired, - messages: ImmutablePropTypes.listOf( - PropTypes.shape({ - user_name: PropTypes.string.isRequired, - message: PropTypes.string.isRequired - }).isRequired - ).isRequired + loginUserName: PropTypes.string.isRequired, + messages: ImmutablePropTypes.listOf( + PropTypes.shape({ + user_name: PropTypes.string.isRequired, + message: PropTypes.string.isRequired, + }).isRequired + ).isRequired, }; export default RoomChatMessageList; - diff --git a/src/components/RoomList.js b/src/components/RoomList.js index 7b02dab..f8e62e4 100644 --- a/src/components/RoomList.js +++ b/src/components/RoomList.js @@ -7,18 +7,18 @@ import SearchableList from "./SearchableList"; const ComposedSearchableList = SearchableList(Room); const RoomList = ({ rooms, roomActions }) => ( - + ); RoomList.propTypes = { - rooms: ImmutablePropTypes.record.isRequired, - roomActions: PropTypes.shape({ - getList: PropTypes.func.isRequired - }).isRequired + rooms: ImmutablePropTypes.record.isRequired, + roomActions: PropTypes.shape({ + getList: PropTypes.func.isRequired, + }).isRequired, }; export default RoomList; diff --git a/src/components/RoomListHeader.js b/src/components/RoomListHeader.js index fc00749..9826c2a 100644 --- a/src/components/RoomListHeader.js +++ b/src/components/RoomListHeader.js @@ -1,20 +1,20 @@ import React, { PropTypes } from "react"; const RoomListHeader = ({ refresh }) => { - return ( -
    -
    -

    Room List

    -
    -
    - -
    -
    - ); + return ( +
    +
    +

    Room List

    +
    +
    + +
    +
    + ); }; RoomListHeader.propTypes = { - refresh: PropTypes.func.isRequired + refresh: PropTypes.func.isRequired, }; export default RoomListHeader; diff --git a/src/components/RoomUserList.js b/src/components/RoomUserList.js index 90f400e..884ec48 100644 --- a/src/components/RoomUserList.js +++ b/src/components/RoomUserList.js @@ -1,25 +1,23 @@ import React, { PropTypes } from "react"; const RoomUserList = ({ users }) => { - // Append all users - const children = []; - let i = 0; - for (const user of users) { - children.push( -
  • - {user} -
  • - ); - i++; - } + // Append all users + const children = []; + let i = 0; + for (const user of users) { + children.push( +
  • + {user} +
  • + ); + i++; + } - return
      {children}
    ; + return
      {children}
    ; }; RoomUserList.propTypes = { - users: PropTypes.arrayOf( - PropTypes.string.isRequired - ).isRequired + users: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired, }; export default RoomUserList; diff --git a/src/components/SearchableList.js b/src/components/SearchableList.js index d7452fb..63aecd5 100644 --- a/src/components/SearchableList.js +++ b/src/components/SearchableList.js @@ -2,64 +2,56 @@ import React, { PropTypes } from "react"; import ImmutablePropTypes from "react-immutable-proptypes"; const SearchableList = (ItemComponent) => { - class ComposedSearchableList extends React.Component { - constructor(props) { - super(props); - } - - componentDidMount() { - const { itemMap, refresh } = this.props; - if (itemMap.shouldUpdate()) { - refresh(); - } - } - - - render() { - const { id, itemMap, refresh } = this.props; - - const children = []; - - for (const [itemName, itemData] of itemMap.byName) { - children.push( -
  • - -
  • - ); - } + class ComposedSearchableList extends React.Component { + constructor(props) { + super(props); + } - const onClick = (event) => { - event.preventDefault(); - refresh(); - }; + componentDidMount() { + const { itemMap, refresh } = this.props; + if (itemMap.shouldUpdate()) { + refresh(); + } + } - return ( -
    -
    -
    - -
    -
    -
      - {children} -
    -
    - ); - } + render() { + const { id, itemMap, refresh } = this.props; + + const children = []; + + for (const [itemName, itemData] of itemMap.byName) { + children.push( +
  • + +
  • + ); + } + + const onClick = (event) => { + event.preventDefault(); + refresh(); + }; + + return ( +
    +
    +
    + +
    +
    +
      {children}
    +
    + ); } + } - ComposedSearchableList.propTypes = { - id: PropTypes.string.isRequired, - itemMap: ImmutablePropTypes.record.isRequired, - refresh: PropTypes.func.isRequired - }; + ComposedSearchableList.propTypes = { + id: PropTypes.string.isRequired, + itemMap: ImmutablePropTypes.record.isRequired, + refresh: PropTypes.func.isRequired, + }; - return ComposedSearchableList; + return ComposedSearchableList; }; export default SearchableList; diff --git a/src/components/SocketStatusPane.tsx b/src/components/SocketStatusPane.tsx index 1fb97ee..d4bbe12 100644 --- a/src/components/SocketStatusPane.tsx +++ b/src/components/SocketStatusPane.tsx @@ -17,8 +17,8 @@ function socketStateToString(socket: SocketSliceState): string { } interface Props { - socket: SocketSliceState, -}; + socket: SocketSliceState; +} const SocketStatusPane: React.FC = ({ socket }: Props) => (
    diff --git a/src/components/SolsticeApp.js b/src/components/SolsticeApp.js index 83a8279..f4c3c9d 100644 --- a/src/components/SolsticeApp.js +++ b/src/components/SolsticeApp.js @@ -25,9 +25,7 @@ const ConnectedApp: React.FC = ({ socket, children }) => { return (
    -
    - {children} -
    +
    {children}
    ); @@ -39,9 +37,7 @@ const SolsticeApp = ({ socket, children }) => ( - - {children} - + {children} ); diff --git a/src/components/User.js b/src/components/User.js index bc969cb..5d7a3ab 100644 --- a/src/components/User.js +++ b/src/components/User.js @@ -3,20 +3,17 @@ import { Link } from "react-router"; import md5 from "md5"; const User = ({ name }) => { - const path = `/app/users/${md5(name)}`; + const path = `/app/users/${md5(name)}`; - return ( - - {name} - - ); + return ( + + {name} + + ); }; User.propTypes = { - name: PropTypes.string.isRequired + name: PropTypes.string.isRequired, }; export default User; diff --git a/src/components/UserList.js b/src/components/UserList.js index 213f5c7..e438c13 100644 --- a/src/components/UserList.js +++ b/src/components/UserList.js @@ -7,16 +7,16 @@ import User from "./User"; const ComposedSearchableList = SearchableList(User); const UserList = ({ users, userActions }) => ( - + ); UserList.propTypes = { - users: ImmutablePropTypes.record.isRequired, - userActions: PropTypes.object.isRequired + users: ImmutablePropTypes.record.isRequired, + userActions: PropTypes.object.isRequired, }; export default UserList; diff --git a/src/constants/ActionTypes.js b/src/constants/ActionTypes.js index 1571f0d..7b2239b 100644 --- a/src/constants/ActionTypes.js +++ b/src/constants/ActionTypes.js @@ -1,21 +1,21 @@ // Socket actions -export const SOCKET_SET_OPEN = "SOCKET_SET_OPEN"; -export const SOCKET_SET_OPENING = "SOCKET_SET_OPENING"; -export const SOCKET_SET_CLOSED = "SOCKET_SET_CLOSED"; -export const SOCKET_SET_CLOSING = "SOCKET_SET_CLOSING"; -export const SOCKET_SET_ERROR = "SOCKET_SET_ERROR"; +export const SOCKET_SET_OPEN = "SOCKET_SET_OPEN"; +export const SOCKET_SET_OPENING = "SOCKET_SET_OPENING"; +export const SOCKET_SET_CLOSED = "SOCKET_SET_CLOSED"; +export const SOCKET_SET_CLOSING = "SOCKET_SET_CLOSING"; +export const SOCKET_SET_ERROR = "SOCKET_SET_ERROR"; export const SOCKET_RECEIVE_MESSAGE = "SOCKET_RECEIVE_MESSAGE"; -export const SOCKET_SEND_MESSAGE = "SOCKET_SEND_MESSAGE"; +export const SOCKET_SEND_MESSAGE = "SOCKET_SEND_MESSAGE"; // Login actions export const LOGIN_GET_STATUS = "LOGIN_GET_STATUS"; // Room actions -export const ROOM_GET_LIST = "ROOM_GET_LIST"; -export const ROOM_SELECT = "ROOM_SELECT"; -export const ROOM_JOIN = "ROOM_JOIN"; -export const ROOM_LEAVE = "ROOM_LEAVE"; -export const ROOM_MESSAGE = "ROOM_MESSAGE"; +export const ROOM_GET_LIST = "ROOM_GET_LIST"; +export const ROOM_SELECT = "ROOM_SELECT"; +export const ROOM_JOIN = "ROOM_JOIN"; +export const ROOM_LEAVE = "ROOM_LEAVE"; +export const ROOM_MESSAGE = "ROOM_MESSAGE"; export const ROOM_SHOW_USERS = "ROOM_SHOW_USERS"; export const ROOM_HIDE_USERS = "ROOM_HIDE_USERS"; diff --git a/src/containers/ConnectPage.js b/src/containers/ConnectPage.js index 49d6917..d7cc2b0 100644 --- a/src/containers/ConnectPage.js +++ b/src/containers/ConnectPage.js @@ -2,7 +2,7 @@ import { useDispatch, useSelector } from "react-redux"; import { useLocation } from "react-router"; import { Redirect } from "react-router-dom"; -import ConnectForm from "../components/ConnectForm"; +import ConnectForm from "../components/ConnectForm"; import { LoginStatus, selectLogin } from "../modules/login/slice"; import { loginStatusRequest } from "../modules/login/message"; import { @@ -27,18 +27,20 @@ const ConnectPage: React.FC = () => { // not seem to respond to our request for the login status, however. Until // that is the case, we cannot block on receiving a response... return ( - + ); } return ( -
    - -
    +
    + +
    ); -} +}; export default ConnectPage; diff --git a/src/containers/Footer.js b/src/containers/Footer.js index 2aded09..71c634a 100644 --- a/src/containers/Footer.js +++ b/src/containers/Footer.js @@ -16,6 +16,6 @@ const Footer: FC = () => { ); -} +}; export default Footer; diff --git a/src/containers/RoomsPane.js b/src/containers/RoomsPane.js index 954d568..7adc4a8 100644 --- a/src/containers/RoomsPane.js +++ b/src/containers/RoomsPane.js @@ -10,66 +10,58 @@ import RoomList from "../components/RoomList"; import RoomUserList from "../components/RoomUserList"; const RoomsPane = (props) => { - const { loginUserName, params, rooms, roomActions } = props; + const { loginUserName, params, rooms, roomActions } = props; - let roomName; - let roomChat; + let roomName; + let roomChat; - if (params && params.roomNameHash) { - roomName = rooms.getNameByHash(params.roomNameHash); + if (params && params.roomNameHash) { + roomName = rooms.getNameByHash(params.roomNameHash); - const roomData = rooms.getByName(roomName); + const roomData = rooms.getByName(roomName); - if (roomData) { - const room = { - name: roomName, - membership: roomData.membership, - messages: roomData.messages, - showUsers: roomData.showUsers - }; + if (roomData) { + const room = { + name: roomName, + membership: roomData.membership, + messages: roomData.messages, + showUsers: roomData.showUsers, + }; - roomChat = ( - - ); - } + roomChat = ( + + ); } + } - return ( -
    - -
    - {roomChat} -
    -
    - ); + return ( +
    + +
    {roomChat}
    +
    + ); }; RoomsPane.propTypes = { - loginUserName: PropTypes.string.isRequired, - params: PropTypes.shape({ - roomNameHash: PropTypes.string - }), - rooms: ImmutablePropTypes.record.isRequired, - roomActions: PropTypes.object.isRequired + loginUserName: PropTypes.string.isRequired, + params: PropTypes.shape({ + roomNameHash: PropTypes.string, + }), + rooms: ImmutablePropTypes.record.isRequired, + roomActions: PropTypes.object.isRequired, }; const mapStateToProps = (state) => ({ - loginUserName: state.login.username, - rooms: state.rooms + loginUserName: state.login.username, + rooms: state.rooms, }); const mapDispatchToProps = (dispatch) => ({ - roomActions: bindActionCreators(RoomActions, dispatch) + roomActions: bindActionCreators(RoomActions, dispatch), }); -export default connect( - mapStateToProps, - mapDispatchToProps -)(RoomsPane); +export default connect(mapStateToProps, mapDispatchToProps)(RoomsPane); diff --git a/src/containers/UsersPane.js b/src/containers/UsersPane.js index a300a80..aceaf73 100644 --- a/src/containers/UsersPane.js +++ b/src/containers/UsersPane.js @@ -8,25 +8,22 @@ import UserActions from "../actions/UserActions"; import UserList from "../components/UserList"; const UsersPane = ({ users, userActions }) => { - return ( -
    - -
    - ); + return ( +
    + +
    + ); }; UsersPane.propTypes = { - users: ImmutablePropTypes.record.isRequired, - userActions: PropTypes.object.isRequired + users: ImmutablePropTypes.record.isRequired, + userActions: PropTypes.object.isRequired, }; const mapStateToProps = ({ users }) => ({ users }); const mapDispatchToProps = (dispatch) => ({ - userActions: bindActionCreators(UserActions, dispatch) + userActions: bindActionCreators(UserActions, dispatch), }); -export default connect( - mapStateToProps, - mapDispatchToProps -)(UsersPane); +export default connect(mapStateToProps, mapDispatchToProps)(UsersPane); diff --git a/src/features/counter/Counter.module.css b/src/features/counter/Counter.module.css index 025bb72..ce3e2d3 100644 --- a/src/features/counter/Counter.module.css +++ b/src/features/counter/Counter.module.css @@ -18,7 +18,7 @@ padding-left: 16px; padding-right: 16px; margin-top: 2px; - font-family: 'Courier New', Courier, monospace; + font-family: "Courier New", Courier, monospace; } .button { @@ -60,7 +60,7 @@ } .asyncButton:after { - content: ''; + content: ""; background-color: rgba(112, 76, 182, 0.15); display: block; position: absolute; diff --git a/src/features/counter/Counter.tsx b/src/features/counter/Counter.tsx index ece5191..39063d8 100644 --- a/src/features/counter/Counter.tsx +++ b/src/features/counter/Counter.tsx @@ -1,6 +1,6 @@ -import React, { useState } from 'react'; +import React, { useState } from "react"; -import { useAppSelector, useAppDispatch } from '../../app/hooks'; +import { useAppSelector, useAppDispatch } from "../../app/hooks"; import { decrement, increment, @@ -8,13 +8,13 @@ import { incrementAsync, incrementIfOdd, selectCount, -} from './counterSlice'; -import styles from './Counter.module.css'; +} from "./counterSlice"; +import styles from "./Counter.module.css"; export function Counter() { const count = useAppSelector(selectCount); const dispatch = useAppDispatch(); - const [incrementAmount, setIncrementAmount] = useState('2'); + const [incrementAmount, setIncrementAmount] = useState("2"); const incrementValue = Number(incrementAmount) || 0; diff --git a/src/features/counter/counterSlice.spec.ts b/src/features/counter/counterSlice.spec.ts index 098163b..1ce1976 100644 --- a/src/features/counter/counterSlice.spec.ts +++ b/src/features/counter/counterSlice.spec.ts @@ -3,31 +3,31 @@ import counterReducer, { increment, decrement, incrementByAmount, -} from './counterSlice'; +} from "./counterSlice"; -describe('counter reducer', () => { +describe("counter reducer", () => { const initialState: CounterState = { value: 3, - status: 'idle', + status: "idle", }; - it('should handle initial state', () => { - expect(counterReducer(undefined, { type: 'unknown' })).toEqual({ + it("should handle initial state", () => { + expect(counterReducer(undefined, { type: "unknown" })).toEqual({ value: 0, - status: 'idle', + status: "idle", }); }); - it('should handle increment', () => { + it("should handle increment", () => { const actual = counterReducer(initialState, increment()); expect(actual.value).toEqual(4); }); - it('should handle decrement', () => { + it("should handle decrement", () => { const actual = counterReducer(initialState, decrement()); expect(actual.value).toEqual(2); }); - it('should handle incrementByAmount', () => { + it("should handle incrementByAmount", () => { const actual = counterReducer(initialState, incrementByAmount(2)); expect(actual.value).toEqual(5); }); diff --git a/src/features/counter/counterSlice.ts b/src/features/counter/counterSlice.ts index 0010332..8fbc431 100644 --- a/src/features/counter/counterSlice.ts +++ b/src/features/counter/counterSlice.ts @@ -1,15 +1,15 @@ -import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'; -import { RootState, AppThunk } from '../../app/store'; -import { fetchCount } from './counterAPI'; +import { createAsyncThunk, createSlice, PayloadAction } from "@reduxjs/toolkit"; +import { RootState, AppThunk } from "../../app/store"; +import { fetchCount } from "./counterAPI"; export interface CounterState { value: number; - status: 'idle' | 'loading' | 'failed'; + status: "idle" | "loading" | "failed"; } const initialState: CounterState = { value: 0, - status: 'idle', + status: "idle", }; // The function below is called a thunk and allows us to perform async logic. It @@ -18,7 +18,7 @@ const initialState: CounterState = { // code can then be executed and other actions can be dispatched. Thunks are // typically used to make async requests. export const incrementAsync = createAsyncThunk( - 'counter/fetchCount', + "counter/fetchCount", async (amount: number) => { const response = await fetchCount(amount); // The value we return becomes the `fulfilled` action payload @@ -27,7 +27,7 @@ export const incrementAsync = createAsyncThunk( ); export const counterSlice = createSlice({ - name: 'counter', + name: "counter", initialState, // The `reducers` field lets us define reducers and generate associated actions reducers: { @@ -51,10 +51,10 @@ export const counterSlice = createSlice({ extraReducers: (builder) => { builder .addCase(incrementAsync.pending, (state) => { - state.status = 'loading'; + state.status = "loading"; }) .addCase(incrementAsync.fulfilled, (state, action) => { - state.status = 'idle'; + state.status = "idle"; state.value += action.payload; }); }, @@ -69,14 +69,13 @@ export const selectCount = (state: RootState) => state.counter.value; // We can also write thunks by hand, which may contain both sync and async logic. // Here's an example of conditionally dispatching actions based on current state. -export const incrementIfOdd = (amount: number): AppThunk => ( - dispatch, - getState -) => { - const currentValue = selectCount(getState()); - if (currentValue % 2 === 1) { - dispatch(incrementByAmount(amount)); - } -}; +export const incrementIfOdd = + (amount: number): AppThunk => + (dispatch, getState) => { + const currentValue = selectCount(getState()); + if (currentValue % 2 === 1) { + dispatch(incrementByAmount(amount)); + } + }; export default counterSlice.reducer; diff --git a/src/index.tsx b/src/index.tsx index bdc712e..0a790c1 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,9 +1,9 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import { Provider } from 'react-redux'; +import React from "react"; +import ReactDOM from "react-dom"; +import { Provider } from "react-redux"; import { BrowserRouter as Router } from "react-router-dom"; -import './styles/styles.scss'; +import "./styles/styles.scss"; import { store } from "./app/store"; @@ -17,5 +17,5 @@ ReactDOM.render( , - document.getElementById('root') + document.getElementById("root") ); diff --git a/src/modules/login/message.ts b/src/modules/login/message.ts index cb4f6af..1848bc1 100644 --- a/src/modules/login/message.ts +++ b/src/modules/login/message.ts @@ -5,7 +5,7 @@ export function loginStatusRequest(): SocketMessage { return { variant: "LoginStatusRequest", fields: [], - } + }; } const loginStatusResponseHandler: SocketMessageHandler = { @@ -14,14 +14,14 @@ const loginStatusResponseHandler: SocketMessageHandler = { handleData: ({ variant, fields }) => { switch (variant) { case "Pending": { - const [ username ] = fields; + const [username] = fields; return loginSetState({ status: LoginStatus.Pending, username, }); } case "Success": { - const [ username, motd ] = data.fields; + const [username, motd] = data.fields; return loginSetState({ status: LoginStatus.Success, username, @@ -30,7 +30,7 @@ const loginStatusResponseHandler: SocketMessageHandler = { } case "Failure": { - const [ username, reason ] = data.fields; + const [username, reason] = data.fields; return loginSetState({ status: LoginStatus.Failure, username, @@ -41,6 +41,4 @@ const loginStatusResponseHandler: SocketMessageHandler = { }, }; -export const loginSocketMessageHandlers = [ - loginStatusResponseHandler, -]; +export const loginSocketMessageHandlers = [loginStatusResponseHandler]; diff --git a/src/modules/login/slice.ts b/src/modules/login/slice.ts index 981a95c..9a1c6af 100644 --- a/src/modules/login/slice.ts +++ b/src/modules/login/slice.ts @@ -1,6 +1,6 @@ -import { createAction, createSlice, PayloadAction } from '@reduxjs/toolkit'; +import { createAction, createSlice, PayloadAction } from "@reduxjs/toolkit"; -import { RootState } from '../../app/store'; +import { RootState } from "../../app/store"; export enum LoginStatus { Unknown, @@ -8,14 +8,14 @@ export enum LoginStatus { Pending, Success, Failure, -}; +} export interface LoginSliceState { - status: LoginStatus, - username?: string, - motd?: string, - reason?: string, -}; + status: LoginStatus; + username?: string; + motd?: string; + reason?: string; +} const initialState: LoginSliceState = { status: LoginStatus.Unknown, @@ -25,13 +25,15 @@ const initialState: LoginSliceState = { }; export const loginSlice = createSlice({ - name: 'login', + name: "login", initialState, reducers: { - loginSetState: - (state: RootState, action: PayloadAction) => { - state = action.payload; - }, + loginSetState: ( + state: RootState, + action: PayloadAction + ) => { + state = action.payload; + }, }, }); diff --git a/src/modules/websocket/actions.ts b/src/modules/websocket/actions.ts index 6aa538f..eb33ce7 100644 --- a/src/modules/websocket/actions.ts +++ b/src/modules/websocket/actions.ts @@ -1,5 +1,5 @@ -export const wsConnect = host => ({ type: 'WS_CONNECT', host }); -export const wsConnecting = host => ({ type: 'WS_CONNECTING', host }); -export const wsConnected = host => ({ type: 'WS_CONNECTED', host }); -export const wsDisconnect = () => ({ type: 'WS_DISCONNECT' }); -export const wsDisconnected = () => ({ type: 'WS_DISCONNECTED' }); +export const wsConnect = (host) => ({ type: "WS_CONNECT", host }); +export const wsConnecting = (host) => ({ type: "WS_CONNECTING", host }); +export const wsConnected = (host) => ({ type: "WS_CONNECTED", host }); +export const wsDisconnect = () => ({ type: "WS_DISCONNECT" }); +export const wsDisconnected = () => ({ type: "WS_DISCONNECTED" }); diff --git a/src/modules/websocket/message.ts b/src/modules/websocket/message.ts index 2a555f6..3162b29 100644 --- a/src/modules/websocket/message.ts +++ b/src/modules/websocket/message.ts @@ -4,9 +4,9 @@ import { PayloadAction } from "@reduxjs/toolkit"; // The type of a message exchanged over a websocket. export interface SocketMessage { - variant: string, - fields?: List, -}; + variant: string; + fields?: List; +} // Serializes the given message for sending over the wire. export function serializeMessage(message: SocketMessage): string { @@ -22,7 +22,7 @@ export function parseMessage(serialized: string): SocketMessage { } return { variant, fields }; -}; +} // Handles the `data` field of a `SocketMessage` and returns an optional action. export type SocketMessageDataHandler = (any) => PayloadAction | undefined; @@ -34,6 +34,6 @@ export type SocketMessageDataHandler = (any) => PayloadAction | undefined; // their actions only. Otherwise, most reducers would have to handle all // incoming messages. export interface SocketMessageHandler { - variant: string, - handleData: SocketMessageDataHandler, -}; + variant: string; + handleData: SocketMessageDataHandler; +} diff --git a/src/modules/websocket/middleware.ts b/src/modules/websocket/middleware.ts index 5f658e4..6a762dd 100644 --- a/src/modules/websocket/middleware.ts +++ b/src/modules/websocket/middleware.ts @@ -23,8 +23,8 @@ import { AppDispatch, RootState } from "../app/store"; // TODO: use undefined instead, it is more idiomatic. let socket: WebSocket | null = null; -const onOpen = (dispatch: AppDispatch) => event => { - console.log('Websocket open', event.target.url); +const onOpen = (dispatch: AppDispatch) => (event) => { + console.log("Websocket open", event.target.url); dispatch(socketOpened(event.target.url)); }; @@ -32,8 +32,9 @@ const onClose = (dispatch: AppDispatch) => () => { dispatch(socketClosed()); }; -function prepareHandlers(handlers: SocketMessageHandler[]) - : Map { +function prepareHandlers( + handlers: SocketMessageHandler[] +): Map { const map = new Map(); for (const handler of handlers) { @@ -54,35 +55,36 @@ function prepareHandlers(handlers: SocketMessageHandler[]) const onMessage = (dispatch: AppDispatch, handlers: Map) => (event: MessageEvent) => { - console.log("Websocket received message", event.data); - - let message; - try { - message = parseMessage(event.data); - } catch (err) { - console.log("Websocket received invalid message:", err); - return; - } + console.log("Websocket received message", event.data); + + let message; + try { + message = parseMessage(event.data); + } catch (err) { + console.log("Websocket received invalid message:", err); + return; + } - const list = handlers.get(message.variant); - if (list === undefined) { - return; - } + const list = handlers.get(message.variant); + if (list === undefined) { + return; + } - for (handler of list) { - const action = handler(message); - if (action !== undefined) { - dispatch(action); + for (handler of list) { + const action = handler(message); + if (action !== undefined) { + dispatch(action); + } } - } -}; + }; // See: https://redux.js.org/tutorials/fundamentals/part-4-store#writing-custom-middleware -function makeMiddleware(handlers: SocketMessageHandler[]) - : Middleware<{}, RootState> { +function makeMiddleware( + handlers: SocketMessageHandler[] +): Middleware<{}, RootState> { const handlerMap = prepareHandlers(handlers); - return storeApi => next => action => { + return (storeApi) => (next) => (action) => { if (socketOpen.match(action)) { if (socket !== null) { socket.close(); @@ -95,17 +97,15 @@ function makeMiddleware(handlers: SocketMessageHandler[]) socket.onmessage = onMessage(storeApi.dispatch, handlerMap); socket.onclose = onClose(storeApi.dispatch); socket.onopen = onOpen(storeApi.dispatch); - } else if (socketClose.match(action)) { if (socket !== null) { socket.close(); } socket = null; - console.log('Websocket closed.'); - + console.log("Websocket closed."); } else if (socketSendMessage.match(action)) { - console.log('Websocket sending message', action.payload); + console.log("Websocket sending message", action.payload); socket.send(JSON.stringify(action.payload)); } diff --git a/src/modules/websocket/slice.ts b/src/modules/websocket/slice.ts index ce4f8d7..0ff6a34 100644 --- a/src/modules/websocket/slice.ts +++ b/src/modules/websocket/slice.ts @@ -1,26 +1,26 @@ -import { createAction, createSlice, PayloadAction } from '@reduxjs/toolkit'; +import { createAction, createSlice, PayloadAction } from "@reduxjs/toolkit"; import { SocketMessage } from "./message"; -import { RootState } from '../../app/store'; +import { RootState } from "../../app/store"; export enum SocketState { Opening, Open, Closing, Closed, -}; +} export interface SocketSliceState { - state: SocketState, - url?: string, -}; + state: SocketState; + url?: string; +} const initialState: SocketSliceState = { state: SocketState.Closed, }; export const socketSlice = createSlice({ - name: 'socket', + name: "socket", initialState, reducers: { socketOpen: (state, action: PayloadAction) => { diff --git a/src/reducers/index.js b/src/reducers/index.js index 7fe5266..2f29fd4 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -7,11 +7,11 @@ import socket from "./socket"; import users from "./users"; const rootReducer = combineReducers({ - login, - rooms, - socket, - users, - form + login, + rooms, + socket, + users, + form, }); export default rootReducer; diff --git a/src/reducers/login.js b/src/reducers/login.js index 5c490df..3977fbc 100644 --- a/src/reducers/login.js +++ b/src/reducers/login.js @@ -1,75 +1,75 @@ import Immutable from "immutable"; import { - LOGIN_GET_STATUS, - SOCKET_RECEIVE_MESSAGE + LOGIN_GET_STATUS, + SOCKET_RECEIVE_MESSAGE, } from "../constants/ActionTypes"; import { - LOGIN_STATUS_UNKNOWN, - LOGIN_STATUS_GETTING, - LOGIN_STATUS_PENDING, - LOGIN_STATUS_SUCCESS, - LOGIN_STATUS_FAILURE + LOGIN_STATUS_UNKNOWN, + LOGIN_STATUS_GETTING, + LOGIN_STATUS_PENDING, + LOGIN_STATUS_SUCCESS, + LOGIN_STATUS_FAILURE, } from "../constants/login"; const LoginRecord = Immutable.Record({ - status: LOGIN_STATUS_UNKNOWN, - username: undefined, - motd: undefined, - reason: undefined + status: LOGIN_STATUS_UNKNOWN, + username: undefined, + motd: undefined, + reason: undefined, }); const initialState = new LoginRecord(); const reduceReceiveMessage = (state, message) => { - const { variant, data } = message; + const { variant, data } = message; - if (variant !== "LoginStatusResponse") { - return state; - } - - switch (data.variant) { - case "Pending": - { // sub-block required otherwise const username declarations clash - const [ username ] = data.fields; - return state - .set("status", LOGIN_STATUS_PENDING) - .set("username", username); - } - case "Success": - { // sub-block required otherwise const username declarations clash - const [ username, motd ] = data.fields; - return state - .set("status", LOGIN_STATUS_SUCCESS) - .set("username", username) - .set("motd", motd); - } + if (variant !== "LoginStatusResponse") { + return state; + } - case "Failure": - { // sub-block required otherwise const username declarations clash - const [ username, reason ] = data.fields; - return state - .set("status", LOGIN_STATUS_FAILURE) - .set("username", username) - .set("reason", reason); - } + switch (data.variant) { + case "Pending": { + // sub-block required otherwise const username declarations clash + const [username] = data.fields; + return state + .set("status", LOGIN_STATUS_PENDING) + .set("username", username); + } + case "Success": { + // sub-block required otherwise const username declarations clash + const [username, motd] = data.fields; + return state + .set("status", LOGIN_STATUS_SUCCESS) + .set("username", username) + .set("motd", motd); + } - default: - return state; + case "Failure": { + // sub-block required otherwise const username declarations clash + const [username, reason] = data.fields; + return state + .set("status", LOGIN_STATUS_FAILURE) + .set("username", username) + .set("reason", reason); } + + default: + return state; + } }; export default (state = initialState, action) => { - const { type, payload } = action; - switch (type) { - case LOGIN_GET_STATUS: - return state.set("status", LOGIN_STATUS_GETTING); + const { type, payload } = action; + switch (type) { + case LOGIN_GET_STATUS: + return state.set("status", LOGIN_STATUS_GETTING); - case SOCKET_RECEIVE_MESSAGE: - return reduceReceiveMessage(state, payload); + case SOCKET_RECEIVE_MESSAGE: + return reduceReceiveMessage(state, payload); - default: - return state; - } + default: + return state; + } }; diff --git a/src/reducers/rooms.js b/src/reducers/rooms.js index 8c36549..617bd4a 100644 --- a/src/reducers/rooms.js +++ b/src/reducers/rooms.js @@ -3,124 +3,121 @@ import Immutable from "immutable"; import OrderedMap from "../utils/OrderedMap"; import { - ROOM_JOIN, - ROOM_LEAVE, - ROOM_MESSAGE, - ROOM_SHOW_USERS, - ROOM_HIDE_USERS, - SOCKET_RECEIVE_MESSAGE + ROOM_JOIN, + ROOM_LEAVE, + ROOM_MESSAGE, + ROOM_SHOW_USERS, + ROOM_HIDE_USERS, + SOCKET_RECEIVE_MESSAGE, } from "../constants/ActionTypes"; const RoomRecord = Immutable.Record({ - membership: "", - visibility: "", - operated: false, - userCount: 0, - owner: "", - operators: Immutable.Map(), - members: Immutable.Map(), - messages: Immutable.List(), - tickers: Immutable.List() + membership: "", + visibility: "", + operated: false, + userCount: 0, + owner: "", + operators: Immutable.Map(), + members: Immutable.Map(), + messages: Immutable.List(), + tickers: Immutable.List(), }); const initialState = OrderedMap(); const reduceReceiveMessageRoom = (roomData, { variant, data }) => { - switch (variant) { - case "RoomJoinResponse": - return roomData.set("membership", "Member"); - - case "RoomLeaveResponse": - return roomData.set("membership", "NonMember"); - - case "RoomMessageResponse": - { - const { user_name, message } = data; - const messages = roomData.messages.push({user_name, message}); - return roomData.set("messages", messages); - } + switch (variant) { + case "RoomJoinResponse": + return roomData.set("membership", "Member"); + + case "RoomLeaveResponse": + return roomData.set("membership", "NonMember"); + + case "RoomMessageResponse": { + const { user_name, message } = data; + const messages = roomData.messages.push({ user_name, message }); + return roomData.set("messages", messages); } + } }; const reduceReceiveMessage = (state, message) => { - const { variant, data } = message; - switch (variant) { - case "RoomJoinResponse": - case "RoomLeaveResponse": - case "RoomMessageResponse": - { - const { room_name } = data; - return state.updateByName(data.room_name, (roomData) => { - if (roomData) { - return reduceReceiveMessageRoom(roomData, message); - } else { - console.log(`Error: unknown room ${data.room_name}`); - return roomData; - } - }); + const { variant, data } = message; + switch (variant) { + case "RoomJoinResponse": + case "RoomLeaveResponse": + case "RoomMessageResponse": { + const { room_name } = data; + return state.updateByName(data.room_name, (roomData) => { + if (roomData) { + return reduceReceiveMessageRoom(roomData, message); + } else { + console.log(`Error: unknown room ${data.room_name}`); + return roomData; } - - case "RoomListResponse": - return state.updateAll(data.rooms, (newData, oldData) => { - if(!oldData) { - oldData = RoomRecord(); - } - return oldData - .set("membership", newData.membership) - .set("visibility", newData.visibility) - .set("operated", newData.operated) - .set("userCount", newData.user_count) - .set("owner", newData.owner) - .set("operators", newData.operators) - .set("members", newData.members) - .set("tickers", newData.tickers); - }); - - default: - return state; + }); } + + case "RoomListResponse": + return state.updateAll(data.rooms, (newData, oldData) => { + if (!oldData) { + oldData = RoomRecord(); + } + return oldData + .set("membership", newData.membership) + .set("visibility", newData.visibility) + .set("operated", newData.operated) + .set("userCount", newData.user_count) + .set("owner", newData.owner) + .set("operators", newData.operators) + .set("members", newData.members) + .set("tickers", newData.tickers); + }); + + default: + return state; + } }; const reduceRoom = (roomData, { type, payload }) => { - switch (type) { - case ROOM_JOIN: - return roomData.set("membership", "Joining"); + switch (type) { + case ROOM_JOIN: + return roomData.set("membership", "Joining"); - case ROOM_LEAVE: - return roomData.set("membership", "Leaving"); + case ROOM_LEAVE: + return roomData.set("membership", "Leaving"); - case ROOM_SHOW_USERS: - return roomData.set("showUsers", true); + case ROOM_SHOW_USERS: + return roomData.set("showUsers", true); - case ROOM_HIDE_USERS: - return roomData.set("showUsers", false); - } + case ROOM_HIDE_USERS: + return roomData.set("showUsers", false); + } }; export default (state = initialState, action) => { - const { type, payload } = action; - - switch (type) { - case SOCKET_RECEIVE_MESSAGE: - return reduceReceiveMessage(state, payload); - - case ROOM_JOIN: - case ROOM_LEAVE: - case ROOM_SHOW_USERS: - case ROOM_HIDE_USERS: - { - return state.updateByName(payload, (roomData) => { - if (roomData) { - return reduceRoom(roomData, action); - } else { - console.log(`Error: unknown room ${payload}`); - return roomData; - } - }); + const { type, payload } = action; + + switch (type) { + case SOCKET_RECEIVE_MESSAGE: + return reduceReceiveMessage(state, payload); + + case ROOM_JOIN: + case ROOM_LEAVE: + case ROOM_SHOW_USERS: + case ROOM_HIDE_USERS: { + return state.updateByName(payload, (roomData) => { + if (roomData) { + return reduceRoom(roomData, action); + } else { + console.log(`Error: unknown room ${payload}`); + return roomData; } - - case ROOM_MESSAGE: - default: - return state; + }); } + + case ROOM_MESSAGE: + default: + return state; + } }; diff --git a/src/reducers/socket.js b/src/reducers/socket.js index 5d37545..9a8f270 100644 --- a/src/reducers/socket.js +++ b/src/reducers/socket.js @@ -2,92 +2,93 @@ import Immutable from "immutable"; import * as types from "../constants/ActionTypes"; import { - STATE_OPENING, STATE_OPEN, STATE_CLOSING, STATE_CLOSED + STATE_OPENING, + STATE_OPEN, + STATE_CLOSING, + STATE_CLOSED, } from "../constants/socket"; import ControlRequest from "../utils/ControlRequest"; export const SocketRecord = Immutable.Record({ - state: STATE_CLOSED, - socket: undefined, - url: undefined + state: STATE_CLOSED, + socket: undefined, + url: undefined, }); const initialState = new SocketRecord(); export default (state = initialState, { type, payload }) => { - const sendRequest = (controlRequest) => { - try { - state.socket.send(JSON.stringify(controlRequest)); - } catch (err) { - console.log(`Socket error: failed to send ${controlRequest}`); - } - }; - - switch (type) { - case types.SOCKET_SET_OPENING: - { - if (state.state !== STATE_CLOSED) { - console.log("Cannot open socket, already open"); - return state; - } - - const { url, onopen, onclose, onerror, onmessage } = payload; - const socket = new WebSocket(url); - socket.onopen = onopen; - socket.onclose = onclose; - socket.onerror = onerror; - socket.onmessage = onmessage; - - return state - .set("state", STATE_OPENING) - .set("socket", socket) - .set("url", url); - } - - case types.SOCKET_SET_OPEN: - return state.set("state", STATE_OPEN); - - case types.SOCKET_SET_CLOSING: - // Ooh bad stateful reducing... - state.socket.close(); - return state.set("state", STATE_CLOSING); - - case types.SOCKET_SET_CLOSED: - return state.set("state", STATE_CLOSED); - - case types.SOCKET_SET_ERROR: - console.log("Socket error"); - return state.set("state", state.socket.readyState); - - case types.LOGIN_GET_STATUS: - sendRequest(ControlRequest.loginStatus()); - return state; - - case types.ROOM_GET_LIST: - sendRequest(ControlRequest.roomList()); - return state; - - case types.ROOM_JOIN: - sendRequest(ControlRequest.roomJoin(payload)); - return state; - - case types.ROOM_LEAVE: - sendRequest(ControlRequest.roomLeave(payload)); - return state; - - case types.ROOM_MESSAGE: - { - const { room_name, message } = payload; - sendRequest(ControlRequest.roomMessage(room_name, message)); - return state; - } - - case types.USER_GET_LIST: - sendRequest(ControlRequest.userList()); - return state; - - default: - return state; + const sendRequest = (controlRequest) => { + try { + state.socket.send(JSON.stringify(controlRequest)); + } catch (err) { + console.log(`Socket error: failed to send ${controlRequest}`); } + }; + + switch (type) { + case types.SOCKET_SET_OPENING: { + if (state.state !== STATE_CLOSED) { + console.log("Cannot open socket, already open"); + return state; + } + + const { url, onopen, onclose, onerror, onmessage } = payload; + const socket = new WebSocket(url); + socket.onopen = onopen; + socket.onclose = onclose; + socket.onerror = onerror; + socket.onmessage = onmessage; + + return state + .set("state", STATE_OPENING) + .set("socket", socket) + .set("url", url); + } + + case types.SOCKET_SET_OPEN: + return state.set("state", STATE_OPEN); + + case types.SOCKET_SET_CLOSING: + // Ooh bad stateful reducing... + state.socket.close(); + return state.set("state", STATE_CLOSING); + + case types.SOCKET_SET_CLOSED: + return state.set("state", STATE_CLOSED); + + case types.SOCKET_SET_ERROR: + console.log("Socket error"); + return state.set("state", state.socket.readyState); + + case types.LOGIN_GET_STATUS: + sendRequest(ControlRequest.loginStatus()); + return state; + + case types.ROOM_GET_LIST: + sendRequest(ControlRequest.roomList()); + return state; + + case types.ROOM_JOIN: + sendRequest(ControlRequest.roomJoin(payload)); + return state; + + case types.ROOM_LEAVE: + sendRequest(ControlRequest.roomLeave(payload)); + return state; + + case types.ROOM_MESSAGE: { + const { room_name, message } = payload; + sendRequest(ControlRequest.roomMessage(room_name, message)); + return state; + } + + case types.USER_GET_LIST: + sendRequest(ControlRequest.userList()); + return state; + + default: + return state; + } }; diff --git a/src/reducers/users.js b/src/reducers/users.js index 550ecd5..5887a31 100644 --- a/src/reducers/users.js +++ b/src/reducers/users.js @@ -5,49 +5,47 @@ import OrderedMap from "../utils/OrderedMap"; import { SOCKET_RECEIVE_MESSAGE } from "../constants/ActionTypes"; const UserRecord = Immutable.Record({ - status: "", - averageSpeed: 0, - numDownloads: 0, - numFiles: 0, - numFolders: 0, - numFreeSlots: 0, - country: "" + status: "", + averageSpeed: 0, + numDownloads: 0, + numFiles: 0, + numFolders: 0, + numFreeSlots: 0, + country: "", }); const initialState = OrderedMap(); const reduceUsersReceiveMessage = (users, message) => { - switch (message.variant) { - case "UserListResponse": - return users.updateAll(message.data.user_list, - (newUser, oldUser) => { - if (!oldUser) { - oldUser = UserRecord(); - } - return oldUser - .set("status", newUser.status) - .set("averageSpeed", newUser.average_speed) - .set("numDownloads", newUser.num_downloads) - .set("numFiles", newUser.num_files) - .set("numFolders", newUser.num_folders) - .set("numFreeSlots", newUser.num_free_slots) - .set("country", newUser.country); - } - ); - - default: - return users; - } + switch (message.variant) { + case "UserListResponse": + return users.updateAll(message.data.user_list, (newUser, oldUser) => { + if (!oldUser) { + oldUser = UserRecord(); + } + return oldUser + .set("status", newUser.status) + .set("averageSpeed", newUser.average_speed) + .set("numDownloads", newUser.num_downloads) + .set("numFiles", newUser.num_files) + .set("numFolders", newUser.num_folders) + .set("numFreeSlots", newUser.num_free_slots) + .set("country", newUser.country); + }); + + default: + return users; + } }; const reduceUsers = (users = initialState, action) => { - switch (action.type) { - case SOCKET_RECEIVE_MESSAGE: - return reduceUsersReceiveMessage(users, action.payload); + switch (action.type) { + case SOCKET_RECEIVE_MESSAGE: + return reduceUsersReceiveMessage(users, action.payload); - default: - return users; - } + default: + return users; + } }; export default reduceUsers; diff --git a/src/setupTests.ts b/src/setupTests.ts index 74b1a27..5fdf001 100644 --- a/src/setupTests.ts +++ b/src/setupTests.ts @@ -2,4 +2,4 @@ // allows you to do things like: // expect(element).toHaveTextContent(/react/i) // learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom/extend-expect'; +import "@testing-library/jest-dom/extend-expect"; diff --git a/src/styles/styles.scss b/src/styles/styles.scss index b9c1a7c..5577348 100644 --- a/src/styles/styles.scss +++ b/src/styles/styles.scss @@ -1,236 +1,238 @@ /* Styles */ body { - font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; - line-height: 1.4em; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - font-smoothing: antialiased; - font-weight: 300; - padding: 0 !important; - margin: 0 !important; - height: 100%; + font: 14px "Helvetica Neue", Helvetica, Arial, sans-serif; + line-height: 1.4em; + -webkit-font-smoothing: antialiased; + -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; + padding: 0; + margin: 0; + height: 100vh; } -#app, #solstice-app { - height: 100%; - margin: 0; - padding: 0; +#app, +#solstice-app { + height: 100%; + margin: 0; + padding: 0; } #solstice-app { - display: flex; - flex-flow: column; - justify-content: center; - align-items: center; + display: flex; + flex-flow: column; + justify-content: center; + align-items: center; } header { - margin: 0; - padding: 1.5em 2em; - display: flex; - flex-flow: row; - justify-content: space-around; - width: 100%; + margin: 0; + padding: 1.5em 2em; + display: flex; + flex-flow: row; + justify-content: space-around; + width: 100%; } header h1 { - margin: 0; + margin: 0; } footer { - display: flex; - padding: 0.5em; - width: 100%; - box-sizing: border-box; + display: flex; + padding: 0.5em; + width: 100%; + box-sizing: border-box; } main { - width: 100%; - height: 85%; - margin: 0; - padding: 0; + width: 100%; + height: 85%; + margin: 0; + padding: 0; } #rooms-pane { - display: flex; - border: solid grey 0.1em; - height: 100%; + display: flex; + border: solid grey 0.1em; + height: 100%; } #room-chat { - border: solid grey 0.1em; + border: solid grey 0.1em; } #room-list { - flex: 1; - display: flex; - flex-flow: column; - border: solid grey 0.1em; - height: 100%; + flex: 1; + display: flex; + flex-flow: column; + border: solid grey 0.1em; + height: 100%; } #room-selected-pane { - height: 100%; - flex: 3; - display: flex; - flex-flow: row; + height: 100%; + flex: 3; + display: flex; + flex-flow: row; } #room-chat { - height: 100%; - max-width: 100%; - flex: 2; - display: flex; - flex-flow: column; - justify-content: space-between; + height: 100%; + max-width: 100%; + flex: 2; + display: flex; + flex-flow: column; + justify-content: space-between; } #room-user-list { - flex: 1; - display: block; - list-style: none; - margin: 0; - padding: 1em; + flex: 1; + display: block; + list-style: none; + margin: 0; + padding: 1em; } #room-chat-header { - flex: 1; - padding: 0.8em; - border: solid grey 0.1em; + flex: 1; + padding: 0.8em; + border: solid grey 0.1em; - display: flex; - flex-flow: row; - justify-content: space-between; - align-items: center; + display: flex; + flex-flow: row; + justify-content: space-between; + align-items: center; } #room-chat-header-title { - font-size: 1.3em; - text-align: center; - flex: 1; + font-size: 1.3em; + text-align: center; + flex: 1; } #room-chat-header > button { - padding: 0.5em 1em; + padding: 0.5em 1em; } #room-chat-message-list { - display: block; - height: 83%; - width: 100%; - box-sizing: border-box; - margin: 0; - padding: 1em; + display: block; + height: 83%; + width: 100%; + box-sizing: border-box; + margin: 0; + padding: 1em; - overflow: auto; + overflow: auto; - list-style: none; + list-style: none; } .room-chat-message { - display: flex; - flex-flow: column; - align-items: flex-start; - margin-right: 3em; + display: flex; + flex-flow: column; + align-items: flex-start; + margin-right: 3em; } .room-chat-message-me { - align-items: flex-end; - margin-left: 3em; - margin-right: 0; + align-items: flex-end; + margin-left: 3em; + margin-right: 0; } .room-chat-message-user { - font-weight: bold; - color: blue; + font-weight: bold; + color: blue; } .room-chat-message-text { - padding: 0.5em 0.7em; - margin: 0.2em 0.5em; - border-radius: 0.8em; - background-color: lightgrey; + padding: 0.5em 0.7em; + margin: 0.2em 0.5em; + border-radius: 0.8em; + background-color: lightgrey; } .room-chat-message-me > .room-chat-message-text { - background-color: blue; - color: white; + background-color: blue; + color: white; } #room-chat-form { - width: 100%; - border: solid grey 0.1em; + width: 100%; + border: solid grey 0.1em; } #room-chat-form form { - width: 100%; - display: flex; - flex-flow: row; + width: 100%; + display: flex; + flex-flow: row; } #room-chat-form input { - flex: 1; - padding: 0.8em; + flex: 1; + padding: 0.8em; } #room-list-header { - flex: 1; - display: flex; - flex-flow: row; - border: solid grey 0.1em; + flex: 1; + display: flex; + flex-flow: row; + border: solid grey 0.1em; } #room-list-header > div { - flex: 1; - border: solid grey 0.1em; + flex: 1; + border: solid grey 0.1em; } #room-list ul { - display: block; - list-style: none; - padding: 0; - margin: 0; - height: 84%; - overflow-y: auto; + display: block; + list-style: none; + padding: 0; + margin: 0; + height: 84%; + overflow-y: auto; } .room { - display: flex; - justify-content: space-between; - color: inherit; - text-decoration: inherit; - padding: .5em 1em; - border: solid grey 0.1em; + display: flex; + justify-content: space-between; + color: inherit; + text-decoration: inherit; + padding: 0.5em 1em; + border: solid grey 0.1em; } .room:hover { - background: lightgrey; + background: lightgrey; } .room:active { - background: grey; + background: grey; } .room-joined { - background: lightgreen; + background: lightgreen; } .room-selected { - background: lightblue; + background: lightblue; } -#login-status-pane, #socket-status-pane { - flex: 1; +#login-status-pane, +#socket-status-pane { + flex: 1; } #connect-form { - display: flex; - flex-flow: column; - align-items: center; + display: flex; + flex-flow: column; + align-items: center; } diff --git a/src/utils/ControlRequest.js b/src/utils/ControlRequest.js index cd84558..425663c 100644 --- a/src/utils/ControlRequest.js +++ b/src/utils/ControlRequest.js @@ -1,34 +1,36 @@ export default { - loginStatus: () => ({ - variant: "LoginStatusRequest", - fields: [] - }), + loginStatus: () => ({ + variant: "LoginStatusRequest", + fields: [], + }), - roomJoin: (room_name) => ({ - variant: "RoomJoinRequest", - fields: [room_name] - }), + roomJoin: (room_name) => ({ + variant: "RoomJoinRequest", + fields: [room_name], + }), - roomLeave: (room_name) => ({ - variant: "RoomLeaveRequest", - fields: [room_name] - }), + roomLeave: (room_name) => ({ + variant: "RoomLeaveRequest", + fields: [room_name], + }), - roomList: () => ({ - variant: "RoomListRequest", - fields: [] - }), + roomList: () => ({ + variant: "RoomListRequest", + fields: [], + }), - roomMessage: (room_name, message) => ({ - variant: "RoomMessageRequest", - fields: [{ - room_name, - message - }] - }), + roomMessage: (room_name, message) => ({ + variant: "RoomMessageRequest", + fields: [ + { + room_name, + message, + }, + ], + }), - userList: () =>({ - variant: "UserListRequest", - fields: [] - }) + userList: () => ({ + variant: "UserListRequest", + fields: [], + }), }; diff --git a/src/utils/OrderedMap.js b/src/utils/OrderedMap.js index 54dc95c..8ba7311 100644 --- a/src/utils/OrderedMap.js +++ b/src/utils/OrderedMap.js @@ -5,80 +5,80 @@ import md5 from "md5"; const UPDATE_INTERVAL_MS = 5 * 60 * 1000; const MapRecord = Immutable.Record({ - byName: Immutable.OrderedMap(), - byHash: Immutable.Map(), - lastUpdated: 0 + byName: Immutable.OrderedMap(), + byHash: Immutable.Map(), + lastUpdated: 0, }); // TODO: use a regular map and a reversible name -> hash encoding (e.g. base64). // This would entirely remove the need for this complicated logic. class OrderedMap extends MapRecord { - constructor(arg) { - super(arg); - } + constructor(arg) { + super(arg); + } - getByName(name) { - return this.getIn(["byName", name]); - } + getByName(name) { + return this.getIn(["byName", name]); + } - setByName(name, data) { - let thisModified = this; - if (!this.getByName(name)) { - // That key was not there yet, add hash -> name mapping. - thisModified = this.setIn(["byHash", md5(name)], name); - } - // Add data to map. - return thisModified.setIn(["byName", name], data); + setByName(name, data) { + let thisModified = this; + if (!this.getByName(name)) { + // That key was not there yet, add hash -> name mapping. + thisModified = this.setIn(["byHash", md5(name)], name); } + // Add data to map. + return thisModified.setIn(["byName", name], data); + } - updateByName(name, updater) { - return this.updateIn(["byName", name], updater); - } + updateByName(name, updater) { + return this.updateIn(["byName", name], updater); + } - getNameByHash(hash) { - return this.getIn(["byHash", hash]); - } + getNameByHash(hash) { + return this.getIn(["byHash", hash]); + } - updateAll(nameAndDataList, merger) { - const { byName } = this; - let { byHash } = this; + updateAll(nameAndDataList, merger) { + const { byName } = this; + let { byHash } = this; - // First sort the room list by room name - nameAndDataList.sort(([ name1 ], [ name2 ]) => { - if (name1 < name2) { - return -1; - } else if (name1 > name2) { - return 1; - } - return 0; - }); + // First sort the room list by room name + nameAndDataList.sort(([name1], [name2]) => { + if (name1 < name2) { + return -1; + } else if (name1 > name2) { + return 1; + } + return 0; + }); - // Then build the new map. - let newByName = Immutable.OrderedMap(); + // Then build the new map. + let newByName = Immutable.OrderedMap(); - for (const [ name, newData ] of nameAndDataList) { - // Get the old room data. - let data = byName.get(name); - if (!data) { - // Add the hash -> name mapping. - byHash = byHash.set(md5(name), name); - } - // Merge the old data and the new data using the provided function. - const mergedData = merger(newData, data); - // Insert that in the new room map. - newByName = newByName.set(name, mergedData); - } - - return new OrderedMap({ - byName: newByName, - byHash, - lastUpdated: Date.now() - }); + for (const [name, newData] of nameAndDataList) { + // Get the old room data. + let data = byName.get(name); + if (!data) { + // Add the hash -> name mapping. + byHash = byHash.set(md5(name), name); + } + // Merge the old data and the new data using the provided function. + const mergedData = merger(newData, data); + // Insert that in the new room map. + newByName = newByName.set(name, mergedData); } - shouldUpdate() { - return (Date.now() - this.lastUpdated) > UPDATE_INTERVAL_MS; - } + return new OrderedMap({ + byName: newByName, + byHash, + lastUpdated: Date.now(), + }); + } + + shouldUpdate() { + return Date.now() - this.lastUpdated > UPDATE_INTERVAL_MS; + } } export default () => new OrderedMap(); diff --git a/src/utils/propTypeRequiredWrapper.js b/src/utils/propTypeRequiredWrapper.js index c53f837..c7e14fb 100644 --- a/src/utils/propTypeRequiredWrapper.js +++ b/src/utils/propTypeRequiredWrapper.js @@ -1,17 +1,16 @@ -const checkRequiredThenValidate = (validator) => - (props, propName, componentName, location) => -{ +const checkRequiredThenValidate = + (validator) => (props, propName, componentName, location) => { if (props[propName] != null) { - return validator(props, propName, componentName, location); + return validator(props, propName, componentName, location); } return new Error( - `Required prop \`${propName}\` was not specified in ` + + `Required prop \`${propName}\` was not specified in ` + `\`${componentName}\`.` ); -}; + }; export default (validator) => { - validator.isRequired = checkRequiredThenValidate(validator); - return validator; + validator.isRequired = checkRequiredThenValidate(validator); + return validator; }; diff --git a/src/utils/propTypeSymbol.js b/src/utils/propTypeSymbol.js index 7c39ffa..c14d3d4 100644 --- a/src/utils/propTypeSymbol.js +++ b/src/utils/propTypeSymbol.js @@ -2,19 +2,18 @@ import propTypeRequiredWrapper from "./propTypeRequiredWrapper"; // Simple validator for Symbols (instanceof does not work on symbols). function propTypeSymbol(props, propName, componentName) { - const prop = props[propName]; - if (prop === null) { - return; - } - const type = typeof prop; - if (type === "symbol") { - return; - } - return new Error( - `Invalid prop \`${propName}\` of type \`${type}\` ` + - `supplied to \`${componentName}\`, expected \`symbol\`` - ); + const prop = props[propName]; + if (prop === null) { + return; + } + const type = typeof prop; + if (type === "symbol") { + return; + } + return new Error( + `Invalid prop \`${propName}\` of type \`${type}\` ` + + `supplied to \`${componentName}\`, expected \`symbol\`` + ); } export default propTypeRequiredWrapper(propTypeSymbol); -