From d8a38e6b74b185724ecbc7a13c0cb24341a46dc9 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 10 Dec 2019 17:53:51 +0100 Subject: [PATCH 01/25] WIP --- src/components/structures/MatrixChat.js | 12 +-- src/components/structures/RightPanel.js | 4 + src/components/structures/ToastContainer.js | 1 + .../views/dialogs/DeviceVerifyDialog.js | 8 +- .../views/messages/MKeyVerificationRequest.js | 98 +++++++++++-------- .../views/right_panel/EncryptionPanel.js | 33 +++++++ .../views/right_panel/RoomHeaderButtons.js | 1 + src/stores/RightPanelStore.js | 6 +- src/stores/RightPanelStorePhases.js | 2 +- src/utils/KeyVerificationStateObserver.js | 1 + 10 files changed, 112 insertions(+), 54 deletions(-) create mode 100644 src/components/views/right_panel/EncryptionPanel.js diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index af3f4d2598..e093e9dff9 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -1458,20 +1458,16 @@ export default createReactClass({ if (SettingsStore.isFeatureEnabled("feature_cross_signing")) { cli.on("crypto.verification.request", request => { - let requestObserver; - if (request.event.getRoomId()) { - requestObserver = new KeyVerificationStateObserver( - request.event, MatrixClientPeg.get()); - } - - if (!requestObserver || requestObserver.pending) { + console.log(`MatrixChat got a .request ${request.channel.transactionId}`, request.event.getRoomId()); + if (request.pending) { + console.log(`emitting toast for verification request with txnid ${request.channel.transactionId}`, request.event && request.event.getId()); dis.dispatch({ action: "show_toast", toast: { key: request.event.getId(), title: _t("Verification Request"), icon: "verification", - props: {request, requestObserver}, + props: {request}, component: sdk.getComponent("toasts.VerificationRequestToast"), }, }); diff --git a/src/components/structures/RightPanel.js b/src/components/structures/RightPanel.js index ff987a8f30..f5648a6d02 100644 --- a/src/components/structures/RightPanel.js +++ b/src/components/structures/RightPanel.js @@ -160,6 +160,7 @@ export default class RightPanel extends React.Component { groupId: payload.groupId, member: payload.member, event: payload.event, + verificationRequest: payload.verificationRequest, }); } } @@ -168,6 +169,7 @@ export default class RightPanel extends React.Component { const MemberList = sdk.getComponent('rooms.MemberList'); const MemberInfo = sdk.getComponent('rooms.MemberInfo'); const UserInfo = sdk.getComponent('right_panel.UserInfo'); + const EncryptionPanel = sdk.getComponent('right_panel.EncryptionPanel'); const ThirdPartyMemberInfo = sdk.getComponent('rooms.ThirdPartyMemberInfo'); const NotificationPanel = sdk.getComponent('structures.NotificationPanel'); const FilePanel = sdk.getComponent('structures.FilePanel'); @@ -235,6 +237,8 @@ export default class RightPanel extends React.Component { panel = ; } else if (this.state.phase === RIGHT_PANEL_PHASES.FilePanel) { panel = ; + } else if (this.state.phase === RIGHT_PANEL_PHASES.EncryptionPanel) { + panel = ; } const classes = classNames("mx_RightPanel", "mx_fadable", { diff --git a/src/components/structures/ToastContainer.js b/src/components/structures/ToastContainer.js index a8dca35747..a9c8267d0d 100644 --- a/src/components/structures/ToastContainer.js +++ b/src/components/structures/ToastContainer.js @@ -26,6 +26,7 @@ export default class ToastContainer extends React.Component { } componentDidMount() { + console.log("ToastContainer mounted"); this._dispatcherRef = dis.register(this.onAction); } diff --git a/src/components/views/dialogs/DeviceVerifyDialog.js b/src/components/views/dialogs/DeviceVerifyDialog.js index 6408245452..ca05d74964 100644 --- a/src/components/views/dialogs/DeviceVerifyDialog.js +++ b/src/components/views/dialogs/DeviceVerifyDialog.js @@ -100,9 +100,15 @@ export default class DeviceVerifyDialog extends React.Component { if (!verifyingOwnDevice && SettingsStore.getValue("feature_cross_signing")) { const roomId = await ensureDMExistsAndOpen(this.props.userId); // throws upon cancellation before having started - this._verifier = await client.requestVerificationDM( + const request = await client.requestVerificationDM( this.props.userId, roomId, [verificationMethods.SAS], ); + await request.waitFor(r => r.ready || r.started); + if (request.ready) { + this._verifier = request.beginKeyVerification(verificationMethods.SAS); + } else { + this._verifier = request.verifier; + } } else { this._verifier = client.beginKeyVerification( verificationMethods.SAS, this.props.userId, this.props.device.deviceId, diff --git a/src/components/views/messages/MKeyVerificationRequest.js b/src/components/views/messages/MKeyVerificationRequest.js index 4faa1b20aa..f31a823ac9 100644 --- a/src/components/views/messages/MKeyVerificationRequest.js +++ b/src/components/views/messages/MKeyVerificationRequest.js @@ -17,48 +17,62 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; import MatrixClientPeg from '../../../MatrixClientPeg'; -import {verificationMethods} from 'matrix-js-sdk/lib/crypto'; import sdk from '../../../index'; -import Modal from "../../../Modal"; import { _t } from '../../../languageHandler'; -import KeyVerificationStateObserver, {getNameForEventRoom, userLabelForEventRoom} +import {getNameForEventRoom, userLabelForEventRoom} from '../../../utils/KeyVerificationStateObserver'; +import dis from "../../../dispatcher"; +import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases"; export default class MKeyVerificationRequest extends React.Component { constructor(props) { super(props); - this.keyVerificationState = new KeyVerificationStateObserver(this.props.mxEvent, MatrixClientPeg.get(), () => { - this.setState(this._copyState()); - }); - this.state = this._copyState(); - } - - _copyState() { - const {accepted, done, cancelled, cancelPartyUserId, otherPartyUserId} = this.keyVerificationState; - return {accepted, done, cancelled, cancelPartyUserId, otherPartyUserId}; } componentDidMount() { - this.keyVerificationState.attach(); + const request = this.props.mxEvent.verificationRequest; + if (request) { + request.on("change", this._onRequestChanged); + } } componentWillUnmount() { - this.keyVerificationState.detach(); + const request = this.props.mxEvent.verificationRequest; + if (request) { + request.off("change", this._onRequestChanged); + } } - _onAcceptClicked = () => { - const IncomingSasDialog = sdk.getComponent('views.dialogs.IncomingSasDialog'); - // todo: validate event, for example if it has sas in the methods. - const verifier = MatrixClientPeg.get().acceptVerificationDM(this.props.mxEvent, verificationMethods.SAS); - Modal.createTrackedDialog('Incoming Verification', '', IncomingSasDialog, { - verifier, - }, null, /* priority = */ false, /* static = */ true); + _onRequestChanged = () => { + this.forceUpdate(); }; - _onRejectClicked = () => { - // todo: validate event, for example if it has sas in the methods. - const verifier = MatrixClientPeg.get().acceptVerificationDM(this.props.mxEvent, verificationMethods.SAS); - verifier.cancel("User declined"); + _onAcceptClicked = async () => { + const request = this.props.mxEvent.verificationRequest; + if (request) { + try { + await request.accept(); + dis.dispatch({action: "show_right_panel"}); + dis.dispatch({ + action: "set_right_panel_phase", + phase: RIGHT_PANEL_PHASES.EncryptionPanel, + verificationRequest: request, + }); + } catch (err) { + console.error(err.message); + } + } + }; + + _onRejectClicked = async () => { + const request = this.props.mxEvent.verificationRequest; + if (request) { + try { + await request.cancel(); + } catch (err) { + console.error(err.message); + } + } }; _acceptedLabel(userId) { @@ -83,45 +97,43 @@ export default class MKeyVerificationRequest extends React.Component { render() { const {mxEvent} = this.props; - const fromUserId = mxEvent.getSender(); - const content = mxEvent.getContent(); - const toUserId = content.to; - const client = MatrixClientPeg.get(); - const myUserId = client.getUserId(); - const isOwn = fromUserId === myUserId; + const request = mxEvent.verificationRequest; let title; let subtitle; let stateNode; - if (this.state.accepted || this.state.cancelled) { + if (!request) { + return

This is an invalid request, ho ho ho!

; + } + + if (request.ready || request.started || request.cancelled) { let stateLabel; - if (this.state.accepted) { - stateLabel = this._acceptedLabel(toUserId); - } else if (this.state.cancelled) { - stateLabel = this._cancelledLabel(this.state.cancelPartyUserId); + if (request.ready || request.started) { + stateLabel = this._acceptedLabel(request.receivingUserId); + } else if (request.cancelled) { + stateLabel = this._cancelledLabel(request.cancellingUserId); } stateNode = (
{stateLabel}
); } - if (toUserId === myUserId) { // request sent to us + if (!request.initiatedByMe) { title = (
{ - _t("%(name)s wants to verify", {name: getNameForEventRoom(fromUserId, mxEvent)})}
); + _t("%(name)s wants to verify", {name: getNameForEventRoom(request.requestingUserId, mxEvent)})}); subtitle = (
{ - userLabelForEventRoom(fromUserId, mxEvent)}
); - const isResolved = !(this.state.accepted || this.state.cancelled || this.state.done); - if (isResolved) { + userLabelForEventRoom(request.requestingUserId, mxEvent)}); + if (request.requested) { const FormButton = sdk.getComponent("elements.FormButton"); stateNode = (
); } - } else if (isOwn) { // request sent by us + } else { // request sent by us title = (
{ _t("You sent a verification request")}
); subtitle = (
{ - userLabelForEventRoom(this.state.otherPartyUserId, mxEvent)}
); + userLabelForEventRoom(request.receivingUserId, mxEvent)}); } if (title) { diff --git a/src/components/views/right_panel/EncryptionPanel.js b/src/components/views/right_panel/EncryptionPanel.js new file mode 100644 index 0000000000..2050ad8072 --- /dev/null +++ b/src/components/views/right_panel/EncryptionPanel.js @@ -0,0 +1,33 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd +Copyright 2017, 2018 Vector Creations Ltd +Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> +Copyright 2019 The Matrix.org Foundation C.I.C. + +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'; + +export default class EncryptionPanel extends React.PureComponent { + render() { + const request = this.props.verificationRequest; + if (request) { + return

got a request, go straight to wizard

; + } else if (this.props.member) { + return

show encryption options for member {this.props.member.name}

; + } else { + return

nada

; + } + } +} diff --git a/src/components/views/right_panel/RoomHeaderButtons.js b/src/components/views/right_panel/RoomHeaderButtons.js index f59159d1d9..2b5ea3aa27 100644 --- a/src/components/views/right_panel/RoomHeaderButtons.js +++ b/src/components/views/right_panel/RoomHeaderButtons.js @@ -27,6 +27,7 @@ import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases"; const MEMBER_PHASES = [ RIGHT_PANEL_PHASES.RoomMemberList, RIGHT_PANEL_PHASES.RoomMemberInfo, + RIGHT_PANEL_PHASES.EncryptionPanel, RIGHT_PANEL_PHASES.Room3pidMemberInfo, ]; diff --git a/src/stores/RightPanelStore.js b/src/stores/RightPanelStore.js index 02775b847b..1b3cb3d64b 100644 --- a/src/stores/RightPanelStore.js +++ b/src/stores/RightPanelStore.js @@ -123,7 +123,11 @@ export default class RightPanelStore extends Store { if (payload.action === 'view_room' || payload.action === 'view_group') { // Reset to the member list if we're viewing member info - const memberInfoPhases = [RIGHT_PANEL_PHASES.RoomMemberInfo, RIGHT_PANEL_PHASES.Room3pidMemberInfo]; + const memberInfoPhases = [ + RIGHT_PANEL_PHASES.RoomMemberInfo, + RIGHT_PANEL_PHASES.Room3pidMemberInfo, + RIGHT_PANEL_PHASES.EncryptionPanel, + ]; if (memberInfoPhases.includes(this._state.lastRoomPhase)) { this._setState({lastRoomPhase: RIGHT_PANEL_PHASES.RoomMemberList, lastRoomPhaseParams: {}}); } diff --git a/src/stores/RightPanelStorePhases.js b/src/stores/RightPanelStorePhases.js index 96807ebf5b..7783f960d6 100644 --- a/src/stores/RightPanelStorePhases.js +++ b/src/stores/RightPanelStorePhases.js @@ -22,7 +22,7 @@ export const RIGHT_PANEL_PHASES = Object.freeze({ NotificationPanel: 'NotificationPanel', RoomMemberInfo: 'RoomMemberInfo', Room3pidMemberInfo: 'Room3pidMemberInfo', - + EncryptionPanel: 'EncryptionPanel', // Group stuff GroupMemberList: 'GroupMemberList', GroupRoomList: 'GroupRoomList', diff --git a/src/utils/KeyVerificationStateObserver.js b/src/utils/KeyVerificationStateObserver.js index 2f7c0367ad..7e33c29214 100644 --- a/src/utils/KeyVerificationStateObserver.js +++ b/src/utils/KeyVerificationStateObserver.js @@ -161,6 +161,7 @@ export default class KeyVerificationStateObserver { } this.otherPartyUserId = fromUserId === this._client.getUserId() ? toUserId : fromUserId; + console.log("KeyVerificationStateObserver update for txnId", this._requestEvent.getId(), {accepted: this.accepted, cancelled: this.cancelled, done: this.done}); } } From c02fc44d256a07c5c39802b05112194fc73ed9f0 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 16 Dec 2019 18:16:22 +0100 Subject: [PATCH 02/25] WIP sas in right panel --- .../views/right_panel/EncryptionInfo.js | 30 +++++++ .../views/right_panel/EncryptionPanel.js | 26 ++++-- .../views/right_panel/VerificationPanel.js | 88 +++++++++++++++++++ 3 files changed, 135 insertions(+), 9 deletions(-) create mode 100644 src/components/views/right_panel/EncryptionInfo.js create mode 100644 src/components/views/right_panel/VerificationPanel.js diff --git a/src/components/views/right_panel/EncryptionInfo.js b/src/components/views/right_panel/EncryptionInfo.js new file mode 100644 index 0000000000..88aa3f9a6c --- /dev/null +++ b/src/components/views/right_panel/EncryptionInfo.js @@ -0,0 +1,30 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +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 "../../.."; + +export default class EncryptionInfo extends React.PureComponent { + render() { + const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); + return (
+

End-to-end encryption is great!

+
+ Start verification +
+
); + } +} diff --git a/src/components/views/right_panel/EncryptionPanel.js b/src/components/views/right_panel/EncryptionPanel.js index 2050ad8072..c37d4f2b61 100644 --- a/src/components/views/right_panel/EncryptionPanel.js +++ b/src/components/views/right_panel/EncryptionPanel.js @@ -1,7 +1,4 @@ /* -Copyright 2015, 2016 OpenMarket Ltd -Copyright 2017, 2018 Vector Creations Ltd -Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,16 +15,27 @@ limitations under the License. */ import React from 'react'; +import EncryptionInfo from "./EncryptionInfo"; +import VerificationPanel from "./VerificationPanel"; +import MatrixClientPeg from "../../../MatrixClientPeg"; export default class EncryptionPanel extends React.PureComponent { render() { - const request = this.props.verificationRequest; + const request = this.props.verificationRequest || this.state.verificationRequest; + const {member} = this.props; if (request) { - return

got a request, go straight to wizard

; - } else if (this.props.member) { - return

show encryption options for member {this.props.member.name}

; - } else { - return

nada

; + return ; + } else if (member) { + return ; } } + + _onStartVerification = async () => { + const client = MatrixClientPeg.get(); + const {member} = this.props; + // TODO: get the room id of the DM here? + // will this panel be shown in non-DM rooms? + const verificationRequest = await client.requestVerificationDM(member.userId, member.roomId); + this.setState({verificationRequest}); + }; } diff --git a/src/components/views/right_panel/VerificationPanel.js b/src/components/views/right_panel/VerificationPanel.js new file mode 100644 index 0000000000..3546e81bf3 --- /dev/null +++ b/src/components/views/right_panel/VerificationPanel.js @@ -0,0 +1,88 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +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 "../../.."; +import {verificationMethods} from 'matrix-js-sdk/lib/crypto'; + +export default class VerificationPanel extends React.PureComponent { + constructor(props) { + super(props); + this.state = {}; + } + + render() { + const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); + const Spinner = sdk.getComponent('elements.Spinner'); + const {request} = this.props; + + if (request.requested) { + return (

Waiting for {request.otherUserId} to accept ...

); + } else if (request.ready) { + return (

{request.otherUserId} is ready, start Verify by emoji

); + } else if (request.started) { + if (this.state.sasEvent) { + const VerificationShowSas = sdk.getComponent('views.verification.VerificationShowSas'); + return (
+ +
); + } else { + return (

Setting up SAS verification...

); + } + } else if (request.done) { + return

verified {request.otherUserId}!!

; + } + } + + _startSAS = async () => { + const verifier = this.props.request.beginKeyVerification(verificationMethods.SAS); + verifier.on('show_sas', this._onVerifierShowSas); + try { + await this._verifier.verify(); + } finally { + this.setState({sasEvent: null}); + verifier.removeListener('show_sas', this._onVerifierShowSas); + } + }; + + _onSasMatchesClick = () => { + this.state.sasEvent.confirm(); + }; + + _onSasMismatchesClick = () => { + this.state.sasEvent.cancel(); + }; + + _onVerifierShowSas = (sasEvent) => { + this.setState({sasEvent}); + }; + + _onRequestChange = () => { + this.forceUpdate(); + }; + + componentDidMount() { + this.props.request.on("change", this._onRequestChange); + } + + componentWillUnmount() { + this.props.request.off("change", this._onRequestChange); + } +} From 0f415038beccd1d730ca6ecceb1275df0809fa2c Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 18 Dec 2019 17:26:54 +0000 Subject: [PATCH 03/25] actually start verify process in verification panel temporary code, as ux is not complete --- .../views/right_panel/VerificationPanel.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/components/views/right_panel/VerificationPanel.js b/src/components/views/right_panel/VerificationPanel.js index 3546e81bf3..f73693a0ea 100644 --- a/src/components/views/right_panel/VerificationPanel.js +++ b/src/components/views/right_panel/VerificationPanel.js @@ -22,6 +22,7 @@ export default class VerificationPanel extends React.PureComponent { constructor(props) { super(props); this.state = {}; + this._hasVerifier = !!props.request.verifier; } render() { @@ -53,12 +54,10 @@ export default class VerificationPanel extends React.PureComponent { _startSAS = async () => { const verifier = this.props.request.beginKeyVerification(verificationMethods.SAS); - verifier.on('show_sas', this._onVerifierShowSas); try { - await this._verifier.verify(); + await verifier.verify(); } finally { this.setState({sasEvent: null}); - verifier.removeListener('show_sas', this._onVerifierShowSas); } }; @@ -75,6 +74,18 @@ export default class VerificationPanel extends React.PureComponent { }; _onRequestChange = () => { + const {request} = this.props; + if (!this._hasVerifier && !!request.verifier) { + request.verifier.on('show_sas', this._onVerifierShowSas); + try { + request.verifier.verify(); + } catch (err) { + console.error("error verify", err); + } + } else if (this._hasVerifier && !request.verifier) { + request.verifier.removeListener('show_sas', this._onVerifierShowSas); + } + this._hasVerifier = !!request.verifier; this.forceUpdate(); }; From 9e4b65de6a9f4ed0595a67e5bfa6d8166678c8a9 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 19 Dec 2019 16:08:11 +0000 Subject: [PATCH 04/25] fixup after rebase --- src/components/views/messages/MKeyVerificationRequest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/messages/MKeyVerificationRequest.js b/src/components/views/messages/MKeyVerificationRequest.js index f31a823ac9..c851cd30b5 100644 --- a/src/components/views/messages/MKeyVerificationRequest.js +++ b/src/components/views/messages/MKeyVerificationRequest.js @@ -56,7 +56,7 @@ export default class MKeyVerificationRequest extends React.Component { dis.dispatch({ action: "set_right_panel_phase", phase: RIGHT_PANEL_PHASES.EncryptionPanel, - verificationRequest: request, + refireParams: {verificationRequest: request}, }); } catch (err) { console.error(err.message); From 2c28fa0568b196fd7e6147d12883a540761e3153 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 19 Dec 2019 16:08:53 +0000 Subject: [PATCH 05/25] use verif in right panel from "verify" button --- .../views/right_panel/EncryptionInfo.js | 4 ++-- .../views/right_panel/EncryptionPanel.js | 7 +++++++ src/components/views/right_panel/UserInfo.js | 15 ++++++++++++--- .../views/right_panel/VerificationPanel.js | 4 +++- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/components/views/right_panel/EncryptionInfo.js b/src/components/views/right_panel/EncryptionInfo.js index 88aa3f9a6c..f10d0b3499 100644 --- a/src/components/views/right_panel/EncryptionInfo.js +++ b/src/components/views/right_panel/EncryptionInfo.js @@ -21,9 +21,9 @@ export default class EncryptionInfo extends React.PureComponent { render() { const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); return (
-

End-to-end encryption is great!

+

End-to-end encryption is great! You should try it.

- Start verification + Start verification
); } diff --git a/src/components/views/right_panel/EncryptionPanel.js b/src/components/views/right_panel/EncryptionPanel.js index c37d4f2b61..9b32dba30e 100644 --- a/src/components/views/right_panel/EncryptionPanel.js +++ b/src/components/views/right_panel/EncryptionPanel.js @@ -20,6 +20,11 @@ import VerificationPanel from "./VerificationPanel"; import MatrixClientPeg from "../../../MatrixClientPeg"; export default class EncryptionPanel extends React.PureComponent { + constructor(props) { + super(props); + this.state = {}; + } + render() { const request = this.props.verificationRequest || this.state.verificationRequest; const {member} = this.props; @@ -27,6 +32,8 @@ export default class EncryptionPanel extends React.PureComponent { return ; } else if (member) { return ; + } else { + return

Not a member nor request, not sure what to render

; } } diff --git a/src/components/views/right_panel/UserInfo.js b/src/components/views/right_panel/UserInfo.js index 208c5e8906..542e010edd 100644 --- a/src/components/views/right_panel/UserInfo.js +++ b/src/components/views/right_panel/UserInfo.js @@ -40,6 +40,7 @@ import E2EIcon from "../rooms/E2EIcon"; import {useEventEmitter} from "../../../hooks/useEventEmitter"; import {textualPowerLevel} from '../../../Roles'; import MatrixClientContext from "../../../contexts/MatrixClientContext"; +import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases"; const _disambiguateDevices = (devices) => { const names = Object.create(null); @@ -128,6 +129,14 @@ function verifyDevice(userId, device) { }, null, /* priority = */ false, /* static = */ true); } +function verifyUser(user) { + dis.dispatch({ + action: "set_right_panel_phase", + phase: RIGHT_PANEL_PHASES.EncryptionPanel, + refireParams: {member: user}, + }); +} + function DeviceItem({userId, device}) { const cli = useContext(MatrixClientContext); const deviceTrust = cli.checkDeviceTrust(userId, device.deviceId); @@ -1296,7 +1305,7 @@ const UserInfo = ({user, groupId, roomId, onClose}) => { const userVerified = cli.checkUserTrust(user.userId).isVerified(); let verifyButton; if (!userVerified) { - verifyButton = verifyDevice(user.userId, null)}> + verifyButton = verifyUser(user)}> {_t("Verify")} ; } @@ -1305,7 +1314,7 @@ const UserInfo = ({user, groupId, roomId, onClose}) => {

{ _t("Security") }

{ text }

- {verifyButton} + { verifyButton } { devicesSection }
); @@ -1323,7 +1332,7 @@ const UserInfo = ({user, groupId, roomId, onClose}) => {
-
+

{ e2eIcon } { displayName } diff --git a/src/components/views/right_panel/VerificationPanel.js b/src/components/views/right_panel/VerificationPanel.js index f73693a0ea..e1f36cbc0f 100644 --- a/src/components/views/right_panel/VerificationPanel.js +++ b/src/components/views/right_panel/VerificationPanel.js @@ -33,7 +33,7 @@ export default class VerificationPanel extends React.PureComponent { if (request.requested) { return (

Waiting for {request.otherUserId} to accept ...

); } else if (request.ready) { - return (

{request.otherUserId} is ready, start Verify by emoji

); + return (

{request.otherUserId} is ready, start Verify by emoji

); } else if (request.started) { if (this.state.sasEvent) { const VerificationShowSas = sdk.getComponent('views.verification.VerificationShowSas'); @@ -56,6 +56,8 @@ export default class VerificationPanel extends React.PureComponent { const verifier = this.props.request.beginKeyVerification(verificationMethods.SAS); try { await verifier.verify(); + } catch (err) { + console.error(err); } finally { this.setState({sasEvent: null}); } From 41cf18701fe45c502fcb1d30bcd98107db56f5bb Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 19 Dec 2019 16:09:08 +0000 Subject: [PATCH 06/25] remove now-unused import --- src/components/structures/MatrixChat.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index e093e9dff9..88448311d2 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -62,7 +62,6 @@ import { countRoomsWithNotif } from '../../RoomNotifs'; import { ThemeWatcher } from "../../theme"; import { storeRoomAliasInCache } from '../../RoomAliasCache'; import { defer } from "../../utils/promise"; -import KeyVerificationStateObserver from '../../utils/KeyVerificationStateObserver'; /** constants for MatrixChat.state.view */ const VIEWS = { From b36df7330061d28c31c20ef4934e3d945a166af7 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 19 Dec 2019 16:09:19 +0000 Subject: [PATCH 07/25] fix for ref refactor pr --- src/components/structures/TimelinePanel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index 9d929d313b..53ae240653 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -1136,7 +1136,7 @@ const TimelinePanel = createReactClass({ const allowPartial = opts.allowPartial || false; const messagePanel = this._messagePanel.current; - if (messagePanel === undefined) return null; + if (!messagePanel) return null; const EventTile = sdk.getComponent('rooms.EventTile'); From db17321777ee7975cec10362cee11329482fd568 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 19 Dec 2019 16:28:23 +0000 Subject: [PATCH 08/25] await verify so errors (like cancellation) are caught --- src/components/views/right_panel/VerificationPanel.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/views/right_panel/VerificationPanel.js b/src/components/views/right_panel/VerificationPanel.js index e1f36cbc0f..2879916f37 100644 --- a/src/components/views/right_panel/VerificationPanel.js +++ b/src/components/views/right_panel/VerificationPanel.js @@ -75,12 +75,14 @@ export default class VerificationPanel extends React.PureComponent { this.setState({sasEvent}); }; - _onRequestChange = () => { + _onRequestChange = async () => { const {request} = this.props; if (!this._hasVerifier && !!request.verifier) { request.verifier.on('show_sas', this._onVerifierShowSas); try { - request.verifier.verify(); + // on the requester side, this is also awaited in _startSAS, + // but that's ok as verify should return the same promise. + await request.verifier.verify(); } catch (err) { console.error("error verify", err); } From b866c16071e81c5ed1c35e1cf5f10b80db4c2dc5 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 19 Dec 2019 16:28:44 +0000 Subject: [PATCH 09/25] show cancellation --- src/components/views/right_panel/VerificationPanel.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/views/right_panel/VerificationPanel.js b/src/components/views/right_panel/VerificationPanel.js index 2879916f37..c3fce4314b 100644 --- a/src/components/views/right_panel/VerificationPanel.js +++ b/src/components/views/right_panel/VerificationPanel.js @@ -49,6 +49,8 @@ export default class VerificationPanel extends React.PureComponent { } } else if (request.done) { return

verified {request.otherUserId}!!

; + } else if (request.cancelled) { + return

cancelled by {request.cancellingUserId}!

; } } From b80bfd04b221a356232fd590619118db85ed1a1b Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 19 Dec 2019 16:43:10 +0000 Subject: [PATCH 10/25] make sure to have roomId of DM when starting verif. req. --- .../views/dialogs/DeviceVerifyDialog.js | 21 ++-------------- .../views/right_panel/EncryptionPanel.js | 6 ++--- src/createRoom.js | 25 ++++++++++++++++--- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/components/views/dialogs/DeviceVerifyDialog.js b/src/components/views/dialogs/DeviceVerifyDialog.js index ca05d74964..fcb5089785 100644 --- a/src/components/views/dialogs/DeviceVerifyDialog.js +++ b/src/components/views/dialogs/DeviceVerifyDialog.js @@ -24,8 +24,7 @@ import sdk from '../../../index'; import * as FormattingUtils from '../../../utils/FormattingUtils'; import { _t } from '../../../languageHandler'; import {verificationMethods} from 'matrix-js-sdk/lib/crypto'; -import DMRoomMap from '../../../utils/DMRoomMap'; -import createRoom from "../../../createRoom"; +import {ensureDMExists} from "../../../createRoom"; import dis from "../../../dispatcher"; import SettingsStore from '../../../settings/SettingsStore'; @@ -322,23 +321,7 @@ export default class DeviceVerifyDialog extends React.Component { } async function ensureDMExistsAndOpen(userId) { - const client = MatrixClientPeg.get(); - const roomIds = DMRoomMap.shared().getDMRoomsForUserId(userId); - const rooms = roomIds.map(id => client.getRoom(id)); - const suitableDMRooms = rooms.filter(r => { - if (r && r.getMyMembership() === "join") { - const member = r.getMember(userId); - return member && (member.membership === "invite" || member.membership === "join"); - } - return false; - }); - let roomId; - if (suitableDMRooms.length) { - const room = suitableDMRooms[0]; - roomId = room.roomId; - } else { - roomId = await createRoom({dmUserId: userId, spinner: false, andView: false}); - } + const roomId = ensureDMExists(MatrixClientPeg.get(), userId); // don't use andView and spinner in createRoom, together, they cause this dialog to close and reopen, // we causes us to loose the verifier and restart, and we end up having two verification requests dis.dispatch({ diff --git a/src/components/views/right_panel/EncryptionPanel.js b/src/components/views/right_panel/EncryptionPanel.js index 9b32dba30e..7992933864 100644 --- a/src/components/views/right_panel/EncryptionPanel.js +++ b/src/components/views/right_panel/EncryptionPanel.js @@ -18,6 +18,7 @@ import React from 'react'; import EncryptionInfo from "./EncryptionInfo"; import VerificationPanel from "./VerificationPanel"; import MatrixClientPeg from "../../../MatrixClientPeg"; +import {ensureDMExists} from "../../../createRoom"; export default class EncryptionPanel extends React.PureComponent { constructor(props) { @@ -40,9 +41,8 @@ export default class EncryptionPanel extends React.PureComponent { _onStartVerification = async () => { const client = MatrixClientPeg.get(); const {member} = this.props; - // TODO: get the room id of the DM here? - // will this panel be shown in non-DM rooms? - const verificationRequest = await client.requestVerificationDM(member.userId, member.roomId); + const roomId = await ensureDMExists(client, member.userId); + const verificationRequest = await client.requestVerificationDM(member.userId, roomId); this.setState({verificationRequest}); }; } diff --git a/src/createRoom.js b/src/createRoom.js index 0ee90beba8..766c9b0c8f 100644 --- a/src/createRoom.js +++ b/src/createRoom.js @@ -1,5 +1,6 @@ /* Copyright 2015, 2016 OpenMarket Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,7 +21,7 @@ import sdk from './index'; import { _t } from './languageHandler'; import dis from "./dispatcher"; import * as Rooms from "./Rooms"; - +import DMRoomMap from "./utils/DMRoomMap"; import {getAddressType} from "./UserAddress"; /** @@ -35,7 +36,7 @@ import {getAddressType} from "./UserAddress"; * @returns {Promise} which resolves to the room id, or null if the * action was aborted or failed. */ -function createRoom(opts) { +export default function createRoom(opts) { opts = opts || {}; if (opts.spinner === undefined) opts.spinner = true; @@ -140,4 +141,22 @@ function createRoom(opts) { }); } -module.exports = createRoom; +export async function ensureDMExists(client, userId) { + const roomIds = DMRoomMap.shared().getDMRoomsForUserId(userId); + const rooms = roomIds.map(id => client.getRoom(id)); + const suitableDMRooms = rooms.filter(r => { + if (r && r.getMyMembership() === "join") { + const member = r.getMember(userId); + return member && (member.membership === "invite" || member.membership === "join"); + } + return false; + }); + let roomId; + if (suitableDMRooms.length) { + const room = suitableDMRooms[0]; + roomId = room.roomId; + } else { + roomId = await createRoom({dmUserId: userId, spinner: false, andView: false}); + } + return roomId; +} From 8c5f3d6d9428e6d971604cc95782b4b75988535c Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 19 Dec 2019 16:43:55 +0000 Subject: [PATCH 11/25] show devices and unverify action also in unencrypted rooms --- src/components/views/right_panel/UserInfo.js | 24 ++++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/components/views/right_panel/UserInfo.js b/src/components/views/right_panel/UserInfo.js index 542e010edd..0f248d65d4 100644 --- a/src/components/views/right_panel/UserInfo.js +++ b/src/components/views/right_panel/UserInfo.js @@ -1249,15 +1249,13 @@ const UserInfo = ({user, groupId, roomId, onClose}) => { setDevices(null); } } - if (isRoomEncrypted) { - _downloadDeviceList(); - } + _downloadDeviceList(); // Handle being unmounted return () => { cancelled = true; }; - }, [cli, user.userId, isRoomEncrypted]); + }, [cli, user.userId]); // Listen to changes useEffect(() => { @@ -1273,18 +1271,13 @@ const UserInfo = ({user, groupId, roomId, onClose}) => { }); } }; - - if (isRoomEncrypted) { - cli.on("deviceVerificationChanged", onDeviceVerificationChanged); - } + cli.on("deviceVerificationChanged", onDeviceVerificationChanged); // Handle being unmounted return () => { cancel = true; - if (isRoomEncrypted) { - cli.removeListener("deviceVerificationChanged", onDeviceVerificationChanged); - } + cli.removeListener("deviceVerificationChanged", onDeviceVerificationChanged); }; - }, [cli, user.userId, isRoomEncrypted]); + }, [cli, user.userId]); let text; if (!isRoomEncrypted) { @@ -1299,9 +1292,6 @@ const UserInfo = ({user, groupId, roomId, onClose}) => { text = _t("Messages in this room are end-to-end encrypted."); } - const devicesSection = isRoomEncrypted ? - () : null; - const userVerified = cli.checkUserTrust(user.userId).isVerified(); let verifyButton; if (!userVerified) { @@ -1310,6 +1300,10 @@ const UserInfo = ({user, groupId, roomId, onClose}) => { ; } + const devicesSection = ; + const securitySection = (

{ _t("Security") }

From d57e76f3e271601a0b7c6367377c29eeaf121145 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 19 Dec 2019 16:48:15 +0000 Subject: [PATCH 12/25] hide verify button for own member --- src/components/views/right_panel/UserInfo.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/views/right_panel/UserInfo.js b/src/components/views/right_panel/UserInfo.js index 0f248d65d4..a69390d932 100644 --- a/src/components/views/right_panel/UserInfo.js +++ b/src/components/views/right_panel/UserInfo.js @@ -1293,8 +1293,9 @@ const UserInfo = ({user, groupId, roomId, onClose}) => { } const userVerified = cli.checkUserTrust(user.userId).isVerified(); + const isMe = user.userId === cli.getUserId(); let verifyButton; - if (!userVerified) { + if (!userVerified && !isMe) { verifyButton = verifyUser(user)}> {_t("Verify")} ; From f4a276c052601abf3d5b3bb977894a9335e75ee8 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 20 Dec 2019 11:24:10 +0000 Subject: [PATCH 13/25] port MVerificationConclusion to use VerificationRequest --- .../messages/MKeyVerificationConclusion.js | 90 +++++-------------- 1 file changed, 24 insertions(+), 66 deletions(-) diff --git a/src/components/views/messages/MKeyVerificationConclusion.js b/src/components/views/messages/MKeyVerificationConclusion.js index 0bd8e2d3d8..fb83eda803 100644 --- a/src/components/views/messages/MKeyVerificationConclusion.js +++ b/src/components/views/messages/MKeyVerificationConclusion.js @@ -19,102 +19,60 @@ import classNames from 'classnames'; import PropTypes from 'prop-types'; import MatrixClientPeg from '../../../MatrixClientPeg'; import { _t } from '../../../languageHandler'; -import KeyVerificationStateObserver, {getNameForEventRoom, userLabelForEventRoom} +import {getNameForEventRoom, userLabelForEventRoom} from '../../../utils/KeyVerificationStateObserver'; export default class MKeyVerificationConclusion extends React.Component { constructor(props) { super(props); - this.keyVerificationState = null; - this.state = { - done: false, - cancelled: false, - otherPartyUserId: null, - cancelPartyUserId: null, - }; - const rel = this.props.mxEvent.getRelation(); - if (rel) { - const client = MatrixClientPeg.get(); - const room = client.getRoom(this.props.mxEvent.getRoomId()); - const requestEvent = room.findEventById(rel.event_id); - if (requestEvent) { - this._createStateObserver(requestEvent, client); - this.state = this._copyState(); - } else { - const findEvent = event => { - if (event.getId() === rel.event_id) { - this._createStateObserver(event, client); - this.setState(this._copyState()); - room.removeListener("Room.timeline", findEvent); - } - }; - room.on("Room.timeline", findEvent); - } - } - } - - _createStateObserver(requestEvent, client) { - this.keyVerificationState = new KeyVerificationStateObserver(requestEvent, client, () => { - this.setState(this._copyState()); - }); - } - - _copyState() { - const {done, cancelled, otherPartyUserId, cancelPartyUserId} = this.keyVerificationState; - return {done, cancelled, otherPartyUserId, cancelPartyUserId}; } componentDidMount() { - if (this.keyVerificationState) { - this.keyVerificationState.attach(); + const request = this.props.mxEvent.verificationRequest; + if (request) { + request.on("change", this._onRequestChanged); } } componentWillUnmount() { - if (this.keyVerificationState) { - this.keyVerificationState.detach(); + const request = this.props.mxEvent.verificationRequest; + if (request) { + request.off("change", this._onRequestChanged); } } - _getName(userId) { - const roomId = this.props.mxEvent.getRoomId(); - const client = MatrixClientPeg.get(); - const room = client.getRoom(roomId); - const member = room.getMember(userId); - return member ? member.name : userId; - } - - _userLabel(userId) { - const name = this._getName(userId); - if (name !== userId) { - return _t("%(name)s (%(userId)s)", {name, userId}); - } else { - return userId; - } - } + _onRequestChanged = () => { + this.forceUpdate(); + }; render() { const {mxEvent} = this.props; + const request = mxEvent.verificationRequest; const client = MatrixClientPeg.get(); const myUserId = client.getUserId(); + + if (!request) { + return

This is a verification conclusion tile without a request.

; + } + let title; - if (this.state.done) { - title = _t("You verified %(name)s", {name: getNameForEventRoom(this.state.otherPartyUserId, mxEvent)}); - } else if (this.state.cancelled) { + if (request.done) { + title = _t("You verified %(name)s", {name: getNameForEventRoom(request.otherUserId, mxEvent)}); + } else if (request.cancelled) { if (mxEvent.getSender() === myUserId) { title = _t("You cancelled verifying %(name)s", - {name: getNameForEventRoom(this.state.otherPartyUserId, mxEvent)}); - } else if (mxEvent.getSender() === this.state.otherPartyUserId) { + {name: getNameForEventRoom(request.otherUserId, mxEvent)}); + } else if (mxEvent.getSender() === request.otherUserId) { title = _t("%(name)s cancelled verifying", - {name: getNameForEventRoom(this.state.otherPartyUserId, mxEvent)}); + {name: getNameForEventRoom(request.otherUserId, mxEvent)}); } } if (title) { - const subtitle = userLabelForEventRoom(this.state.otherPartyUserId, mxEvent); + const subtitle = userLabelForEventRoom(request.otherUserId, mxEvent); const classes = classNames("mx_EventTile_bubble", "mx_KeyVerification", "mx_KeyVerification_icon", { - mx_KeyVerification_icon_verified: this.state.done, + mx_KeyVerification_icon_verified: request.done, }); return (
{title}
From 1aebc9579330d6b5e1793d173f77abf3013a413a Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 20 Dec 2019 11:24:27 +0000 Subject: [PATCH 14/25] slightly better copy --- src/components/views/messages/MKeyVerificationRequest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/messages/MKeyVerificationRequest.js b/src/components/views/messages/MKeyVerificationRequest.js index c851cd30b5..0aa2188116 100644 --- a/src/components/views/messages/MKeyVerificationRequest.js +++ b/src/components/views/messages/MKeyVerificationRequest.js @@ -104,7 +104,7 @@ export default class MKeyVerificationRequest extends React.Component { let stateNode; if (!request) { - return

This is an invalid request, ho ho ho!

; + return

This is a verification request tile without a request.

; } if (request.ready || request.started || request.cancelled) { From dd633bd8fe0284eb4ceb6cc1f4c190a75d4d3593 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 20 Dec 2019 12:02:37 +0000 Subject: [PATCH 15/25] port toast to use VerificationRequest and open right panel, not dialog --- .../views/toasts/VerificationRequestToast.js | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/components/views/toasts/VerificationRequestToast.js b/src/components/views/toasts/VerificationRequestToast.js index 7e043f4d83..30478ce281 100644 --- a/src/components/views/toasts/VerificationRequestToast.js +++ b/src/components/views/toasts/VerificationRequestToast.js @@ -18,10 +18,9 @@ import React from 'react'; import PropTypes from 'prop-types'; import sdk from "../../../index"; import { _t } from '../../../languageHandler'; -import Modal from "../../../Modal"; import MatrixClientPeg from '../../../MatrixClientPeg'; -import {verificationMethods} from 'matrix-js-sdk/lib/crypto'; -import KeyVerificationStateObserver, {userLabelForEventRoom} from "../../../utils/KeyVerificationStateObserver"; +import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases"; +import {userLabelForEventRoom} from "../../../utils/KeyVerificationStateObserver"; import dis from "../../../dispatcher"; export default class VerificationRequestToast extends React.PureComponent { @@ -33,16 +32,9 @@ export default class VerificationRequestToast extends React.PureComponent { const remaining = Math.max(0, timeout - age); const counter = Math.ceil(remaining / 1000); this.state = {counter}; - if (this.props.requestObserver) { - this.props.requestObserver.setCallback(this._checkRequestIsPending); - } } componentDidMount() { - if (this.props.requestObserver) { - this.props.requestObserver.attach(); - this._checkRequestIsPending(); - } this._intervalHandle = setInterval(() => { let {counter} = this.state; counter -= 1; @@ -52,20 +44,22 @@ export default class VerificationRequestToast extends React.PureComponent { this.setState({counter}); } }, 1000); + const {request} = this.props; + request.on("change", this._checkRequestIsPending); } componentWillUnmount() { clearInterval(this._intervalHandle); - if (this.props.requestObserver) { - this.props.requestObserver.detach(); - } + const {request} = this.props; + request.off("change", this._checkRequestIsPending); } _checkRequestIsPending = () => { - if (!this.props.requestObserver.pending) { + const {request} = this.props; + if (request.ready || request.started || request.done || request.cancelled) { this.props.dismiss(); } - } + }; cancel = () => { this.props.dismiss(); @@ -76,9 +70,10 @@ export default class VerificationRequestToast extends React.PureComponent { } } - accept = () => { + accept = async () => { this.props.dismiss(); - const {event} = this.props.request; + const {request} = this.props; + const {event} = request; // no room id for to_device requests if (event.getRoomId()) { dis.dispatch({ @@ -87,18 +82,24 @@ export default class VerificationRequestToast extends React.PureComponent { should_peek: false, }); } - - const verifier = this.props.request.beginKeyVerification(verificationMethods.SAS); - const IncomingSasDialog = sdk.getComponent('views.dialogs.IncomingSasDialog'); - Modal.createTrackedDialog('Incoming Verification', '', IncomingSasDialog, { - verifier, - }, null, /* priority = */ false, /* static = */ true); + try { + await request.accept(); + dis.dispatch({action: "show_right_panel"}); + dis.dispatch({ + action: "set_right_panel_phase", + phase: RIGHT_PANEL_PHASES.EncryptionPanel, + refireParams: {verificationRequest: request}, + }); + } catch (err) { + console.error(err.message); + } }; render() { const FormButton = sdk.getComponent("elements.FormButton"); - const {event} = this.props.request; - const userId = event.getSender(); + const {request} = this.props; + const {event} = request; + const userId = request.otherUserId; let nameLabel = event.getRoomId() ? userLabelForEventRoom(userId, event) : userId; // for legacy to_device verification requests if (nameLabel === userId) { @@ -121,5 +122,4 @@ export default class VerificationRequestToast extends React.PureComponent { VerificationRequestToast.propTypes = { dismiss: PropTypes.func.isRequired, request: PropTypes.object.isRequired, - requestObserver: PropTypes.instanceOf(KeyVerificationStateObserver), }; From 2dd1e93d84dbf5a0b20de70d09ecb8cb8fece4f2 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 20 Dec 2019 21:30:18 +0100 Subject: [PATCH 16/25] show message after clicking "they match" --- src/components/views/right_panel/VerificationPanel.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/views/right_panel/VerificationPanel.js b/src/components/views/right_panel/VerificationPanel.js index c3fce4314b..ff7a356e70 100644 --- a/src/components/views/right_panel/VerificationPanel.js +++ b/src/components/views/right_panel/VerificationPanel.js @@ -35,7 +35,9 @@ export default class VerificationPanel extends React.PureComponent { } else if (request.ready) { return (

{request.otherUserId} is ready, start Verify by emoji

); } else if (request.started) { - if (this.state.sasEvent) { + if (this.state.sasWaitingForOtherParty) { + return

Waiting for {request.otherUserId} to confirm ...

; + } else if (this.state.sasEvent) { const VerificationShowSas = sdk.getComponent('views.verification.VerificationShowSas'); return (
{ + this.setState({sasWaitingForOtherParty: true}); this.state.sasEvent.confirm(); }; From de1c3e2f83b7f387adbb3432684545eb4882868d Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 20 Dec 2019 21:30:47 +0100 Subject: [PATCH 17/25] use same style as UserInfo for sections and paragraphs --- src/components/views/right_panel/EncryptionInfo.js | 13 +++++++------ .../views/right_panel/VerificationPanel.js | 8 ++++++++ src/i18n/strings/en_EN.json | 4 ++++ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/components/views/right_panel/EncryptionInfo.js b/src/components/views/right_panel/EncryptionInfo.js index f10d0b3499..49f4fc5da3 100644 --- a/src/components/views/right_panel/EncryptionInfo.js +++ b/src/components/views/right_panel/EncryptionInfo.js @@ -16,15 +16,16 @@ limitations under the License. import React from 'react'; import sdk from "../../.."; +import {_t} from "../../../languageHandler"; export default class EncryptionInfo extends React.PureComponent { render() { const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); - return (
-

End-to-end encryption is great! You should try it.

-
- Start verification -
-
); + return (
+

{_t("Verify User")}

+

{_t("For extra security, verify this user by checking a one-time code on both of your devices.")}

+

{_t("For maximum security, do this in person.")}

+ {_t("Start Verification")} +
); } } diff --git a/src/components/views/right_panel/VerificationPanel.js b/src/components/views/right_panel/VerificationPanel.js index ff7a356e70..9e5a448239 100644 --- a/src/components/views/right_panel/VerificationPanel.js +++ b/src/components/views/right_panel/VerificationPanel.js @@ -26,6 +26,14 @@ export default class VerificationPanel extends React.PureComponent { } render() { + return
+
+ { this.renderStatus() } +
+
; + } + + renderStatus() { const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); const Spinner = sdk.getComponent('elements.Spinner'); const {request} = this.props; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 6979759cd2..59c1684076 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1106,6 +1106,10 @@ "URL previews are disabled by default for participants in this room.": "URL previews are disabled by default for participants in this room.", "In encrypted rooms, like this one, URL previews are disabled by default to ensure that your homeserver (where the previews are generated) cannot gather information about links you see in this room.": "In encrypted rooms, like this one, URL previews are disabled by default to ensure that your homeserver (where the previews are generated) cannot gather information about links you see in this room.", "When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.": "When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.", + "Verify User": "Verify User", + "For extra security, verify this user by checking a one-time code on both of your devices.": "For extra security, verify this user by checking a one-time code on both of your devices.", + "For maximum security, do this in person.": "For maximum security, do this in person.", + "Start Verification": "Start Verification", "Members": "Members", "Files": "Files", "Trusted": "Trusted", From 075e42c076d28ef6f252e183571ea066e3f06574 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 20 Dec 2019 21:31:07 +0100 Subject: [PATCH 18/25] use transactionId as key for react --- src/components/structures/MatrixChat.js | 2 +- src/components/views/right_panel/EncryptionPanel.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 88448311d2..0449c2b4fd 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -1463,7 +1463,7 @@ export default createReactClass({ dis.dispatch({ action: "show_toast", toast: { - key: request.event.getId(), + key: request.channel.transactionId, title: _t("Verification Request"), icon: "verification", props: {request}, diff --git a/src/components/views/right_panel/EncryptionPanel.js b/src/components/views/right_panel/EncryptionPanel.js index 7992933864..e3f3b86940 100644 --- a/src/components/views/right_panel/EncryptionPanel.js +++ b/src/components/views/right_panel/EncryptionPanel.js @@ -30,7 +30,7 @@ export default class EncryptionPanel extends React.PureComponent { const request = this.props.verificationRequest || this.state.verificationRequest; const {member} = this.props; if (request) { - return ; + return ; } else if (member) { return ; } else { From b49c471f057dd51786ed2bbb297a1d37e42954ac Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 20 Dec 2019 21:31:36 +0100 Subject: [PATCH 19/25] render empty tiles when no request --- .../views/messages/MKeyVerificationConclusion.js | 10 +++++++--- .../views/messages/MKeyVerificationRequest.js | 7 ++++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/components/views/messages/MKeyVerificationConclusion.js b/src/components/views/messages/MKeyVerificationConclusion.js index fb83eda803..6dce9dcd48 100644 --- a/src/components/views/messages/MKeyVerificationConclusion.js +++ b/src/components/views/messages/MKeyVerificationConclusion.js @@ -48,12 +48,14 @@ export default class MKeyVerificationConclusion extends React.Component { render() { const {mxEvent} = this.props; const request = mxEvent.verificationRequest; + + if (!request) { + return null; + } + const client = MatrixClientPeg.get(); const myUserId = client.getUserId(); - if (!request) { - return

This is a verification conclusion tile without a request.

; - } let title; @@ -67,6 +69,8 @@ export default class MKeyVerificationConclusion extends React.Component { title = _t("%(name)s cancelled verifying", {name: getNameForEventRoom(request.otherUserId, mxEvent)}); } + } else { + title = `request conclusion tile with phase ${request.phase}`; } if (title) { diff --git a/src/components/views/messages/MKeyVerificationRequest.js b/src/components/views/messages/MKeyVerificationRequest.js index 0aa2188116..ea99435d85 100644 --- a/src/components/views/messages/MKeyVerificationRequest.js +++ b/src/components/views/messages/MKeyVerificationRequest.js @@ -99,13 +99,14 @@ export default class MKeyVerificationRequest extends React.Component { const {mxEvent} = this.props; const request = mxEvent.verificationRequest; + if (!request) { + return null; + } + let title; let subtitle; let stateNode; - if (!request) { - return

This is a verification request tile without a request.

; - } if (request.ready || request.started || request.cancelled) { let stateLabel; From 52c7c5b8379dbb70912a15353993e2703d774949 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 20 Dec 2019 21:31:53 +0100 Subject: [PATCH 20/25] render done tile as accepted --- src/components/views/messages/MKeyVerificationRequest.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/views/messages/MKeyVerificationRequest.js b/src/components/views/messages/MKeyVerificationRequest.js index ea99435d85..ebdfa7c636 100644 --- a/src/components/views/messages/MKeyVerificationRequest.js +++ b/src/components/views/messages/MKeyVerificationRequest.js @@ -108,11 +108,12 @@ export default class MKeyVerificationRequest extends React.Component { let stateNode; - if (request.ready || request.started || request.cancelled) { + const accepted = request.ready || request.started || request.done; + if (accepted || request.cancelled) { let stateLabel; - if (request.ready || request.started) { + if (accepted) { stateLabel = this._acceptedLabel(request.receivingUserId); - } else if (request.cancelled) { + } else { stateLabel = this._cancelledLabel(request.cancellingUserId); } stateNode = (
{stateLabel}
); From 3b9c5c0a27aa7bdbc3fe8a2dbbe74ea4ef9ae9af Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 20 Dec 2019 21:32:11 +0100 Subject: [PATCH 21/25] remove unused code --- src/utils/KeyVerificationStateObserver.js | 148 ---------------------- 1 file changed, 148 deletions(-) diff --git a/src/utils/KeyVerificationStateObserver.js b/src/utils/KeyVerificationStateObserver.js index 7e33c29214..ef69a6b2f0 100644 --- a/src/utils/KeyVerificationStateObserver.js +++ b/src/utils/KeyVerificationStateObserver.js @@ -17,154 +17,6 @@ limitations under the License. import MatrixClientPeg from '../MatrixClientPeg'; import { _t } from '../languageHandler'; -const SUB_EVENT_TYPES_OF_INTEREST = ["start", "cancel", "done"]; - -export default class KeyVerificationStateObserver { - constructor(requestEvent, client, updateCallback) { - this._requestEvent = requestEvent; - this._client = client; - this._updateCallback = updateCallback; - this.accepted = false; - this.done = false; - this.cancelled = false; - this._updateVerificationState(); - } - - get concluded() { - return this.accepted || this.done || this.cancelled; - } - - get pending() { - return !this.concluded; - } - - setCallback(callback) { - this._updateCallback = callback; - } - - attach() { - this._requestEvent.on("Event.relationsCreated", this._onRelationsCreated); - for (const phaseName of SUB_EVENT_TYPES_OF_INTEREST) { - this._tryListenOnRelationsForType(`m.key.verification.${phaseName}`); - } - } - - detach() { - const roomId = this._requestEvent.getRoomId(); - const room = this._client.getRoom(roomId); - - for (const phaseName of SUB_EVENT_TYPES_OF_INTEREST) { - const relations = room.getUnfilteredTimelineSet() - .getRelationsForEvent(this._requestEvent.getId(), "m.reference", `m.key.verification.${phaseName}`); - if (relations) { - relations.removeListener("Relations.add", this._onRelationsUpdated); - relations.removeListener("Relations.remove", this._onRelationsUpdated); - relations.removeListener("Relations.redaction", this._onRelationsUpdated); - } - } - this._requestEvent.removeListener("Event.relationsCreated", this._onRelationsCreated); - } - - _onRelationsCreated = (relationType, eventType) => { - if (relationType !== "m.reference") { - return; - } - if ( - eventType !== "m.key.verification.start" && - eventType !== "m.key.verification.cancel" && - eventType !== "m.key.verification.done" - ) { - return; - } - this._tryListenOnRelationsForType(eventType); - this._updateVerificationState(); - this._updateCallback(); - }; - - _tryListenOnRelationsForType(eventType) { - const roomId = this._requestEvent.getRoomId(); - const room = this._client.getRoom(roomId); - const relations = room.getUnfilteredTimelineSet() - .getRelationsForEvent(this._requestEvent.getId(), "m.reference", eventType); - if (relations) { - relations.on("Relations.add", this._onRelationsUpdated); - relations.on("Relations.remove", this._onRelationsUpdated); - relations.on("Relations.redaction", this._onRelationsUpdated); - } - } - - _onRelationsUpdated = (event) => { - this._updateVerificationState(); - this._updateCallback && this._updateCallback(); - }; - - _updateVerificationState() { - const roomId = this._requestEvent.getRoomId(); - const room = this._client.getRoom(roomId); - const timelineSet = room.getUnfilteredTimelineSet(); - const fromUserId = this._requestEvent.getSender(); - const content = this._requestEvent.getContent(); - const toUserId = content.to; - - this.cancelled = false; - this.done = false; - this.accepted = false; - this.otherPartyUserId = null; - this.cancelPartyUserId = null; - - const startRelations = timelineSet.getRelationsForEvent( - this._requestEvent.getId(), "m.reference", "m.key.verification.start"); - if (startRelations) { - for (const startEvent of startRelations.getRelations()) { - if (startEvent.getSender() === toUserId) { - this.accepted = true; - } - } - } - - const doneRelations = timelineSet.getRelationsForEvent( - this._requestEvent.getId(), "m.reference", "m.key.verification.done"); - if (doneRelations) { - let senderDone = false; - let receiverDone = false; - for (const doneEvent of doneRelations.getRelations()) { - if (doneEvent.getSender() === toUserId) { - receiverDone = true; - } else if (doneEvent.getSender() === fromUserId) { - senderDone = true; - } - } - if (senderDone && receiverDone) { - this.done = true; - } - } - - if (!this.done) { - const cancelRelations = timelineSet.getRelationsForEvent( - this._requestEvent.getId(), "m.reference", "m.key.verification.cancel"); - - if (cancelRelations) { - let earliestCancelEvent; - for (const cancelEvent of cancelRelations.getRelations()) { - // only accept cancellation from the users involved - if (cancelEvent.getSender() === toUserId || cancelEvent.getSender() === fromUserId) { - this.cancelled = true; - if (!earliestCancelEvent || cancelEvent.getTs() < earliestCancelEvent.getTs()) { - earliestCancelEvent = cancelEvent; - } - } - } - if (earliestCancelEvent) { - this.cancelPartyUserId = earliestCancelEvent.getSender(); - } - } - } - - this.otherPartyUserId = fromUserId === this._client.getUserId() ? toUserId : fromUserId; - console.log("KeyVerificationStateObserver update for txnId", this._requestEvent.getId(), {accepted: this.accepted, cancelled: this.cancelled, done: this.done}); - } -} - export function getNameForEventRoom(userId, mxEvent) { const roomId = mxEvent.getRoomId(); const client = MatrixClientPeg.get(); From 7a88a94936bf3471bf82852a15775f2fca12f016 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 3 Jan 2020 13:40:20 +0100 Subject: [PATCH 22/25] fixes! --- .../messages/MKeyVerificationConclusion.js | 33 +++++++++++++++---- .../views/messages/MKeyVerificationRequest.js | 30 ++++++++++------- .../views/right_panel/EncryptionInfo.js | 2 +- .../views/right_panel/VerificationPanel.js | 4 +-- .../views/toasts/VerificationRequestToast.js | 19 +++-------- 5 files changed, 53 insertions(+), 35 deletions(-) diff --git a/src/components/views/messages/MKeyVerificationConclusion.js b/src/components/views/messages/MKeyVerificationConclusion.js index 6dce9dcd48..90edb2f05d 100644 --- a/src/components/views/messages/MKeyVerificationConclusion.js +++ b/src/components/views/messages/MKeyVerificationConclusion.js @@ -45,32 +45,51 @@ export default class MKeyVerificationConclusion extends React.Component { this.forceUpdate(); }; + _shouldRender(mxEvent, request) { + // normally should not happen + if (!request) { + return false; + } + // .cancel event that was sent after the verification finished, ignore + if (mxEvent.getType() === "m.key.verification.cancel" && !request.cancelled) { + return false; + } + // .done event that was sent after the verification cancelled, ignore + if (mxEvent.getType() === "m.key.verification.done" && !request.done) { + return false; + } + + // request hasn't concluded yet + if (request.pending) { + return false; + } + return true; + } + render() { const {mxEvent} = this.props; const request = mxEvent.verificationRequest; - if (!request) { + if (!this._shouldRender(mxEvent, request)) { return null; } const client = MatrixClientPeg.get(); const myUserId = client.getUserId(); - let title; if (request.done) { title = _t("You verified %(name)s", {name: getNameForEventRoom(request.otherUserId, mxEvent)}); } else if (request.cancelled) { - if (mxEvent.getSender() === myUserId) { + const userId = request.cancellingUserId; + if (userId === myUserId) { title = _t("You cancelled verifying %(name)s", {name: getNameForEventRoom(request.otherUserId, mxEvent)}); - } else if (mxEvent.getSender() === request.otherUserId) { + } else { title = _t("%(name)s cancelled verifying", - {name: getNameForEventRoom(request.otherUserId, mxEvent)}); + {name: getNameForEventRoom(userId, mxEvent)}); } - } else { - title = `request conclusion tile with phase ${request.phase}`; } if (title) { diff --git a/src/components/views/messages/MKeyVerificationRequest.js b/src/components/views/messages/MKeyVerificationRequest.js index ebdfa7c636..1bef3b4469 100644 --- a/src/components/views/messages/MKeyVerificationRequest.js +++ b/src/components/views/messages/MKeyVerificationRequest.js @@ -43,6 +43,16 @@ export default class MKeyVerificationRequest extends React.Component { } } + _openRequest = () => { + const {verificationRequest} = this.props.mxEvent; + dis.dispatch({action: "show_right_panel"}); + dis.dispatch({ + action: "set_right_panel_phase", + phase: RIGHT_PANEL_PHASES.EncryptionPanel, + refireParams: {verificationRequest}, + }); + }; + _onRequestChanged = () => { this.forceUpdate(); }; @@ -52,12 +62,7 @@ export default class MKeyVerificationRequest extends React.Component { if (request) { try { await request.accept(); - dis.dispatch({action: "show_right_panel"}); - dis.dispatch({ - action: "set_right_panel_phase", - phase: RIGHT_PANEL_PHASES.EncryptionPanel, - refireParams: {verificationRequest: request}, - }); + this._openRequest(); } catch (err) { console.error(err.message); } @@ -96,10 +101,13 @@ export default class MKeyVerificationRequest extends React.Component { } render() { + const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); + const FormButton = sdk.getComponent("elements.FormButton"); + const {mxEvent} = this.props; const request = mxEvent.verificationRequest; - if (!request) { + if (!request || request.invalid) { return null; } @@ -107,12 +115,13 @@ export default class MKeyVerificationRequest extends React.Component { let subtitle; let stateNode; - const accepted = request.ready || request.started || request.done; if (accepted || request.cancelled) { let stateLabel; if (accepted) { - stateLabel = this._acceptedLabel(request.receivingUserId); + stateLabel = ( + {this._acceptedLabel(request.receivingUserId)} + ); } else { stateLabel = this._cancelledLabel(request.cancellingUserId); } @@ -124,8 +133,7 @@ export default class MKeyVerificationRequest extends React.Component { _t("%(name)s wants to verify", {name: getNameForEventRoom(request.requestingUserId, mxEvent)})}
); subtitle = (
{ userLabelForEventRoom(request.requestingUserId, mxEvent)}
); - if (request.requested) { - const FormButton = sdk.getComponent("elements.FormButton"); + if (request.requested && !request.observeOnly) { stateNode = (
diff --git a/src/components/views/right_panel/EncryptionInfo.js b/src/components/views/right_panel/EncryptionInfo.js index 49f4fc5da3..3d5de829b7 100644 --- a/src/components/views/right_panel/EncryptionInfo.js +++ b/src/components/views/right_panel/EncryptionInfo.js @@ -21,7 +21,7 @@ import {_t} from "../../../languageHandler"; export default class EncryptionInfo extends React.PureComponent { render() { const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); - return (
+ return (

{_t("Verify User")}

{_t("For extra security, verify this user by checking a one-time code on both of your devices.")}

{_t("For maximum security, do this in person.")}

diff --git a/src/components/views/right_panel/VerificationPanel.js b/src/components/views/right_panel/VerificationPanel.js index 9e5a448239..e25aaabcfa 100644 --- a/src/components/views/right_panel/VerificationPanel.js +++ b/src/components/views/right_panel/VerificationPanel.js @@ -26,8 +26,8 @@ export default class VerificationPanel extends React.PureComponent { } render() { - return
-
+ return
+
{ this.renderStatus() }
; diff --git a/src/components/views/toasts/VerificationRequestToast.js b/src/components/views/toasts/VerificationRequestToast.js index 30478ce281..f616575973 100644 --- a/src/components/views/toasts/VerificationRequestToast.js +++ b/src/components/views/toasts/VerificationRequestToast.js @@ -26,25 +26,16 @@ import dis from "../../../dispatcher"; export default class VerificationRequestToast extends React.PureComponent { constructor(props) { super(props); - const {event, timeout} = props.request; - // to_device requests don't have a timestamp, so consider them age=0 - const age = event.getTs() ? event.getLocalAge() : 0; - const remaining = Math.max(0, timeout - age); - const counter = Math.ceil(remaining / 1000); - this.state = {counter}; + this.state = {counter: Math.ceil(props.request.timeout / 1000)}; } componentDidMount() { + const {request} = this.props; this._intervalHandle = setInterval(() => { let {counter} = this.state; - counter -= 1; - if (counter <= 0) { - this.cancel(); - } else { - this.setState({counter}); - } + counter = Math.max(0, counter - 1); + this.setState({counter}); }, 1000); - const {request} = this.props; request.on("change", this._checkRequestIsPending); } @@ -56,7 +47,7 @@ export default class VerificationRequestToast extends React.PureComponent { _checkRequestIsPending = () => { const {request} = this.props; - if (request.ready || request.started || request.done || request.cancelled) { + if (request.ready || request.started || request.done || request.cancelled || request.observeOnly) { this.props.dismiss(); } }; From d20db3560a20c2a7075cfb77ef1e3e35fceed897 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 17 Jan 2020 15:50:27 +0100 Subject: [PATCH 23/25] fix import paths after build system refactor --- src/components/views/right_panel/EncryptionInfo.js | 2 +- src/components/views/right_panel/EncryptionPanel.js | 2 +- src/components/views/right_panel/VerificationPanel.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/views/right_panel/EncryptionInfo.js b/src/components/views/right_panel/EncryptionInfo.js index 3d5de829b7..5770e9b086 100644 --- a/src/components/views/right_panel/EncryptionInfo.js +++ b/src/components/views/right_panel/EncryptionInfo.js @@ -15,7 +15,7 @@ limitations under the License. */ import React from 'react'; -import sdk from "../../.."; +import * as sdk from '../../../index'; import {_t} from "../../../languageHandler"; export default class EncryptionInfo extends React.PureComponent { diff --git a/src/components/views/right_panel/EncryptionPanel.js b/src/components/views/right_panel/EncryptionPanel.js index e3f3b86940..4b3473935a 100644 --- a/src/components/views/right_panel/EncryptionPanel.js +++ b/src/components/views/right_panel/EncryptionPanel.js @@ -17,7 +17,7 @@ limitations under the License. import React from 'react'; import EncryptionInfo from "./EncryptionInfo"; import VerificationPanel from "./VerificationPanel"; -import MatrixClientPeg from "../../../MatrixClientPeg"; +import {MatrixClientPeg} from "../../../MatrixClientPeg"; import {ensureDMExists} from "../../../createRoom"; export default class EncryptionPanel extends React.PureComponent { diff --git a/src/components/views/right_panel/VerificationPanel.js b/src/components/views/right_panel/VerificationPanel.js index fff9c37358..4dee3e6ae8 100644 --- a/src/components/views/right_panel/VerificationPanel.js +++ b/src/components/views/right_panel/VerificationPanel.js @@ -15,7 +15,7 @@ limitations under the License. */ import React from 'react'; -import sdk from "../../.."; +import * as sdk from '../../../index'; import {verificationMethods} from 'matrix-js-sdk/src/crypto'; export default class VerificationPanel extends React.PureComponent { From a73b7229a7ec50a65f3b147ce7f760691e03a254 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 17 Jan 2020 16:31:38 +0100 Subject: [PATCH 24/25] fix lint --- src/components/structures/MatrixChat.js | 1 - src/components/views/right_panel/VerificationPanel.js | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 7cbec11e08..3ac8a93e3d 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -1455,7 +1455,6 @@ export default createReactClass({ cli.on("crypto.verification.request", request => { console.log(`MatrixChat got a .request ${request.channel.transactionId}`, request.event.getRoomId()); if (request.pending) { - console.log(`emitting toast for verification request with txnid ${request.channel.transactionId}`, request.event && request.event.getId()); ToastStore.sharedInstance().addOrReplaceToast({ key: 'verifreq_' + request.channel.transactionId, title: _t("Verification Request"), diff --git a/src/components/views/right_panel/VerificationPanel.js b/src/components/views/right_panel/VerificationPanel.js index 4dee3e6ae8..0d28e1568f 100644 --- a/src/components/views/right_panel/VerificationPanel.js +++ b/src/components/views/right_panel/VerificationPanel.js @@ -41,7 +41,10 @@ export default class VerificationPanel extends React.PureComponent { if (request.requested) { return (

Waiting for {request.otherUserId} to accept ...

); } else if (request.ready) { - return (

{request.otherUserId} is ready, start Verify by emoji

); + const verifyButton = + Verify by emoji + ; + return (

{request.otherUserId} is ready, start {verifyButton}

); } else if (request.started) { if (this.state.sasWaitingForOtherParty) { return

Waiting for {request.otherUserId} to confirm ...

; From 716c8ba68f392d3d1f727918475381aae26f24c4 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 17 Jan 2020 17:02:31 +0100 Subject: [PATCH 25/25] pr feedback --- src/components/views/messages/MKeyVerificationRequest.js | 1 - src/components/views/toasts/VerificationRequestToast.js | 1 - src/stores/RightPanelStorePhases.js | 3 ++- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/views/messages/MKeyVerificationRequest.js b/src/components/views/messages/MKeyVerificationRequest.js index 32f75d9895..ae793556d8 100644 --- a/src/components/views/messages/MKeyVerificationRequest.js +++ b/src/components/views/messages/MKeyVerificationRequest.js @@ -45,7 +45,6 @@ export default class MKeyVerificationRequest extends React.Component { _openRequest = () => { const {verificationRequest} = this.props.mxEvent; - dis.dispatch({action: "show_right_panel"}); dis.dispatch({ action: "set_right_panel_phase", phase: RIGHT_PANEL_PHASES.EncryptionPanel, diff --git a/src/components/views/toasts/VerificationRequestToast.js b/src/components/views/toasts/VerificationRequestToast.js index c681ca0951..479a3e3f93 100644 --- a/src/components/views/toasts/VerificationRequestToast.js +++ b/src/components/views/toasts/VerificationRequestToast.js @@ -76,7 +76,6 @@ export default class VerificationRequestToast extends React.PureComponent { } try { await request.accept(); - dis.dispatch({action: "show_right_panel"}); dis.dispatch({ action: "set_right_panel_phase", phase: RIGHT_PANEL_PHASES.EncryptionPanel, diff --git a/src/stores/RightPanelStorePhases.js b/src/stores/RightPanelStorePhases.js index 7783f960d6..d9af320233 100644 --- a/src/stores/RightPanelStorePhases.js +++ b/src/stores/RightPanelStorePhases.js @@ -21,8 +21,9 @@ export const RIGHT_PANEL_PHASES = Object.freeze({ FilePanel: 'FilePanel', NotificationPanel: 'NotificationPanel', RoomMemberInfo: 'RoomMemberInfo', - Room3pidMemberInfo: 'Room3pidMemberInfo', EncryptionPanel: 'EncryptionPanel', + + Room3pidMemberInfo: 'Room3pidMemberInfo', // Group stuff GroupMemberList: 'GroupMemberList', GroupRoomList: 'GroupRoomList',