mirror of
https://github.com/element-hq/element-web
synced 2024-11-28 04:21:57 +03:00
Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
commit
07cd3d8b2e
10 changed files with 347 additions and 214 deletions
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2017 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -58,6 +59,7 @@ import sdk from './index';
|
|||
import { _t } from './languageHandler';
|
||||
import Matrix from 'matrix-js-sdk';
|
||||
import dis from './dispatcher';
|
||||
import { showUnknownDeviceDialogForCalls } from './cryptodevices';
|
||||
|
||||
global.mxCalls = {
|
||||
//room_id: MatrixCall
|
||||
|
@ -97,19 +99,54 @@ function pause(audioId) {
|
|||
}
|
||||
}
|
||||
|
||||
function _reAttemptCall(call) {
|
||||
if (call.direction === 'outbound') {
|
||||
dis.dispatch({
|
||||
action: 'place_call',
|
||||
room_id: call.roomId,
|
||||
type: call.type,
|
||||
});
|
||||
} else {
|
||||
call.answer();
|
||||
}
|
||||
}
|
||||
|
||||
function _setCallListeners(call) {
|
||||
call.on("error", function(err) {
|
||||
console.error("Call error: %s", err);
|
||||
console.error(err.stack);
|
||||
call.hangup();
|
||||
_setCallState(undefined, call.roomId, "ended");
|
||||
});
|
||||
call.on('send_event_error', function(err) {
|
||||
if (err.name === "UnknownDeviceError") {
|
||||
dis.dispatch({
|
||||
action: 'unknown_device_error',
|
||||
err: err,
|
||||
room: MatrixClientPeg.get().getRoom(call.roomId),
|
||||
if (err.code === 'unknown_devices') {
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
|
||||
Modal.createTrackedDialog('Call Failed', '', QuestionDialog, {
|
||||
title: _t('Call Failed'),
|
||||
description: _t(
|
||||
"There are unknown devices in this room: "+
|
||||
"if you proceed without verifying them, it will be "+
|
||||
"possible for someone to eavesdrop on your call."
|
||||
),
|
||||
button: _t('Review Devices'),
|
||||
onFinished: function(confirmed) {
|
||||
if (confirmed) {
|
||||
const room = MatrixClientPeg.get().getRoom(call.roomId);
|
||||
showUnknownDeviceDialogForCalls(
|
||||
MatrixClientPeg.get(),
|
||||
room,
|
||||
() => {
|
||||
_reAttemptCall(call);
|
||||
},
|
||||
call.direction === 'outbound' ? _t("Call Anyway") : _t("Answer Anyway"),
|
||||
call.direction === 'outbound' ? _t("Call") : _t("Answer"),
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
|
||||
Modal.createTrackedDialog('Call Failed', '', ErrorDialog, {
|
||||
title: _t('Call Failed'),
|
||||
description: err.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -179,7 +216,6 @@ function _setCallState(call, roomId, status) {
|
|||
function _onAction(payload) {
|
||||
function placeCall(newCall) {
|
||||
_setCallListeners(newCall);
|
||||
_setCallState(newCall, newCall.roomId, "ringback");
|
||||
if (payload.type === 'voice') {
|
||||
newCall.placeVoiceCall();
|
||||
} else if (payload.type === 'video') {
|
||||
|
|
|
@ -44,13 +44,6 @@ module.exports = {
|
|||
// XXX: temporary logging to try to diagnose
|
||||
// https://github.com/vector-im/riot-web/issues/3148
|
||||
console.log('Resend got send failure: ' + err.name + '('+err+')');
|
||||
if (err.name === "UnknownDeviceError") {
|
||||
dis.dispatch({
|
||||
action: 'unknown_device_error',
|
||||
err: err,
|
||||
room: room,
|
||||
});
|
||||
}
|
||||
|
||||
dis.dispatch({
|
||||
action: 'message_send_failed',
|
||||
|
@ -60,9 +53,5 @@ module.exports = {
|
|||
},
|
||||
removeFromQueue: function(event) {
|
||||
MatrixClientPeg.get().cancelPendingEvent(event);
|
||||
dis.dispatch({
|
||||
action: 'message_send_cancelled',
|
||||
event: event,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
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 dis from './dispatcher';
|
||||
import sdk from './index';
|
||||
import Modal from './Modal';
|
||||
|
||||
let isDialogOpen = false;
|
||||
|
||||
const onAction = function(payload) {
|
||||
if (payload.action === 'unknown_device_error' && !isDialogOpen) {
|
||||
const UnknownDeviceDialog = sdk.getComponent('dialogs.UnknownDeviceDialog');
|
||||
isDialogOpen = true;
|
||||
Modal.createTrackedDialog('Unknown Device Error', '', UnknownDeviceDialog, {
|
||||
devices: payload.err.devices,
|
||||
room: payload.room,
|
||||
onFinished: (r) => {
|
||||
isDialogOpen = false;
|
||||
// XXX: temporary logging to try to diagnose
|
||||
// https://github.com/vector-im/riot-web/issues/3148
|
||||
console.log('UnknownDeviceDialog closed with '+r);
|
||||
},
|
||||
}, 'mx_Dialog_unknownDevice');
|
||||
}
|
||||
};
|
||||
|
||||
let ref = null;
|
||||
|
||||
export function startListening() {
|
||||
ref = dis.register(onAction);
|
||||
}
|
||||
|
||||
export function stopListening() {
|
||||
if (ref) {
|
||||
dis.unregister(ref);
|
||||
ref = null;
|
||||
}
|
||||
}
|
|
@ -40,7 +40,6 @@ require('../../stores/LifecycleStore');
|
|||
import PageTypes from '../../PageTypes';
|
||||
|
||||
import createRoom from "../../createRoom";
|
||||
import * as UDEHandler from '../../UnknownDeviceErrorHandler';
|
||||
import KeyRequestHandler from '../../KeyRequestHandler';
|
||||
import { _t, getCurrentLanguage } from '../../languageHandler';
|
||||
import SettingsStore, {SettingLevel} from "../../settings/SettingsStore";
|
||||
|
@ -295,7 +294,6 @@ module.exports = React.createClass({
|
|||
|
||||
componentDidMount: function() {
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
UDEHandler.startListening();
|
||||
|
||||
this.focusComposer = false;
|
||||
|
||||
|
@ -361,7 +359,6 @@ module.exports = React.createClass({
|
|||
componentWillUnmount: function() {
|
||||
Lifecycle.stopMatrixClient();
|
||||
dis.unregister(this.dispatcherRef);
|
||||
UDEHandler.stopListening();
|
||||
window.removeEventListener("focus", this.onFocus);
|
||||
window.removeEventListener('resize', this.handleResize);
|
||||
},
|
||||
|
@ -1398,13 +1395,6 @@ module.exports = React.createClass({
|
|||
cli.sendEvent(roomId, event.getType(), event.getContent()).done(() => {
|
||||
dis.dispatch({action: 'message_sent'});
|
||||
}, (err) => {
|
||||
if (err.name === 'UnknownDeviceError') {
|
||||
dis.dispatch({
|
||||
action: 'unknown_device_error',
|
||||
err: err,
|
||||
room: cli.getRoom(roomId),
|
||||
});
|
||||
}
|
||||
dis.dispatch({action: 'message_send_failed'});
|
||||
});
|
||||
},
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2017 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -15,16 +16,26 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import Matrix from 'matrix-js-sdk';
|
||||
import { _t } from '../../languageHandler';
|
||||
import sdk from '../../index';
|
||||
import WhoIsTyping from '../../WhoIsTyping';
|
||||
import MatrixClientPeg from '../../MatrixClientPeg';
|
||||
import MemberAvatar from '../views/avatars/MemberAvatar';
|
||||
import Resend from '../../Resend';
|
||||
import { showUnknownDeviceDialogForMessages } from '../../cryptodevices';
|
||||
|
||||
const STATUS_BAR_HIDDEN = 0;
|
||||
const STATUS_BAR_EXPANDED = 1;
|
||||
const STATUS_BAR_EXPANDED_LARGE = 2;
|
||||
|
||||
function getUnsentMessages(room) {
|
||||
if (!room) { return []; }
|
||||
return room.getPendingEvents().filter(function(ev) {
|
||||
return ev.status === Matrix.EventStatus.NOT_SENT;
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomStatusBar',
|
||||
|
||||
|
@ -35,9 +46,6 @@ module.exports = React.createClass({
|
|||
// the number of messages which have arrived since we've been scrolled up
|
||||
numUnreadMessages: React.PropTypes.number,
|
||||
|
||||
// string to display when there are messages in the room which had errors on send
|
||||
unsentMessageError: React.PropTypes.string,
|
||||
|
||||
// this is true if we are fully scrolled-down, and are looking at
|
||||
// the end of the live timeline.
|
||||
atEndOfLiveTimeline: React.PropTypes.bool,
|
||||
|
@ -98,12 +106,14 @@ module.exports = React.createClass({
|
|||
return {
|
||||
syncState: MatrixClientPeg.get().getSyncState(),
|
||||
usersTyping: WhoIsTyping.usersTypingApartFromMe(this.props.room),
|
||||
unsentMessages: getUnsentMessages(this.props.room),
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
MatrixClientPeg.get().on("sync", this.onSyncStateChange);
|
||||
MatrixClientPeg.get().on("RoomMember.typing", this.onRoomMemberTyping);
|
||||
MatrixClientPeg.get().on("Room.localEchoUpdated", this._onRoomLocalEchoUpdated);
|
||||
|
||||
this._checkSize();
|
||||
},
|
||||
|
@ -118,6 +128,7 @@ module.exports = React.createClass({
|
|||
if (client) {
|
||||
client.removeListener("sync", this.onSyncStateChange);
|
||||
client.removeListener("RoomMember.typing", this.onRoomMemberTyping);
|
||||
client.removeListener("Room.localEchoUpdated", this._onRoomLocalEchoUpdated);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -136,6 +147,26 @@ module.exports = React.createClass({
|
|||
});
|
||||
},
|
||||
|
||||
_onResendAllClick: function() {
|
||||
Resend.resendUnsentEvents(this.props.room);
|
||||
},
|
||||
|
||||
_onCancelAllClick: function() {
|
||||
Resend.cancelUnsentEvents(this.props.room);
|
||||
},
|
||||
|
||||
_onShowDevicesClick: function() {
|
||||
showUnknownDeviceDialogForMessages(MatrixClientPeg.get(), this.props.room);
|
||||
},
|
||||
|
||||
_onRoomLocalEchoUpdated: function(event, room, oldEventId, oldStatus) {
|
||||
if (room.roomId !== this.props.room.roomId) return;
|
||||
|
||||
this.setState({
|
||||
unsentMessages: getUnsentMessages(this.props.room),
|
||||
});
|
||||
},
|
||||
|
||||
// Check whether current size is greater than 0, if yes call props.onVisible
|
||||
_checkSize: function() {
|
||||
if (this.props.onVisible && this._getSize()) {
|
||||
|
@ -155,7 +186,7 @@ module.exports = React.createClass({
|
|||
this.props.sentMessageAndIsAlone
|
||||
) {
|
||||
return STATUS_BAR_EXPANDED;
|
||||
} else if (this.props.unsentMessageError) {
|
||||
} else if (this.state.unsentMessages.length > 0) {
|
||||
return STATUS_BAR_EXPANDED_LARGE;
|
||||
}
|
||||
return STATUS_BAR_HIDDEN;
|
||||
|
@ -241,6 +272,61 @@ module.exports = React.createClass({
|
|||
return avatars;
|
||||
},
|
||||
|
||||
_getUnsentMessageContent: function() {
|
||||
const unsentMessages = this.state.unsentMessages;
|
||||
if (!unsentMessages.length) return null;
|
||||
|
||||
let title;
|
||||
let content;
|
||||
|
||||
const hasUDE = unsentMessages.some((m) => {
|
||||
return m.error && m.error.name === "UnknownDeviceError";
|
||||
});
|
||||
|
||||
if (hasUDE) {
|
||||
title = _t("Message not sent due to unknown devices being present");
|
||||
content = _t(
|
||||
"<showDevicesText>Show devices</showDevicesText> or <cancelText>cancel all</cancelText>.",
|
||||
{},
|
||||
{
|
||||
'showDevicesText': (sub) => <a className="mx_RoomStatusBar_resend_link" key="resend" onClick={this._onShowDevicesClick}>{ sub }</a>,
|
||||
'cancelText': (sub) => <a className="mx_RoomStatusBar_resend_link" key="cancel" onClick={this._onCancelAllClick}>{ sub }</a>,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
if (
|
||||
unsentMessages.length === 1 &&
|
||||
unsentMessages[0].error &&
|
||||
unsentMessages[0].error.data &&
|
||||
unsentMessages[0].error.data.error
|
||||
) {
|
||||
title = unsentMessages[0].error.data.error;
|
||||
} else {
|
||||
title = _t("Some of your messages have not been sent.");
|
||||
}
|
||||
content = _t("<resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. " +
|
||||
"You can also select individual messages to resend or cancel.",
|
||||
{},
|
||||
{
|
||||
'resendText': (sub) =>
|
||||
<a className="mx_RoomStatusBar_resend_link" key="resend" onClick={this._onResendAllClick}>{ sub }</a>,
|
||||
'cancelText': (sub) =>
|
||||
<a className="mx_RoomStatusBar_resend_link" key="cancel" onClick={this._onCancelAllClick}>{ sub }</a>,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return <div className="mx_RoomStatusBar_connectionLostBar">
|
||||
<img src="img/warning.svg" width="24" height="23" title={_t("Warning")} alt={_t("Warning")} />
|
||||
<div className="mx_RoomStatusBar_connectionLostBar_title">
|
||||
{ title }
|
||||
</div>
|
||||
<div className="mx_RoomStatusBar_connectionLostBar_desc">
|
||||
{ content }
|
||||
</div>
|
||||
</div>;
|
||||
},
|
||||
|
||||
// return suitable content for the main (text) part of the status bar.
|
||||
_getContent: function() {
|
||||
const EmojiText = sdk.getComponent('elements.EmojiText');
|
||||
|
@ -263,28 +349,8 @@ module.exports = React.createClass({
|
|||
);
|
||||
}
|
||||
|
||||
if (this.props.unsentMessageError) {
|
||||
return (
|
||||
<div className="mx_RoomStatusBar_connectionLostBar">
|
||||
<img src="img/warning.svg" width="24" height="23" title="/!\ " alt="/!\ " />
|
||||
<div className="mx_RoomStatusBar_connectionLostBar_title">
|
||||
{ this.props.unsentMessageError }
|
||||
</div>
|
||||
<div className="mx_RoomStatusBar_connectionLostBar_desc">
|
||||
{
|
||||
_t("<resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. " +
|
||||
"You can also select individual messages to resend or cancel.",
|
||||
{},
|
||||
{
|
||||
'resendText': (sub) =>
|
||||
<a className="mx_RoomStatusBar_resend_link" key="resend" onClick={this.props.onResendAllClick}>{ sub }</a>,
|
||||
'cancelText': (sub) =>
|
||||
<a className="mx_RoomStatusBar_resend_link" key="cancel" onClick={this.props.onCancelAllClick}>{ sub }</a>,
|
||||
},
|
||||
) }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
if (this.state.unsentMessages.length > 0) {
|
||||
return this._getUnsentMessageContent();
|
||||
}
|
||||
|
||||
// unread count trumps who is typing since the unread count is only
|
||||
|
@ -342,7 +408,6 @@ module.exports = React.createClass({
|
|||
return null;
|
||||
},
|
||||
|
||||
|
||||
render: function() {
|
||||
const content = this._getContent();
|
||||
const indicator = this._getIndicator(this.state.usersTyping.length > 0);
|
||||
|
|
|
@ -26,7 +26,6 @@ const React = require("react");
|
|||
const ReactDOM = require("react-dom");
|
||||
import Promise from 'bluebird';
|
||||
const classNames = require("classnames");
|
||||
const Matrix = require("matrix-js-sdk");
|
||||
import { _t } from '../../languageHandler';
|
||||
|
||||
const MatrixClientPeg = require("../../MatrixClientPeg");
|
||||
|
@ -34,7 +33,6 @@ const ContentMessages = require("../../ContentMessages");
|
|||
const Modal = require("../../Modal");
|
||||
const sdk = require('../../index');
|
||||
const CallHandler = require('../../CallHandler');
|
||||
const Resend = require("../../Resend");
|
||||
const dis = require("../../dispatcher");
|
||||
const Tinter = require("../../Tinter");
|
||||
const rate_limited_func = require('../../ratelimitedfunc');
|
||||
|
@ -110,7 +108,6 @@ module.exports = React.createClass({
|
|||
draggingFile: false,
|
||||
searching: false,
|
||||
searchResults: null,
|
||||
unsentMessageError: '',
|
||||
callState: null,
|
||||
guestsCanJoin: false,
|
||||
canPeek: false,
|
||||
|
@ -202,7 +199,6 @@ module.exports = React.createClass({
|
|||
if (initial) {
|
||||
newState.room = MatrixClientPeg.get().getRoom(newState.roomId);
|
||||
if (newState.room) {
|
||||
newState.unsentMessageError = this._getUnsentMessageError(newState.room);
|
||||
newState.showApps = this._shouldShowApps(newState.room);
|
||||
this._onRoomLoaded(newState.room);
|
||||
}
|
||||
|
@ -462,11 +458,6 @@ module.exports = React.createClass({
|
|||
case 'message_send_failed':
|
||||
case 'message_sent':
|
||||
this._checkIfAlone(this.state.room);
|
||||
// no break; to intentionally fall through
|
||||
case 'message_send_cancelled':
|
||||
this.setState({
|
||||
unsentMessageError: this._getUnsentMessageError(this.state.room),
|
||||
});
|
||||
break;
|
||||
case 'notifier_enabled':
|
||||
case 'upload_failed':
|
||||
|
@ -711,35 +702,6 @@ module.exports = React.createClass({
|
|||
this.setState({isAlone: joinedMembers.length === 1});
|
||||
},
|
||||
|
||||
_getUnsentMessageError: function(room) {
|
||||
const unsentMessages = this._getUnsentMessages(room);
|
||||
if (!unsentMessages.length) return "";
|
||||
|
||||
if (
|
||||
unsentMessages.length === 1 &&
|
||||
unsentMessages[0].error &&
|
||||
unsentMessages[0].error.data &&
|
||||
unsentMessages[0].error.data.error &&
|
||||
unsentMessages[0].error.name !== "UnknownDeviceError"
|
||||
) {
|
||||
return unsentMessages[0].error.data.error;
|
||||
}
|
||||
|
||||
for (const event of unsentMessages) {
|
||||
if (!event.error || event.error.name !== "UnknownDeviceError") {
|
||||
return _t("Some of your messages have not been sent.");
|
||||
}
|
||||
}
|
||||
return _t("Message not sent due to unknown devices being present");
|
||||
},
|
||||
|
||||
_getUnsentMessages: function(room) {
|
||||
if (!room) { return []; }
|
||||
return room.getPendingEvents().filter(function(ev) {
|
||||
return ev.status === Matrix.EventStatus.NOT_SENT;
|
||||
});
|
||||
},
|
||||
|
||||
_updateConfCallNotification: function() {
|
||||
const room = this.state.room;
|
||||
if (!room || !this.props.ConferenceHandler) {
|
||||
|
@ -784,14 +746,6 @@ module.exports = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
onResendAllClick: function() {
|
||||
Resend.resendUnsentEvents(this.state.room);
|
||||
},
|
||||
|
||||
onCancelAllClick: function() {
|
||||
Resend.cancelUnsentEvents(this.state.room);
|
||||
},
|
||||
|
||||
onInviteButtonClick: function() {
|
||||
// call AddressPickerDialog
|
||||
dis.dispatch({
|
||||
|
@ -935,11 +889,7 @@ module.exports = React.createClass({
|
|||
file, this.state.room.roomId, MatrixClientPeg.get(),
|
||||
).done(undefined, (error) => {
|
||||
if (error.name === "UnknownDeviceError") {
|
||||
dis.dispatch({
|
||||
action: 'unknown_device_error',
|
||||
err: error,
|
||||
room: this.state.room,
|
||||
});
|
||||
// Let the staus bar handle this
|
||||
return;
|
||||
}
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
|
@ -1571,12 +1521,9 @@ module.exports = React.createClass({
|
|||
statusBar = <RoomStatusBar
|
||||
room={this.state.room}
|
||||
numUnreadMessages={this.state.numUnreadMessages}
|
||||
unsentMessageError={this.state.unsentMessageError}
|
||||
atEndOfLiveTimeline={this.state.atEndOfLiveTimeline}
|
||||
sentMessageAndIsAlone={this.state.isAlone}
|
||||
hasActiveCall={inCall}
|
||||
onResendAllClick={this.onResendAllClick}
|
||||
onCancelAllClick={this.onCancelAllClick}
|
||||
onInviteClick={this.onInviteButtonClick}
|
||||
onStopWarningClick={this.onStopAloneWarningClick}
|
||||
onScrollToBottomClick={this.jumpToLiveTimeline}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2017 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -15,6 +16,7 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import sdk from '../../../index';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
import GeminiScrollbar from 'react-gemini-scrollbar';
|
||||
|
@ -22,6 +24,14 @@ import Resend from '../../../Resend';
|
|||
import { _t } from '../../../languageHandler';
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
|
||||
function markAllDevicesKnown(devices) {
|
||||
Object.keys(devices).forEach((userId) => {
|
||||
Object.keys(devices[userId]).map((deviceId) => {
|
||||
MatrixClientPeg.get().setDeviceKnown(userId, deviceId, true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function DeviceListEntry(props) {
|
||||
const {userId, device} = props;
|
||||
|
||||
|
@ -38,10 +48,10 @@ function DeviceListEntry(props) {
|
|||
}
|
||||
|
||||
DeviceListEntry.propTypes = {
|
||||
userId: React.PropTypes.string.isRequired,
|
||||
userId: PropTypes.string.isRequired,
|
||||
|
||||
// deviceinfo
|
||||
device: React.PropTypes.object.isRequired,
|
||||
device: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
|
||||
|
@ -61,10 +71,10 @@ function UserUnknownDeviceList(props) {
|
|||
}
|
||||
|
||||
UserUnknownDeviceList.propTypes = {
|
||||
userId: React.PropTypes.string.isRequired,
|
||||
userId: PropTypes.string.isRequired,
|
||||
|
||||
// map from deviceid -> deviceinfo
|
||||
userDevices: React.PropTypes.object.isRequired,
|
||||
userDevices: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
|
||||
|
@ -83,7 +93,7 @@ function UnknownDeviceList(props) {
|
|||
|
||||
UnknownDeviceList.propTypes = {
|
||||
// map from userid -> deviceid -> deviceinfo
|
||||
devices: React.PropTypes.object.isRequired,
|
||||
devices: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
|
||||
|
@ -91,28 +101,63 @@ export default React.createClass({
|
|||
displayName: 'UnknownDeviceDialog',
|
||||
|
||||
propTypes: {
|
||||
room: React.PropTypes.object.isRequired,
|
||||
room: PropTypes.object.isRequired,
|
||||
|
||||
// map from userid -> deviceid -> deviceinfo
|
||||
devices: React.PropTypes.object.isRequired,
|
||||
onFinished: React.PropTypes.func.isRequired,
|
||||
// map from userid -> deviceid -> deviceinfo or null if devices are not yet loaded
|
||||
devices: PropTypes.object,
|
||||
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
|
||||
// Label for the button that marks all devices known and tries the send again
|
||||
sendAnywayLabel: PropTypes.string.isRequired,
|
||||
|
||||
// Label for the button that to send the event if you've verified all devices
|
||||
sendLabel: PropTypes.string.isRequired,
|
||||
|
||||
// function to retry the request once all devices are verified / known
|
||||
onSend: 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);
|
||||
});
|
||||
});
|
||||
componentWillMount: function() {
|
||||
MatrixClientPeg.get().on("deviceVerificationChanged", this._onDeviceVerificationChanged);
|
||||
},
|
||||
|
||||
// XXX: temporary logging to try to diagnose
|
||||
// https://github.com/vector-im/riot-web/issues/3148
|
||||
console.log('Opening UnknownDeviceDialog');
|
||||
componentWillUnmount: function() {
|
||||
if (MatrixClientPeg.get()) {
|
||||
MatrixClientPeg.get().removeListener("deviceVerificationChanged", this._onDeviceVerificationChanged);
|
||||
}
|
||||
},
|
||||
|
||||
_onDeviceVerificationChanged: function(userId, deviceId, deviceInfo) {
|
||||
if (this.props.devices[userId] && this.props.devices[userId][deviceId]) {
|
||||
// XXX: Mutating props :/
|
||||
this.props.devices[userId][deviceId] = deviceInfo;
|
||||
this.forceUpdate();
|
||||
}
|
||||
},
|
||||
|
||||
_onDismissClicked: function() {
|
||||
this.props.onFinished();
|
||||
},
|
||||
|
||||
_onSendAnywayClicked: function() {
|
||||
markAllDevicesKnown(this.props.devices);
|
||||
|
||||
this.props.onFinished();
|
||||
this.props.onSend();
|
||||
},
|
||||
|
||||
_onSendClicked: function() {
|
||||
this.props.onFinished();
|
||||
this.props.onSend();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (this.props.devices === null) {
|
||||
const Spinner = sdk.getComponent("elements.Spinner");
|
||||
return <Spinner />;
|
||||
}
|
||||
|
||||
let warning;
|
||||
if (SettingsStore.getValue("blacklistUnverifiedDevices", this.props.room.roomId)) {
|
||||
warning = (
|
||||
|
@ -133,15 +178,30 @@ export default React.createClass({
|
|||
);
|
||||
}
|
||||
|
||||
let haveUnknownDevices = false;
|
||||
Object.keys(this.props.devices).forEach((userId) => {
|
||||
Object.keys(this.props.devices[userId]).map((deviceId) => {
|
||||
const device = this.props.devices[userId][deviceId];
|
||||
if (device.isUnverified() && !device.isKnown()) {
|
||||
haveUnknownDevices = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
let sendButton;
|
||||
if (haveUnknownDevices) {
|
||||
sendButton = <button onClick={this._onSendAnywayClicked}>
|
||||
{ this.props.sendAnywayLabel }
|
||||
</button>;
|
||||
} else {
|
||||
sendButton = <button onClick={this._onSendClicked}>
|
||||
{ this.props.sendLabel }
|
||||
</button>;
|
||||
}
|
||||
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
return (
|
||||
<BaseDialog className='mx_UnknownDeviceDialog'
|
||||
onFinished={() => {
|
||||
// XXX: temporary logging to try to diagnose
|
||||
// https://github.com/vector-im/riot-web/issues/3148
|
||||
console.log("UnknownDeviceDialog closed by escape");
|
||||
this.props.onFinished();
|
||||
}}
|
||||
onFinished={this.props.onFinished}
|
||||
title={_t('Room contains unknown devices')}
|
||||
>
|
||||
<GeminiScrollbar autoshow={false} className="mx_Dialog_content">
|
||||
|
@ -154,21 +214,11 @@ export default React.createClass({
|
|||
<UnknownDeviceList devices={this.props.devices} />
|
||||
</GeminiScrollbar>
|
||||
<div className="mx_Dialog_buttons">
|
||||
{sendButton}
|
||||
<button className="mx_Dialog_primary" autoFocus={true}
|
||||
onClick={() => {
|
||||
this.props.onFinished();
|
||||
Resend.resendUnsentEvents(this.props.room);
|
||||
}}>
|
||||
{ _t("Send anyway") }
|
||||
</button>
|
||||
<button className="mx_Dialog_primary" autoFocus={true}
|
||||
onClick={() => {
|
||||
// XXX: temporary logging to try to diagnose
|
||||
// https://github.com/vector-im/riot-web/issues/3148
|
||||
console.log("UnknownDeviceDialog closed by OK");
|
||||
this.props.onFinished();
|
||||
}}>
|
||||
OK
|
||||
onClick={this._onDismissClicked}
|
||||
>
|
||||
{_t("Dismiss")}
|
||||
</button>
|
||||
</div>
|
||||
</BaseDialog>
|
||||
|
|
|
@ -74,13 +74,6 @@ function onSendMessageFailed(err, room) {
|
|||
// XXX: temporary logging to try to diagnose
|
||||
// https://github.com/vector-im/riot-web/issues/3148
|
||||
console.log('MessageComposer got send failure: ' + err.name + '('+err+')');
|
||||
if (err.name === "UnknownDeviceError") {
|
||||
dis.dispatch({
|
||||
action: 'unknown_device_error',
|
||||
err: err,
|
||||
room: room,
|
||||
});
|
||||
}
|
||||
dis.dispatch({
|
||||
action: 'message_send_failed',
|
||||
});
|
||||
|
|
104
src/cryptodevices.js
Normal file
104
src/cryptodevices.js
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
Copyright 2017 New Vector 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 Resend from './Resend';
|
||||
import sdk from './index';
|
||||
import Modal from './Modal';
|
||||
import { _t } from './languageHandler';
|
||||
|
||||
/**
|
||||
* Gets all crypto devices in a room that are marked neither known
|
||||
* nor verified.
|
||||
*
|
||||
* @param {MatrixClient} matrixClient A MatrixClient
|
||||
* @param {Room} room js-sdk room object representing the room
|
||||
* @return {Promise} A promise which resolves to a map userId->deviceId->{@link
|
||||
* module:crypto~DeviceInfo|DeviceInfo}.
|
||||
*/
|
||||
export function getUnknownDevicesForRoom(matrixClient, room) {
|
||||
const roomMembers = room.getJoinedMembers().map((m) => {
|
||||
return m.userId;
|
||||
});
|
||||
return matrixClient.downloadKeys(roomMembers, false).then((devices) => {
|
||||
const unknownDevices = {};
|
||||
// This is all devices in this room, so find the unknown ones.
|
||||
Object.keys(devices).forEach((userId) => {
|
||||
Object.keys(devices[userId]).map((deviceId) => {
|
||||
const device = devices[userId][deviceId];
|
||||
|
||||
if (device.isUnverified() && !device.isKnown()) {
|
||||
if (unknownDevices[userId] === undefined) {
|
||||
unknownDevices[userId] = {};
|
||||
}
|
||||
unknownDevices[userId][deviceId] = device;
|
||||
}
|
||||
});
|
||||
});
|
||||
return unknownDevices;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the UnknownDeviceDialog for a given room. The dialog will inform the user
|
||||
* that messages they sent to this room have not been sent due to unknown devices
|
||||
* being present.
|
||||
*
|
||||
* @param {MatrixClient} matrixClient A MatrixClient
|
||||
* @param {Room} room js-sdk room object representing the room
|
||||
*/
|
||||
export function showUnknownDeviceDialogForMessages(matrixClient, room) {
|
||||
getUnknownDevicesForRoom(matrixClient, room).then((unknownDevices) => {
|
||||
const onSendClicked = () => {
|
||||
Resend.resendUnsentEvents(room);
|
||||
};
|
||||
|
||||
const UnknownDeviceDialog = sdk.getComponent('dialogs.UnknownDeviceDialog');
|
||||
Modal.createTrackedDialog('Unknown Device Dialog', '', UnknownDeviceDialog, {
|
||||
room: room,
|
||||
devices: unknownDevices,
|
||||
sendAnywayLabel: _t("Send anyway"),
|
||||
sendLabel: _t("Send"),
|
||||
onSend: onSendClicked,
|
||||
}, 'mx_Dialog_unknownDevice');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the UnknownDeviceDialog for a given room. The dialog will inform the user
|
||||
* that a call they tried to place or answer in the room couldn't be placed or
|
||||
* answered due to unknown devices being present.
|
||||
*
|
||||
* @param {MatrixClient} matrixClient A MatrixClient
|
||||
* @param {Room} room js-sdk room object representing the room
|
||||
* @param {func} sendAnyway Function called when the 'call anyway' or 'call'
|
||||
* button is pressed. This should attempt to place or answer the call again.
|
||||
* @param {string} sendAnywayLabel Label for the button displayed to retry the call
|
||||
* when unknown devices are still present (eg. "Call Anyway")
|
||||
* @param {string} sendLabel Label for the button displayed to retry the call
|
||||
* after all devices have been verified (eg. "Call")
|
||||
*/
|
||||
export function showUnknownDeviceDialogForCalls(matrixClient, room, sendAnyway, sendAnywayLabel, sendLabel) {
|
||||
getUnknownDevicesForRoom(matrixClient, room).then((unknownDevices) => {
|
||||
const UnknownDeviceDialog = sdk.getComponent('dialogs.UnknownDeviceDialog');
|
||||
Modal.createTrackedDialog('Unknown Device Dialog', '', UnknownDeviceDialog, {
|
||||
room: room,
|
||||
devices: unknownDevices,
|
||||
sendAnywayLabel: sendAnywayLabel,
|
||||
sendLabel: sendLabel,
|
||||
onSend: sendAnyway,
|
||||
}, 'mx_Dialog_unknownDevice');
|
||||
});
|
||||
}
|
|
@ -2,6 +2,13 @@
|
|||
"This email address is already in use": "This email address is already in use",
|
||||
"This phone number is already in use": "This phone number is already in use",
|
||||
"Failed to verify email address: make sure you clicked the link in the email": "Failed to verify email address: make sure you clicked the link in the email",
|
||||
"Call Failed": "Call Failed",
|
||||
"There are unknown devices in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "There are unknown devices in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.",
|
||||
"Review Devices": "Review Devices",
|
||||
"Call Anyway": "Call Anyway",
|
||||
"Answer Anyway": "Answer Anyway",
|
||||
"Call": "Call",
|
||||
"Answer": "Answer",
|
||||
"Call Timeout": "Call Timeout",
|
||||
"The remote side failed to pick up": "The remote side failed to pick up",
|
||||
"Unable to capture screen": "Unable to capture screen",
|
||||
|
@ -156,6 +163,8 @@
|
|||
"%(names)s and %(lastPerson)s are typing": "%(names)s and %(lastPerson)s are typing",
|
||||
"Failure to create room": "Failure to create room",
|
||||
"Server may be unavailable, overloaded, or you hit a bug.": "Server may be unavailable, overloaded, or you hit a bug.",
|
||||
"Send anyway": "Send anyway",
|
||||
"Send": "Send",
|
||||
"Unnamed Room": "Unnamed Room",
|
||||
"Your browser does not support the required cryptography extensions": "Your browser does not support the required cryptography extensions",
|
||||
"Not a valid Riot keyfile": "Not a valid Riot keyfile",
|
||||
|
@ -695,7 +704,6 @@
|
|||
"Room contains unknown devices": "Room contains unknown devices",
|
||||
"\"%(RoomName)s\" contains devices that you haven't seen before.": "\"%(RoomName)s\" contains devices that you haven't seen before.",
|
||||
"Unknown devices": "Unknown devices",
|
||||
"Send anyway": "Send anyway",
|
||||
"Private Chat": "Private Chat",
|
||||
"Public Chat": "Public Chat",
|
||||
"Custom": "Custom",
|
||||
|
@ -760,17 +768,19 @@
|
|||
"To join an existing community you'll have to know its community identifier; this will look something like <i>+example:matrix.org</i>.": "To join an existing community you'll have to know its community identifier; this will look something like <i>+example:matrix.org</i>.",
|
||||
"You have no visible notifications": "You have no visible notifications",
|
||||
"Scroll to bottom of page": "Scroll to bottom of page",
|
||||
"Message not sent due to unknown devices being present": "Message not sent due to unknown devices being present",
|
||||
"<showDevicesText>Show devices</showDevicesText> or <cancelText>cancel all</cancelText>.": "<showDevicesText>Show devices</showDevicesText> or <cancelText>cancel all</cancelText>.",
|
||||
"Some of your messages have not been sent.": "Some of your messages have not been sent.",
|
||||
"<resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.": "<resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.",
|
||||
"Warning": "Warning",
|
||||
"Connectivity to the server has been lost.": "Connectivity to the server has been lost.",
|
||||
"Sent messages will be stored until your connection has returned.": "Sent messages will be stored until your connection has returned.",
|
||||
"<resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.": "<resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.",
|
||||
"%(count)s new messages|other": "%(count)s new messages",
|
||||
"%(count)s new messages|one": "%(count)s new message",
|
||||
"Active call": "Active call",
|
||||
"There's no one else here! Would you like to <inviteText>invite others</inviteText> or <nowarnText>stop warning about the empty room</nowarnText>?": "There's no one else here! Would you like to <inviteText>invite others</inviteText> or <nowarnText>stop warning about the empty room</nowarnText>?",
|
||||
"You seem to be uploading files, are you sure you want to quit?": "You seem to be uploading files, are you sure you want to quit?",
|
||||
"You seem to be in a call, are you sure you want to quit?": "You seem to be in a call, are you sure you want to quit?",
|
||||
"Some of your messages have not been sent.": "Some of your messages have not been sent.",
|
||||
"Message not sent due to unknown devices being present": "Message not sent due to unknown devices being present",
|
||||
"Failed to upload file": "Failed to upload file",
|
||||
"Server may be unavailable, overloaded, or the file too big": "Server may be unavailable, overloaded, or the file too big",
|
||||
"Search failed": "Search failed",
|
||||
|
|
Loading…
Reference in a new issue