Merge pull request #3859 from matrix-org/dbkr/new_session_toast
Toasts for new, unverified sessions
This commit is contained in:
commit
a525ba57c7
7 changed files with 171 additions and 3 deletions
|
@ -51,7 +51,7 @@ limitations under the License.
|
||||||
&.mx_Toast_hasIcon {
|
&.mx_Toast_hasIcon {
|
||||||
&::after {
|
&::after {
|
||||||
content: "";
|
content: "";
|
||||||
width: 20px;
|
width: 21px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
grid-column: 1;
|
grid-column: 1;
|
||||||
grid-row: 1;
|
grid-row: 1;
|
||||||
|
@ -64,6 +64,10 @@ limitations under the License.
|
||||||
background-color: $primary-fg-color;
|
background-color: $primary-fg-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.mx_Toast_icon_verification_warning::after {
|
||||||
|
background-image: url("$(res)/img/e2e/warning.svg");
|
||||||
|
}
|
||||||
|
|
||||||
h2, .mx_Toast_body {
|
h2, .mx_Toast_body {
|
||||||
grid-column: 2;
|
grid-column: 2;
|
||||||
}
|
}
|
||||||
|
|
92
src/DeviceListener.js
Normal file
92
src/DeviceListener.js
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 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 { MatrixClientPeg } from './MatrixClientPeg';
|
||||||
|
import SettingsStore from './settings/SettingsStore';
|
||||||
|
import * as sdk from './index';
|
||||||
|
import { _t } from './languageHandler';
|
||||||
|
import ToastStore from './stores/ToastStore';
|
||||||
|
|
||||||
|
function toastKey(device) {
|
||||||
|
return 'newsession_' + device.deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class DeviceListener {
|
||||||
|
static sharedInstance() {
|
||||||
|
if (!global.mx_DeviceListener) global.mx_DeviceListener = new DeviceListener();
|
||||||
|
return global.mx_DeviceListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
// device IDs for which the user has dismissed the verify toast ('Later')
|
||||||
|
this._dismissed = new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
MatrixClientPeg.get().on('crypto.devicesUpdated', this._onDevicesUpdated);
|
||||||
|
MatrixClientPeg.get().on('deviceVerificationChanged', this._onDeviceVerificationChanged);
|
||||||
|
this.recheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
if (MatrixClientPeg.get()) {
|
||||||
|
MatrixClientPeg.get().removeListener('crypto.devicesUpdated', this._onDevicesUpdated);
|
||||||
|
MatrixClientPeg.get().removeListener('deviceVerificationChanged', this._onDeviceVerificationChanged);
|
||||||
|
}
|
||||||
|
this._dismissed.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
dismissVerification(deviceId) {
|
||||||
|
this._dismissed.add(deviceId);
|
||||||
|
this.recheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
_onDevicesUpdated = (users) => {
|
||||||
|
if (!users.includes(MatrixClientPeg.get().getUserId())) return;
|
||||||
|
if (!SettingsStore.isFeatureEnabled("feature_cross_signing")) return;
|
||||||
|
this.recheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
_onDeviceVerificationChanged = (users) => {
|
||||||
|
if (!users.includes(MatrixClientPeg.get().getUserId())) return;
|
||||||
|
if (!SettingsStore.isFeatureEnabled("feature_cross_signing")) return;
|
||||||
|
this.recheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
async recheck() {
|
||||||
|
const cli = MatrixClientPeg.get();
|
||||||
|
|
||||||
|
if (!cli.isCryptoEnabled()) return false;
|
||||||
|
|
||||||
|
const devices = await cli.getStoredDevicesForUser(cli.getUserId());
|
||||||
|
for (const device of devices) {
|
||||||
|
if (device.deviceId == cli.deviceId) continue;
|
||||||
|
|
||||||
|
const deviceTrust = await cli.checkDeviceTrust(cli.getUserId(), device.deviceId);
|
||||||
|
if (deviceTrust.isVerified() || this._dismissed.has(device.deviceId)) {
|
||||||
|
ToastStore.sharedInstance().dismissToast(toastKey(device));
|
||||||
|
} else {
|
||||||
|
ToastStore.sharedInstance().addOrReplaceToast({
|
||||||
|
key: toastKey(device),
|
||||||
|
title: _t("New Session"),
|
||||||
|
icon: "verification_warning",
|
||||||
|
props: {deviceId: device.deviceId},
|
||||||
|
component: sdk.getComponent("toasts.NewSessionToast"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
Copyright 2018 New Vector Ltd
|
Copyright 2018 New Vector Ltd
|
||||||
|
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -35,8 +36,10 @@ import { sendLoginRequest } from "./Login";
|
||||||
import * as StorageManager from './utils/StorageManager';
|
import * as StorageManager from './utils/StorageManager';
|
||||||
import SettingsStore from "./settings/SettingsStore";
|
import SettingsStore from "./settings/SettingsStore";
|
||||||
import TypingStore from "./stores/TypingStore";
|
import TypingStore from "./stores/TypingStore";
|
||||||
|
import ToastStore from "./stores/ToastStore";
|
||||||
import {IntegrationManagers} from "./integrations/IntegrationManagers";
|
import {IntegrationManagers} from "./integrations/IntegrationManagers";
|
||||||
import {Mjolnir} from "./mjolnir/Mjolnir";
|
import {Mjolnir} from "./mjolnir/Mjolnir";
|
||||||
|
import DeviceListener from "./DeviceListener";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called at startup, to attempt to build a logged-in Matrix session. It tries
|
* Called at startup, to attempt to build a logged-in Matrix session. It tries
|
||||||
|
@ -575,6 +578,7 @@ async function startMatrixClient(startSyncing=true) {
|
||||||
Notifier.start();
|
Notifier.start();
|
||||||
UserActivity.sharedInstance().start();
|
UserActivity.sharedInstance().start();
|
||||||
TypingStore.sharedInstance().reset(); // just in case
|
TypingStore.sharedInstance().reset(); // just in case
|
||||||
|
ToastStore.sharedInstance().reset();
|
||||||
if (!SettingsStore.getValue("lowBandwidth")) {
|
if (!SettingsStore.getValue("lowBandwidth")) {
|
||||||
Presence.start();
|
Presence.start();
|
||||||
}
|
}
|
||||||
|
@ -595,6 +599,9 @@ async function startMatrixClient(startSyncing=true) {
|
||||||
await MatrixClientPeg.assign();
|
await MatrixClientPeg.assign();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This needs to be started after crypto is set up
|
||||||
|
DeviceListener.sharedInstance().start();
|
||||||
|
|
||||||
// dispatch that we finished starting up to wire up any other bits
|
// dispatch that we finished starting up to wire up any other bits
|
||||||
// of the matrix client that cannot be set prior to starting up.
|
// of the matrix client that cannot be set prior to starting up.
|
||||||
dis.dispatch({action: 'client_started'});
|
dis.dispatch({action: 'client_started'});
|
||||||
|
@ -651,6 +658,7 @@ export function stopMatrixClient(unsetClient=true) {
|
||||||
ActiveWidgetStore.stop();
|
ActiveWidgetStore.stop();
|
||||||
IntegrationManagers.sharedInstance().stopWatching();
|
IntegrationManagers.sharedInstance().stopWatching();
|
||||||
Mjolnir.sharedInstance().stop();
|
Mjolnir.sharedInstance().stop();
|
||||||
|
DeviceListener.sharedInstance().stop();
|
||||||
if (DMRoomMap.shared()) DMRoomMap.shared().stop();
|
if (DMRoomMap.shared()) DMRoomMap.shared().stop();
|
||||||
EventIndexPeg.stop();
|
EventIndexPeg.stop();
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
|
|
|
@ -22,7 +22,7 @@ import classNames from "classnames";
|
||||||
export default class ToastContainer extends React.Component {
|
export default class ToastContainer extends React.Component {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.state = {toasts: []};
|
this.state = {toasts: ToastStore.sharedInstance().getToasts()};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
|
57
src/components/views/toasts/NewSessionToast.js
Normal file
57
src/components/views/toasts/NewSessionToast.js
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 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 * as sdk from "../../../index";
|
||||||
|
import { _t } from '../../../languageHandler';
|
||||||
|
import Modal from "../../../Modal";
|
||||||
|
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
||||||
|
import DeviceListener from '../../../DeviceListener';
|
||||||
|
|
||||||
|
export default class VerifySessionToast extends React.PureComponent {
|
||||||
|
static propTypes = {
|
||||||
|
toastKey: PropTypes.string.isRequired,
|
||||||
|
deviceId: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
_onLaterClick = () => {
|
||||||
|
DeviceListener.sharedInstance().dismissVerification(this.props.deviceId);
|
||||||
|
};
|
||||||
|
|
||||||
|
_onVerifyClick = async () => {
|
||||||
|
const cli = MatrixClientPeg.get();
|
||||||
|
const DeviceVerifyDialog = sdk.getComponent('views.dialogs.DeviceVerifyDialog');
|
||||||
|
|
||||||
|
const device = await cli.getStoredDevice(cli.getUserId(), this.props.deviceId);
|
||||||
|
|
||||||
|
Modal.createTrackedDialog('New Session Verify', 'Starting dialog', DeviceVerifyDialog, {
|
||||||
|
userId: MatrixClientPeg.get().getUserId(),
|
||||||
|
device,
|
||||||
|
}, null, /* priority = */ false, /* static = */ true);
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const FormButton = sdk.getComponent("elements.FormButton");
|
||||||
|
return (<div>
|
||||||
|
<div className="mx_Toast_description">{_t("Other users may not trust it")}</div>
|
||||||
|
<div className="mx_Toast_buttons" aria-live="off">
|
||||||
|
<FormButton label={_t("Later")} kind="danger" onClick={this._onLaterClick} />
|
||||||
|
<FormButton label={_t("Verify")} onClick={this._onVerifyClick} />
|
||||||
|
</div>
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
}
|
|
@ -85,6 +85,7 @@
|
||||||
"%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(time)s",
|
"%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(time)s",
|
||||||
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s",
|
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s",
|
||||||
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s",
|
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s",
|
||||||
|
"New Session": "New Session",
|
||||||
"Who would you like to add to this community?": "Who would you like to add to this community?",
|
"Who would you like to add to this community?": "Who would you like to add to this community?",
|
||||||
"Warning: any person you add to a community will be publicly visible to anyone who knows the community ID": "Warning: any person you add to a community will be publicly visible to anyone who knows the community ID",
|
"Warning: any person you add to a community will be publicly visible to anyone who knows the community ID": "Warning: any person you add to a community will be publicly visible to anyone who knows the community ID",
|
||||||
"Invite new community members": "Invite new community members",
|
"Invite new community members": "Invite new community members",
|
||||||
|
@ -513,6 +514,9 @@
|
||||||
"Headphones": "Headphones",
|
"Headphones": "Headphones",
|
||||||
"Folder": "Folder",
|
"Folder": "Folder",
|
||||||
"Pin": "Pin",
|
"Pin": "Pin",
|
||||||
|
"Other users may not trust it": "Other users may not trust it",
|
||||||
|
"Later": "Later",
|
||||||
|
"Verify": "Verify",
|
||||||
"Decline (%(counter)s)": "Decline (%(counter)s)",
|
"Decline (%(counter)s)": "Decline (%(counter)s)",
|
||||||
"Accept <policyLink /> to continue:": "Accept <policyLink /> to continue:",
|
"Accept <policyLink /> to continue:": "Accept <policyLink /> to continue:",
|
||||||
"Upload": "Upload",
|
"Upload": "Upload",
|
||||||
|
@ -1130,7 +1134,6 @@
|
||||||
"This client does not support end-to-end encryption.": "This client does not support end-to-end encryption.",
|
"This client does not support end-to-end encryption.": "This client does not support end-to-end encryption.",
|
||||||
"Messages in this room are not end-to-end encrypted.": "Messages in this room are not end-to-end encrypted.",
|
"Messages in this room are not end-to-end encrypted.": "Messages in this room are not end-to-end encrypted.",
|
||||||
"Messages in this room are end-to-end encrypted.": "Messages in this room are end-to-end encrypted.",
|
"Messages in this room are end-to-end encrypted.": "Messages in this room are end-to-end encrypted.",
|
||||||
"Verify": "Verify",
|
|
||||||
"Security": "Security",
|
"Security": "Security",
|
||||||
"Sunday": "Sunday",
|
"Sunday": "Sunday",
|
||||||
"Monday": "Monday",
|
"Monday": "Monday",
|
||||||
|
|
|
@ -31,6 +31,10 @@ export default class ToastStore extends EventEmitter {
|
||||||
this._toasts = [];
|
this._toasts = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this._toasts = [];
|
||||||
|
}
|
||||||
|
|
||||||
addOrReplaceToast(newToast) {
|
addOrReplaceToast(newToast) {
|
||||||
const oldIndex = this._toasts.findIndex(t => t.key === newToast.key);
|
const oldIndex = this._toasts.findIndex(t => t.key === newToast.key);
|
||||||
if (oldIndex === -1) {
|
if (oldIndex === -1) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue