mirror of
https://github.com/darkwire/darkwire.io.git
synced 2025-07-19 02:59:57 +00:00
Merge pull request #76 from darkwire/refactor
Refactor actions and reducers
This commit is contained in:
commit
a70efc5674
@ -21,7 +21,6 @@
|
|||||||
"moment": "^2.24.0",
|
"moment": "^2.24.0",
|
||||||
"node-sass": "^4.12.0",
|
"node-sass": "^4.12.0",
|
||||||
"popper.js": "^1.15.0",
|
"popper.js": "^1.15.0",
|
||||||
"query-string": "^6.5.0",
|
|
||||||
"randomcolor": "^0.5.4",
|
"randomcolor": "^0.5.4",
|
||||||
"react": "^16.8.6",
|
"react": "^16.8.6",
|
||||||
"react-dom": "^16.8.6",
|
"react-dom": "^16.8.6",
|
||||||
|
@ -18,3 +18,11 @@ export const toggleSoundEnabled = payload => async (dispatch) => {
|
|||||||
export const toggleSocketConnected = payload => async (dispatch) => {
|
export const toggleSocketConnected = payload => async (dispatch) => {
|
||||||
dispatch({ type: 'TOGGLE_SOCKET_CONNECTED', payload })
|
dispatch({ type: 'TOGGLE_SOCKET_CONNECTED', payload })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const createUser = payload => async (dispatch) => {
|
||||||
|
dispatch({ type: 'CREATE_USER', payload })
|
||||||
|
}
|
||||||
|
|
||||||
|
export const clearActivities = () => async (dispatch) => {
|
||||||
|
dispatch({ type: 'CLEAR_ACTIVITIES' })
|
||||||
|
}
|
||||||
|
19
client/src/actions/encrypted_messages.js
Normal file
19
client/src/actions/encrypted_messages.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { getSocket } from 'utils/socket'
|
||||||
|
import {
|
||||||
|
prepare as prepareMessage,
|
||||||
|
process as processMessage,
|
||||||
|
} from 'utils/message'
|
||||||
|
|
||||||
|
export const sendEncryptedMessage = payload => async (dispatch, getState) => {
|
||||||
|
const state = getState()
|
||||||
|
const msg = await prepareMessage(payload, state)
|
||||||
|
dispatch({ type: `SEND_ENCRYPTED_MESSAGE_${msg.original.type}`, payload: msg.original.payload })
|
||||||
|
getSocket().emit('ENCRYPTED_MESSAGE', msg.toSend)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const receiveEncryptedMessage = payload => async (dispatch, getState) => {
|
||||||
|
const state = getState()
|
||||||
|
const message = await processMessage(payload, state)
|
||||||
|
// Pass current state to all RECEIVE_ENCRYPTED_MESSAGE reducers for convenience, since each may have different needs
|
||||||
|
dispatch({ type: `RECEIVE_ENCRYPTED_MESSAGE_${message.type}`, payload: { payload: message.payload, state } })
|
||||||
|
}
|
@ -1,12 +0,0 @@
|
|||||||
const methodMap = {
|
|
||||||
GET: '',
|
|
||||||
POST: 'CREATE_',
|
|
||||||
PUT: 'UPDATE_',
|
|
||||||
DELETE: 'DELETE_',
|
|
||||||
}
|
|
||||||
|
|
||||||
export const fetchStart = (name, method, resourceId, meta) => ({ type: `FETCH_${methodMap[method]}${name.toUpperCase()}_START`, payload: { resourceId, meta } })
|
|
||||||
|
|
||||||
export const fetchSuccess = (name, method, response) => ({ type: `FETCH_${methodMap[method]}${name.toUpperCase()}_SUCCESS`, payload: response })
|
|
||||||
|
|
||||||
export const fetchFailure = (name, method, response) => ({ type: `FETCH_${methodMap[method]}${name.toUpperCase()}_FAILURE`, payload: response })
|
|
@ -1,4 +1,4 @@
|
|||||||
export * from './fetch'
|
|
||||||
export * from './room'
|
|
||||||
export * from './app'
|
export * from './app'
|
||||||
|
export * from './unencrypted_messages'
|
||||||
|
export * from './encrypted_messages'
|
||||||
|
|
||||||
|
@ -1,104 +0,0 @@
|
|||||||
import fetch from 'api'
|
|
||||||
import isEqual from 'lodash/isEqual'
|
|
||||||
import {
|
|
||||||
process as processMessage,
|
|
||||||
prepare as prepareMessage,
|
|
||||||
} from 'utils/message'
|
|
||||||
import { getSocket } from 'utils/socket'
|
|
||||||
|
|
||||||
export const receiveSocketMessage = payload => async (dispatch, getState) => {
|
|
||||||
const state = getState()
|
|
||||||
const message = await processMessage(payload, state)
|
|
||||||
// Pass current state to all HANDLE_SOCKET_MESSAGE reducers for convenience, since each may have different needs
|
|
||||||
dispatch({ type: `HANDLE_SOCKET_MESSAGE_${message.type}`, payload: { payload: message.payload, state } })
|
|
||||||
}
|
|
||||||
|
|
||||||
export const createUser = payload => async (dispatch) => {
|
|
||||||
dispatch({ type: 'CREATE_USER', payload })
|
|
||||||
}
|
|
||||||
|
|
||||||
export const sendUserEnter = payload => async () => {
|
|
||||||
getSocket().emit('USER_ENTER', {
|
|
||||||
publicKey: payload.publicKey,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export const receiveUserExit = payload => async (dispatch, getState) => {
|
|
||||||
const state = getState()
|
|
||||||
const exitingUser = state.room.members.find(m => !payload.map(p => JSON.stringify(p.publicKey)).includes(JSON.stringify(m.publicKey)))
|
|
||||||
|
|
||||||
if (!exitingUser) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const exitingUserId = exitingUser.id
|
|
||||||
const exitingUsername = exitingUser.username
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: 'USER_EXIT',
|
|
||||||
payload: {
|
|
||||||
members: payload,
|
|
||||||
id: exitingUserId,
|
|
||||||
username: exitingUsername,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export const receiveUserEnter = payload => async (dispatch) => {
|
|
||||||
dispatch({ type: 'USER_ENTER', payload })
|
|
||||||
}
|
|
||||||
|
|
||||||
export const onFileTransfer = payload => async (dispatch) => {
|
|
||||||
dispatch({ type: 'PREFLIGHT_FILE_TRANSFER', payload })
|
|
||||||
}
|
|
||||||
|
|
||||||
export const sendSocketMessage = payload => async (dispatch, getState) => {
|
|
||||||
const state = getState()
|
|
||||||
const msg = await prepareMessage(payload, state)
|
|
||||||
dispatch({ type: `SEND_SOCKET_MESSAGE_${msg.original.type}`, payload: msg.original.payload })
|
|
||||||
getSocket().emit('PAYLOAD', msg.toSend)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const toggleLockRoom = () => async (dispatch, getState) => {
|
|
||||||
const state = getState()
|
|
||||||
getSocket().emit('TOGGLE_LOCK_ROOM', null, (res) => {
|
|
||||||
dispatch({
|
|
||||||
type: 'TOGGLE_LOCK_ROOM',
|
|
||||||
payload: {
|
|
||||||
locked: res.isLocked,
|
|
||||||
username: state.user.username,
|
|
||||||
sender: state.user.id,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export const receiveToggleLockRoom = payload => async (dispatch, getState) => {
|
|
||||||
const state = getState()
|
|
||||||
|
|
||||||
const lockedByUser = state.room.members.find(m => isEqual(m.publicKey, payload.publicKey))
|
|
||||||
const lockedByUsername = lockedByUser.username
|
|
||||||
const lockedByUserId = lockedByUser.id
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: 'RECEIVE_TOGGLE_LOCK_ROOM',
|
|
||||||
payload: {
|
|
||||||
username: lockedByUsername,
|
|
||||||
locked: payload.locked,
|
|
||||||
id: lockedByUserId,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export const clearActivities = () => async (dispatch) => {
|
|
||||||
dispatch({ type: 'CLEAR_ACTIVITIES' })
|
|
||||||
}
|
|
||||||
|
|
||||||
export const onConnected = payload => async (dispatch) => {
|
|
||||||
dispatch({ type: 'CONNECTED', payload })
|
|
||||||
}
|
|
||||||
|
|
||||||
export const sendUserDisconnect = () => async () => {
|
|
||||||
getSocket().emit('USER_DISCONNECT')
|
|
||||||
}
|
|
||||||
|
|
80
client/src/actions/unencrypted_messages.js
Normal file
80
client/src/actions/unencrypted_messages.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import { getSocket } from 'utils/socket'
|
||||||
|
|
||||||
|
const receiveUserEnter = (payload, dispatch) => {
|
||||||
|
dispatch({ type: 'USER_ENTER', payload })
|
||||||
|
}
|
||||||
|
|
||||||
|
const receiveToggleLockRoom = (payload, dispatch, getState) => {
|
||||||
|
const state = getState()
|
||||||
|
|
||||||
|
const lockedByUser = state.room.members.find(m => m.publicKey.n === payload.publicKey.n)
|
||||||
|
const lockedByUsername = lockedByUser.username
|
||||||
|
const lockedByUserId = lockedByUser.id
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: 'RECEIVE_TOGGLE_LOCK_ROOM',
|
||||||
|
payload: {
|
||||||
|
username: lockedByUsername,
|
||||||
|
locked: payload.locked,
|
||||||
|
id: lockedByUserId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const receiveUserExit = (payload, dispatch, getState) => {
|
||||||
|
const state = getState()
|
||||||
|
const payloadPublicKeys = payload.map(member => member.publicKey.n);
|
||||||
|
const exitingUser = state.room.members.find(m => !payloadPublicKeys.includes(m.publicKey.n))
|
||||||
|
|
||||||
|
if (!exitingUser) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const exitingUserId = exitingUser.id
|
||||||
|
const exitingUsername = exitingUser.username
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: 'USER_EXIT',
|
||||||
|
payload: {
|
||||||
|
members: payload,
|
||||||
|
id: exitingUserId,
|
||||||
|
username: exitingUsername,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const receiveUnencryptedMessage = (type, payload) => async (dispatch, getState) => {
|
||||||
|
switch(type) {
|
||||||
|
case 'USER_ENTER':
|
||||||
|
return receiveUserEnter(payload, dispatch);
|
||||||
|
case 'USER_EXIT':
|
||||||
|
return receiveUserExit(payload, dispatch, getState);
|
||||||
|
case 'TOGGLE_LOCK_ROOM':
|
||||||
|
return receiveToggleLockRoom(payload, dispatch, getState);
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sendToggleLockRoom = (dispatch, getState) => {
|
||||||
|
const state = getState()
|
||||||
|
getSocket().emit('TOGGLE_LOCK_ROOM', null, (res) => {
|
||||||
|
dispatch({
|
||||||
|
type: 'TOGGLE_LOCK_ROOM',
|
||||||
|
payload: {
|
||||||
|
locked: res.isLocked,
|
||||||
|
username: state.user.username,
|
||||||
|
sender: state.user.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sendUnencryptedMessage = (type, payload) => async (dispatch, getState) => {
|
||||||
|
switch(type) {
|
||||||
|
case 'TOGGLE_LOCK_ROOM':
|
||||||
|
return sendToggleLockRoom(dispatch, getState);
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
@ -1,59 +0,0 @@
|
|||||||
import {
|
|
||||||
fetchStart,
|
|
||||||
fetchSuccess,
|
|
||||||
fetchFailure,
|
|
||||||
} from 'actions'
|
|
||||||
import queryString from 'querystring'
|
|
||||||
import generateUrl from './generator'
|
|
||||||
|
|
||||||
export default (opts, dispatch, name, metaOpts = {}) => {
|
|
||||||
const method = opts.method || 'GET'
|
|
||||||
const resourceId = opts.resourceId
|
|
||||||
let url = generateUrl(opts.resourceName, resourceId)
|
|
||||||
|
|
||||||
const config = {
|
|
||||||
method,
|
|
||||||
headers: {},
|
|
||||||
type: 'cors',
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts.body) {
|
|
||||||
config.body = JSON.stringify(opts.body)
|
|
||||||
config.headers['Content-Type'] = 'application/json'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts.query) {
|
|
||||||
url = `${url}?${queryString.stringify(opts.query)}`
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const meta = { ...metaOpts, timestamp: Date.now() }
|
|
||||||
dispatch(fetchStart(name, method, resourceId, meta))
|
|
||||||
return window.fetch(url, config)
|
|
||||||
.then(async (response) => {
|
|
||||||
let json = {}
|
|
||||||
|
|
||||||
try {
|
|
||||||
json = await response.json()
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
const dispatchOps = {
|
|
||||||
response,
|
|
||||||
json,
|
|
||||||
resourceId,
|
|
||||||
meta,
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
dispatch(fetchSuccess(name, method, dispatchOps))
|
|
||||||
return resolve(dispatchOps)
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(fetchFailure(name, method, dispatchOps))
|
|
||||||
|
|
||||||
return reject(dispatchOps)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
@ -71,7 +71,7 @@ class About extends Component {
|
|||||||
<div className="input-group-append">
|
<div className="input-group-append">
|
||||||
<button
|
<button
|
||||||
className="btn btn-secondary"
|
className="btn btn-secondary"
|
||||||
type="button"
|
type="submit"
|
||||||
>
|
>
|
||||||
Submit
|
Submit
|
||||||
</button>
|
</button>
|
||||||
|
@ -3,11 +3,11 @@ import { mount } from 'enzyme'
|
|||||||
import toJson from 'enzyme-to-json'
|
import toJson from 'enzyme-to-json'
|
||||||
import { Chat } from './index.js'
|
import { Chat } from './index.js'
|
||||||
|
|
||||||
const sendSocketMessage = jest.fn()
|
const sendEncryptedMessage = jest.fn()
|
||||||
|
|
||||||
test('Chat Component', () => {
|
test('Chat Component', () => {
|
||||||
const component = mount(
|
const component = mount(
|
||||||
<Chat scrollToBottom={() => {}} focusChat={false} userId="foo" username="user" showNotice={() => {}} clearActivities={() => {}} sendSocketMessage={sendSocketMessage} />
|
<Chat scrollToBottom={() => {}} focusChat={false} userId="foo" username="user" showNotice={() => {}} clearActivities={() => {}} sendEncryptedMessage={sendEncryptedMessage} />
|
||||||
)
|
)
|
||||||
|
|
||||||
const componentJSON = toJson(component)
|
const componentJSON = toJson(component)
|
||||||
|
@ -4,8 +4,9 @@ import sanitizeHtml from 'sanitize-html'
|
|||||||
import FileTransfer from 'components/FileTransfer'
|
import FileTransfer from 'components/FileTransfer'
|
||||||
import { CornerDownRight } from 'react-feather'
|
import { CornerDownRight } from 'react-feather'
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import { clearActivities, showNotice } from '../../actions'
|
import { clearActivities, showNotice, sendEncryptedMessage } from '../../actions'
|
||||||
import { getSelectedText, hasTouchSupport } from '../../utils/dom'
|
import { getSelectedText, hasTouchSupport } from '../../utils/dom'
|
||||||
|
|
||||||
// Disable for now
|
// Disable for now
|
||||||
// import autosize from 'autosize'
|
// import autosize from 'autosize'
|
||||||
|
|
||||||
@ -51,7 +52,7 @@ export class Chat extends Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.sendSocketMessage({
|
this.props.sendEncryptedMessage({
|
||||||
type: 'CHANGE_USERNAME',
|
type: 'CHANGE_USERNAME',
|
||||||
payload: {
|
payload: {
|
||||||
id: this.props.userId,
|
id: this.props.userId,
|
||||||
@ -85,7 +86,7 @@ export class Chat extends Component {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.sendSocketMessage({
|
this.props.sendEncryptedMessage({
|
||||||
type: 'USER_ACTION',
|
type: 'USER_ACTION',
|
||||||
payload: {
|
payload: {
|
||||||
action: actionMessage,
|
action: actionMessage,
|
||||||
@ -216,8 +217,8 @@ export class Chat extends Component {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.props.sendSocketMessage({
|
this.props.sendEncryptedMessage({
|
||||||
type: 'SEND_MESSAGE',
|
type: 'TEXT_MESSAGE',
|
||||||
payload: {
|
payload: {
|
||||||
text: message,
|
text: message,
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
@ -256,7 +257,7 @@ export class Chat extends Component {
|
|||||||
placeholder="Type here"
|
placeholder="Type here"
|
||||||
onChange={this.handleInputChange.bind(this)} />
|
onChange={this.handleInputChange.bind(this)} />
|
||||||
<div className="input-controls">
|
<div className="input-controls">
|
||||||
<FileTransfer sendSocketMessage={this.props.sendSocketMessage} />
|
<FileTransfer sendEncryptedMessage={this.props.sendEncryptedMessage} />
|
||||||
{touchSupport &&
|
{touchSupport &&
|
||||||
<button onClick={this.handleSendClick.bind(this)} className={`icon is-right send btn btn-link ${this.canSend() ? 'active' : ''}`}>
|
<button onClick={this.handleSendClick.bind(this)} className={`icon is-right send btn btn-link ${this.canSend() ? 'active' : ''}`}>
|
||||||
<CornerDownRight className={this.canSend() ? '' : 'disabled'} />
|
<CornerDownRight className={this.canSend() ? '' : 'disabled'} />
|
||||||
@ -269,7 +270,7 @@ export class Chat extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Chat.propTypes = {
|
Chat.propTypes = {
|
||||||
sendSocketMessage: PropTypes.func.isRequired,
|
sendEncryptedMessage: PropTypes.func.isRequired,
|
||||||
showNotice: PropTypes.func.isRequired,
|
showNotice: PropTypes.func.isRequired,
|
||||||
userId: PropTypes.string.isRequired,
|
userId: PropTypes.string.isRequired,
|
||||||
username: PropTypes.string.isRequired,
|
username: PropTypes.string.isRequired,
|
||||||
@ -286,6 +287,7 @@ const mapStateToProps = state => ({
|
|||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
clearActivities,
|
clearActivities,
|
||||||
showNotice,
|
showNotice,
|
||||||
|
sendEncryptedMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
|
@ -81,7 +81,7 @@ export default class FileTransfer extends Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
localFileQueue,
|
localFileQueue,
|
||||||
}, async () => {
|
}, async () => {
|
||||||
this.props.sendSocketMessage({
|
this.props.sendEncryptedMessage({
|
||||||
type: 'SEND_FILE',
|
type: 'SEND_FILE',
|
||||||
payload: {
|
payload: {
|
||||||
fileName: fileData.fileName,
|
fileName: fileData.fileName,
|
||||||
@ -112,5 +112,5 @@ export default class FileTransfer extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FileTransfer.propTypes = {
|
FileTransfer.propTypes = {
|
||||||
sendSocketMessage: PropTypes.func.isRequired,
|
sendEncryptedMessage: PropTypes.func.isRequired,
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
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 } 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 ChatInput from 'containers/Chat'
|
import ChatInput from 'components/Chat'
|
||||||
import Connecting from 'components/Connecting'
|
import Connecting from 'components/Connecting'
|
||||||
import Message from 'components/Message'
|
import Message from 'components/Message'
|
||||||
import Username from 'components/Username'
|
import Username from 'components/Username'
|
||||||
@ -21,6 +21,20 @@ import beepFile from 'audio/beep.mp3'
|
|||||||
import Zoom from 'utils/ImageZoom'
|
import Zoom from 'utils/ImageZoom'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { getObjectUrl } from 'utils/file'
|
import { getObjectUrl } from 'utils/file'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import {
|
||||||
|
receiveEncryptedMessage,
|
||||||
|
createUser,
|
||||||
|
openModal,
|
||||||
|
closeModal,
|
||||||
|
setScrolledToBottom,
|
||||||
|
toggleWindowFocus,
|
||||||
|
toggleSoundEnabled,
|
||||||
|
toggleSocketConnected,
|
||||||
|
receiveUnencryptedMessage,
|
||||||
|
sendUnencryptedMessage,
|
||||||
|
sendEncryptedMessage
|
||||||
|
} from 'actions'
|
||||||
|
|
||||||
import styles from './styles.module.scss'
|
import styles from './styles.module.scss'
|
||||||
|
|
||||||
@ -28,7 +42,7 @@ const crypto = new Crypto()
|
|||||||
|
|
||||||
Modal.setAppElement('#root');
|
Modal.setAppElement('#root');
|
||||||
|
|
||||||
export default class Home extends Component {
|
class Home extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
@ -45,32 +59,22 @@ export default class Home extends Component {
|
|||||||
|
|
||||||
const user = await this.createUser()
|
const user = await this.createUser()
|
||||||
|
|
||||||
const socket = connect(roomId)
|
const socket = connectSocket(roomId)
|
||||||
|
|
||||||
const disconnectEvents = [
|
this.socket = socket;
|
||||||
'disconnect',
|
|
||||||
]
|
|
||||||
|
|
||||||
disconnectEvents.forEach((evt) => {
|
socket.on('disconnect', () => {
|
||||||
socket.on(evt, () => {
|
this.props.toggleSocketConnected(false)
|
||||||
this.props.toggleSocketConnected(false)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const connectEvents = [
|
socket.on('connect', () => {
|
||||||
'connect',
|
this.initApp(user)
|
||||||
]
|
this.props.toggleSocketConnected(true)
|
||||||
|
|
||||||
connectEvents.forEach((evt) => {
|
|
||||||
socket.on(evt, () => {
|
|
||||||
this.initApp(user)
|
|
||||||
this.props.toggleSocketConnected(true)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on('USER_ENTER', (payload) => {
|
socket.on('USER_ENTER', (payload) => {
|
||||||
this.props.receiveUserEnter(payload)
|
this.props.receiveUnencryptedMessage('USER_ENTER', payload)
|
||||||
this.props.sendSocketMessage({
|
this.props.sendEncryptedMessage({
|
||||||
type: 'ADD_USER',
|
type: 'ADD_USER',
|
||||||
payload: {
|
payload: {
|
||||||
username: this.props.username,
|
username: this.props.username,
|
||||||
@ -82,45 +86,33 @@ export default class Home extends Component {
|
|||||||
})
|
})
|
||||||
|
|
||||||
socket.on('USER_EXIT', (payload) => {
|
socket.on('USER_EXIT', (payload) => {
|
||||||
this.props.receiveUserExit(payload)
|
this.props.receiveUnencryptedMessage('USER_EXIT', payload)
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on('PAYLOAD', (payload) => {
|
socket.on('ENCRYPTED_MESSAGE', (payload) => {
|
||||||
this.props.receiveSocketMessage(payload)
|
this.props.receiveEncryptedMessage(payload)
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on('TOGGLE_LOCK_ROOM', (payload) => {
|
socket.on('TOGGLE_LOCK_ROOM', (payload) => {
|
||||||
this.props.receiveToggleLockRoom(payload)
|
this.props.receiveUnencryptedMessage('TOGGLE_LOCK_ROOM', payload)
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on('CONNECTED', (payload) => {
|
|
||||||
this.props.onConnected(payload);
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on('ROOM_LOCKED', (payload) => {
|
socket.on('ROOM_LOCKED', (payload) => {
|
||||||
this.props.openModal('Room Locked')
|
this.props.openModal('Room Locked')
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('beforeunload', (evt) => {
|
window.addEventListener('beforeunload', (evt) => {
|
||||||
this.props.sendUserDisconnect();
|
socket.emit('USER_DISCONNECT')
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.bindEvents()
|
this.bindEvents()
|
||||||
|
|
||||||
if (this.props.joining) {
|
|
||||||
this.props.openModal('Connecting')
|
|
||||||
}
|
|
||||||
|
|
||||||
this.beep = window.Audio && new window.Audio(beepFile)
|
this.beep = window.Audio && new window.Audio(beepFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
if (this.props.joining && !nextProps.joining) {
|
|
||||||
this.props.closeModal()
|
|
||||||
}
|
|
||||||
|
|
||||||
Tinycon.setBubble(nextProps.faviconCount)
|
Tinycon.setBubble(nextProps.faviconCount)
|
||||||
|
|
||||||
if (nextProps.faviconCount !== 0 && nextProps.faviconCount !== this.props.faviconCount && this.props.soundIsEnabled) {
|
if (nextProps.faviconCount !== 0 && nextProps.faviconCount !== this.props.faviconCount && this.props.soundIsEnabled) {
|
||||||
@ -171,7 +163,7 @@ export default class Home extends Component {
|
|||||||
|
|
||||||
getActivityComponent(activity) {
|
getActivityComponent(activity) {
|
||||||
switch (activity.type) {
|
switch (activity.type) {
|
||||||
case 'SEND_MESSAGE':
|
case 'TEXT_MESSAGE':
|
||||||
return (
|
return (
|
||||||
<Message
|
<Message
|
||||||
sender={activity.username}
|
sender={activity.username}
|
||||||
@ -275,7 +267,7 @@ export default class Home extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initApp(user) {
|
initApp(user) {
|
||||||
this.props.sendUserEnter({
|
this.socket.emit('USER_ENTER', {
|
||||||
publicKey: user.publicKey,
|
publicKey: user.publicKey,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -349,7 +341,7 @@ export default class Home extends Component {
|
|||||||
members={this.props.members}
|
members={this.props.members}
|
||||||
roomId={this.props.roomId}
|
roomId={this.props.roomId}
|
||||||
roomLocked={this.props.roomLocked}
|
roomLocked={this.props.roomLocked}
|
||||||
toggleLockRoom={this.props.toggleLockRoom}
|
toggleLockRoom={() => this.props.sendUnencryptedMessage('TOGGLE_LOCK_ROOM')}
|
||||||
openModal={this.props.openModal}
|
openModal={this.props.openModal}
|
||||||
iAmOwner={this.props.iAmOwner}
|
iAmOwner={this.props.iAmOwner}
|
||||||
userId={this.props.userId}
|
userId={this.props.userId}
|
||||||
@ -411,11 +403,8 @@ Home.defaultProps = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Home.propTypes = {
|
Home.propTypes = {
|
||||||
receiveSocketMessage: PropTypes.func.isRequired,
|
receiveEncryptedMessage: PropTypes.func.isRequired,
|
||||||
sendSocketMessage: PropTypes.func.isRequired,
|
|
||||||
createUser: PropTypes.func.isRequired,
|
createUser: PropTypes.func.isRequired,
|
||||||
receiveUserExit: PropTypes.func.isRequired,
|
|
||||||
receiveUserEnter: PropTypes.func.isRequired,
|
|
||||||
activities: PropTypes.array.isRequired,
|
activities: PropTypes.array.isRequired,
|
||||||
username: PropTypes.string.isRequired,
|
username: PropTypes.string.isRequired,
|
||||||
publicKey: PropTypes.object.isRequired,
|
publicKey: PropTypes.object.isRequired,
|
||||||
@ -423,23 +412,59 @@ Home.propTypes = {
|
|||||||
match: PropTypes.object.isRequired,
|
match: PropTypes.object.isRequired,
|
||||||
roomId: PropTypes.string.isRequired,
|
roomId: PropTypes.string.isRequired,
|
||||||
roomLocked: PropTypes.bool.isRequired,
|
roomLocked: PropTypes.bool.isRequired,
|
||||||
toggleLockRoom: PropTypes.func.isRequired,
|
|
||||||
receiveToggleLockRoom: PropTypes.func.isRequired,
|
|
||||||
modalComponent: PropTypes.string,
|
modalComponent: PropTypes.string,
|
||||||
openModal: PropTypes.func.isRequired,
|
openModal: PropTypes.func.isRequired,
|
||||||
closeModal: PropTypes.func.isRequired,
|
closeModal: PropTypes.func.isRequired,
|
||||||
setScrolledToBottom: PropTypes.func.isRequired,
|
setScrolledToBottom: PropTypes.func.isRequired,
|
||||||
scrolledToBottom: PropTypes.bool.isRequired,
|
scrolledToBottom: PropTypes.bool.isRequired,
|
||||||
iAmOwner: PropTypes.bool.isRequired,
|
iAmOwner: PropTypes.bool.isRequired,
|
||||||
sendUserEnter: PropTypes.func.isRequired,
|
|
||||||
sendUserDisconnect: PropTypes.func.isRequired,
|
|
||||||
userId: PropTypes.string.isRequired,
|
userId: PropTypes.string.isRequired,
|
||||||
joining: PropTypes.bool.isRequired,
|
|
||||||
toggleWindowFocus: PropTypes.func.isRequired,
|
toggleWindowFocus: PropTypes.func.isRequired,
|
||||||
faviconCount: PropTypes.number.isRequired,
|
faviconCount: PropTypes.number.isRequired,
|
||||||
soundIsEnabled: PropTypes.bool.isRequired,
|
soundIsEnabled: PropTypes.bool.isRequired,
|
||||||
toggleSoundEnabled: PropTypes.func.isRequired,
|
toggleSoundEnabled: PropTypes.func.isRequired,
|
||||||
toggleSocketConnected: PropTypes.func.isRequired,
|
toggleSocketConnected: PropTypes.func.isRequired,
|
||||||
socketConnected: PropTypes.bool.isRequired,
|
socketConnected: PropTypes.bool.isRequired,
|
||||||
onConnected: PropTypes.func.isRequired
|
sendUnencryptedMessage: PropTypes.func.isRequired,
|
||||||
|
sendEncryptedMessage: PropTypes.func.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state) => {
|
||||||
|
const me = state.room.members.find(m => m.id === state.user.id)
|
||||||
|
|
||||||
|
return {
|
||||||
|
activities: state.activities.items,
|
||||||
|
userId: state.user.id,
|
||||||
|
username: state.user.username,
|
||||||
|
publicKey: state.user.publicKey,
|
||||||
|
privateKey: state.user.privateKey,
|
||||||
|
members: state.room.members.filter(m => m.username && m.publicKey),
|
||||||
|
roomId: state.room.id,
|
||||||
|
roomLocked: state.room.isLocked,
|
||||||
|
modalComponent: state.app.modalComponent,
|
||||||
|
scrolledToBottom: state.app.scrolledToBottom,
|
||||||
|
iAmOwner: Boolean(me && me.isOwner),
|
||||||
|
faviconCount: state.app.unreadMessageCount,
|
||||||
|
soundIsEnabled: state.app.soundIsEnabled,
|
||||||
|
socketConnected: state.app.socketConnected,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
receiveEncryptedMessage,
|
||||||
|
createUser,
|
||||||
|
openModal,
|
||||||
|
closeModal,
|
||||||
|
setScrolledToBottom,
|
||||||
|
toggleWindowFocus,
|
||||||
|
toggleSoundEnabled,
|
||||||
|
toggleSocketConnected,
|
||||||
|
receiveUnencryptedMessage,
|
||||||
|
sendUnencryptedMessage,
|
||||||
|
sendEncryptedMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(Home)
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
import { connect } from 'react-redux'
|
|
||||||
import { sendSocketMessage } from 'actions'
|
|
||||||
import ChatInput from 'components/Chat'
|
|
||||||
|
|
||||||
const mapStateToProps = () => ({
|
|
||||||
})
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
sendSocketMessage,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
mapDispatchToProps
|
|
||||||
)(ChatInput)
|
|
@ -1,67 +0,0 @@
|
|||||||
import { connect } from 'react-redux'
|
|
||||||
import Home from 'components/Home'
|
|
||||||
import {
|
|
||||||
receiveSocketMessage,
|
|
||||||
sendSocketMessage,
|
|
||||||
createUser,
|
|
||||||
receiveUserExit,
|
|
||||||
receiveUserEnter,
|
|
||||||
toggleLockRoom,
|
|
||||||
receiveToggleLockRoom,
|
|
||||||
openModal,
|
|
||||||
closeModal,
|
|
||||||
setScrolledToBottom,
|
|
||||||
sendUserEnter,
|
|
||||||
toggleWindowFocus,
|
|
||||||
toggleSoundEnabled,
|
|
||||||
toggleSocketConnected,
|
|
||||||
onConnected,
|
|
||||||
sendUserDisconnect
|
|
||||||
} from 'actions'
|
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
|
||||||
const me = state.room.members.find(m => m.id === state.user.id)
|
|
||||||
|
|
||||||
return {
|
|
||||||
activities: state.activities.items,
|
|
||||||
userId: state.user.id,
|
|
||||||
username: state.user.username,
|
|
||||||
publicKey: state.user.publicKey,
|
|
||||||
privateKey: state.user.privateKey,
|
|
||||||
members: state.room.members.filter(m => m.username && m.publicKey),
|
|
||||||
roomId: state.room.id,
|
|
||||||
roomLocked: state.room.isLocked,
|
|
||||||
modalComponent: state.app.modalComponent,
|
|
||||||
scrolledToBottom: state.app.scrolledToBottom,
|
|
||||||
iAmOwner: Boolean(me && me.isOwner),
|
|
||||||
joining: state.room.joining,
|
|
||||||
faviconCount: state.app.unreadMessageCount,
|
|
||||||
soundIsEnabled: state.app.soundIsEnabled,
|
|
||||||
socketConnected: state.app.socketConnected,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
receiveSocketMessage,
|
|
||||||
sendSocketMessage,
|
|
||||||
receiveUserExit,
|
|
||||||
receiveUserEnter,
|
|
||||||
createUser,
|
|
||||||
toggleLockRoom,
|
|
||||||
receiveToggleLockRoom,
|
|
||||||
openModal,
|
|
||||||
closeModal,
|
|
||||||
setScrolledToBottom,
|
|
||||||
sendUserEnter,
|
|
||||||
toggleWindowFocus,
|
|
||||||
toggleSoundEnabled,
|
|
||||||
toggleSocketConnected,
|
|
||||||
onConnected,
|
|
||||||
sendUserDisconnect
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
mapDispatchToProps
|
|
||||||
)(Home)
|
|
||||||
|
|
@ -1,12 +1,5 @@
|
|||||||
const initialState = {
|
const initialState = {
|
||||||
items: [
|
items: [],
|
||||||
// {
|
|
||||||
// type: 'message | file | isTyping | usernameChange | slashCommand',
|
|
||||||
// data,
|
|
||||||
// username,
|
|
||||||
// timestamp
|
|
||||||
// }
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const activities = (state = initialState, action) => {
|
const activities = (state = initialState, action) => {
|
||||||
@ -16,7 +9,7 @@ const activities = (state = initialState, action) => {
|
|||||||
...state,
|
...state,
|
||||||
items: [],
|
items: [],
|
||||||
}
|
}
|
||||||
case 'SEND_SOCKET_MESSAGE_SLASH_COMMAND':
|
case 'SEND_ENCRYPTED_MESSAGE_SLASH_COMMAND':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
items: [
|
items: [
|
||||||
@ -27,7 +20,7 @@ const activities = (state = initialState, action) => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
case 'SEND_SOCKET_MESSAGE_FILE_TRANSFER':
|
case 'SEND_ENCRYPTED_MESSAGE_FILE_TRANSFER':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
items: [
|
items: [
|
||||||
@ -38,29 +31,29 @@ const activities = (state = initialState, action) => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
case 'SEND_SOCKET_MESSAGE_SEND_MESSAGE':
|
case 'SEND_ENCRYPTED_MESSAGE_TEXT_MESSAGE':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
items: [
|
items: [
|
||||||
...state.items,
|
...state.items,
|
||||||
{
|
{
|
||||||
...action.payload,
|
...action.payload,
|
||||||
type: 'SEND_MESSAGE',
|
type: 'TEXT_MESSAGE',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
case 'HANDLE_SOCKET_MESSAGE_SEND_MESSAGE':
|
case 'RECEIVE_ENCRYPTED_MESSAGE_TEXT_MESSAGE':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
items: [
|
items: [
|
||||||
...state.items,
|
...state.items,
|
||||||
{
|
{
|
||||||
...action.payload.payload,
|
...action.payload.payload,
|
||||||
type: 'SEND_MESSAGE',
|
type: 'TEXT_MESSAGE',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
case 'SEND_SOCKET_MESSAGE_SEND_FILE':
|
case 'SEND_ENCRYPTED_MESSAGE_SEND_FILE':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
items: [
|
items: [
|
||||||
@ -71,7 +64,7 @@ const activities = (state = initialState, action) => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
case 'HANDLE_SOCKET_MESSAGE_SEND_FILE':
|
case 'RECEIVE_ENCRYPTED_MESSAGE_SEND_FILE':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
items: [
|
items: [
|
||||||
@ -82,7 +75,7 @@ const activities = (state = initialState, action) => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
case 'HANDLE_SOCKET_MESSAGE_ADD_USER':
|
case 'RECEIVE_ENCRYPTED_MESSAGE_ADD_USER':
|
||||||
const newUserId = action.payload.payload.id
|
const newUserId = action.payload.payload.id
|
||||||
|
|
||||||
const haveUser = action.payload.state.room.members.find(m => m.id === newUserId)
|
const haveUser = action.payload.state.room.members.find(m => m.id === newUserId)
|
||||||
@ -90,10 +83,6 @@ const activities = (state = initialState, action) => {
|
|||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.payload.state.room.joining) {
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
items: [
|
items: [
|
||||||
@ -159,7 +148,7 @@ const activities = (state = initialState, action) => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
case 'SEND_SOCKET_MESSAGE_CHANGE_USERNAME':
|
case 'SEND_ENCRYPTED_MESSAGE_CHANGE_USERNAME':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
items: [
|
items: [
|
||||||
@ -179,7 +168,7 @@ const activities = (state = initialState, action) => {
|
|||||||
return item
|
return item
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
case 'HANDLE_SOCKET_MESSAGE_CHANGE_USERNAME':
|
case 'RECEIVE_ENCRYPTED_MESSAGE_CHANGE_USERNAME':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
items: [
|
items: [
|
||||||
@ -190,7 +179,7 @@ const activities = (state = initialState, action) => {
|
|||||||
newUsername: action.payload.payload.newUsername,
|
newUsername: action.payload.payload.newUsername,
|
||||||
},
|
},
|
||||||
].map((item) => {
|
].map((item) => {
|
||||||
if (['SEND_MESSAGE', 'USER_ACTION'].includes(item.type) && item.sender === action.payload.payload.sender) {
|
if (['TEXT_MESSAGE', 'USER_ACTION'].includes(item.type) && item.sender === action.payload.payload.sender) {
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
username: action.payload.payload.newUsername,
|
username: action.payload.payload.newUsername,
|
||||||
@ -199,7 +188,7 @@ const activities = (state = initialState, action) => {
|
|||||||
return item
|
return item
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
case 'SEND_SOCKET_MESSAGE_USER_ACTION':
|
case 'SEND_ENCRYPTED_MESSAGE_USER_ACTION':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
items: [
|
items: [
|
||||||
@ -210,7 +199,7 @@ const activities = (state = initialState, action) => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
case 'HANDLE_SOCKET_MESSAGE_USER_ACTION':
|
case 'RECEIVE_ENCRYPTED_MESSAGE_USER_ACTION':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
items: [
|
items: [
|
||||||
|
@ -30,7 +30,7 @@ const app = (state = initialState, action) => {
|
|||||||
windowIsFocused: action.payload,
|
windowIsFocused: action.payload,
|
||||||
unreadMessageCount: 0,
|
unreadMessageCount: 0,
|
||||||
}
|
}
|
||||||
case 'HANDLE_SOCKET_MESSAGE_SEND_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,
|
||||||
|
@ -9,43 +9,22 @@ const initialState = {
|
|||||||
],
|
],
|
||||||
id: '',
|
id: '',
|
||||||
isLocked: false,
|
isLocked: false,
|
||||||
joining: true,
|
|
||||||
size: 0,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const room = (state = initialState, action) => {
|
const room = (state = initialState, action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'CONNECTED':
|
|
||||||
const size = action.payload.users ? action.payload.users.length : 1;
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
id: action.payload.id,
|
|
||||||
isLocked: Boolean(action.payload.isLocked),
|
|
||||||
size,
|
|
||||||
joining: false
|
|
||||||
}
|
|
||||||
case 'USER_EXIT':
|
case 'USER_EXIT':
|
||||||
const memberPubKeys = action.payload.members.map(m => JSON.stringify(m.publicKey))
|
const memberPubKeys = action.payload.members.map(m => m.publicKey.n)
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
members: state.members
|
members: state.members
|
||||||
.filter(m => memberPubKeys.includes(JSON.stringify(m.publicKey)))
|
.filter(member => memberPubKeys.includes(member.publicKey.n))
|
||||||
.map((m) => {
|
|
||||||
const payloadMember = action.payload.members.find(member => _.isEqual(m.publicKey, member.publicKey))
|
|
||||||
return {
|
|
||||||
...m,
|
|
||||||
...payloadMember,
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
case 'HANDLE_SOCKET_MESSAGE_ADD_USER':
|
case 'RECEIVE_ENCRYPTED_MESSAGE_ADD_USER':
|
||||||
const membersWithId = state.members.filter(m => m.id)
|
|
||||||
const joining = false
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
members: state.members.map((member) => {
|
members: state.members.map((member) => {
|
||||||
if (_.isEqual(member.publicKey, action.payload.payload.publicKey)) {
|
if (member.publicKey.n === action.payload.payload.publicKey.n) {
|
||||||
return {
|
return {
|
||||||
...member,
|
...member,
|
||||||
username: action.payload.payload.username,
|
username: action.payload.payload.username,
|
||||||
@ -55,7 +34,6 @@ const room = (state = initialState, action) => {
|
|||||||
}
|
}
|
||||||
return member
|
return member
|
||||||
}),
|
}),
|
||||||
joining,
|
|
||||||
}
|
}
|
||||||
case 'CREATE_USER':
|
case 'CREATE_USER':
|
||||||
return {
|
return {
|
||||||
@ -70,17 +48,12 @@ const room = (state = initialState, action) => {
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
case 'USER_ENTER':
|
case 'USER_ENTER':
|
||||||
/*
|
const members = _.uniqBy(action.payload.users, member => member.publicKey.n);
|
||||||
In this payload the server sends all users' public keys. Normally the server
|
|
||||||
will have all the users the client does, but in some cases - such as when
|
|
||||||
new users join before this client has registered with the server (this can
|
|
||||||
happen when lots of users join in quick succession) - the client
|
|
||||||
will receive a USER_ENTER event that doesn't contain itself. In that case we
|
|
||||||
want to prepend "me" to the members payload
|
|
||||||
*/
|
|
||||||
const members = _.uniqBy(action.payload, member => member.publicKey.n);
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
id: action.payload.id,
|
||||||
|
isLocked: Boolean(action.payload.isLocked),
|
||||||
members: members.reduce((acc, user) => {
|
members: members.reduce((acc, user) => {
|
||||||
const exists = state.members.find(m => m.publicKey.n === user.publicKey.n)
|
const exists = state.members.find(m => m.publicKey.n === user.publicKey.n)
|
||||||
if (exists) {
|
if (exists) {
|
||||||
@ -112,7 +85,7 @@ const room = (state = initialState, action) => {
|
|||||||
...state,
|
...state,
|
||||||
isLocked: action.payload.locked,
|
isLocked: action.payload.locked,
|
||||||
}
|
}
|
||||||
case 'SEND_SOCKET_MESSAGE_CHANGE_USERNAME':
|
case 'SEND_ENCRYPTED_MESSAGE_CHANGE_USERNAME':
|
||||||
const newUsername = action.payload.newUsername
|
const newUsername = action.payload.newUsername
|
||||||
const userId = action.payload.id
|
const userId = action.payload.id
|
||||||
return {
|
return {
|
||||||
@ -125,7 +98,7 @@ const room = (state = initialState, action) => {
|
|||||||
} : member
|
} : member
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
case 'HANDLE_SOCKET_MESSAGE_CHANGE_USERNAME':
|
case 'RECEIVE_ENCRYPTED_MESSAGE_CHANGE_USERNAME':
|
||||||
const newUsername2 = action.payload.payload.newUsername
|
const newUsername2 = action.payload.payload.newUsername
|
||||||
const userId2 = action.payload.payload.id
|
const userId2 = action.payload.payload.id
|
||||||
return {
|
return {
|
||||||
|
@ -12,7 +12,7 @@ const user = (state = initialState, action) => {
|
|||||||
...action.payload,
|
...action.payload,
|
||||||
id: action.payload.publicKey.n,
|
id: action.payload.publicKey.n,
|
||||||
}
|
}
|
||||||
case 'SEND_SOCKET_MESSAGE_CHANGE_USERNAME':
|
case 'SEND_ENCRYPTED_MESSAGE_CHANGE_USERNAME':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
username: action.payload.newUsername,
|
username: action.payload.newUsername,
|
||||||
|
@ -8,7 +8,7 @@ import { Provider } from 'react-redux'
|
|||||||
import configureStore from 'store'
|
import configureStore from 'store'
|
||||||
import { BrowserRouter, Route, Switch } from 'react-router-dom'
|
import { BrowserRouter, Route, Switch } from 'react-router-dom'
|
||||||
import shortId from 'shortid'
|
import shortId from 'shortid'
|
||||||
import Home from 'containers/Home'
|
import Home from 'components/Home'
|
||||||
import { hasTouchSupport } from './utils/dom'
|
import { hasTouchSupport } from './utils/dom'
|
||||||
|
|
||||||
const store = configureStore()
|
const store = configureStore()
|
||||||
|
@ -142,7 +142,7 @@ textarea
|
|||||||
|
|
||||||
.activity-item
|
.activity-item
|
||||||
margin-bottom: 15px
|
margin-bottom: 15px
|
||||||
&.SEND_MESSAGE
|
&.TEXT_MESSAGE
|
||||||
.chat-meta
|
.chat-meta
|
||||||
font-size: 13px
|
font-size: 13px
|
||||||
.timestamp
|
.timestamp
|
||||||
|
@ -8513,15 +8513,6 @@ qs@6.5.2, qs@~6.5.2:
|
|||||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
||||||
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
|
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
|
||||||
|
|
||||||
query-string@^6.5.0:
|
|
||||||
version "6.5.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.5.0.tgz#2e1a70125af01f6f04573692d02c09302a1d8bfc"
|
|
||||||
integrity sha512-TYC4hDjZSvVxLMEucDMySkuAS9UIzSbAiYGyA9GWCjLKB8fQpviFbjd20fD7uejCDxZS+ftSdBKE6DS+xucJFg==
|
|
||||||
dependencies:
|
|
||||||
decode-uri-component "^0.2.0"
|
|
||||||
split-on-first "^1.0.0"
|
|
||||||
strict-uri-encode "^2.0.0"
|
|
||||||
|
|
||||||
querystring-es3@^0.2.0:
|
querystring-es3@^0.2.0:
|
||||||
version "0.2.1"
|
version "0.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
|
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
|
||||||
@ -9751,11 +9742,6 @@ spdy@^4.0.0:
|
|||||||
select-hose "^2.0.0"
|
select-hose "^2.0.0"
|
||||||
spdy-transport "^3.0.0"
|
spdy-transport "^3.0.0"
|
||||||
|
|
||||||
split-on-first@^1.0.0:
|
|
||||||
version "1.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f"
|
|
||||||
integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==
|
|
||||||
|
|
||||||
split-string@^3.0.1, split-string@^3.0.2:
|
split-string@^3.0.1, split-string@^3.0.2:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
|
resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
|
||||||
@ -9870,11 +9856,6 @@ stream-shift@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952"
|
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952"
|
||||||
integrity sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=
|
integrity sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=
|
||||||
|
|
||||||
strict-uri-encode@^2.0.0:
|
|
||||||
version "2.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
|
|
||||||
integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY=
|
|
||||||
|
|
||||||
string-length@^2.0.0:
|
string-length@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed"
|
resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed"
|
||||||
|
@ -103,7 +103,7 @@ const protocol = (process.env.PROTOCOL || 'http') === 'http' ? http : https;
|
|||||||
const server = protocol.createServer(app.callback());
|
const server = protocol.createServer(app.callback());
|
||||||
const io = Io(server, {
|
const io = Io(server, {
|
||||||
pingInterval: 5000,
|
pingInterval: 5000,
|
||||||
pingTimeout: 3000
|
pingTimeout: 5000
|
||||||
});
|
});
|
||||||
io.adapter(socketRedis(process.env.REDIS_URL));
|
io.adapter(socketRedis(process.env.REDIS_URL));
|
||||||
|
|
||||||
|
@ -21,24 +21,9 @@ export default class Socket {
|
|||||||
async init(opts) {
|
async init(opts) {
|
||||||
const { roomId, socket, room } = opts
|
const { roomId, socket, room } = opts
|
||||||
await this.joinRoom(roomId, socket.id)
|
await this.joinRoom(roomId, socket.id)
|
||||||
this.sendRoomInfo();
|
|
||||||
this.handleSocket(socket)
|
this.handleSocket(socket)
|
||||||
}
|
}
|
||||||
|
|
||||||
sendRoomInfo() {
|
|
||||||
let room;
|
|
||||||
if (_.isEmpty(this.room)) {
|
|
||||||
room = {
|
|
||||||
id: this.roomIdOriginal,
|
|
||||||
users: [],
|
|
||||||
isLocked: false,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
room = this.room;
|
|
||||||
}
|
|
||||||
this.socket.emit('CONNECTED', room);
|
|
||||||
}
|
|
||||||
|
|
||||||
sendRoomLocked() {
|
sendRoomLocked() {
|
||||||
this.socket.emit('ROOM_LOCKED');
|
this.socket.emit('ROOM_LOCKED');
|
||||||
}
|
}
|
||||||
@ -75,11 +60,11 @@ export default class Socket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async handleSocket(socket) {
|
async handleSocket(socket) {
|
||||||
socket.on('PAYLOAD', (payload) => {
|
socket.on('ENCRYPTED_MESSAGE', (payload) => {
|
||||||
socket.to(this._roomId).emit('PAYLOAD', payload);
|
socket.to(this._roomId).emit('ENCRYPTED_MESSAGE', payload);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('USER_ENTER', async payload => {
|
socket.on('USER_ENTER', async (payload) => {
|
||||||
let room = await this.fetchRoom()
|
let room = await this.fetchRoom()
|
||||||
if (_.isEmpty(room)) {
|
if (_.isEmpty(room)) {
|
||||||
room = {
|
room = {
|
||||||
@ -99,10 +84,11 @@ export default class Socket {
|
|||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
await this.saveRoom(newRoom)
|
await this.saveRoom(newRoom)
|
||||||
getIO().to(this._roomId).emit('USER_ENTER', newRoom.users.map(u => ({
|
|
||||||
publicKey: u.publicKey,
|
getIO().to(this._roomId).emit('USER_ENTER', {
|
||||||
isOwner: u.isOwner,
|
...newRoom,
|
||||||
})));
|
id: this.roomIdOriginal
|
||||||
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on('TOGGLE_LOCK_ROOM', async (data, callback) => {
|
socket.on('TOGGLE_LOCK_ROOM', async (data, callback) => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user