Webclient: Handle firing an event once (#4499)
* draft: handle firing an event once * lint * Prevent rapid double-click on sending messages * no rest spread on single primative when sibling components exist * clear message instead of using a fireOnce handler. * fix tests * remove unnecessary validate mock
This commit is contained in:
parent
4bb13677c8
commit
513fcb0908
16 changed files with 21467 additions and 161 deletions
21225
webclient/package-lock.json
generated
21225
webclient/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -6,6 +6,8 @@
|
|||
"@material-ui/core": "^4.12.3",
|
||||
"@material-ui/icons": "^4.11.2",
|
||||
"@material-ui/styles": "^4.11.4",
|
||||
"@testing-library/jest-dom": "^5.16.1",
|
||||
"@testing-library/react": "^12.1.2",
|
||||
"crypto-js": "^4.1.1",
|
||||
"dexie": "^3.0.3",
|
||||
"final-form": "^4.20.4",
|
||||
|
@ -57,6 +59,11 @@
|
|||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"jest": {
|
||||
"moduleNameMapper": {
|
||||
"\\.(css|less)$": "identity-obj-proxy"
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "27.0.2",
|
||||
"@types/jquery": "^3.5.8",
|
||||
|
@ -76,6 +83,7 @@
|
|||
"@typescript-eslint/parser": "^5.3.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^7.32.0",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"run-script-os": "^1.1.6"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,23 +1,27 @@
|
|||
// eslint-disable-next-line
|
||||
import React from "react";
|
||||
import { Field } from 'redux-form'
|
||||
import React from 'react';
|
||||
import { Field } from 'react-final-form'
|
||||
import Button from '@material-ui/core/Button';
|
||||
|
||||
import { InputField } from 'components';
|
||||
|
||||
import './InputAction.css';
|
||||
|
||||
const InputAction = ({ action, label, name }) => (
|
||||
const InputAction = ({ action, label, name, validate, disabled }) => (
|
||||
<div className="input-action">
|
||||
<div className="input-action__item">
|
||||
<Field label={label} name={name} component={InputField} />
|
||||
<Field label={label} name={name} component={InputField} validate={validate} />
|
||||
</div>
|
||||
<div className="input-action__submit">
|
||||
<Button color="primary" variant="contained" type="submit">
|
||||
<Button color="primary" variant="contained" type="submit" disabled={disabled}>
|
||||
{action}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
InputAction.defaultProps = {
|
||||
disabled: false,
|
||||
validate: () => true,
|
||||
}
|
||||
|
||||
export default InputAction;
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
// eslint-disable-next-line
|
||||
import React from "react";
|
||||
import { connect } from 'react-redux';
|
||||
import { Form, reduxForm } from 'redux-form'
|
||||
import React from 'react';
|
||||
import { Form } from 'react-final-form'
|
||||
|
||||
import { InputAction } from 'components';
|
||||
import { FormKey } from 'types';
|
||||
|
||||
const AddToBuddies = ({ handleSubmit }) => (
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<InputAction action="Add" label="Add to Buddies" name="userName" />
|
||||
const AddToBuddies = ({ onSubmit }) => (
|
||||
<Form onSubmit={values => onSubmit(values)}>
|
||||
{({ handleSubmit }) => (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<InputAction action="Add" label="Add to Buddies" name="userName" />
|
||||
</form>
|
||||
)}
|
||||
</Form>
|
||||
);
|
||||
|
||||
const propsMap = {
|
||||
form: FormKey.ADD_TO_BUDDIES
|
||||
};
|
||||
|
||||
export default connect()(reduxForm(propsMap)(AddToBuddies));
|
||||
export default AddToBuddies;
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
// eslint-disable-next-line
|
||||
import React from "react";
|
||||
import { connect } from 'react-redux';
|
||||
import { Form, reduxForm } from 'redux-form'
|
||||
import React from 'react';
|
||||
import { Form } from 'react-final-form'
|
||||
|
||||
import { InputAction } from 'components';
|
||||
import { FormKey } from 'types';
|
||||
|
||||
const AddToIgnore = ({ handleSubmit }) => (
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<InputAction action="Add" label="Add to Ignore" name="userName" />
|
||||
const AddToIgnore = ({ onSubmit }) => (
|
||||
<Form onSubmit={values => onSubmit(values)}>
|
||||
{({ handleSubmit }) => (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<InputAction action="Add" label="Add to Ignore" name="userName" />
|
||||
</form>
|
||||
)}
|
||||
</Form>
|
||||
);
|
||||
|
||||
const propsMap = {
|
||||
form: FormKey.ADD_TO_IGNORE,
|
||||
};
|
||||
|
||||
export default connect()(reduxForm(propsMap)(AddToIgnore));
|
||||
export default AddToIgnore;
|
||||
|
|
|
@ -11,7 +11,7 @@ import Typography from '@material-ui/core/Typography';
|
|||
import { AuthenticationService } from 'api';
|
||||
import { RegistrationDialog, RequestPasswordResetDialog, ResetPasswordDialog } from 'dialogs';
|
||||
import { LoginForm } from 'forms';
|
||||
import { useReduxEffect } from 'hooks';
|
||||
import { useReduxEffect, useFireOnce } from 'hooks';
|
||||
import { Images } from 'images';
|
||||
import { HostDTO } from 'services';
|
||||
import { RouteEnum, WebSocketConnectOptions, getHostPort } from 'types';
|
||||
|
@ -77,15 +77,6 @@ const Login = ({ state, description }: LoginProps) => {
|
|||
closeResetPasswordDialog();
|
||||
}, ServerTypes.RESET_PASSWORD_SUCCESS, []);
|
||||
|
||||
useReduxEffect(({ options: { hashedPassword } }) => {
|
||||
if (hostIdToRemember) {
|
||||
HostDTO.get(hostIdToRemember).then(host => {
|
||||
host.hashedPassword = hashedPassword;
|
||||
host.save();
|
||||
});
|
||||
}
|
||||
}, ServerTypes.LOGIN_SUCCESSFUL, [hostIdToRemember]);
|
||||
|
||||
const showDescription = () => {
|
||||
return !isConnected && description?.length;
|
||||
};
|
||||
|
@ -121,6 +112,19 @@ const Login = ({ state, description }: LoginProps) => {
|
|||
AuthenticationService.login(options as WebSocketConnectOptions);
|
||||
}, []);
|
||||
|
||||
const [submitButtonDisabled, resetSubmitButton, handleLogin] = useFireOnce(onSubmitLogin)
|
||||
|
||||
useReduxEffect(({ options: { hashedPassword } }) => {
|
||||
if (hostIdToRemember) {
|
||||
HostDTO.get(hostIdToRemember).then(host => {
|
||||
host.hashedPassword = hashedPassword;
|
||||
host.save();
|
||||
});
|
||||
}
|
||||
resetSubmitButton()
|
||||
}, ServerTypes.LOGIN_SUCCESSFUL, [hostIdToRemember]);
|
||||
|
||||
|
||||
const updateHost = ({ selectedHost, userName, hashedPassword, remember }) => {
|
||||
HostDTO.get(selectedHost.id).then(hostDTO => {
|
||||
hostDTO.remember = remember;
|
||||
|
@ -208,7 +212,11 @@ const Login = ({ state, description }: LoginProps) => {
|
|||
<Typography variant="h1">Login</Typography>
|
||||
<Typography variant="subtitle1">A cross-platform virtual tabletop for multiplayer card games.</Typography>
|
||||
<div className="login-form">
|
||||
<LoginForm onSubmit={onSubmitLogin} onResetPassword={openRequestPasswordResetDialog} />
|
||||
<LoginForm
|
||||
onSubmit={handleLogin}
|
||||
onResetPassword={openRequestPasswordResetDialog}
|
||||
disableSubmitButton={submitButtonDisabled}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
// eslint-disable-next-line
|
||||
import React, { Component } from "react";
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { withRouter, generatePath } from 'react-router-dom';
|
||||
|
||||
|
@ -8,7 +7,7 @@ import Paper from '@material-ui/core/Paper';
|
|||
|
||||
import { RoomsService } from 'api';
|
||||
import { ScrollToBottomOnChanges, ThreePaneLayout, UserDisplay, VirtualList, AuthGuard } from 'components';
|
||||
import { RoomsStateMessages, RoomsStateRooms, JoinedRooms, RoomsSelectors } from 'store';
|
||||
import { RoomsStateMessages, RoomsStateRooms, JoinedRooms, RoomsSelectors, RoomsTypes } from 'store';
|
||||
import { RouteEnum } from 'types';
|
||||
|
||||
import OpenGames from './OpenGames';
|
||||
|
@ -18,86 +17,73 @@ import SayMessage from './SayMessage';
|
|||
import './Room.css';
|
||||
|
||||
// @TODO (3)
|
||||
class Room extends Component<any> {
|
||||
componentDidUpdate() {
|
||||
const { joined, match, history } = this.props;
|
||||
let { roomId } = match.params;
|
||||
function Room(props) {
|
||||
|
||||
roomId = parseInt(roomId, 0);
|
||||
const { joined, match, history, rooms, messages } = props;
|
||||
const roomId = parseInt(match.params.roomId, 0);
|
||||
|
||||
if (!joined.find(({ roomId: id }) => id === roomId)) {
|
||||
history.push(generatePath(RouteEnum.SERVER));
|
||||
}
|
||||
if (!joined.find(({ roomId: id }) => id === roomId)) {
|
||||
history.push(generatePath(RouteEnum.SERVER));
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleRoomSay = this.handleRoomSay.bind(this);
|
||||
}
|
||||
|
||||
handleRoomSay({ message }) {
|
||||
function handleRoomSay({ message }) {
|
||||
if (message) {
|
||||
const { roomId } = this.props.match.params;
|
||||
RoomsService.roomSay(roomId, message);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { match, rooms } = this.props;
|
||||
const { roomId } = match.params;
|
||||
const room = rooms[roomId];
|
||||
const room = rooms[roomId];
|
||||
|
||||
const messages = this.props.messages[roomId];
|
||||
const users = room.userList;
|
||||
const roomMessages = messages[roomId];
|
||||
const users = room.userList;
|
||||
|
||||
return (
|
||||
<div className="room-view">
|
||||
<AuthGuard />
|
||||
return (
|
||||
<div className="room-view">
|
||||
<AuthGuard />
|
||||
|
||||
<div className="room-view__main">
|
||||
<ThreePaneLayout
|
||||
fixedHeight
|
||||
<div className="room-view__main">
|
||||
<ThreePaneLayout
|
||||
fixedHeight
|
||||
|
||||
top={(
|
||||
<Paper className="room-view__games overflow-scroll">
|
||||
<OpenGames room={room} />
|
||||
top={(
|
||||
<Paper className="room-view__games overflow-scroll">
|
||||
<OpenGames room={room} />
|
||||
</Paper>
|
||||
)}
|
||||
|
||||
bottom={(
|
||||
<div className="room-view__messages">
|
||||
<Paper className="room-view__messages-content overflow-scroll">
|
||||
<ScrollToBottomOnChanges changes={roomMessages} content={(
|
||||
<Messages messages={roomMessages} />
|
||||
)} />
|
||||
</Paper>
|
||||
)}
|
||||
<Paper className="room-view__messages-sayMessage">
|
||||
<SayMessage onSubmit={handleRoomSay} />
|
||||
</Paper>
|
||||
</div>
|
||||
)}
|
||||
|
||||
bottom={(
|
||||
<div className="room-view__messages">
|
||||
<Paper className="room-view__messages-content overflow-scroll">
|
||||
<ScrollToBottomOnChanges changes={messages} content={(
|
||||
<Messages messages={messages} />
|
||||
)} />
|
||||
</Paper>
|
||||
<Paper className="room-view__messages-sayMessage">
|
||||
<SayMessage onSubmit={this.handleRoomSay} />
|
||||
</Paper>
|
||||
</div>
|
||||
)}
|
||||
|
||||
side={(
|
||||
<Paper className="room-view__side overflow-scroll">
|
||||
<div className="room-view__side-label">
|
||||
side={(
|
||||
<Paper className="room-view__side overflow-scroll">
|
||||
<div className="room-view__side-label">
|
||||
Users in this room: {users.length}
|
||||
</div>
|
||||
<VirtualList
|
||||
className="room-view__side-list"
|
||||
itemKey={(index, data) => users[index].name }
|
||||
items={ users.map(user => (
|
||||
<ListItem button className="room-view__side-list__item">
|
||||
<UserDisplay user={user} />
|
||||
</ListItem>
|
||||
)) }
|
||||
/>
|
||||
</Paper>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<VirtualList
|
||||
className="room-view__side-list"
|
||||
itemKey={(index, data) => users[index].name }
|
||||
items={ users.map(user => (
|
||||
<ListItem button className="room-view__side-list__item">
|
||||
<UserDisplay user={user} />
|
||||
</ListItem>
|
||||
)) }
|
||||
/>
|
||||
</Paper>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface RoomProps {
|
||||
|
|
|
@ -1,18 +1,24 @@
|
|||
// eslint-disable-next-line
|
||||
import React from "react";
|
||||
import { connect } from 'react-redux';
|
||||
import { Form, reduxForm } from 'redux-form'
|
||||
import React from 'react';
|
||||
import { Form } from 'react-final-form'
|
||||
|
||||
import { InputAction } from 'components';
|
||||
|
||||
const SayMessage = ({ handleSubmit }) => (
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<InputAction action="Send" label="Chat" name="message" />
|
||||
</Form>
|
||||
);
|
||||
const required = (value) => (value ? undefined : 'Required');
|
||||
|
||||
const propsMap = {
|
||||
form: 'sayMessage'
|
||||
};
|
||||
const SayMessage = (props) => {
|
||||
const { onSubmit } = props
|
||||
return (
|
||||
<Form onSubmit={values => onSubmit(values)}>
|
||||
{({ handleSubmit, form }) => (
|
||||
<form onSubmit={e => {
|
||||
handleSubmit(e)
|
||||
form.restart()
|
||||
}}>
|
||||
<InputAction action="Send" label="Chat" name="message" validate={required}/>
|
||||
</form>
|
||||
)}
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect()(reduxForm(propsMap)(SayMessage));
|
||||
export default SayMessage;
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
// eslint-disable-next-line
|
||||
import React, { Component, useCallback, useEffect, useState, useRef } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Form, Field, useField } from 'react-final-form';
|
||||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import { Form, Field } from 'react-final-form';
|
||||
import { OnChange } from 'react-final-form-listeners';
|
||||
|
||||
import Button from '@material-ui/core/Button';
|
||||
|
@ -10,14 +8,14 @@ import { AuthenticationService } from 'api';
|
|||
import { CheckboxField, InputField, KnownHosts } from 'components';
|
||||
import { useAutoConnect } from 'hooks';
|
||||
import { HostDTO, SettingDTO } from 'services';
|
||||
import { FormKey, APP_USER } from 'types';
|
||||
import { APP_USER } from 'types';
|
||||
|
||||
import './LoginForm.css';
|
||||
|
||||
const PASSWORD_LABEL = 'Password';
|
||||
const STORED_PASSWORD_LABEL = '* SAVED *';
|
||||
|
||||
const LoginForm = ({ onSubmit, onResetPassword }: LoginFormProps) => {
|
||||
const LoginForm = ({ onSubmit, disableSubmitButton, onResetPassword }: LoginFormProps) => {
|
||||
const [host, setHost] = useState(null);
|
||||
const [passwordLabel, setPasswordLabel] = useState(PASSWORD_LABEL);
|
||||
const [autoConnect, setAutoConnect] = useAutoConnect();
|
||||
|
@ -43,6 +41,8 @@ const LoginForm = ({ onSubmit, onResetPassword }: LoginFormProps) => {
|
|||
setPasswordLabel(useStoredLabel ? STORED_PASSWORD_LABEL : PASSWORD_LABEL);
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<Form onSubmit={onSubmit} validate={validate}>
|
||||
{({ handleSubmit, form }) => {
|
||||
|
@ -140,7 +140,13 @@ const LoginForm = ({ onSubmit, onResetPassword }: LoginFormProps) => {
|
|||
<OnChange name="autoConnect">{onAutoConnectChange}</OnChange>
|
||||
</div>
|
||||
</div>
|
||||
<Button className='loginForm-submit rounded tall' color='primary' variant='contained' type='submit'>
|
||||
<Button
|
||||
className='loginForm-submit rounded tall'
|
||||
color='primary'
|
||||
variant='contained'
|
||||
type='submit'
|
||||
disabled={disableSubmitButton}
|
||||
>
|
||||
Login
|
||||
</Button>
|
||||
</form>
|
||||
|
@ -152,6 +158,7 @@ const LoginForm = ({ onSubmit, onResetPassword }: LoginFormProps) => {
|
|||
|
||||
interface LoginFormProps {
|
||||
onSubmit: any;
|
||||
disableSubmitButton: boolean,
|
||||
onResetPassword: any;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
export * from './useDebounce';
|
||||
export * from './useAutoConnect';
|
||||
export * from './useFireOnce';
|
||||
export * from './useDebounce';
|
||||
export * from './useReduxEffect';
|
||||
|
|
1
webclient/src/hooks/useFireOnce/index.ts
Normal file
1
webclient/src/hooks/useFireOnce/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from './useFireOnce'
|
103
webclient/src/hooks/useFireOnce/useFireOnce.spec.tsx
Normal file
103
webclient/src/hooks/useFireOnce/useFireOnce.spec.tsx
Normal file
|
@ -0,0 +1,103 @@
|
|||
import {
|
||||
render,
|
||||
fireEvent,
|
||||
getByRole,
|
||||
waitFor,
|
||||
act
|
||||
} from '@testing-library/react';
|
||||
import { useFireOnce } from './useFireOnce';
|
||||
|
||||
describe('useFireOnce hook', () => {
|
||||
test('it only fires once when button is clicked twice', async () => {
|
||||
// Mock a promise with a delay
|
||||
const onClickWithPromise = jest.fn((e) => {
|
||||
e.preventDefault()
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve(true);
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
|
||||
function Button(props) {
|
||||
const { children, onClick } = props
|
||||
const [buttonIsDisabled, setButtonIsDisabled, handleClickOnce] = useFireOnce(onClick)
|
||||
return <button onClick={handleClickOnce} disabled={buttonIsDisabled}>{children}</button>
|
||||
}
|
||||
|
||||
// render the button
|
||||
const { getByRole } = render(
|
||||
<Button onClick={onClickWithPromise}>Click Me!</Button>
|
||||
);
|
||||
|
||||
//Grab the button from the DOM and confirm it initialized in an enabled state
|
||||
const button = getByRole('button', { name: 'Click Me!' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
// Simulate two click events in a row
|
||||
fireEvent.click(button);
|
||||
fireEvent.click(button);
|
||||
|
||||
// Confirm that it's disabled
|
||||
await waitFor(() => {
|
||||
expect(button).toBeDisabled();
|
||||
});
|
||||
|
||||
// Confirm it became enabled after the timeout and that the click event was only fired once
|
||||
await waitFor(
|
||||
() => {
|
||||
expect(onClickWithPromise).toHaveBeenCalledTimes(1);
|
||||
},
|
||||
{ timeout: 100 }
|
||||
);
|
||||
});
|
||||
|
||||
test('it only fires once when form is submitted twice', async () => {
|
||||
// Mock a promise with a delay
|
||||
const onClickWithPromise = jest.fn((e) => {
|
||||
e.preventDefault()
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve(true);
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
|
||||
function Form(props) {
|
||||
const { onSubmit } = props
|
||||
const [buttonIsDisabled, setButtonIsDisabled, handleSubmitOnce] = useFireOnce(onSubmit)
|
||||
return (
|
||||
<form onSubmit={handleSubmitOnce}>
|
||||
<input type="text" defaultValue="Hell World" name="thing-to-say" />
|
||||
<button disabled={buttonIsDisabled}>Click Me!</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
// render the form
|
||||
const { getByRole } = render(
|
||||
<Form onSubmit={onClickWithPromise} />
|
||||
);
|
||||
|
||||
//Grab the button from the DOM and confirm it initialized in an enabled state
|
||||
const button = getByRole('button', { name: 'Click Me!' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
// Simulate two click events in a row
|
||||
fireEvent.click(button);
|
||||
fireEvent.click(button);
|
||||
|
||||
// Confirm that it's disabled
|
||||
await waitFor(() => {
|
||||
expect(button).toBeDisabled();
|
||||
});
|
||||
|
||||
// Confirm it became enabled after the timeout and that the click event was only fired once
|
||||
await waitFor(
|
||||
() => {
|
||||
expect(onClickWithPromise).toHaveBeenCalledTimes(1);
|
||||
},
|
||||
{ timeout: 100 }
|
||||
);
|
||||
});
|
||||
});
|
17
webclient/src/hooks/useFireOnce/useFireOnce.ts
Normal file
17
webclient/src/hooks/useFireOnce/useFireOnce.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { useCallback, useState } from 'react';
|
||||
import { useReduxEffect } from 'hooks';
|
||||
import { ServerTypes } from 'store';
|
||||
|
||||
type UseFireOnceType = (...args: any) => any;
|
||||
|
||||
export function useFireOnce<T extends UseFireOnceType>(fn: T): [boolean, any, any] {
|
||||
const [actionIsInFlight, setActionIsInFlight] = useState(false)
|
||||
const handleFireOnce = useCallback((args) => {
|
||||
setActionIsInFlight(true);
|
||||
fn(args);
|
||||
}, [])
|
||||
function resetInFlightStatus() {
|
||||
setActionIsInFlight(false);
|
||||
}
|
||||
return [actionIsInFlight, resetInFlightStatus, handleFireOnce]
|
||||
}
|
|
@ -1,5 +1,8 @@
|
|||
import protobuf from 'protobufjs';
|
||||
|
||||
// ensure jest-dom is always available during testing to cut down on boilerplate
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
class MockProtobufRoot {
|
||||
load() {}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ export {
|
|||
export * from 'store/server/server.interfaces';
|
||||
|
||||
export {
|
||||
Types as RoomsTypes,
|
||||
Selectors as RoomsSelectors,
|
||||
Dispatch as RoomsDispatch } from 'store/rooms';
|
||||
|
||||
|
|
|
@ -57,7 +57,9 @@ export class WebClient {
|
|||
this.handleStatusChange(status);
|
||||
});
|
||||
|
||||
console.log(this);
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
console.log(this);
|
||||
}
|
||||
}
|
||||
|
||||
public connect(options: WebSocketConnectOptions) {
|
||||
|
|
Loading…
Reference in a new issue