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}); } }