mirror of
https://github.com/darkwire/darkwire.io.git
synced 2025-07-18 18:54:52 +00:00
Fix notification autorisation request process
This commit is contained in:
parent
f01f995d9f
commit
d88fcbdc33
@ -19,6 +19,10 @@ export const toggleNotificationEnabled = payload => async (dispatch) => {
|
|||||||
dispatch({ type: 'TOGGLE_NOTIFICATION_ENABLED', payload })
|
dispatch({ type: 'TOGGLE_NOTIFICATION_ENABLED', payload })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const toggleNotificationAllowed = payload => async (dispatch) => {
|
||||||
|
dispatch({ type: 'TOGGLE_NOTIFICATION_ALLOWED', payload })
|
||||||
|
}
|
||||||
|
|
||||||
export const toggleSocketConnected = payload => async (dispatch) => {
|
export const toggleSocketConnected = payload => async (dispatch) => {
|
||||||
dispatch({ type: 'TOGGLE_SOCKET_CONNECTED', payload })
|
dispatch({ type: 'TOGGLE_SOCKET_CONNECTED', payload })
|
||||||
}
|
}
|
||||||
|
@ -1,47 +1,46 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types';
|
||||||
import Crypto from 'utils/crypto'
|
import Crypto from 'utils/crypto';
|
||||||
import { connect as connectSocket } from 'utils/socket'
|
import { connect as connectSocket } from 'utils/socket';
|
||||||
import Nav from 'components/Nav'
|
import Nav from 'components/Nav';
|
||||||
import shortId from 'shortid'
|
import shortId from 'shortid';
|
||||||
import Connecting from 'components/Connecting'
|
import Connecting from 'components/Connecting';
|
||||||
import Modal from 'react-modal'
|
import Modal from 'react-modal';
|
||||||
import About from 'components/About'
|
import About from 'components/About';
|
||||||
import Settings from 'components/Settings'
|
import Settings from 'components/Settings';
|
||||||
import Welcome from 'components/Welcome'
|
import Welcome from 'components/Welcome';
|
||||||
import RoomLocked from 'components/RoomLocked'
|
import RoomLocked from 'components/RoomLocked';
|
||||||
import { X, AlertCircle } from 'react-feather'
|
import { X, AlertCircle } from 'react-feather';
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames';
|
||||||
import ActivityList from './ActivityList'
|
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');
|
Modal.setAppElement('#root');
|
||||||
|
|
||||||
class Home extends Component {
|
class Home extends Component {
|
||||||
|
|
||||||
async componentWillMount() {
|
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;
|
this.socket = socket;
|
||||||
|
|
||||||
socket.on('disconnect', () => {
|
socket.on('disconnect', () => {
|
||||||
this.props.toggleSocketConnected(false)
|
this.props.toggleSocketConnected(false);
|
||||||
})
|
});
|
||||||
|
|
||||||
socket.on('connect', () => {
|
socket.on('connect', () => {
|
||||||
this.initApp(user)
|
this.initApp(user);
|
||||||
this.props.toggleSocketConnected(true)
|
this.props.toggleSocketConnected(true);
|
||||||
})
|
});
|
||||||
|
|
||||||
socket.on('USER_ENTER', (payload) => {
|
socket.on('USER_ENTER', payload => {
|
||||||
this.props.receiveUnencryptedMessage('USER_ENTER', payload)
|
this.props.receiveUnencryptedMessage('USER_ENTER', payload);
|
||||||
this.props.sendEncryptedMessage({
|
this.props.sendEncryptedMessage({
|
||||||
type: 'ADD_USER',
|
type: 'ADD_USER',
|
||||||
payload: {
|
payload: {
|
||||||
@ -50,35 +49,35 @@ class Home extends Component {
|
|||||||
isOwner: this.props.iAmOwner,
|
isOwner: this.props.iAmOwner,
|
||||||
id: this.props.userId,
|
id: this.props.userId,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
if (payload.users.length === 1) {
|
if (payload.users.length === 1) {
|
||||||
this.props.openModal('Welcome');
|
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.on('USER_EXIT', payload => {
|
||||||
socket.emit('USER_DISCONNECT')
|
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() {
|
componentDidMount() {
|
||||||
this.bindEvents()
|
this.bindEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
getModal() {
|
getModal() {
|
||||||
@ -88,91 +87,100 @@ class Home extends Component {
|
|||||||
component: <Connecting />,
|
component: <Connecting />,
|
||||||
title: 'Connecting...',
|
title: 'Connecting...',
|
||||||
preventClose: true,
|
preventClose: true,
|
||||||
}
|
};
|
||||||
case 'About':
|
case 'About':
|
||||||
return {
|
return {
|
||||||
component: <About roomId={this.props.roomId} />,
|
component: <About roomId={this.props.roomId} />,
|
||||||
title: this.props.translations.aboutHeader,
|
title: this.props.translations.aboutHeader,
|
||||||
}
|
};
|
||||||
case 'Settings':
|
case 'Settings':
|
||||||
return {
|
return {
|
||||||
component: <Settings
|
component: (
|
||||||
roomId={this.props.roomId}
|
<Settings
|
||||||
toggleSoundEnabled={this.props.toggleSoundEnabled}
|
roomId={this.props.roomId}
|
||||||
soundIsEnabled={this.props.soundIsEnabled}
|
toggleSoundEnabled={this.props.toggleSoundEnabled}
|
||||||
toggleNotificationEnabled={this.props.toggleNotificationEnabled}
|
soundIsEnabled={this.props.soundIsEnabled}
|
||||||
notificationIsEnabled={this.props.notificationIsEnabled}
|
toggleNotificationEnabled={this.props.toggleNotificationEnabled}
|
||||||
setLanguage={this.props.setLanguage}
|
toggleNotificationAllowed={this.props.toggleNotificationAllowed}
|
||||||
language={this.props.language}
|
notificationIsEnabled={this.props.notificationIsEnabled}
|
||||||
translations={this.props.translations}
|
notificationIsAllowed={this.props.notificationIsAllowed}
|
||||||
/>,
|
setLanguage={this.props.setLanguage}
|
||||||
|
language={this.props.language}
|
||||||
|
translations={this.props.translations}
|
||||||
|
/>
|
||||||
|
),
|
||||||
title: this.props.translations.settingsHeader,
|
title: this.props.translations.settingsHeader,
|
||||||
}
|
};
|
||||||
case 'Welcome':
|
case 'Welcome':
|
||||||
return {
|
return {
|
||||||
component: <Welcome roomId={this.props.roomId} close={this.props.closeModal} translations={this.props.translations} />,
|
component: (
|
||||||
|
<Welcome roomId={this.props.roomId} close={this.props.closeModal} translations={this.props.translations} />
|
||||||
|
),
|
||||||
title: this.props.translations.welcomeHeader,
|
title: this.props.translations.welcomeHeader,
|
||||||
}
|
};
|
||||||
case 'Room Locked':
|
case 'Room Locked':
|
||||||
return {
|
return {
|
||||||
component: <RoomLocked modalContent={this.props.translations.lockedRoomHeader} />,
|
component: <RoomLocked modalContent={this.props.translations.lockedRoomHeader} />,
|
||||||
title: this.props.translations.lockedRoomHeader,
|
title: this.props.translations.lockedRoomHeader,
|
||||||
preventClose: true,
|
preventClose: true,
|
||||||
}
|
};
|
||||||
default:
|
default:
|
||||||
return {
|
return {
|
||||||
component: null,
|
component: null,
|
||||||
title: null,
|
title: null,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
initApp(user) {
|
initApp(user) {
|
||||||
this.socket.emit('USER_ENTER', {
|
this.socket.emit('USER_ENTER', {
|
||||||
publicKey: user.publicKey,
|
publicKey: user.publicKey,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bindEvents() {
|
bindEvents() {
|
||||||
window.onfocus = () => {
|
window.onfocus = () => {
|
||||||
this.props.toggleWindowFocus(true)
|
this.props.toggleWindowFocus(true);
|
||||||
}
|
};
|
||||||
|
|
||||||
window.onblur = () => {
|
window.onblur = () => {
|
||||||
this.props.toggleWindowFocus(false)
|
this.props.toggleWindowFocus(false);
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
createUser() {
|
createUser() {
|
||||||
return new Promise(async (resolve) => {
|
return new Promise(async resolve => {
|
||||||
const username = shortId.generate()
|
const username = shortId.generate();
|
||||||
|
|
||||||
const encryptDecryptKeys = await crypto.createEncryptDecryptKeys()
|
const encryptDecryptKeys = await crypto.createEncryptDecryptKeys();
|
||||||
const exportedEncryptDecryptPrivateKey = await crypto.exportKey(encryptDecryptKeys.privateKey)
|
const exportedEncryptDecryptPrivateKey = await crypto.exportKey(encryptDecryptKeys.privateKey);
|
||||||
const exportedEncryptDecryptPublicKey = await crypto.exportKey(encryptDecryptKeys.publicKey)
|
const exportedEncryptDecryptPublicKey = await crypto.exportKey(encryptDecryptKeys.publicKey);
|
||||||
|
|
||||||
this.props.createUser({
|
this.props.createUser({
|
||||||
username,
|
username,
|
||||||
publicKey: exportedEncryptDecryptPublicKey,
|
publicKey: exportedEncryptDecryptPublicKey,
|
||||||
privateKey: exportedEncryptDecryptPrivateKey,
|
privateKey: exportedEncryptDecryptPrivateKey,
|
||||||
})
|
});
|
||||||
|
|
||||||
resolve({
|
resolve({
|
||||||
publicKey: exportedEncryptDecryptPublicKey,
|
publicKey: exportedEncryptDecryptPublicKey,
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const modalOpts = this.getModal()
|
const modalOpts = this.getModal();
|
||||||
return (
|
return (
|
||||||
<div className={classNames(styles.styles, 'h-100')}>
|
<div className={classNames(styles.styles, 'h-100')}>
|
||||||
<div className="nav-container">
|
<div className="nav-container">
|
||||||
{!this.props.socketConnected &&
|
{!this.props.socketConnected && (
|
||||||
<div className="alert-banner">
|
<div className="alert-banner">
|
||||||
<span className="icon"><AlertCircle size="15" /></span> <span>Disconnected</span>
|
<span className="icon">
|
||||||
|
<AlertCircle size="15" />
|
||||||
|
</span>{' '}
|
||||||
|
<span>Disconnected</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
<Nav
|
<Nav
|
||||||
members={this.props.members}
|
members={this.props.members}
|
||||||
roomId={this.props.roomId}
|
roomId={this.props.roomId}
|
||||||
@ -184,10 +192,7 @@ class Home extends Component {
|
|||||||
translations={this.props.translations}
|
translations={this.props.translations}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<ActivityList
|
<ActivityList openModal={this.props.openModal} activities={this.props.activities} />
|
||||||
openModal={this.props.openModal}
|
|
||||||
activities={this.props.activities}
|
|
||||||
/>
|
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={Boolean(this.props.modalComponent)}
|
isOpen={Boolean(this.props.modalComponent)}
|
||||||
contentLabel="Modal"
|
contentLabel="Modal"
|
||||||
@ -206,27 +211,23 @@ class Home extends Component {
|
|||||||
onRequestClose={this.props.closeModal}
|
onRequestClose={this.props.closeModal}
|
||||||
>
|
>
|
||||||
<div className="react-modal-header">
|
<div className="react-modal-header">
|
||||||
{!modalOpts.preventClose &&
|
{!modalOpts.preventClose && (
|
||||||
<button onClick={this.props.closeModal} className="btn btn-link btn-plain close-modal">
|
<button onClick={this.props.closeModal} className="btn btn-link btn-plain close-modal">
|
||||||
<X />
|
<X />
|
||||||
</button>
|
</button>
|
||||||
}
|
)}
|
||||||
<h3 className="react-modal-title">
|
<h3 className="react-modal-title">{modalOpts.title}</h3>
|
||||||
{modalOpts.title}
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<div className="react-modal-component">
|
|
||||||
{modalOpts.component}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="react-modal-component">{modalOpts.component}</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Home.defaultProps = {
|
Home.defaultProps = {
|
||||||
modalComponent: null,
|
modalComponent: null,
|
||||||
}
|
};
|
||||||
|
|
||||||
Home.propTypes = {
|
Home.propTypes = {
|
||||||
receiveEncryptedMessage: PropTypes.func.isRequired,
|
receiveEncryptedMessage: PropTypes.func.isRequired,
|
||||||
@ -249,11 +250,13 @@ Home.propTypes = {
|
|||||||
soundIsEnabled: PropTypes.bool.isRequired,
|
soundIsEnabled: PropTypes.bool.isRequired,
|
||||||
toggleSoundEnabled: PropTypes.func.isRequired,
|
toggleSoundEnabled: PropTypes.func.isRequired,
|
||||||
notificationIsEnabled: PropTypes.bool.isRequired,
|
notificationIsEnabled: PropTypes.bool.isRequired,
|
||||||
|
notificationIsAllowed: PropTypes.bool,
|
||||||
toggleNotificationEnabled: PropTypes.func.isRequired,
|
toggleNotificationEnabled: PropTypes.func.isRequired,
|
||||||
|
toggleNotificationAllowed: PropTypes.func.isRequired,
|
||||||
toggleSocketConnected: PropTypes.func.isRequired,
|
toggleSocketConnected: PropTypes.func.isRequired,
|
||||||
socketConnected: PropTypes.bool.isRequired,
|
socketConnected: PropTypes.bool.isRequired,
|
||||||
sendUnencryptedMessage: PropTypes.func.isRequired,
|
sendUnencryptedMessage: PropTypes.func.isRequired,
|
||||||
sendEncryptedMessage: PropTypes.func.isRequired
|
sendEncryptedMessage: PropTypes.func.isRequired,
|
||||||
}
|
};
|
||||||
|
|
||||||
export default Home;
|
export default Home;
|
||||||
|
@ -62,6 +62,7 @@ test('Home component is displaying', async () => {
|
|||||||
toggleSoundEnabled={() => {}}
|
toggleSoundEnabled={() => {}}
|
||||||
soundIsEnabled={false}
|
soundIsEnabled={false}
|
||||||
toggleNotificationEnabled={() => {}}
|
toggleNotificationEnabled={() => {}}
|
||||||
|
toggleNotificationAllowed={() => {}}
|
||||||
notificationIsEnabled={false}
|
notificationIsEnabled={false}
|
||||||
faviconCount={0}
|
faviconCount={0}
|
||||||
toggleWindowFocus={() => {}}
|
toggleWindowFocus={() => {}}
|
||||||
|
@ -2,6 +2,7 @@ import React, { Component } from 'react';
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { notify, beep } from 'utils/notifications';
|
import { notify, beep } from 'utils/notifications';
|
||||||
import Tinycon from 'tinycon';
|
import Tinycon from 'tinycon';
|
||||||
|
import { toggleNotificationAllowed, toggleNotificationEnabled } from 'actions';
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
@ -10,12 +11,21 @@ const mapStateToProps = state => {
|
|||||||
windowIsFocused: state.app.windowIsFocused,
|
windowIsFocused: state.app.windowIsFocused,
|
||||||
soundIsEnabled: state.app.soundIsEnabled,
|
soundIsEnabled: state.app.soundIsEnabled,
|
||||||
notificationIsEnabled: state.app.notificationIsEnabled,
|
notificationIsEnabled: state.app.notificationIsEnabled,
|
||||||
|
notificationIsAllowed: state.app.notificationIsAllowed,
|
||||||
room: state.room,
|
room: state.room,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
toggleNotificationAllowed,
|
||||||
|
toggleNotificationEnabled
|
||||||
|
};
|
||||||
|
|
||||||
const WithNewMessageNotification = WrappedComponent => {
|
const WithNewMessageNotification = WrappedComponent => {
|
||||||
return connect(mapStateToProps)(
|
return connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps,
|
||||||
|
)(
|
||||||
class WithNotificationHOC extends Component {
|
class WithNotificationHOC extends Component {
|
||||||
state = { lastMessage: null, unreadMessageCount: 0 };
|
state = { lastMessage: null, unreadMessageCount: 0 };
|
||||||
|
|
||||||
@ -24,6 +34,7 @@ const WithNewMessageNotification = WrappedComponent => {
|
|||||||
room: { id: roomId },
|
room: { id: roomId },
|
||||||
activities,
|
activities,
|
||||||
notificationIsEnabled,
|
notificationIsEnabled,
|
||||||
|
notificationIsAllowed,
|
||||||
soundIsEnabled,
|
soundIsEnabled,
|
||||||
unreadMessageCount,
|
unreadMessageCount,
|
||||||
windowIsFocused,
|
windowIsFocused,
|
||||||
@ -38,7 +49,7 @@ const WithNewMessageNotification = WrappedComponent => {
|
|||||||
|
|
||||||
if (lastMessage !== prevState.lastMessage && !windowIsFocused) {
|
if (lastMessage !== prevState.lastMessage && !windowIsFocused) {
|
||||||
const title = `Message from ${username} (${roomId})`;
|
const title = `Message from ${username} (${roomId})`;
|
||||||
if (notificationIsEnabled) notify(title, text);
|
if (notificationIsAllowed && notificationIsEnabled) notify(title, text);
|
||||||
if (soundIsEnabled) beep.play();
|
if (soundIsEnabled) beep.play();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,15 +60,32 @@ const WithNewMessageNotification = WrappedComponent => {
|
|||||||
return { lastMessage, unreadMessageCount };
|
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() {
|
render() {
|
||||||
// Filter props
|
// Filter props
|
||||||
const {
|
const {
|
||||||
room,
|
room,
|
||||||
activities,
|
activities,
|
||||||
notificationIsEnabled,
|
notificationIsEnabled,
|
||||||
|
motificationIsAllowed,
|
||||||
soundIsEnabled,
|
soundIsEnabled,
|
||||||
unreadMessageCount,
|
unreadMessageCount,
|
||||||
windowIsFocused,
|
windowIsFocused,
|
||||||
|
toggleNotificationAllowed,
|
||||||
|
toggleNotificationnEnabled,
|
||||||
...rest
|
...rest
|
||||||
} = this.props;
|
} = this.props;
|
||||||
return <WrappedComponent {...rest} />;
|
return <WrappedComponent {...rest} />;
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
toggleWindowFocus,
|
toggleWindowFocus,
|
||||||
toggleSoundEnabled,
|
toggleSoundEnabled,
|
||||||
toggleNotificationEnabled,
|
toggleNotificationEnabled,
|
||||||
|
toggleNotificationAllowed,
|
||||||
toggleSocketConnected,
|
toggleSocketConnected,
|
||||||
receiveUnencryptedMessage,
|
receiveUnencryptedMessage,
|
||||||
sendUnencryptedMessage,
|
sendUnencryptedMessage,
|
||||||
@ -33,6 +34,7 @@ const mapStateToProps = (state) => {
|
|||||||
faviconCount: state.app.unreadMessageCount,
|
faviconCount: state.app.unreadMessageCount,
|
||||||
soundIsEnabled: state.app.soundIsEnabled,
|
soundIsEnabled: state.app.soundIsEnabled,
|
||||||
notificationIsEnabled: state.app.notificationIsEnabled,
|
notificationIsEnabled: state.app.notificationIsEnabled,
|
||||||
|
notificationIsAllowed: state.app.notificationIsAllowed,
|
||||||
socketConnected: state.app.socketConnected,
|
socketConnected: state.app.socketConnected,
|
||||||
language: state.app.language,
|
language: state.app.language,
|
||||||
translations: state.app.translations,
|
translations: state.app.translations,
|
||||||
@ -47,6 +49,7 @@ const mapDispatchToProps = {
|
|||||||
toggleWindowFocus,
|
toggleWindowFocus,
|
||||||
toggleSoundEnabled,
|
toggleSoundEnabled,
|
||||||
toggleNotificationEnabled,
|
toggleNotificationEnabled,
|
||||||
|
toggleNotificationAllowed,
|
||||||
toggleSocketConnected,
|
toggleSocketConnected,
|
||||||
receiveUnencryptedMessage,
|
receiveUnencryptedMessage,
|
||||||
sendUnencryptedMessage,
|
sendUnencryptedMessage,
|
||||||
|
@ -87,6 +87,16 @@ jest.mock('tinycon', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('Connected Home component', () => {
|
describe('Connected Home component', () => {
|
||||||
|
beforeEach(()=>{
|
||||||
|
global.Notification = {
|
||||||
|
permission: 'granted'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(()=>{
|
||||||
|
delete global.Notification
|
||||||
|
})
|
||||||
|
|
||||||
it('should display', () => {
|
it('should display', () => {
|
||||||
const { asFragment } = render(
|
const { asFragment } = render(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
@ -97,6 +107,43 @@ describe('Connected Home component', () => {
|
|||||||
expect(asFragment()).toMatchSnapshot();
|
expect(asFragment()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should set notification', () => {
|
||||||
|
render(
|
||||||
|
<Provider store={store}>
|
||||||
|
<ConnectedHome match={{ params: { roomId: 'roomTest' } }} userId="testUserId" />
|
||||||
|
</Provider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(store.getState().app.notificationIsAllowed).toBe(true);
|
||||||
|
expect(store.getState().app.notificationIsEnabled).toBe(true);
|
||||||
|
|
||||||
|
global.Notification = {
|
||||||
|
permission: 'denied'
|
||||||
|
}
|
||||||
|
|
||||||
|
render(
|
||||||
|
<Provider store={store}>
|
||||||
|
<ConnectedHome match={{ params: { roomId: 'roomTest' } }} userId="testUserId" />
|
||||||
|
</Provider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(store.getState().app.notificationIsAllowed).toBe(false);
|
||||||
|
|
||||||
|
global.Notification = {
|
||||||
|
permission: 'default'
|
||||||
|
}
|
||||||
|
|
||||||
|
render(
|
||||||
|
<Provider store={store}>
|
||||||
|
<ConnectedHome match={{ params: { roomId: 'roomTest' } }} userId="testUserId" />
|
||||||
|
</Provider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(store.getState().app.notificationIsAllowed).toBe(null);
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
it('should send notifications', async () => {
|
it('should send notifications', async () => {
|
||||||
Modal.prototype.getSnapshotBeforeUpdate = jest.fn().mockReturnValue(null);
|
Modal.prototype.getSnapshotBeforeUpdate = jest.fn().mockReturnValue(null);
|
||||||
const { rerender } = render(
|
const { rerender } = render(
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
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 { Provider } from 'react-redux';
|
||||||
import configureStore from 'store';
|
import configureStore from 'store';
|
||||||
|
|
||||||
@ -11,17 +11,20 @@ const mockTranslations = {
|
|||||||
sound: 'soundCheck',
|
sound: 'soundCheck',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
jest.useFakeTimers();
|
||||||
|
|
||||||
jest.mock('components/RoomLink');
|
jest.mock('components/RoomLink');
|
||||||
|
|
||||||
describe('Settings component', () => {
|
describe('Settings component', () => {
|
||||||
it('should display', async () => {
|
it('should display', async () => {
|
||||||
const { asFragment } = render(
|
const { asFragment, rerender } = render(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<Settings
|
<Settings
|
||||||
soundIsEnabled={true}
|
soundIsEnabled={true}
|
||||||
toggleSoundEnabled={() => {}}
|
toggleSoundEnabled={() => {}}
|
||||||
notificationIsEnabled={true}
|
notificationIsEnabled={true}
|
||||||
toggleNotificationEnabled={() => {}}
|
toggleNotificationEnabled={() => {}}
|
||||||
|
toggleNotificationAllowed={jest.fn()}
|
||||||
roomId="roomId"
|
roomId="roomId"
|
||||||
setLanguage={() => {}}
|
setLanguage={() => {}}
|
||||||
translations={{}}
|
translations={{}}
|
||||||
@ -30,6 +33,25 @@ describe('Settings component', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(asFragment()).toMatchSnapshot();
|
expect(asFragment()).toMatchSnapshot();
|
||||||
|
|
||||||
|
rerender(
|
||||||
|
<Provider store={store}>
|
||||||
|
<Settings
|
||||||
|
soundIsEnabled={true}
|
||||||
|
toggleSoundEnabled={() => {}}
|
||||||
|
notificationIsEnabled={true}
|
||||||
|
notificationIsAllowed={false}
|
||||||
|
toggleNotificationEnabled={() => {}}
|
||||||
|
toggleNotificationAllowed={jest.fn()}
|
||||||
|
roomId="roomId"
|
||||||
|
setLanguage={() => {}}
|
||||||
|
translations={{}}
|
||||||
|
/>
|
||||||
|
</Provider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(asFragment()).toMatchSnapshot();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should toggle sound', async () => {
|
it('should toggle sound', async () => {
|
||||||
@ -40,7 +62,9 @@ describe('Settings component', () => {
|
|||||||
soundIsEnabled={true}
|
soundIsEnabled={true}
|
||||||
toggleSoundEnabled={toggleSound}
|
toggleSoundEnabled={toggleSound}
|
||||||
notificationIsEnabled={true}
|
notificationIsEnabled={true}
|
||||||
|
notificationIsAllowed={true}
|
||||||
toggleNotificationEnabled={() => {}}
|
toggleNotificationEnabled={() => {}}
|
||||||
|
toggleNotificationAllowed={jest.fn()}
|
||||||
roomId="roomId"
|
roomId="roomId"
|
||||||
setLanguage={() => {}}
|
setLanguage={() => {}}
|
||||||
translations={{}}
|
translations={{}}
|
||||||
@ -55,6 +79,10 @@ describe('Settings component', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should toggle notifications', async () => {
|
it('should toggle notifications', async () => {
|
||||||
|
global.Notification = {
|
||||||
|
requestPermission: jest.fn().mockResolvedValue('granted'),
|
||||||
|
};
|
||||||
|
|
||||||
const toggleNotifications = jest.fn();
|
const toggleNotifications = jest.fn();
|
||||||
const { getByText } = render(
|
const { getByText } = render(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
@ -62,7 +90,9 @@ describe('Settings component', () => {
|
|||||||
soundIsEnabled={true}
|
soundIsEnabled={true}
|
||||||
toggleSoundEnabled={() => {}}
|
toggleSoundEnabled={() => {}}
|
||||||
notificationIsEnabled={true}
|
notificationIsEnabled={true}
|
||||||
|
notificationIsAllowed={true}
|
||||||
toggleNotificationEnabled={toggleNotifications}
|
toggleNotificationEnabled={toggleNotifications}
|
||||||
|
toggleNotificationAllowed={jest.fn()}
|
||||||
roomId="roomId"
|
roomId="roomId"
|
||||||
setLanguage={() => {}}
|
setLanguage={() => {}}
|
||||||
translations={{}}
|
translations={{}}
|
||||||
@ -72,9 +102,48 @@ describe('Settings component', () => {
|
|||||||
|
|
||||||
fireEvent.click(getByText('Desktop Notification'));
|
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(
|
||||||
|
<Provider store={store}>
|
||||||
|
<Settings
|
||||||
|
soundIsEnabled={true}
|
||||||
|
toggleSoundEnabled={() => {}}
|
||||||
|
notificationIsEnabled={true}
|
||||||
|
notificationIsAllowed={true}
|
||||||
|
toggleNotificationEnabled={toggleNotifications}
|
||||||
|
toggleNotificationAllowed={toggleAllowed}
|
||||||
|
roomId="roomId"
|
||||||
|
setLanguage={() => {}}
|
||||||
|
translations={{}}
|
||||||
|
/>
|
||||||
|
</Provider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
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 () => {
|
it('should change lang', async () => {
|
||||||
const changeLang = jest.fn();
|
const changeLang = jest.fn();
|
||||||
|
|
||||||
@ -85,6 +154,7 @@ describe('Settings component', () => {
|
|||||||
toggleSoundEnabled={() => {}}
|
toggleSoundEnabled={() => {}}
|
||||||
notificationIsEnabled={true}
|
notificationIsEnabled={true}
|
||||||
toggleNotificationEnabled={() => {}}
|
toggleNotificationEnabled={() => {}}
|
||||||
|
toggleNotificationAllowed={jest.fn()}
|
||||||
roomId="roomId"
|
roomId="roomId"
|
||||||
setLanguage={changeLang}
|
setLanguage={changeLang}
|
||||||
translations={{}}
|
translations={{}}
|
||||||
|
@ -171,3 +171,169 @@ exports[`Settings component should display 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</DocumentFragment>
|
</DocumentFragment>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`Settings component should display 2`] = `
|
||||||
|
<DocumentFragment>
|
||||||
|
<div
|
||||||
|
class="styles"
|
||||||
|
>
|
||||||
|
<section>
|
||||||
|
<h4>
|
||||||
|
New message notification
|
||||||
|
</h4>
|
||||||
|
<form>
|
||||||
|
<div
|
||||||
|
class="form-check"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
class="form-check-label"
|
||||||
|
for="sound-control"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
checked=""
|
||||||
|
class="form-check-input"
|
||||||
|
id="sound-control"
|
||||||
|
type="checkbox"
|
||||||
|
/>
|
||||||
|
Sound
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="form-check"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
class="form-check-label"
|
||||||
|
for="notif-control"
|
||||||
|
>
|
||||||
|
Desktop notifications have been denied
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h4
|
||||||
|
class="mb-3"
|
||||||
|
>
|
||||||
|
This room
|
||||||
|
</h4>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h4
|
||||||
|
class="mb-3"
|
||||||
|
>
|
||||||
|
Language
|
||||||
|
</h4>
|
||||||
|
<p>
|
||||||
|
<a
|
||||||
|
href="https://github.com/darkwire/darkwire.io/blob/master/client/README.md#translations"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Help us translate Darkwire!
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<div
|
||||||
|
class="form-group"
|
||||||
|
>
|
||||||
|
<select
|
||||||
|
class="form-control"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
value="en"
|
||||||
|
>
|
||||||
|
English
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="fr"
|
||||||
|
>
|
||||||
|
Français
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="oc"
|
||||||
|
>
|
||||||
|
Occitan
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="de"
|
||||||
|
>
|
||||||
|
Deutsch
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="nl"
|
||||||
|
>
|
||||||
|
Nederlands
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="it"
|
||||||
|
>
|
||||||
|
Italiano
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="zhCN"
|
||||||
|
>
|
||||||
|
中文
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h4>
|
||||||
|
Room Ownership
|
||||||
|
</h4>
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h4>
|
||||||
|
Lock Room
|
||||||
|
</h4>
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h4>
|
||||||
|
Slash Commands
|
||||||
|
</h4>
|
||||||
|
<p>
|
||||||
|
The following slash commands are available:
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
/nick [username]
|
||||||
|
<span
|
||||||
|
class="text-muted"
|
||||||
|
>
|
||||||
|
changes username
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
/me [action]
|
||||||
|
<span
|
||||||
|
class="text-muted"
|
||||||
|
>
|
||||||
|
performs an action
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
/clear
|
||||||
|
<span
|
||||||
|
class="text-muted"
|
||||||
|
>
|
||||||
|
clears your message history
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
/help
|
||||||
|
<span
|
||||||
|
class="text-muted"
|
||||||
|
>
|
||||||
|
lists all commands
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</DocumentFragment>
|
||||||
|
`;
|
||||||
|
@ -1,18 +1,26 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types';
|
||||||
import RoomLink from 'components/RoomLink'
|
import RoomLink from 'components/RoomLink';
|
||||||
import {styles} from './styles.module.scss'
|
import { styles } from './styles.module.scss';
|
||||||
import Cookie from 'js-cookie';
|
import Cookie from 'js-cookie';
|
||||||
import T from 'components/T'
|
import T from 'components/T';
|
||||||
|
|
||||||
class Settings extends Component {
|
class Settings extends Component {
|
||||||
|
|
||||||
handleSoundToggle() {
|
handleSoundToggle() {
|
||||||
this.props.toggleSoundEnabled(!this.props.soundIsEnabled)
|
this.props.toggleSoundEnabled(!this.props.soundIsEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleNotificationToggle() {
|
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) {
|
handleLanguageChange(evt) {
|
||||||
@ -25,33 +33,69 @@ class Settings extends Component {
|
|||||||
return (
|
return (
|
||||||
<div className={styles}>
|
<div className={styles}>
|
||||||
<section>
|
<section>
|
||||||
<h4><T path='newMessageNotification'/></h4>
|
<h4>
|
||||||
|
<T path="newMessageNotification" />
|
||||||
|
</h4>
|
||||||
<form>
|
<form>
|
||||||
<div className="form-check">
|
<div className="form-check">
|
||||||
<label className="form-check-label" htmlFor="sound-control">
|
<label className="form-check-label" htmlFor="sound-control">
|
||||||
<input id="sound-control" onChange={this.handleSoundToggle.bind(this)} className="form-check-input" type="checkbox" checked={this.props.soundIsEnabled} />
|
<input
|
||||||
<T path='sound'/>
|
id="sound-control"
|
||||||
|
onChange={this.handleSoundToggle.bind(this)}
|
||||||
|
className="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
checked={this.props.soundIsEnabled}
|
||||||
|
/>
|
||||||
|
<T path="sound" />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-check">
|
<div className="form-check">
|
||||||
<label className="form-check-label" htmlFor="notif-control">
|
<label className="form-check-label" htmlFor="notif-control">
|
||||||
<input id="notif-control" onChange={this.handleNotificationToggle.bind(this)} className="form-check-input" type="checkbox" checked={this.props.notificationIsEnabled} />
|
{this.props.notificationIsAllowed !== false &&
|
||||||
<T path='desktopNotification'/>
|
<>
|
||||||
|
<input
|
||||||
|
id="notif-control"
|
||||||
|
onChange={this.handleNotificationToggle.bind(this)}
|
||||||
|
className="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
checked={this.props.notificationIsEnabled}
|
||||||
|
disabled={this.props.notificationIsAllowed === false} // Important to keep '=== false' here
|
||||||
|
/>
|
||||||
|
<T path="desktopNotification" />
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
{this.props.notificationIsAllowed === false && <T path="desktopNotificationBlocked" />}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h4 className='mb-3'><T path='copyRoomHeader'/></h4>
|
<h4 className="mb-3">
|
||||||
|
<T path="copyRoomHeader" />
|
||||||
|
</h4>
|
||||||
<RoomLink roomId={this.props.roomId} translations={this.props.translations} />
|
<RoomLink roomId={this.props.roomId} translations={this.props.translations} />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h4 className='mb-3'><T path='languageDropdownHeader'/></h4>
|
<h4 className="mb-3">
|
||||||
<p><a href="https://github.com/darkwire/darkwire.io/blob/master/client/README.md#translations" rel="noopener noreferrer" target="_blank"><T path='helpTranslate'/></a></p>
|
<T path="languageDropdownHeader" />
|
||||||
|
</h4>
|
||||||
|
<p>
|
||||||
|
<a
|
||||||
|
href="https://github.com/darkwire/darkwire.io/blob/master/client/README.md#translations"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<T path="helpTranslate" />
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<select value={this.props.language} className="form-control" onChange={this.handleLanguageChange.bind(this)}>
|
<select
|
||||||
|
value={this.props.language}
|
||||||
|
className="form-control"
|
||||||
|
onChange={this.handleLanguageChange.bind(this)}
|
||||||
|
>
|
||||||
<option value="en">English</option>
|
<option value="en">English</option>
|
||||||
<option value="fr">Français</option>
|
<option value="fr">Français</option>
|
||||||
<option value="oc">Occitan</option>
|
<option value="oc">Occitan</option>
|
||||||
@ -64,25 +108,57 @@ class Settings extends Component {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h4><T path='roomOwnerHeader'/></h4>
|
<h4>
|
||||||
<p><T path='roomOwnerText'/></p>
|
<T path="roomOwnerHeader" />
|
||||||
|
</h4>
|
||||||
|
<p>
|
||||||
|
<T path="roomOwnerText" />
|
||||||
|
</p>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h4><T path='lockRoomHeader'/></h4>
|
<h4>
|
||||||
<p><T path='lockRoomText'/></p>
|
<T path="lockRoomHeader" />
|
||||||
|
</h4>
|
||||||
|
<p>
|
||||||
|
<T path="lockRoomText" />
|
||||||
|
</p>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h4><T path='slashCommandsHeader'/></h4>
|
<h4>
|
||||||
<p><T path='slashCommandsText'/></p>
|
<T path="slashCommandsHeader" />
|
||||||
|
</h4>
|
||||||
|
<p>
|
||||||
|
<T path="slashCommandsText" />
|
||||||
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>/nick [username] <span className="text-muted"><T path='slashCommandsBullets.0'/></span></li>
|
<li>
|
||||||
<li>/me [action] <span className="text-muted"><T path='slashCommandsBullets.1'/></span></li>
|
/nick [username]{' '}
|
||||||
<li>/clear <span className="text-muted"><T path='slashCommandsBullets.2'/></span></li>
|
<span className="text-muted">
|
||||||
<li>/help <span className="text-muted"><T path='slashCommandsBullets.3'/></span></li>
|
<T path="slashCommandsBullets.0" />
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
/me [action]{' '}
|
||||||
|
<span className="text-muted">
|
||||||
|
<T path="slashCommandsBullets.1" />
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
/clear{' '}
|
||||||
|
<span className="text-muted">
|
||||||
|
<T path="slashCommandsBullets.2" />
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
/help{' '}
|
||||||
|
<span className="text-muted">
|
||||||
|
<T path="slashCommandsBullets.3" />
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,10 +166,12 @@ Settings.propTypes = {
|
|||||||
soundIsEnabled: PropTypes.bool.isRequired,
|
soundIsEnabled: PropTypes.bool.isRequired,
|
||||||
toggleSoundEnabled: PropTypes.func.isRequired,
|
toggleSoundEnabled: PropTypes.func.isRequired,
|
||||||
notificationIsEnabled: PropTypes.bool.isRequired,
|
notificationIsEnabled: PropTypes.bool.isRequired,
|
||||||
|
notificationIsAllowed: PropTypes.bool,
|
||||||
toggleNotificationEnabled: PropTypes.func.isRequired,
|
toggleNotificationEnabled: PropTypes.func.isRequired,
|
||||||
|
toggleNotificationAllowed: PropTypes.func.isRequired,
|
||||||
roomId: PropTypes.string.isRequired,
|
roomId: PropTypes.string.isRequired,
|
||||||
setLanguage: PropTypes.func.isRequired,
|
setLanguage: PropTypes.func.isRequired,
|
||||||
translations: PropTypes.object.isRequired,
|
translations: PropTypes.object.isRequired,
|
||||||
}
|
};
|
||||||
|
|
||||||
export default Settings
|
export default Settings;
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
"sound": "Sound",
|
"sound": "Sound",
|
||||||
"newMessageNotification": "New message notification",
|
"newMessageNotification": "New message notification",
|
||||||
"desktopNotification": "Desktop Notification",
|
"desktopNotification": "Desktop Notification",
|
||||||
|
"desktopNotificationBlocked": "Desktop notifications have been denied",
|
||||||
"welcomeModalCTA": "Ok",
|
"welcomeModalCTA": "Ok",
|
||||||
"lockedRoomHeader": "This room is locked",
|
"lockedRoomHeader": "This room is locked",
|
||||||
"helpTranslate": "Help us translate Darkwire!"
|
"helpTranslate": "Help us translate Darkwire!"
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
"sound": "Son",
|
"sound": "Son",
|
||||||
"newMessageNotification": "Notification lors d'un nouveau message",
|
"newMessageNotification": "Notification lors d'un nouveau message",
|
||||||
"desktopNotification": "Notification Système",
|
"desktopNotification": "Notification Système",
|
||||||
|
"desktopNotificationBlocked": "Les notifications systèmes ont été refusée",
|
||||||
"welcomeModalCTA": "Ok",
|
"welcomeModalCTA": "Ok",
|
||||||
"lockedRoomHeader": "Ce salon est verrouillé",
|
"lockedRoomHeader": "Ce salon est verrouillé",
|
||||||
"helpTranslate": "Aidez-nous à traduire Darkwire!"
|
"helpTranslate": "Aidez-nous à traduire Darkwire!"
|
||||||
|
@ -9,11 +9,12 @@ const initialState = {
|
|||||||
windowIsFocused: true,
|
windowIsFocused: true,
|
||||||
unreadMessageCount: 0,
|
unreadMessageCount: 0,
|
||||||
soundIsEnabled: true,
|
soundIsEnabled: true,
|
||||||
notificationIsEnabled: true,
|
notificationIsEnabled: false,
|
||||||
|
notificationIsAllowed: null,
|
||||||
socketConnected: false,
|
socketConnected: false,
|
||||||
language,
|
language,
|
||||||
translations: getTranslations(language)
|
translations: getTranslations(language),
|
||||||
}
|
};
|
||||||
|
|
||||||
const app = (state = initialState, action) => {
|
const app = (state = initialState, action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
@ -21,52 +22,57 @@ const app = (state = initialState, action) => {
|
|||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
modalComponent: action.payload,
|
modalComponent: action.payload,
|
||||||
}
|
};
|
||||||
case 'CLOSE_MODAL':
|
case 'CLOSE_MODAL':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
modalComponent: null,
|
modalComponent: null,
|
||||||
}
|
};
|
||||||
case 'SET_SCROLLED_TO_BOTTOM':
|
case 'SET_SCROLLED_TO_BOTTOM':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
scrolledToBottom: action.payload,
|
scrolledToBottom: action.payload,
|
||||||
}
|
};
|
||||||
case 'TOGGLE_WINDOW_FOCUS':
|
case 'TOGGLE_WINDOW_FOCUS':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
windowIsFocused: action.payload,
|
windowIsFocused: action.payload,
|
||||||
unreadMessageCount: 0,
|
unreadMessageCount: 0,
|
||||||
}
|
};
|
||||||
case 'RECEIVE_ENCRYPTED_MESSAGE_TEXT_MESSAGE':
|
case 'RECEIVE_ENCRYPTED_MESSAGE_TEXT_MESSAGE':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
unreadMessageCount: state.windowIsFocused ? 0 : state.unreadMessageCount + 1,
|
unreadMessageCount: state.windowIsFocused ? 0 : state.unreadMessageCount + 1,
|
||||||
}
|
};
|
||||||
case 'TOGGLE_SOUND_ENABLED':
|
case 'TOGGLE_SOUND_ENABLED':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
soundIsEnabled: action.payload,
|
soundIsEnabled: action.payload,
|
||||||
}
|
};
|
||||||
case 'TOGGLE_NOTIFICATION_ENABLED':
|
case 'TOGGLE_NOTIFICATION_ENABLED':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
notificationIsEnabled: action.payload,
|
notificationIsEnabled: action.payload,
|
||||||
}
|
};
|
||||||
|
case 'TOGGLE_NOTIFICATION_ALLOWED':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
notificationIsAllowed: action.payload,
|
||||||
|
};
|
||||||
case 'TOGGLE_SOCKET_CONNECTED':
|
case 'TOGGLE_SOCKET_CONNECTED':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
socketConnected: action.payload,
|
socketConnected: action.payload,
|
||||||
}
|
};
|
||||||
case 'CHANGE_LANGUAGE':
|
case 'CHANGE_LANGUAGE':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
language: action.payload,
|
language: action.payload,
|
||||||
translations: getTranslations(action.payload)
|
translations: getTranslations(action.payload),
|
||||||
}
|
};
|
||||||
default:
|
default:
|
||||||
return state
|
return state;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export default app
|
export default app;
|
||||||
|
@ -14,7 +14,8 @@ describe('App reducer', () => {
|
|||||||
modalComponent: null,
|
modalComponent: null,
|
||||||
scrolledToBottom: true,
|
scrolledToBottom: true,
|
||||||
socketConnected: false,
|
socketConnected: false,
|
||||||
notificationIsEnabled: true,
|
notificationIsEnabled: false,
|
||||||
|
notificationIsAllowed: null,
|
||||||
soundIsEnabled: true,
|
soundIsEnabled: true,
|
||||||
translations: { path: 'test' },
|
translations: { path: 'test' },
|
||||||
unreadMessageCount: 0,
|
unreadMessageCount: 0,
|
||||||
|
@ -21,10 +21,13 @@ const room = (state = initialState, action) => {
|
|||||||
.filter(member => memberPubKeys.includes(member.publicKey.n))
|
.filter(member => memberPubKeys.includes(member.publicKey.n))
|
||||||
.map(member => {
|
.map(member => {
|
||||||
const thisMember = action.payload.members.find(mem => mem.publicKey.n === member.id)
|
const thisMember = action.payload.members.find(mem => mem.publicKey.n === member.id)
|
||||||
return {
|
if (thisMember){
|
||||||
...member,
|
return {
|
||||||
isOwner: thisMember.isOwner
|
...member,
|
||||||
|
isOwner: thisMember.isOwner
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return {...member}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
case 'RECEIVE_ENCRYPTED_MESSAGE_ADD_USER':
|
case 'RECEIVE_ENCRYPTED_MESSAGE_ADD_USER':
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import beepFile from 'audio/beep.mp3'
|
import beepFile from 'audio/beep.mp3';
|
||||||
|
|
||||||
|
|
||||||
const showNotification = (title, message, avatarUrl) => {
|
const showNotification = (title, message, avatarUrl) => {
|
||||||
const notifBody = {
|
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 };
|
export default { notify, beep };
|
||||||
|
Loading…
x
Reference in New Issue
Block a user