From 644fcbe06df48e978720e394c504a579e0f23331 Mon Sep 17 00:00:00 2001 From: Jerko Steiner Date: Mon, 11 Nov 2019 22:14:37 -0300 Subject: [PATCH] Add ability to send file via base64-encoded msg --- src/client/actions/PeerActions.js | 61 ++++++++++++++++++++++++++++--- src/client/components/App.js | 3 ++ src/client/components/Chat.js | 7 +++- src/client/components/Toolbar.js | 31 ++++++++++++++++ src/client/containers/App.js | 3 +- 5 files changed, 98 insertions(+), 7 deletions(-) diff --git a/src/client/actions/PeerActions.js b/src/client/actions/PeerActions.js index 7e44a39..c05a10d 100644 --- a/src/client/actions/PeerActions.js +++ b/src/client/actions/PeerActions.js @@ -1,3 +1,4 @@ +import * as ChatActions from '../actions/ChatActions.js' import * as NotifyActions from '../actions/NotifyActions.js' import * as StreamActions from '../actions/StreamActions.js' import * as constants from '../constants.js' @@ -46,10 +47,25 @@ class PeerHandler { } handleData = object => { const { dispatch, user } = this - object = JSON.parse(new window.TextDecoder('utf-8').decode(object)) + const message = JSON.parse(new window.TextDecoder('utf-8').decode(object)) debug('peer: %s, message: %o', user.id, object) - const message = user.id + ': ' + object.message - dispatch(NotifyActions.info(message)) + switch (object.type) { + case 'file': + dispatch(ChatActions.addMessage({ + userId: user.id, + message: 'Sent a file: "' + message.payload.name, + timestamp: new Date().toLocaleString(), + image: message.payload.data + })) + break + default: + dispatch(ChatActions.addMessage({ + userId: user.id, + message: message.payload, + timestamp: new Date().toLocaleString(), + image: null + })) + } } handleClose = () => { const { dispatch, user } = this @@ -126,7 +142,42 @@ export const destroyPeers = () => ({ }) export const sendMessage = message => (dispatch, getState) => { - message = JSON.stringify({ message }) const { peers } = getState() - _.each(peers, peer => peer.send(message)) + dispatch(NotifyActions.info('Sending message type: {0} to {1} peers.', + message.type, Object.keys(peers).length)) + _.each(peers, peer => { + switch (message.type) { + case 'file': + dispatch(ChatActions.addMessage({ + userId: 'You', + message: 'Send file: "' + + message.payload.name + '" to peer: ' + peer._id, + timestamp: new Date().toLocaleString(), + image: message.payload.data + })) + } + peer.send(JSON.stringify(message)) + }) +} + +export const sendFile = file => async (dispatch, getState) => { + const { name, size, type } = file + if (!window.FileReader) { + dispatch(NotifyActions.error('File API is not supported by your browser')) + return + } + const reader = new window.FileReader() + const base64File = await new Promise(resolve => { + reader.addEventListener('load', () => { + resolve({ + name, + size, + type, + data: reader.result + }) + }) + reader.readAsDataURL(file) + }) + + sendMessage({ payload: base64File, type: 'file' })(dispatch, getState) } diff --git a/src/client/components/App.js b/src/client/components/App.js index 955f003..5cdd1d7 100644 --- a/src/client/components/App.js +++ b/src/client/components/App.js @@ -20,6 +20,7 @@ export default class App extends React.PureComponent { peers: PropTypes.object.isRequired, sendMessage: PropTypes.func.isRequired, streams: PropTypes.objectOf(StreamPropType).isRequired, + onSendFile: PropTypes.func.isRequired, toggleActive: PropTypes.func.isRequired } constructor () { @@ -56,6 +57,7 @@ export default class App extends React.PureComponent { notifications, notify, messages, + onSendFile, peers, sendMessage, toggleActive, @@ -70,6 +72,7 @@ export default class App extends React.PureComponent { chatVisible={this.state.chatVisible} messages={messages} onToggleChat={this.handleToggleChat} + onSendFile={onSendFile} stream={streams[constants.ME]} /> diff --git a/src/client/components/Chat.js b/src/client/components/Chat.js index cc4e428..74d6966 100644 --- a/src/client/components/Chat.js +++ b/src/client/components/Chat.js @@ -82,7 +82,12 @@ export default class Chat extends React.PureComponent { -

{message.message}

+

+ {message.image && ( + + )} + {message.message} +

)} diff --git a/src/client/components/Toolbar.js b/src/client/components/Toolbar.js index 65c52f9..dd060d2 100644 --- a/src/client/components/Toolbar.js +++ b/src/client/components/Toolbar.js @@ -5,13 +5,22 @@ import screenfull from 'screenfull' import { MessagePropTypes } from './Chat.js' import { StreamPropType } from './Video.js' +const hidden = { + display: 'none' +} + export default class Toolbar extends React.PureComponent { static propTypes = { messages: PropTypes.arrayOf(MessagePropTypes).isRequired, stream: StreamPropType, onToggleChat: PropTypes.func.isRequired, + onSendFile: PropTypes.func.isRequired, chatVisible: PropTypes.bool.isRequired } + constructor (props) { + super(props) + this.file = React.createRef() + } handleMicClick = () => { const { stream } = this.props stream.mediaStream.getAudioTracks().forEach(track => { @@ -35,6 +44,14 @@ export default class Toolbar extends React.PureComponent { handleHangoutClick = () => { window.location.href = '/' } + handleSendFile = () => { + this.file.current.click() + } + handleSelectFiles = () => { + Array + .from(this.file.current.files) + .forEach(file => this.props.onSendFile(file)) + } render () { const { messages, stream } = this.props @@ -49,6 +66,20 @@ export default class Toolbar extends React.PureComponent { > +
+ + +
{stream && (
diff --git a/src/client/containers/App.js b/src/client/containers/App.js index 72c4b77..552be30 100644 --- a/src/client/containers/App.js +++ b/src/client/containers/App.js @@ -23,7 +23,8 @@ function mapDispatchToProps (dispatch) { sendMessage: bindActionCreators(PeerActions.sendMessage, dispatch), dismissAlert: bindActionCreators(NotifyActions.dismissAlert, dispatch), init: bindActionCreators(CallActions.init, dispatch), - notify: bindActionCreators(NotifyActions.info, dispatch) + notify: bindActionCreators(NotifyActions.info, dispatch), + onSendFile: bindActionCreators(PeerActions.sendFile, dispatch) } }