Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit e45ac36

Browse files
authored
Merge pull request #635 from matrix-org/matthew/warn-unknown-devices
very barebones support for warning users when rooms contain unknown devices
2 parents 4ab4795 + 5d5125d commit e45ac36

File tree

6 files changed

+141
-16
lines changed

6 files changed

+141
-16
lines changed

src/Modal.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ class ModalManager {
177177

178178
var modal = this._modals[0];
179179
var dialog = (
180-
<div className={"mx_Dialog_wrapper " + modal.className}>
180+
<div className={"mx_Dialog_wrapper " + (modal.className ? modal.className : '') }>
181181
<div className="mx_Dialog">
182182
{modal.elem}
183183
</div>

src/Resend.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,26 @@ limitations under the License.
1616

1717
var MatrixClientPeg = require('./MatrixClientPeg');
1818
var dis = require('./dispatcher');
19+
var sdk = require('./index');
20+
var Modal = require('./Modal');
1921

2022
module.exports = {
2123
resend: function(event) {
2224
MatrixClientPeg.get().resendEvent(
2325
event, MatrixClientPeg.get().getRoom(event.getRoomId())
24-
).done(function() {
26+
).done(function(res) {
2527
dis.dispatch({
2628
action: 'message_sent',
2729
event: event
2830
});
29-
}, function() {
31+
}, function(err) {
32+
if (err.name === "UnknownDeviceError") {
33+
var UnknownDeviceDialog = sdk.getComponent("dialogs.UnknownDeviceDialog");
34+
Modal.createDialog(UnknownDeviceDialog, {
35+
devices: err.devices
36+
}, "mx_Dialog_unknownDevice");
37+
}
38+
3039
dis.dispatch({
3140
action: 'message_send_failed',
3241
event: event

src/component-index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ import views$dialogs$SetDisplayNameDialog from './components/views/dialogs/SetDi
8989
views$dialogs$SetDisplayNameDialog && (module.exports.components['views.dialogs.SetDisplayNameDialog'] = views$dialogs$SetDisplayNameDialog);
9090
import views$dialogs$TextInputDialog from './components/views/dialogs/TextInputDialog';
9191
views$dialogs$TextInputDialog && (module.exports.components['views.dialogs.TextInputDialog'] = views$dialogs$TextInputDialog);
92+
import views$dialogs$UnknownDeviceDialog from './components/views/dialogs/UnknownDeviceDialog';
93+
views$dialogs$UnknownDeviceDialog && (module.exports.components['views.dialogs.UnknownDeviceDialog'] = views$dialogs$UnknownDeviceDialog);
9294
import views$elements$AccessibleButton from './components/views/elements/AccessibleButton';
9395
views$elements$AccessibleButton && (module.exports.components['views.elements.AccessibleButton'] = views$elements$AccessibleButton);
9496
import views$elements$AddressSelector from './components/views/elements/AddressSelector';
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
Copyright 2017 Vector Creations Ltd
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import React from 'react';
18+
import sdk from '../../../index';
19+
import MatrixClientPeg from '../../../MatrixClientPeg';
20+
import GeminiScrollbar from 'react-gemini-scrollbar';
21+
22+
function UserUnknownDeviceList(props) {
23+
const {userDevices} = props;
24+
25+
const deviceListEntries = Object.keys(userDevices).map((deviceId) =>
26+
<li key={ deviceId }>
27+
{ deviceId } ( { userDevices[deviceId].getDisplayName() } )
28+
</li>,
29+
);
30+
31+
return <ul>{deviceListEntries}</ul>;
32+
}
33+
34+
UserUnknownDeviceList.propTypes = {
35+
// map from deviceid -> deviceinfo
36+
userDevices: React.PropTypes.object.isRequired,
37+
};
38+
39+
40+
function UnknownDeviceList(props) {
41+
const {devices} = props;
42+
43+
const userListEntries = Object.keys(devices).map((userId) =>
44+
<li key={ userId }>
45+
<p>{ userId }:</p>
46+
<UserUnknownDeviceList userDevices={devices[userId]} />
47+
</li>,
48+
);
49+
50+
return <ul>{userListEntries}</ul>;
51+
}
52+
53+
UnknownDeviceList.propTypes = {
54+
// map from userid -> deviceid -> deviceinfo
55+
devices: React.PropTypes.object.isRequired,
56+
};
57+
58+
59+
export default React.createClass({
60+
displayName: 'UnknownEventDialog',
61+
62+
propTypes: {
63+
// map from userid -> deviceid -> deviceinfo
64+
devices: React.PropTypes.object.isRequired,
65+
onFinished: React.PropTypes.func.isRequired,
66+
},
67+
68+
componentDidMount: function() {
69+
// Given we've now shown the user the unknown device, it is no longer
70+
// unknown to them. Therefore mark it as 'known'.
71+
Object.keys(this.props.devices).forEach((userId) => {
72+
Object.keys(this.props.devices[userId]).map((deviceId) => {
73+
MatrixClientPeg.get().setDeviceKnown(userId, deviceId, true);
74+
});
75+
});
76+
},
77+
78+
render: function() {
79+
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
80+
return (
81+
<BaseDialog className='mx_UnknownDeviceDialog'
82+
onFinished={this.props.onFinished}
83+
title='Room contains unknown devices'
84+
>
85+
<GeminiScrollbar autoshow={false} className="mx_Dialog_content">
86+
<h4>This room contains devices which have not been
87+
verified.</h4>
88+
<p>
89+
This means there is no guarantee that the devices belong
90+
to a rightful user of the room.
91+
</p><p>
92+
We recommend you go through the verification process
93+
for each device before continuing, but you can resend
94+
the message without verifying if you prefer.
95+
</p>
96+
<p>Unknown devices:</p>
97+
<UnknownDeviceList devices={this.props.devices} />
98+
</GeminiScrollbar>
99+
<div className="mx_Dialog_buttons">
100+
<button className="mx_Dialog_primary" autoFocus={ true }
101+
onClick={ this.props.onFinished } >
102+
OK
103+
</button>
104+
</div>
105+
</BaseDialog>
106+
);
107+
},
108+
});

src/components/views/rooms/MessageComposerInput.js

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import * as HtmlUtils from '../../../HtmlUtils';
4040
import Autocomplete from './Autocomplete';
4141
import {Completion} from "../../../autocomplete/Autocompleter";
4242
import Markdown from '../../../Markdown';
43+
import {onSendMessageFailed} from './MessageComposerInputOld';
4344

4445
const TYPING_USER_TIMEOUT = 10000, TYPING_SERVER_TIMEOUT = 30000;
4546

@@ -553,15 +554,11 @@ export default class MessageComposerInput extends React.Component {
553554
sendMessagePromise = sendTextFn.call(this.client, this.props.room.roomId, contentText);
554555
}
555556

556-
sendMessagePromise.then(() => {
557+
sendMessagePromise.done((res) => {
557558
dis.dispatch({
558559
action: 'message_sent',
559560
});
560-
}, () => {
561-
dis.dispatch({
562-
action: 'message_send_failed',
563-
});
564-
});
561+
}, onSendMessageFailed);
565562

566563
this.setState({
567564
editorState: this.createEditorState(),

src/components/views/rooms/MessageComposerInputOld.js

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,22 @@ var TYPING_USER_TIMEOUT = 10000;
2929
var TYPING_SERVER_TIMEOUT = 30000;
3030
var MARKDOWN_ENABLED = true;
3131

32+
export function onSendMessageFailed(err) {
33+
if (err.name === "UnknownDeviceError") {
34+
const UnknownDeviceDialog = sdk.getComponent("dialogs.UnknownDeviceDialog");
35+
Modal.createDialog(UnknownDeviceDialog, {
36+
devices: err.devices,
37+
}, "mx_Dialog_unknownDevice");
38+
}
39+
dis.dispatch({
40+
action: 'message_send_failed',
41+
});
42+
}
43+
3244
/*
3345
* The textInput part of the MessageComposer
3446
*/
35-
module.exports = React.createClass({
47+
export default React.createClass({
3648
displayName: 'MessageComposerInput',
3749

3850
statics: {
@@ -337,15 +349,12 @@ module.exports = React.createClass({
337349
MatrixClientPeg.get().sendTextMessage(this.props.room.roomId, contentText);
338350
}
339351

340-
sendMessagePromise.done(function() {
352+
sendMessagePromise.done(function(res) {
341353
dis.dispatch({
342354
action: 'message_sent'
343355
});
344-
}, function() {
345-
dis.dispatch({
346-
action: 'message_send_failed'
347-
});
348-
});
356+
}, onSendMessageFailed);
357+
349358
this.refs.textarea.value = '';
350359
this.resizeInput();
351360
ev.preventDefault();

0 commit comments

Comments
 (0)