add toast for verification requests
this uses a verification requests as emitted by the js-sdk with the `crypto.verification.request` rather than a verifier as emitted by `crypto.verification.start` as this works for both to_device and timeline events with the changes made in the js-sdk pr.
This commit is contained in:
parent
f1c62e7dab
commit
c705752317
3 changed files with 139 additions and 1 deletions
123
src/components/views/toasts/VerificationRequestToast.js
Normal file
123
src/components/views/toasts/VerificationRequestToast.js
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
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 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 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};
|
||||||
|
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;
|
||||||
|
if (counter <= 0) {
|
||||||
|
this.cancel();
|
||||||
|
} else {
|
||||||
|
this.setState({counter});
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
clearInterval(this._intervalHandle);
|
||||||
|
if (this.props.requestObserver) {
|
||||||
|
this.props.requestObserver.detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_checkRequestIsPending = () => {
|
||||||
|
if (!this.props.requestObserver.pending) {
|
||||||
|
this.props.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel = () => {
|
||||||
|
this.props.dismiss();
|
||||||
|
try {
|
||||||
|
this.props.request.cancel();
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error while cancelling verification request", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
accept = () => {
|
||||||
|
this.props.dismiss();
|
||||||
|
const {event} = this.props.request;
|
||||||
|
// no room id for to_device requests
|
||||||
|
if (event.getRoomId()) {
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'view_room',
|
||||||
|
room_id: event.getRoomId(),
|
||||||
|
should_peek: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const verifier = this.props.request.beginKeyVerification(verificationMethods.SAS);
|
||||||
|
const IncomingSasDialog = sdk.getComponent('views.dialogs.IncomingSasDialog');
|
||||||
|
Modal.createTrackedDialog('Incoming Verification', '', IncomingSasDialog, {verifier});
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const FormButton = sdk.getComponent("elements.FormButton");
|
||||||
|
const {event} = this.props.request;
|
||||||
|
const userId = event.getSender();
|
||||||
|
let nameLabel = event.getRoomId() ? userLabelForEventRoom(userId, event) : userId;
|
||||||
|
// for legacy to_device verification requests
|
||||||
|
if (nameLabel === userId) {
|
||||||
|
const client = MatrixClientPeg.get();
|
||||||
|
const user = client.getUser(event.getSender());
|
||||||
|
if (user && user.displayName) {
|
||||||
|
nameLabel = _t("%(name)s (%(userId)s)", {name: user.displayName, userId});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (<div>
|
||||||
|
<div className="mx_Toast_description">{nameLabel}</div>
|
||||||
|
<div className="mx_Toast_buttons">
|
||||||
|
<FormButton label={_t("Decline (%(counter)s)", {counter: this.state.counter})} kind="danger" onClick={this.cancel} />
|
||||||
|
<FormButton label={_t("Accept")} onClick={this.accept} />
|
||||||
|
</div>
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VerificationRequestToast.propTypes = {
|
||||||
|
dismiss: PropTypes.func.isRequired,
|
||||||
|
request: PropTypes.object.isRequired,
|
||||||
|
requestObserver: PropTypes.instanceOf(KeyVerificationStateObserver),
|
||||||
|
};
|
|
@ -481,6 +481,7 @@
|
||||||
"Headphones": "Headphones",
|
"Headphones": "Headphones",
|
||||||
"Folder": "Folder",
|
"Folder": "Folder",
|
||||||
"Pin": "Pin",
|
"Pin": "Pin",
|
||||||
|
"Decline (%(counter)s)": "Decline (%(counter)s)",
|
||||||
"Accept <policyLink /> to continue:": "Accept <policyLink /> to continue:",
|
"Accept <policyLink /> to continue:": "Accept <policyLink /> to continue:",
|
||||||
"Failed to upload profile picture!": "Failed to upload profile picture!",
|
"Failed to upload profile picture!": "Failed to upload profile picture!",
|
||||||
"Upload new:": "Upload new:",
|
"Upload new:": "Upload new:",
|
||||||
|
@ -1694,6 +1695,7 @@
|
||||||
"Review terms and conditions": "Review terms and conditions",
|
"Review terms and conditions": "Review terms and conditions",
|
||||||
"Old cryptography data detected": "Old cryptography data detected",
|
"Old cryptography data detected": "Old cryptography data detected",
|
||||||
"Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.": "Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.",
|
"Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.": "Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.",
|
||||||
|
"Verification Request": "Verification Request",
|
||||||
"Logout": "Logout",
|
"Logout": "Logout",
|
||||||
"%(creator)s created and configured the room.": "%(creator)s created and configured the room.",
|
"%(creator)s created and configured the room.": "%(creator)s created and configured the room.",
|
||||||
"Your Communities": "Your Communities",
|
"Your Communities": "Your Communities",
|
||||||
|
@ -1759,6 +1761,7 @@
|
||||||
"Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.",
|
"Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.",
|
||||||
"Tried to load a specific point in this room's timeline, but was unable to find it.": "Tried to load a specific point in this room's timeline, but was unable to find it.",
|
"Tried to load a specific point in this room's timeline, but was unable to find it.": "Tried to load a specific point in this room's timeline, but was unable to find it.",
|
||||||
"Failed to load timeline position": "Failed to load timeline position",
|
"Failed to load timeline position": "Failed to load timeline position",
|
||||||
|
" (1/%(totalCount)s)": " (1/%(totalCount)s)",
|
||||||
"Guest": "Guest",
|
"Guest": "Guest",
|
||||||
"Your profile": "Your profile",
|
"Your profile": "Your profile",
|
||||||
"Uploading %(filename)s and %(count)s others|other": "Uploading %(filename)s and %(count)s others",
|
"Uploading %(filename)s and %(count)s others|other": "Uploading %(filename)s and %(count)s others",
|
||||||
|
|
|
@ -30,6 +30,18 @@ export default class KeyVerificationStateObserver {
|
||||||
this._updateVerificationState();
|
this._updateVerificationState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get concluded() {
|
||||||
|
return this.accepted || this.done || this.cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
get pending() {
|
||||||
|
return !this.concluded;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCallback(callback) {
|
||||||
|
this._updateCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
attach() {
|
attach() {
|
||||||
this._requestEvent.on("Event.relationsCreated", this._onRelationsCreated);
|
this._requestEvent.on("Event.relationsCreated", this._onRelationsCreated);
|
||||||
for (const phaseName of SUB_EVENT_TYPES_OF_INTEREST) {
|
for (const phaseName of SUB_EVENT_TYPES_OF_INTEREST) {
|
||||||
|
@ -83,7 +95,7 @@ export default class KeyVerificationStateObserver {
|
||||||
|
|
||||||
_onRelationsUpdated = (event) => {
|
_onRelationsUpdated = (event) => {
|
||||||
this._updateVerificationState();
|
this._updateVerificationState();
|
||||||
this._updateCallback();
|
this._updateCallback && this._updateCallback();
|
||||||
};
|
};
|
||||||
|
|
||||||
_updateVerificationState() {
|
_updateVerificationState() {
|
||||||
|
|
Loading…
Reference in a new issue