Merge pull request #635 from matrix-org/matthew/warn-unknown-devices
very barebones support for warning users when rooms contain unknown devices
This commit is contained in:
commit
e45ac36a3b
6 changed files with 141 additions and 16 deletions
|
@ -177,7 +177,7 @@ class ModalManager {
|
||||||
|
|
||||||
var modal = this._modals[0];
|
var modal = this._modals[0];
|
||||||
var dialog = (
|
var dialog = (
|
||||||
<div className={"mx_Dialog_wrapper " + modal.className}>
|
<div className={"mx_Dialog_wrapper " + (modal.className ? modal.className : '') }>
|
||||||
<div className="mx_Dialog">
|
<div className="mx_Dialog">
|
||||||
{modal.elem}
|
{modal.elem}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -16,17 +16,26 @@ limitations under the License.
|
||||||
|
|
||||||
var MatrixClientPeg = require('./MatrixClientPeg');
|
var MatrixClientPeg = require('./MatrixClientPeg');
|
||||||
var dis = require('./dispatcher');
|
var dis = require('./dispatcher');
|
||||||
|
var sdk = require('./index');
|
||||||
|
var Modal = require('./Modal');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
resend: function(event) {
|
resend: function(event) {
|
||||||
MatrixClientPeg.get().resendEvent(
|
MatrixClientPeg.get().resendEvent(
|
||||||
event, MatrixClientPeg.get().getRoom(event.getRoomId())
|
event, MatrixClientPeg.get().getRoom(event.getRoomId())
|
||||||
).done(function() {
|
).done(function(res) {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'message_sent',
|
action: 'message_sent',
|
||||||
event: event
|
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({
|
dis.dispatch({
|
||||||
action: 'message_send_failed',
|
action: 'message_send_failed',
|
||||||
event: event
|
event: event
|
||||||
|
|
|
@ -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);
|
views$dialogs$SetDisplayNameDialog && (module.exports.components['views.dialogs.SetDisplayNameDialog'] = views$dialogs$SetDisplayNameDialog);
|
||||||
import views$dialogs$TextInputDialog from './components/views/dialogs/TextInputDialog';
|
import views$dialogs$TextInputDialog from './components/views/dialogs/TextInputDialog';
|
||||||
views$dialogs$TextInputDialog && (module.exports.components['views.dialogs.TextInputDialog'] = 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';
|
import views$elements$AccessibleButton from './components/views/elements/AccessibleButton';
|
||||||
views$elements$AccessibleButton && (module.exports.components['views.elements.AccessibleButton'] = views$elements$AccessibleButton);
|
views$elements$AccessibleButton && (module.exports.components['views.elements.AccessibleButton'] = views$elements$AccessibleButton);
|
||||||
import views$elements$AddressSelector from './components/views/elements/AddressSelector';
|
import views$elements$AddressSelector from './components/views/elements/AddressSelector';
|
||||||
|
|
108
src/components/views/dialogs/UnknownDeviceDialog.js
Normal file
108
src/components/views/dialogs/UnknownDeviceDialog.js
Normal file
|
@ -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) =>
|
||||||
|
<li key={ deviceId }>
|
||||||
|
{ deviceId } ( { userDevices[deviceId].getDisplayName() } )
|
||||||
|
</li>,
|
||||||
|
);
|
||||||
|
|
||||||
|
return <ul>{deviceListEntries}</ul>;
|
||||||
|
}
|
||||||
|
|
||||||
|
UserUnknownDeviceList.propTypes = {
|
||||||
|
// map from deviceid -> deviceinfo
|
||||||
|
userDevices: React.PropTypes.object.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function UnknownDeviceList(props) {
|
||||||
|
const {devices} = props;
|
||||||
|
|
||||||
|
const userListEntries = Object.keys(devices).map((userId) =>
|
||||||
|
<li key={ userId }>
|
||||||
|
<p>{ userId }:</p>
|
||||||
|
<UserUnknownDeviceList userDevices={devices[userId]} />
|
||||||
|
</li>,
|
||||||
|
);
|
||||||
|
|
||||||
|
return <ul>{userListEntries}</ul>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<BaseDialog className='mx_UnknownDeviceDialog'
|
||||||
|
onFinished={this.props.onFinished}
|
||||||
|
title='Room contains unknown devices'
|
||||||
|
>
|
||||||
|
<GeminiScrollbar autoshow={false} className="mx_Dialog_content">
|
||||||
|
<h4>This room contains devices which have not been
|
||||||
|
verified.</h4>
|
||||||
|
<p>
|
||||||
|
This means there is no guarantee that the devices belong
|
||||||
|
to a rightful user of the room.
|
||||||
|
</p><p>
|
||||||
|
We recommend you go through the verification process
|
||||||
|
for each device before continuing, but you can resend
|
||||||
|
the message without verifying if you prefer.
|
||||||
|
</p>
|
||||||
|
<p>Unknown devices:</p>
|
||||||
|
<UnknownDeviceList devices={this.props.devices} />
|
||||||
|
</GeminiScrollbar>
|
||||||
|
<div className="mx_Dialog_buttons">
|
||||||
|
<button className="mx_Dialog_primary" autoFocus={ true }
|
||||||
|
onClick={ this.props.onFinished } >
|
||||||
|
OK
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</BaseDialog>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
|
@ -40,6 +40,7 @@ import * as HtmlUtils from '../../../HtmlUtils';
|
||||||
import Autocomplete from './Autocomplete';
|
import Autocomplete from './Autocomplete';
|
||||||
import {Completion} from "../../../autocomplete/Autocompleter";
|
import {Completion} from "../../../autocomplete/Autocompleter";
|
||||||
import Markdown from '../../../Markdown';
|
import Markdown from '../../../Markdown';
|
||||||
|
import {onSendMessageFailed} from './MessageComposerInputOld';
|
||||||
|
|
||||||
const TYPING_USER_TIMEOUT = 10000, TYPING_SERVER_TIMEOUT = 30000;
|
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 = sendTextFn.call(this.client, this.props.room.roomId, contentText);
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessagePromise.then(() => {
|
sendMessagePromise.done((res) => {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'message_sent',
|
action: 'message_sent',
|
||||||
});
|
});
|
||||||
}, () => {
|
}, onSendMessageFailed);
|
||||||
dis.dispatch({
|
|
||||||
action: 'message_send_failed',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
editorState: this.createEditorState(),
|
editorState: this.createEditorState(),
|
||||||
|
|
|
@ -29,10 +29,22 @@ var TYPING_USER_TIMEOUT = 10000;
|
||||||
var TYPING_SERVER_TIMEOUT = 30000;
|
var TYPING_SERVER_TIMEOUT = 30000;
|
||||||
var MARKDOWN_ENABLED = true;
|
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
|
* The textInput part of the MessageComposer
|
||||||
*/
|
*/
|
||||||
module.exports = React.createClass({
|
export default React.createClass({
|
||||||
displayName: 'MessageComposerInput',
|
displayName: 'MessageComposerInput',
|
||||||
|
|
||||||
statics: {
|
statics: {
|
||||||
|
@ -337,15 +349,12 @@ module.exports = React.createClass({
|
||||||
MatrixClientPeg.get().sendTextMessage(this.props.room.roomId, contentText);
|
MatrixClientPeg.get().sendTextMessage(this.props.room.roomId, contentText);
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessagePromise.done(function() {
|
sendMessagePromise.done(function(res) {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'message_sent'
|
action: 'message_sent'
|
||||||
});
|
});
|
||||||
}, function() {
|
}, onSendMessageFailed);
|
||||||
dis.dispatch({
|
|
||||||
action: 'message_send_failed'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
this.refs.textarea.value = '';
|
this.refs.textarea.value = '';
|
||||||
this.resizeInput();
|
this.resizeInput();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue