diff --git a/src/Modal.js b/src/Modal.js index 89e8b1361c..b6cc46ed45 100644 --- a/src/Modal.js +++ b/src/Modal.js @@ -177,7 +177,7 @@ class ModalManager { var modal = this._modals[0]; var dialog = ( -
+
{modal.elem}
diff --git a/src/Resend.js b/src/Resend.js index ecf504e780..59a8bd4192 100644 --- a/src/Resend.js +++ b/src/Resend.js @@ -16,17 +16,26 @@ limitations under the License. var MatrixClientPeg = require('./MatrixClientPeg'); var dis = require('./dispatcher'); +var sdk = require('./index'); +var Modal = require('./Modal'); module.exports = { resend: function(event) { MatrixClientPeg.get().resendEvent( event, MatrixClientPeg.get().getRoom(event.getRoomId()) - ).done(function() { + ).done(function(res) { dis.dispatch({ action: 'message_sent', event: event }); - }, function() { + }, function(err) { + if (err.name === "UnknownDeviceError") { + var UnknownDeviceDialog = sdk.getComponent("dialogs.UnknownDeviceDialog"); + Modal.createDialog(UnknownDeviceDialog, { + devices: err.devices + }, "mx_Dialog_unknownDevice"); + } + dis.dispatch({ action: 'message_send_failed', event: event diff --git a/src/component-index.js b/src/component-index.js index c50ee0dfc8..5b28be0627 100644 --- a/src/component-index.js +++ b/src/component-index.js @@ -89,6 +89,8 @@ import views$dialogs$SetDisplayNameDialog from './components/views/dialogs/SetDi views$dialogs$SetDisplayNameDialog && (module.exports.components['views.dialogs.SetDisplayNameDialog'] = views$dialogs$SetDisplayNameDialog); import views$dialogs$TextInputDialog from './components/views/dialogs/TextInputDialog'; views$dialogs$TextInputDialog && (module.exports.components['views.dialogs.TextInputDialog'] = views$dialogs$TextInputDialog); +import views$dialogs$UnknownDeviceDialog from './components/views/dialogs/UnknownDeviceDialog'; +views$dialogs$UnknownDeviceDialog && (module.exports.components['views.dialogs.UnknownDeviceDialog'] = views$dialogs$UnknownDeviceDialog); import views$elements$AccessibleButton from './components/views/elements/AccessibleButton'; views$elements$AccessibleButton && (module.exports.components['views.elements.AccessibleButton'] = views$elements$AccessibleButton); import views$elements$AddressSelector from './components/views/elements/AddressSelector'; diff --git a/src/components/views/dialogs/UnknownDeviceDialog.js b/src/components/views/dialogs/UnknownDeviceDialog.js new file mode 100644 index 0000000000..be1138fe43 --- /dev/null +++ b/src/components/views/dialogs/UnknownDeviceDialog.js @@ -0,0 +1,108 @@ +/* +Copyright 2017 Vector Creations Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import sdk from '../../../index'; +import MatrixClientPeg from '../../../MatrixClientPeg'; +import GeminiScrollbar from 'react-gemini-scrollbar'; + +function UserUnknownDeviceList(props) { + const {userDevices} = props; + + const deviceListEntries = Object.keys(userDevices).map((deviceId) => +
  • + { deviceId } ( { userDevices[deviceId].getDisplayName() } ) +
  • , + ); + + return ; +} + +UserUnknownDeviceList.propTypes = { + // map from deviceid -> deviceinfo + userDevices: React.PropTypes.object.isRequired, +}; + + +function UnknownDeviceList(props) { + const {devices} = props; + + const userListEntries = Object.keys(devices).map((userId) => +
  • +

    { userId }:

    + +
  • , + ); + + return ; +} + +UnknownDeviceList.propTypes = { + // map from userid -> deviceid -> deviceinfo + devices: React.PropTypes.object.isRequired, +}; + + +export default React.createClass({ + displayName: 'UnknownEventDialog', + + propTypes: { + // map from userid -> deviceid -> deviceinfo + devices: React.PropTypes.object.isRequired, + onFinished: React.PropTypes.func.isRequired, + }, + + componentDidMount: function() { + // Given we've now shown the user the unknown device, it is no longer + // unknown to them. Therefore mark it as 'known'. + Object.keys(this.props.devices).forEach((userId) => { + Object.keys(this.props.devices[userId]).map((deviceId) => { + MatrixClientPeg.get().setDeviceKnown(userId, deviceId, true); + }); + }); + }, + + render: function() { + const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); + return ( + + +

    This room contains devices which have not been + verified.

    +

    + This means there is no guarantee that the devices belong + to a rightful user of the room. +

    + We recommend you go through the verification process + for each device before continuing, but you can resend + the message without verifying if you prefer. +

    +

    Unknown devices:

    + +
    +
    + +
    +
    + ); + }, +}); diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index bf936a2c13..80e41555a2 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -40,6 +40,7 @@ import * as HtmlUtils from '../../../HtmlUtils'; import Autocomplete from './Autocomplete'; import {Completion} from "../../../autocomplete/Autocompleter"; import Markdown from '../../../Markdown'; +import {onSendMessageFailed} from './MessageComposerInputOld'; const TYPING_USER_TIMEOUT = 10000, TYPING_SERVER_TIMEOUT = 30000; @@ -553,15 +554,11 @@ export default class MessageComposerInput extends React.Component { sendMessagePromise = sendTextFn.call(this.client, this.props.room.roomId, contentText); } - sendMessagePromise.then(() => { + sendMessagePromise.done((res) => { dis.dispatch({ action: 'message_sent', }); - }, () => { - dis.dispatch({ - action: 'message_send_failed', - }); - }); + }, onSendMessageFailed); this.setState({ editorState: this.createEditorState(), diff --git a/src/components/views/rooms/MessageComposerInputOld.js b/src/components/views/rooms/MessageComposerInputOld.js index c5d5f083c1..e8a4727fc6 100644 --- a/src/components/views/rooms/MessageComposerInputOld.js +++ b/src/components/views/rooms/MessageComposerInputOld.js @@ -29,10 +29,22 @@ var TYPING_USER_TIMEOUT = 10000; var TYPING_SERVER_TIMEOUT = 30000; var MARKDOWN_ENABLED = true; +export function onSendMessageFailed(err) { + if (err.name === "UnknownDeviceError") { + const UnknownDeviceDialog = sdk.getComponent("dialogs.UnknownDeviceDialog"); + Modal.createDialog(UnknownDeviceDialog, { + devices: err.devices, + }, "mx_Dialog_unknownDevice"); + } + dis.dispatch({ + action: 'message_send_failed', + }); +} + /* * The textInput part of the MessageComposer */ -module.exports = React.createClass({ +export default React.createClass({ displayName: 'MessageComposerInput', statics: { @@ -337,15 +349,12 @@ module.exports = React.createClass({ MatrixClientPeg.get().sendTextMessage(this.props.room.roomId, contentText); } - sendMessagePromise.done(function() { + sendMessagePromise.done(function(res) { dis.dispatch({ action: 'message_sent' }); - }, function() { - dis.dispatch({ - action: 'message_send_failed' - }); - }); + }, onSendMessageFailed); + this.refs.textarea.value = ''; this.resizeInput(); ev.preventDefault();