diff --git a/client/src/actions/app.js b/client/src/actions/app.js
index 7e224aa..6cbbdb3 100644
--- a/client/src/actions/app.js
+++ b/client/src/actions/app.js
@@ -19,6 +19,10 @@ export const toggleNotificationEnabled = payload => async (dispatch) => {
dispatch({ type: 'TOGGLE_NOTIFICATION_ENABLED', payload })
}
+export const toggleNotificationAllowed = payload => async (dispatch) => {
+ dispatch({ type: 'TOGGLE_NOTIFICATION_ALLOWED', payload })
+}
+
export const toggleSocketConnected = payload => async (dispatch) => {
dispatch({ type: 'TOGGLE_SOCKET_CONNECTED', payload })
}
diff --git a/client/src/components/Home/Home.js b/client/src/components/Home/Home.js
index e31cb6a..6943b38 100644
--- a/client/src/components/Home/Home.js
+++ b/client/src/components/Home/Home.js
@@ -1,47 +1,46 @@
-import React, { Component } from 'react'
-import PropTypes from 'prop-types'
-import Crypto from 'utils/crypto'
-import { connect as connectSocket } from 'utils/socket'
-import Nav from 'components/Nav'
-import shortId from 'shortid'
-import Connecting from 'components/Connecting'
-import Modal from 'react-modal'
-import About from 'components/About'
-import Settings from 'components/Settings'
-import Welcome from 'components/Welcome'
-import RoomLocked from 'components/RoomLocked'
-import { X, AlertCircle } from 'react-feather'
-import classNames from 'classnames'
-import ActivityList from './ActivityList'
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import Crypto from 'utils/crypto';
+import { connect as connectSocket } from 'utils/socket';
+import Nav from 'components/Nav';
+import shortId from 'shortid';
+import Connecting from 'components/Connecting';
+import Modal from 'react-modal';
+import About from 'components/About';
+import Settings from 'components/Settings';
+import Welcome from 'components/Welcome';
+import RoomLocked from 'components/RoomLocked';
+import { X, AlertCircle } from 'react-feather';
+import classNames from 'classnames';
+import ActivityList from './ActivityList';
-import styles from './styles.module.scss'
+import styles from './styles.module.scss';
-const crypto = new Crypto()
+const crypto = new Crypto();
Modal.setAppElement('#root');
class Home extends Component {
-
async componentWillMount() {
- const roomId = encodeURI(this.props.match.params.roomId)
+ const roomId = encodeURI(this.props.match.params.roomId);
- const user = await this.createUser()
+ const user = await this.createUser();
- const socket = connectSocket(roomId)
+ const socket = connectSocket(roomId);
this.socket = socket;
socket.on('disconnect', () => {
- this.props.toggleSocketConnected(false)
- })
+ this.props.toggleSocketConnected(false);
+ });
socket.on('connect', () => {
- this.initApp(user)
- this.props.toggleSocketConnected(true)
- })
+ this.initApp(user);
+ this.props.toggleSocketConnected(true);
+ });
- socket.on('USER_ENTER', (payload) => {
- this.props.receiveUnencryptedMessage('USER_ENTER', payload)
+ socket.on('USER_ENTER', payload => {
+ this.props.receiveUnencryptedMessage('USER_ENTER', payload);
this.props.sendEncryptedMessage({
type: 'ADD_USER',
payload: {
@@ -50,35 +49,35 @@ class Home extends Component {
isOwner: this.props.iAmOwner,
id: this.props.userId,
},
- })
+ });
if (payload.users.length === 1) {
this.props.openModal('Welcome');
}
- })
-
- socket.on('USER_EXIT', (payload) => {
- this.props.receiveUnencryptedMessage('USER_EXIT', payload)
- })
-
- socket.on('ENCRYPTED_MESSAGE', (payload) => {
- this.props.receiveEncryptedMessage(payload)
- })
-
- socket.on('TOGGLE_LOCK_ROOM', (payload) => {
- this.props.receiveUnencryptedMessage('TOGGLE_LOCK_ROOM', payload)
- })
-
- socket.on('ROOM_LOCKED', (payload) => {
- this.props.openModal('Room Locked')
});
- window.addEventListener('beforeunload', (evt) => {
- socket.emit('USER_DISCONNECT')
+ socket.on('USER_EXIT', payload => {
+ this.props.receiveUnencryptedMessage('USER_EXIT', payload);
+ });
+
+ socket.on('ENCRYPTED_MESSAGE', payload => {
+ this.props.receiveEncryptedMessage(payload);
+ });
+
+ socket.on('TOGGLE_LOCK_ROOM', payload => {
+ this.props.receiveUnencryptedMessage('TOGGLE_LOCK_ROOM', payload);
+ });
+
+ socket.on('ROOM_LOCKED', payload => {
+ this.props.openModal('Room Locked');
+ });
+
+ window.addEventListener('beforeunload', evt => {
+ socket.emit('USER_DISCONNECT');
});
}
componentDidMount() {
- this.bindEvents()
+ this.bindEvents();
}
getModal() {
@@ -88,91 +87,100 @@ class Home extends Component {
component: ,
title: 'Connecting...',
preventClose: true,
- }
+ };
case 'About':
return {
component: ,
title: this.props.translations.aboutHeader,
- }
+ };
case 'Settings':
return {
- component: ,
+ component: (
+
+ ),
title: this.props.translations.settingsHeader,
- }
+ };
case 'Welcome':
return {
- component: ,
+ component: (
+
+ ),
title: this.props.translations.welcomeHeader,
- }
+ };
case 'Room Locked':
return {
component: ,
title: this.props.translations.lockedRoomHeader,
preventClose: true,
- }
+ };
default:
return {
component: null,
title: null,
- }
+ };
}
}
initApp(user) {
this.socket.emit('USER_ENTER', {
publicKey: user.publicKey,
- })
+ });
}
bindEvents() {
window.onfocus = () => {
- this.props.toggleWindowFocus(true)
- }
+ this.props.toggleWindowFocus(true);
+ };
window.onblur = () => {
- this.props.toggleWindowFocus(false)
- }
+ this.props.toggleWindowFocus(false);
+ };
}
createUser() {
- return new Promise(async (resolve) => {
- const username = shortId.generate()
+ return new Promise(async resolve => {
+ const username = shortId.generate();
- const encryptDecryptKeys = await crypto.createEncryptDecryptKeys()
- const exportedEncryptDecryptPrivateKey = await crypto.exportKey(encryptDecryptKeys.privateKey)
- const exportedEncryptDecryptPublicKey = await crypto.exportKey(encryptDecryptKeys.publicKey)
+ const encryptDecryptKeys = await crypto.createEncryptDecryptKeys();
+ const exportedEncryptDecryptPrivateKey = await crypto.exportKey(encryptDecryptKeys.privateKey);
+ const exportedEncryptDecryptPublicKey = await crypto.exportKey(encryptDecryptKeys.publicKey);
this.props.createUser({
username,
publicKey: exportedEncryptDecryptPublicKey,
privateKey: exportedEncryptDecryptPrivateKey,
- })
+ });
resolve({
publicKey: exportedEncryptDecryptPublicKey,
- })
- })
+ });
+ });
}
render() {
- const modalOpts = this.getModal()
+ const modalOpts = this.getModal();
return (
- {!this.props.socketConnected &&
+ {!this.props.socketConnected && (
-
Disconnected
+
+
+ {' '}
+
Disconnected
- }
+ )}
-
+
- {!modalOpts.preventClose &&
+ {!modalOpts.preventClose && (
- }
-
- {modalOpts.title}
-
-
-
- {modalOpts.component}
+ )}
+
{modalOpts.title}
+ {modalOpts.component}
- )
+ );
}
}
Home.defaultProps = {
modalComponent: null,
-}
+};
Home.propTypes = {
receiveEncryptedMessage: PropTypes.func.isRequired,
@@ -249,11 +250,13 @@ Home.propTypes = {
soundIsEnabled: PropTypes.bool.isRequired,
toggleSoundEnabled: PropTypes.func.isRequired,
notificationIsEnabled: PropTypes.bool.isRequired,
+ notificationIsAllowed: PropTypes.bool,
toggleNotificationEnabled: PropTypes.func.isRequired,
+ toggleNotificationAllowed: PropTypes.func.isRequired,
toggleSocketConnected: PropTypes.func.isRequired,
socketConnected: PropTypes.bool.isRequired,
sendUnencryptedMessage: PropTypes.func.isRequired,
- sendEncryptedMessage: PropTypes.func.isRequired
-}
+ sendEncryptedMessage: PropTypes.func.isRequired,
+};
export default Home;
diff --git a/client/src/components/Home/Home.test.js b/client/src/components/Home/Home.test.js
index ecd7999..e0892a5 100644
--- a/client/src/components/Home/Home.test.js
+++ b/client/src/components/Home/Home.test.js
@@ -62,6 +62,7 @@ test('Home component is displaying', async () => {
toggleSoundEnabled={() => {}}
soundIsEnabled={false}
toggleNotificationEnabled={() => {}}
+ toggleNotificationAllowed={() => {}}
notificationIsEnabled={false}
faviconCount={0}
toggleWindowFocus={() => {}}
diff --git a/client/src/components/Home/WithNewMessageNotification.js b/client/src/components/Home/WithNewMessageNotification.js
index a3c21d1..4020b81 100644
--- a/client/src/components/Home/WithNewMessageNotification.js
+++ b/client/src/components/Home/WithNewMessageNotification.js
@@ -2,6 +2,7 @@ import React, { Component } from 'react';
import { connect } from 'react-redux';
import { notify, beep } from 'utils/notifications';
import Tinycon from 'tinycon';
+import { toggleNotificationAllowed, toggleNotificationEnabled } from 'actions';
const mapStateToProps = state => {
return {
@@ -10,12 +11,21 @@ const mapStateToProps = state => {
windowIsFocused: state.app.windowIsFocused,
soundIsEnabled: state.app.soundIsEnabled,
notificationIsEnabled: state.app.notificationIsEnabled,
+ notificationIsAllowed: state.app.notificationIsAllowed,
room: state.room,
};
};
+const mapDispatchToProps = {
+ toggleNotificationAllowed,
+ toggleNotificationEnabled
+};
+
const WithNewMessageNotification = WrappedComponent => {
- return connect(mapStateToProps)(
+ return connect(
+ mapStateToProps,
+ mapDispatchToProps,
+ )(
class WithNotificationHOC extends Component {
state = { lastMessage: null, unreadMessageCount: 0 };
@@ -24,6 +34,7 @@ const WithNewMessageNotification = WrappedComponent => {
room: { id: roomId },
activities,
notificationIsEnabled,
+ notificationIsAllowed,
soundIsEnabled,
unreadMessageCount,
windowIsFocused,
@@ -38,7 +49,7 @@ const WithNewMessageNotification = WrappedComponent => {
if (lastMessage !== prevState.lastMessage && !windowIsFocused) {
const title = `Message from ${username} (${roomId})`;
- if (notificationIsEnabled) notify(title, text);
+ if (notificationIsAllowed && notificationIsEnabled) notify(title, text);
if (soundIsEnabled) beep.play();
}
@@ -49,15 +60,32 @@ const WithNewMessageNotification = WrappedComponent => {
return { lastMessage, unreadMessageCount };
}
+ componentDidMount() {
+ switch (Notification.permission) {
+ case 'granted':
+ this.props.toggleNotificationAllowed(true);
+ this.props.toggleNotificationEnabled(true);
+ break;
+ case 'denied':
+ this.props.toggleNotificationAllowed(false);
+ break;
+ default:
+ this.props.toggleNotificationAllowed(null);
+ }
+ }
+
render() {
// Filter props
const {
room,
activities,
notificationIsEnabled,
+ motificationIsAllowed,
soundIsEnabled,
unreadMessageCount,
windowIsFocused,
+ toggleNotificationAllowed,
+ toggleNotificationnEnabled,
...rest
} = this.props;
return ;
diff --git a/client/src/components/Home/index.js b/client/src/components/Home/index.js
index ee0f3d3..f2a8fab 100644
--- a/client/src/components/Home/index.js
+++ b/client/src/components/Home/index.js
@@ -8,6 +8,7 @@ import {
toggleWindowFocus,
toggleSoundEnabled,
toggleNotificationEnabled,
+ toggleNotificationAllowed,
toggleSocketConnected,
receiveUnencryptedMessage,
sendUnencryptedMessage,
@@ -33,6 +34,7 @@ const mapStateToProps = (state) => {
faviconCount: state.app.unreadMessageCount,
soundIsEnabled: state.app.soundIsEnabled,
notificationIsEnabled: state.app.notificationIsEnabled,
+ notificationIsAllowed: state.app.notificationIsAllowed,
socketConnected: state.app.socketConnected,
language: state.app.language,
translations: state.app.translations,
@@ -47,6 +49,7 @@ const mapDispatchToProps = {
toggleWindowFocus,
toggleSoundEnabled,
toggleNotificationEnabled,
+ toggleNotificationAllowed,
toggleSocketConnected,
receiveUnencryptedMessage,
sendUnencryptedMessage,
diff --git a/client/src/components/Home/index.test.js b/client/src/components/Home/index.test.js
index ebbe573..f792038 100644
--- a/client/src/components/Home/index.test.js
+++ b/client/src/components/Home/index.test.js
@@ -87,6 +87,16 @@ jest.mock('tinycon', () => {
});
describe('Connected Home component', () => {
+ beforeEach(()=>{
+ global.Notification = {
+ permission: 'granted'
+ }
+ })
+
+ afterEach(()=>{
+ delete global.Notification
+ })
+
it('should display', () => {
const { asFragment } = render(
@@ -97,6 +107,43 @@ describe('Connected Home component', () => {
expect(asFragment()).toMatchSnapshot();
});
+ it('should set notification', () => {
+ render(
+
+
+ ,
+ );
+
+ expect(store.getState().app.notificationIsAllowed).toBe(true);
+ expect(store.getState().app.notificationIsEnabled).toBe(true);
+
+ global.Notification = {
+ permission: 'denied'
+ }
+
+ render(
+
+
+ ,
+ );
+
+ expect(store.getState().app.notificationIsAllowed).toBe(false);
+
+ global.Notification = {
+ permission: 'default'
+ }
+
+ render(
+
+
+ ,
+ );
+
+ expect(store.getState().app.notificationIsAllowed).toBe(null);
+
+
+ });
+
it('should send notifications', async () => {
Modal.prototype.getSnapshotBeforeUpdate = jest.fn().mockReturnValue(null);
const { rerender } = render(
diff --git a/client/src/components/Settings/Settings.test.js b/client/src/components/Settings/Settings.test.js
index 33bdd0b..6c2ad46 100644
--- a/client/src/components/Settings/Settings.test.js
+++ b/client/src/components/Settings/Settings.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { render, fireEvent } from '@testing-library/react';
+import { render, fireEvent, waitFor } from '@testing-library/react';
import { Provider } from 'react-redux';
import configureStore from 'store';
@@ -11,17 +11,20 @@ const mockTranslations = {
sound: 'soundCheck',
};
+jest.useFakeTimers();
+
jest.mock('components/RoomLink');
describe('Settings component', () => {
it('should display', async () => {
- const { asFragment } = render(
+ const { asFragment, rerender } = render(
{}}
notificationIsEnabled={true}
toggleNotificationEnabled={() => {}}
+ toggleNotificationAllowed={jest.fn()}
roomId="roomId"
setLanguage={() => {}}
translations={{}}
@@ -30,6 +33,25 @@ describe('Settings component', () => {
);
expect(asFragment()).toMatchSnapshot();
+
+ rerender(
+
+ {}}
+ notificationIsEnabled={true}
+ notificationIsAllowed={false}
+ toggleNotificationEnabled={() => {}}
+ toggleNotificationAllowed={jest.fn()}
+ roomId="roomId"
+ setLanguage={() => {}}
+ translations={{}}
+ />
+ ,
+ );
+
+ expect(asFragment()).toMatchSnapshot();
+
});
it('should toggle sound', async () => {
@@ -40,7 +62,9 @@ describe('Settings component', () => {
soundIsEnabled={true}
toggleSoundEnabled={toggleSound}
notificationIsEnabled={true}
+ notificationIsAllowed={true}
toggleNotificationEnabled={() => {}}
+ toggleNotificationAllowed={jest.fn()}
roomId="roomId"
setLanguage={() => {}}
translations={{}}
@@ -55,6 +79,10 @@ describe('Settings component', () => {
});
it('should toggle notifications', async () => {
+ global.Notification = {
+ requestPermission: jest.fn().mockResolvedValue('granted'),
+ };
+
const toggleNotifications = jest.fn();
const { getByText } = render(
@@ -62,7 +90,9 @@ describe('Settings component', () => {
soundIsEnabled={true}
toggleSoundEnabled={() => {}}
notificationIsEnabled={true}
+ notificationIsAllowed={true}
toggleNotificationEnabled={toggleNotifications}
+ toggleNotificationAllowed={jest.fn()}
roomId="roomId"
setLanguage={() => {}}
translations={{}}
@@ -72,9 +102,48 @@ describe('Settings component', () => {
fireEvent.click(getByText('Desktop Notification'));
- expect(toggleNotifications).toHaveBeenCalledWith(false);
+ jest.runAllTimers();
+
+ delete global.Notification;
+
+ waitFor(() =>expect(toggleNotifications).toHaveBeenCalledWith(false));
+
});
+ it('should not toggle notifications', async () => {
+ global.Notification = {
+ requestPermission: jest.fn().mockResolvedValue('denied'),
+ };
+
+ const toggleNotifications = jest.fn();
+ const toggleAllowed = jest.fn();
+ const { getByText } = render(
+
+ {}}
+ notificationIsEnabled={true}
+ notificationIsAllowed={true}
+ toggleNotificationEnabled={toggleNotifications}
+ toggleNotificationAllowed={toggleAllowed}
+ roomId="roomId"
+ setLanguage={() => {}}
+ translations={{}}
+ />
+ ,
+ );
+
+ fireEvent.click(getByText('Desktop Notification'));
+
+ jest.runAllTimers();
+
+ delete global.Notification;
+
+ waitFor(() =>expect(toggleAllowed).toHaveBeenCalledWith(false));
+ waitFor(() =>expect(toggleNotifications).not.toHaveBeenCalled());
+ });
+
+
it('should change lang', async () => {
const changeLang = jest.fn();
@@ -85,6 +154,7 @@ describe('Settings component', () => {
toggleSoundEnabled={() => {}}
notificationIsEnabled={true}
toggleNotificationEnabled={() => {}}
+ toggleNotificationAllowed={jest.fn()}
roomId="roomId"
setLanguage={changeLang}
translations={{}}
diff --git a/client/src/components/Settings/__snapshots__/Settings.test.js.snap b/client/src/components/Settings/__snapshots__/Settings.test.js.snap
index b0e3c81..c77a891 100644
--- a/client/src/components/Settings/__snapshots__/Settings.test.js.snap
+++ b/client/src/components/Settings/__snapshots__/Settings.test.js.snap
@@ -171,3 +171,169 @@ exports[`Settings component should display 1`] = `
`;
+
+exports[`Settings component should display 2`] = `
+
+
+
+
+ New message notification
+
+
+
+
+
+
+ Language
+
+
+
+ Help us translate Darkwire!
+
+
+
+
+
+ English
+
+
+ Français
+
+
+ Occitan
+
+
+ Deutsch
+
+
+ Nederlands
+
+
+ Italiano
+
+
+ 中文
+
+
+
+
+
+
+ Room Ownership
+
+
+ The person who created the room is the room owner and has special privileges, like the ability to lock and unlock the room. If the owner leaves the room, the second person to join assumes ownership. If they leave, the third person becomes owner, and so on. The room owner has a star icon next to their username in the participants dropdown.
+
+
+
+
+ Lock Room
+
+
+ If you are the room owner, you can lock and unlock the room by clicking the lock icon in the nav bar. When a room is locked, no other participants will be able to join.
+
+
+
+
+ Slash Commands
+
+
+ The following slash commands are available:
+
+
+
+ /nick [username]
+
+ changes username
+
+
+
+ /me [action]
+
+ performs an action
+
+
+
+ /clear
+
+ clears your message history
+
+
+
+ /help
+
+ lists all commands
+
+
+
+
+
+
+`;
diff --git a/client/src/components/Settings/index.js b/client/src/components/Settings/index.js
index e3b2397..a21c643 100644
--- a/client/src/components/Settings/index.js
+++ b/client/src/components/Settings/index.js
@@ -1,18 +1,26 @@
-import React, { Component } from 'react'
-import PropTypes from 'prop-types'
-import RoomLink from 'components/RoomLink'
-import {styles} from './styles.module.scss'
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import RoomLink from 'components/RoomLink';
+import { styles } from './styles.module.scss';
import Cookie from 'js-cookie';
-import T from 'components/T'
+import T from 'components/T';
class Settings extends Component {
-
handleSoundToggle() {
- this.props.toggleSoundEnabled(!this.props.soundIsEnabled)
+ this.props.toggleSoundEnabled(!this.props.soundIsEnabled);
}
handleNotificationToggle() {
- this.props.toggleNotificationEnabled(!this.props.notificationIsEnabled)
+ Notification.requestPermission().then(permission => {
+ this.props.toggleNotificationEnabled(true);
+ if (permission === 'granted') {
+ this.props.toggleNotificationEnabled(!this.props.notificationIsEnabled);
+ this.props.toggleNotificationAllowed(true);
+ }
+ if (permission === 'denied') {
+ this.props.toggleNotificationAllowed(false);
+ }
+ });
}
handleLanguageChange(evt) {
@@ -25,33 +33,69 @@ class Settings extends Component {
return (
-
-
+
+
+
+
+
+
+
+
-
+
English
Français
Occitan
@@ -64,25 +108,57 @@ class Settings extends Component {
-
-
+
+
+
+
+
+
- /nick [username]
- /me [action]
- /clear
- /help
+
+ /nick [username]{' '}
+
+
+
+
+
+ /me [action]{' '}
+
+
+
+
+
+ /clear{' '}
+
+
+
+
+
+ /help{' '}
+
+
+
+
- )
+ );
}
}
@@ -90,10 +166,12 @@ Settings.propTypes = {
soundIsEnabled: PropTypes.bool.isRequired,
toggleSoundEnabled: PropTypes.func.isRequired,
notificationIsEnabled: PropTypes.bool.isRequired,
+ notificationIsAllowed: PropTypes.bool,
toggleNotificationEnabled: PropTypes.func.isRequired,
+ toggleNotificationAllowed: PropTypes.func.isRequired,
roomId: PropTypes.string.isRequired,
setLanguage: PropTypes.func.isRequired,
translations: PropTypes.object.isRequired,
-}
+};
-export default Settings
+export default Settings;
diff --git a/client/src/i18n/en.json b/client/src/i18n/en.json
index 4a8d107..f8b6805 100644
--- a/client/src/i18n/en.json
+++ b/client/src/i18n/en.json
@@ -29,6 +29,7 @@
"sound": "Sound",
"newMessageNotification": "New message notification",
"desktopNotification": "Desktop Notification",
+ "desktopNotificationBlocked": "Desktop notifications have been denied",
"welcomeModalCTA": "Ok",
"lockedRoomHeader": "This room is locked",
"helpTranslate": "Help us translate Darkwire!"
diff --git a/client/src/i18n/fr.json b/client/src/i18n/fr.json
index c0a56d0..25d6ec8 100644
--- a/client/src/i18n/fr.json
+++ b/client/src/i18n/fr.json
@@ -29,6 +29,7 @@
"sound": "Son",
"newMessageNotification": "Notification lors d'un nouveau message",
"desktopNotification": "Notification Système",
+ "desktopNotificationBlocked": "Les notifications systèmes ont été refusée",
"welcomeModalCTA": "Ok",
"lockedRoomHeader": "Ce salon est verrouillé",
"helpTranslate": "Aidez-nous à traduire Darkwire!"
diff --git a/client/src/reducers/app.js b/client/src/reducers/app.js
index 2328dcf..0975183 100644
--- a/client/src/reducers/app.js
+++ b/client/src/reducers/app.js
@@ -9,11 +9,12 @@ const initialState = {
windowIsFocused: true,
unreadMessageCount: 0,
soundIsEnabled: true,
- notificationIsEnabled: true,
+ notificationIsEnabled: false,
+ notificationIsAllowed: null,
socketConnected: false,
language,
- translations: getTranslations(language)
-}
+ translations: getTranslations(language),
+};
const app = (state = initialState, action) => {
switch (action.type) {
@@ -21,52 +22,57 @@ const app = (state = initialState, action) => {
return {
...state,
modalComponent: action.payload,
- }
+ };
case 'CLOSE_MODAL':
return {
...state,
modalComponent: null,
- }
+ };
case 'SET_SCROLLED_TO_BOTTOM':
return {
...state,
scrolledToBottom: action.payload,
- }
+ };
case 'TOGGLE_WINDOW_FOCUS':
return {
...state,
windowIsFocused: action.payload,
unreadMessageCount: 0,
- }
+ };
case 'RECEIVE_ENCRYPTED_MESSAGE_TEXT_MESSAGE':
return {
...state,
unreadMessageCount: state.windowIsFocused ? 0 : state.unreadMessageCount + 1,
- }
+ };
case 'TOGGLE_SOUND_ENABLED':
return {
...state,
soundIsEnabled: action.payload,
- }
+ };
case 'TOGGLE_NOTIFICATION_ENABLED':
return {
...state,
notificationIsEnabled: action.payload,
- }
+ };
+ case 'TOGGLE_NOTIFICATION_ALLOWED':
+ return {
+ ...state,
+ notificationIsAllowed: action.payload,
+ };
case 'TOGGLE_SOCKET_CONNECTED':
return {
...state,
socketConnected: action.payload,
- }
+ };
case 'CHANGE_LANGUAGE':
return {
...state,
language: action.payload,
- translations: getTranslations(action.payload)
- }
+ translations: getTranslations(action.payload),
+ };
default:
- return state
+ return state;
}
-}
+};
-export default app
+export default app;
diff --git a/client/src/reducers/app.test.js b/client/src/reducers/app.test.js
index b737067..5a8b004 100644
--- a/client/src/reducers/app.test.js
+++ b/client/src/reducers/app.test.js
@@ -14,7 +14,8 @@ describe('App reducer', () => {
modalComponent: null,
scrolledToBottom: true,
socketConnected: false,
- notificationIsEnabled: true,
+ notificationIsEnabled: false,
+ notificationIsAllowed: null,
soundIsEnabled: true,
translations: { path: 'test' },
unreadMessageCount: 0,
diff --git a/client/src/reducers/room.js b/client/src/reducers/room.js
index 32e4601..93d8d7b 100644
--- a/client/src/reducers/room.js
+++ b/client/src/reducers/room.js
@@ -21,10 +21,13 @@ const room = (state = initialState, action) => {
.filter(member => memberPubKeys.includes(member.publicKey.n))
.map(member => {
const thisMember = action.payload.members.find(mem => mem.publicKey.n === member.id)
- return {
- ...member,
- isOwner: thisMember.isOwner
+ if (thisMember){
+ return {
+ ...member,
+ isOwner: thisMember.isOwner
+ }
}
+ return {...member}
})
}
case 'RECEIVE_ENCRYPTED_MESSAGE_ADD_USER':
diff --git a/client/src/utils/notifications.js b/client/src/utils/notifications.js
index 220176a..18317bb 100644
--- a/client/src/utils/notifications.js
+++ b/client/src/utils/notifications.js
@@ -1,5 +1,4 @@
-import beepFile from 'audio/beep.mp3'
-
+import beepFile from 'audio/beep.mp3';
const showNotification = (title, message, avatarUrl) => {
const notifBody = {
@@ -45,6 +44,6 @@ export const notify = (title, content) => {
}
};
-export const beep = (window.Audio && new window.Audio(beepFile)) || { play: () => { }}
+export const beep = (window.Audio && new window.Audio(beepFile)) || { play: () => {} };
export default { notify, beep };