Convert things to Typescript and re-use a generic component
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
parent
b21e5ba10b
commit
14cee41360
9 changed files with 342 additions and 303 deletions
|
@ -14,19 +14,24 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { MatrixClientPeg } from './MatrixClientPeg';
|
import {MatrixClientPeg} from './MatrixClientPeg';
|
||||||
import SettingsStore from './settings/SettingsStore';
|
import SettingsStore from './settings/SettingsStore';
|
||||||
import * as sdk from './index';
|
import {
|
||||||
import { _t } from './languageHandler';
|
hideToast as hideBulkUnverifiedSessionsToast,
|
||||||
import ToastStore from './stores/ToastStore';
|
showToast as showBulkUnverifiedSessionsToast
|
||||||
|
} from "./toasts/BulkUnverifiedSessionsToast";
|
||||||
|
import {
|
||||||
|
hideToast as hideSetupEncryptionToast,
|
||||||
|
Kind as SetupKind,
|
||||||
|
Kind,
|
||||||
|
showToast as showSetupEncryptionToast
|
||||||
|
} from "./toasts/SetupEncryptionToast";
|
||||||
|
import {
|
||||||
|
hideToast as hideUnverifiedSessionsToast,
|
||||||
|
showToast as showUnverifiedSessionsToast
|
||||||
|
} from "./toasts/UnverifiedSessionToast";
|
||||||
|
|
||||||
const KEY_BACKUP_POLL_INTERVAL = 5 * 60 * 1000;
|
const KEY_BACKUP_POLL_INTERVAL = 5 * 60 * 1000;
|
||||||
const THIS_DEVICE_TOAST_KEY = 'setupencryption';
|
|
||||||
const OTHER_DEVICES_TOAST_KEY = 'reviewsessions';
|
|
||||||
|
|
||||||
function toastKey(deviceId) {
|
|
||||||
return "unverified_session_" + deviceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class DeviceListener {
|
export default class DeviceListener {
|
||||||
// device IDs for which the user has dismissed the verify toast ('Later')
|
// device IDs for which the user has dismissed the verify toast ('Later')
|
||||||
|
@ -82,7 +87,7 @@ export default class DeviceListener {
|
||||||
*
|
*
|
||||||
* @param {String[]} deviceIds List of device IDs to dismiss notifications for
|
* @param {String[]} deviceIds List of device IDs to dismiss notifications for
|
||||||
*/
|
*/
|
||||||
async dismissUnverifiedSessions(deviceIds: string[]) {
|
async dismissUnverifiedSessions(deviceIds: Iterable<string>) {
|
||||||
for (const d of deviceIds) {
|
for (const d of deviceIds) {
|
||||||
this.dismissed.add(d);
|
this.dismissed.add(d);
|
||||||
}
|
}
|
||||||
|
@ -181,51 +186,25 @@ export default class DeviceListener {
|
||||||
|
|
||||||
const crossSigningReady = await cli.isCrossSigningReady();
|
const crossSigningReady = await cli.isCrossSigningReady();
|
||||||
|
|
||||||
if (this.dismissedThisDeviceToast) {
|
if (this.dismissedThisDeviceToast || crossSigningReady) {
|
||||||
ToastStore.sharedInstance().dismissToast(THIS_DEVICE_TOAST_KEY);
|
hideSetupEncryptionToast();
|
||||||
} else {
|
} else {
|
||||||
if (!crossSigningReady) {
|
// make sure our keys are finished downloading
|
||||||
// make sure our keys are finished downlaoding
|
await cli.downloadKeys([cli.getUserId()]);
|
||||||
await cli.downloadKeys([cli.getUserId()]);
|
// cross signing isn't enabled - nag to enable it
|
||||||
// cross signing isn't enabled - nag to enable it
|
// There are 3 different toasts for:
|
||||||
// There are 3 different toasts for:
|
if (cli.getStoredCrossSigningForUser(cli.getUserId())) {
|
||||||
if (cli.getStoredCrossSigningForUser(cli.getUserId())) {
|
// Cross-signing on account but this device doesn't trust the master key (verify this session)
|
||||||
// Cross-signing on account but this device doesn't trust the master key (verify this session)
|
showSetupEncryptionToast(SetupKind.VERIFY_THIS_SESSION);
|
||||||
ToastStore.sharedInstance().addOrReplaceToast({
|
|
||||||
key: THIS_DEVICE_TOAST_KEY,
|
|
||||||
title: _t("Verify this session"),
|
|
||||||
icon: "verification_warning",
|
|
||||||
props: {kind: 'verify_this_session'},
|
|
||||||
component: sdk.getComponent("toasts.SetupEncryptionToast"),
|
|
||||||
priority: 95,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const backupInfo = await this._getKeyBackupInfo();
|
|
||||||
if (backupInfo) {
|
|
||||||
// No cross-signing on account but key backup available (upgrade encryption)
|
|
||||||
ToastStore.sharedInstance().addOrReplaceToast({
|
|
||||||
key: THIS_DEVICE_TOAST_KEY,
|
|
||||||
title: _t("Encryption upgrade available"),
|
|
||||||
icon: "verification_warning",
|
|
||||||
props: {kind: 'upgrade_encryption'},
|
|
||||||
component: sdk.getComponent("toasts.SetupEncryptionToast"),
|
|
||||||
priority: 40,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// No cross-signing or key backup on account (set up encryption)
|
|
||||||
ToastStore.sharedInstance().addOrReplaceToast({
|
|
||||||
key: THIS_DEVICE_TOAST_KEY,
|
|
||||||
title: _t("Set up encryption"),
|
|
||||||
icon: "verification_warning",
|
|
||||||
props: {kind: 'set_up_encryption'},
|
|
||||||
component: sdk.getComponent("toasts.SetupEncryptionToast"),
|
|
||||||
priority: 40,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// cross-signing is ready, and we don't need to upgrade encryption
|
const backupInfo = await this._getKeyBackupInfo();
|
||||||
ToastStore.sharedInstance().dismissToast(THIS_DEVICE_TOAST_KEY);
|
if (backupInfo) {
|
||||||
|
// No cross-signing on account but key backup available (upgrade encryption)
|
||||||
|
showSetupEncryptionToast(Kind.UPGRADE_ENCRYPTION);
|
||||||
|
} else {
|
||||||
|
// No cross-signing or key backup on account (set up encryption)
|
||||||
|
showSetupEncryptionToast(Kind.SET_UP_ENCRYPTION);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,36 +240,20 @@ export default class DeviceListener {
|
||||||
|
|
||||||
// Display or hide the batch toast for old unverified sessions
|
// Display or hide the batch toast for old unverified sessions
|
||||||
if (oldUnverifiedDeviceIds.size > 0) {
|
if (oldUnverifiedDeviceIds.size > 0) {
|
||||||
ToastStore.sharedInstance().addOrReplaceToast({
|
showBulkUnverifiedSessionsToast(oldUnverifiedDeviceIds);
|
||||||
key: OTHER_DEVICES_TOAST_KEY,
|
|
||||||
title: _t("Review where you’re logged in"),
|
|
||||||
icon: "verification_warning",
|
|
||||||
props: {
|
|
||||||
deviceIds: oldUnverifiedDeviceIds,
|
|
||||||
},
|
|
||||||
component: sdk.getComponent("toasts.BulkUnverifiedSessionsToast"),
|
|
||||||
priority: 50,
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
ToastStore.sharedInstance().dismissToast(OTHER_DEVICES_TOAST_KEY);
|
hideBulkUnverifiedSessionsToast();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show toasts for new unverified devices if they aren't already there
|
// Show toasts for new unverified devices if they aren't already there
|
||||||
for (const deviceId of newUnverifiedDeviceIds) {
|
for (const deviceId of newUnverifiedDeviceIds) {
|
||||||
ToastStore.sharedInstance().addOrReplaceToast({
|
showUnverifiedSessionsToast(deviceId);
|
||||||
key: toastKey(deviceId),
|
|
||||||
title: _t("New login. Was this you?"),
|
|
||||||
icon: "verification_warning",
|
|
||||||
props: { deviceId },
|
|
||||||
component: sdk.getComponent("toasts.UnverifiedSessionToast"),
|
|
||||||
priority: 80,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ...and hide any we don't need any more
|
// ...and hide any we don't need any more
|
||||||
for (const deviceId of this.displayingToastsForDeviceIds) {
|
for (const deviceId of this.displayingToastsForDeviceIds) {
|
||||||
if (!newUnverifiedDeviceIds.has(deviceId)) {
|
if (!newUnverifiedDeviceIds.has(deviceId)) {
|
||||||
ToastStore.sharedInstance().dismissToast(toastKey(deviceId));
|
hideUnverifiedSessionsToast(deviceId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
/*
|
|
||||||
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 { _t } from '../../../languageHandler';
|
|
||||||
import dis from "../../../dispatcher/dispatcher";
|
|
||||||
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
|
||||||
import DeviceListener from '../../../DeviceListener';
|
|
||||||
import FormButton from '../elements/FormButton';
|
|
||||||
import { replaceableComponent } from '../../../utils/replaceableComponent';
|
|
||||||
|
|
||||||
@replaceableComponent("views.toasts.BulkUnverifiedSessionsToast")
|
|
||||||
export default class BulkUnverifiedSessionsToast extends React.PureComponent {
|
|
||||||
static propTypes = {
|
|
||||||
deviceIds: PropTypes.array,
|
|
||||||
}
|
|
||||||
|
|
||||||
_onLaterClick = () => {
|
|
||||||
DeviceListener.sharedInstance().dismissUnverifiedSessions(this.props.deviceIds);
|
|
||||||
};
|
|
||||||
|
|
||||||
_onReviewClick = async () => {
|
|
||||||
DeviceListener.sharedInstance().dismissUnverifiedSessions(this.props.deviceIds);
|
|
||||||
|
|
||||||
dis.dispatch({
|
|
||||||
action: 'view_user_info',
|
|
||||||
userId: MatrixClientPeg.get().getUserId(),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (<div>
|
|
||||||
<div className="mx_Toast_description">
|
|
||||||
{_t("Verify all your sessions to ensure your account & messages are safe")}
|
|
||||||
</div>
|
|
||||||
<div className="mx_Toast_buttons" aria-live="off">
|
|
||||||
<FormButton label={_t("Later")} kind="danger" onClick={this._onLaterClick} />
|
|
||||||
<FormButton label={_t("Review")} onClick={this._onReviewClick} />
|
|
||||||
</div>
|
|
||||||
</div>);
|
|
||||||
}
|
|
||||||
}
|
|
42
src/components/views/toasts/GenericToast.tsx
Normal file
42
src/components/views/toasts/GenericToast.tsx
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
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, {ReactChild} from "react";
|
||||||
|
|
||||||
|
import FormButton from "../elements/FormButton";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
description: ReactChild;
|
||||||
|
acceptLabel: string;
|
||||||
|
rejectLabel?: string;
|
||||||
|
|
||||||
|
onAccept();
|
||||||
|
onReject?();
|
||||||
|
}
|
||||||
|
|
||||||
|
const GenericToast: React.FC<IProps> = ({description, acceptLabel, rejectLabel, onAccept, onReject}) => {
|
||||||
|
return <div>
|
||||||
|
<div className="mx_Toast_description">
|
||||||
|
{ description }
|
||||||
|
</div>
|
||||||
|
<div className="mx_Toast_buttons" aria-live="off">
|
||||||
|
{onReject && rejectLabel && <FormButton label={rejectLabel} kind="danger" onClick={onReject} /> }
|
||||||
|
<FormButton label={acceptLabel} onClick={onAccept} />
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GenericToast;
|
|
@ -1,88 +0,0 @@
|
||||||
/*
|
|
||||||
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 Modal from '../../../Modal';
|
|
||||||
import * as sdk from "../../../index";
|
|
||||||
import { _t } from '../../../languageHandler';
|
|
||||||
import DeviceListener from '../../../DeviceListener';
|
|
||||||
import SetupEncryptionDialog from "../dialogs/SetupEncryptionDialog";
|
|
||||||
import { accessSecretStorage } from '../../../CrossSigningManager';
|
|
||||||
|
|
||||||
export default class SetupEncryptionToast extends React.PureComponent {
|
|
||||||
static propTypes = {
|
|
||||||
toastKey: PropTypes.string.isRequired,
|
|
||||||
kind: PropTypes.oneOf([
|
|
||||||
'set_up_encryption',
|
|
||||||
'verify_this_session',
|
|
||||||
'upgrade_encryption',
|
|
||||||
]).isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
_onLaterClick = () => {
|
|
||||||
DeviceListener.sharedInstance().dismissEncryptionSetup();
|
|
||||||
};
|
|
||||||
|
|
||||||
_onSetupClick = async () => {
|
|
||||||
if (this.props.kind === "verify_this_session") {
|
|
||||||
Modal.createTrackedDialog('Verify session', 'Verify session', SetupEncryptionDialog,
|
|
||||||
{}, null, /* priority = */ false, /* static = */ true);
|
|
||||||
} else {
|
|
||||||
const Spinner = sdk.getComponent("elements.Spinner");
|
|
||||||
const modal = Modal.createDialog(
|
|
||||||
Spinner, null, 'mx_Dialog_spinner', /* priority */ false, /* static */ true,
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
await accessSecretStorage();
|
|
||||||
} finally {
|
|
||||||
modal.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
getDescription() {
|
|
||||||
switch (this.props.kind) {
|
|
||||||
case 'set_up_encryption':
|
|
||||||
case 'upgrade_encryption':
|
|
||||||
return _t('Verify yourself & others to keep your chats safe');
|
|
||||||
case 'verify_this_session':
|
|
||||||
return _t('Other users may not trust it');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getSetupCaption() {
|
|
||||||
switch (this.props.kind) {
|
|
||||||
case 'set_up_encryption':
|
|
||||||
return _t('Set up');
|
|
||||||
case 'upgrade_encryption':
|
|
||||||
return _t('Upgrade');
|
|
||||||
case 'verify_this_session':
|
|
||||||
return _t('Verify');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const FormButton = sdk.getComponent("elements.FormButton");
|
|
||||||
return (<div>
|
|
||||||
<div className="mx_Toast_description">{this.getDescription()}</div>
|
|
||||||
<div className="mx_Toast_buttons" aria-live="off">
|
|
||||||
<FormButton label={_t("Later")} kind="danger" onClick={this._onLaterClick} />
|
|
||||||
<FormButton label={this.getSetupCaption()} onClick={this._onSetupClick} />
|
|
||||||
</div>
|
|
||||||
</div>);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
/*
|
|
||||||
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 { _t } from '../../../languageHandler';
|
|
||||||
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
|
||||||
import Modal from '../../../Modal';
|
|
||||||
import DeviceListener from '../../../DeviceListener';
|
|
||||||
import NewSessionReviewDialog from '../dialogs/NewSessionReviewDialog';
|
|
||||||
import FormButton from '../elements/FormButton';
|
|
||||||
import { replaceableComponent } from '../../../utils/replaceableComponent';
|
|
||||||
|
|
||||||
@replaceableComponent("views.toasts.UnverifiedSessionToast")
|
|
||||||
export default class UnverifiedSessionToast extends React.PureComponent {
|
|
||||||
static propTypes = {
|
|
||||||
deviceId: PropTypes.string,
|
|
||||||
}
|
|
||||||
|
|
||||||
_onLaterClick = () => {
|
|
||||||
DeviceListener.sharedInstance().dismissUnverifiedSessions([this.props.deviceId]);
|
|
||||||
};
|
|
||||||
|
|
||||||
_onReviewClick = async () => {
|
|
||||||
const cli = MatrixClientPeg.get();
|
|
||||||
Modal.createTrackedDialog('New Session Review', 'Starting dialog', NewSessionReviewDialog, {
|
|
||||||
userId: cli.getUserId(),
|
|
||||||
device: cli.getStoredDevice(cli.getUserId(), this.props.deviceId),
|
|
||||||
onFinished: (r) => {
|
|
||||||
if (!r) {
|
|
||||||
/* This'll come back false if the user clicks "this wasn't me" and saw a warning dialog */
|
|
||||||
DeviceListener.sharedInstance().dismissUnverifiedSessions([this.props.deviceId]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}, null, /* priority = */ false, /* static = */ true);
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const cli = MatrixClientPeg.get();
|
|
||||||
const device = cli.getStoredDevice(cli.getUserId(), this.props.deviceId);
|
|
||||||
|
|
||||||
return (<div>
|
|
||||||
<div className="mx_Toast_description">
|
|
||||||
{_t(
|
|
||||||
"Verify the new login accessing your account: %(name)s", { name: device.getDisplayName()})}
|
|
||||||
</div>
|
|
||||||
<div className="mx_Toast_buttons" aria-live="off">
|
|
||||||
<FormButton label={_t("Later")} kind="danger" onClick={this._onLaterClick} />
|
|
||||||
<FormButton label={_t("Verify")} onClick={this._onReviewClick} />
|
|
||||||
</div>
|
|
||||||
</div>);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import * as sdk from "../../../index";
|
import * as sdk from "../../../index";
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||||
|
@ -24,8 +24,23 @@ import {userLabelForEventRoom} from "../../../utils/KeyVerificationStateObserver
|
||||||
import dis from "../../../dispatcher/dispatcher";
|
import dis from "../../../dispatcher/dispatcher";
|
||||||
import ToastStore from "../../../stores/ToastStore";
|
import ToastStore from "../../../stores/ToastStore";
|
||||||
import Modal from "../../../Modal";
|
import Modal from "../../../Modal";
|
||||||
|
import GenericToast from "./GenericToast";
|
||||||
|
import {VerificationRequest} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
|
||||||
|
import {DeviceInfo} from "matrix-js-sdk/src/crypto/deviceinfo";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
toastKey: string;
|
||||||
|
request: VerificationRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
counter: number;
|
||||||
|
device?: DeviceInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class VerificationRequestToast extends React.PureComponent<IProps, IState> {
|
||||||
|
private intervalHandle: NodeJS.Timeout;
|
||||||
|
|
||||||
export default class VerificationRequestToast extends React.PureComponent {
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {counter: Math.ceil(props.request.timeout / 1000)};
|
this.state = {counter: Math.ceil(props.request.timeout / 1000)};
|
||||||
|
@ -34,7 +49,7 @@ export default class VerificationRequestToast extends React.PureComponent {
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
const {request} = this.props;
|
const {request} = this.props;
|
||||||
if (request.timeout && request.timeout > 0) {
|
if (request.timeout && request.timeout > 0) {
|
||||||
this._intervalHandle = setInterval(() => {
|
this.intervalHandle = setInterval(() => {
|
||||||
let {counter} = this.state;
|
let {counter} = this.state;
|
||||||
counter = Math.max(0, counter - 1);
|
counter = Math.max(0, counter - 1);
|
||||||
this.setState({counter});
|
this.setState({counter});
|
||||||
|
@ -56,7 +71,7 @@ export default class VerificationRequestToast extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
clearInterval(this._intervalHandle);
|
clearInterval(this.intervalHandle);
|
||||||
const {request} = this.props;
|
const {request} = this.props;
|
||||||
request.off("change", this._checkRequestIsPending);
|
request.off("change", this._checkRequestIsPending);
|
||||||
}
|
}
|
||||||
|
@ -110,7 +125,6 @@ export default class VerificationRequestToast extends React.PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const FormButton = sdk.getComponent("elements.FormButton");
|
|
||||||
const {request} = this.props;
|
const {request} = this.props;
|
||||||
let nameLabel;
|
let nameLabel;
|
||||||
if (request.isSelfVerification) {
|
if (request.isSelfVerification) {
|
||||||
|
@ -133,20 +147,16 @@ export default class VerificationRequestToast extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const declineLabel = this.state.counter == 0 ?
|
const declineLabel = this.state.counter === 0 ?
|
||||||
_t("Decline") :
|
_t("Decline") :
|
||||||
_t("Decline (%(counter)s)", {counter: this.state.counter});
|
_t("Decline (%(counter)s)", {counter: this.state.counter});
|
||||||
return (<div>
|
|
||||||
<div className="mx_Toast_description">{nameLabel}</div>
|
return <GenericToast
|
||||||
<div className="mx_Toast_buttons" aria-live="off">
|
description={nameLabel}
|
||||||
<FormButton label={declineLabel} kind="danger" onClick={this.cancel} />
|
acceptLabel={_t("Accept")}
|
||||||
<FormButton label={_t("Accept")} onClick={this.accept} />
|
onAccept={this.accept}
|
||||||
</div>
|
rejectLabel={declineLabel}
|
||||||
</div>);
|
onReject={this.cancel}
|
||||||
|
/>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VerificationRequestToast.propTypes = {
|
|
||||||
request: PropTypes.object.isRequired,
|
|
||||||
toastKey: PropTypes.string.isRequired,
|
|
||||||
};
|
|
58
src/toasts/BulkUnverifiedSessionsToast.ts
Normal file
58
src/toasts/BulkUnverifiedSessionsToast.ts
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
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 { _t } from '../languageHandler';
|
||||||
|
import dis from "../dispatcher/dispatcher";
|
||||||
|
import { MatrixClientPeg } from '../MatrixClientPeg';
|
||||||
|
import DeviceListener from '../DeviceListener';
|
||||||
|
import GenericToast from "../components/views/toasts/GenericToast";
|
||||||
|
import ToastStore from "../stores/ToastStore";
|
||||||
|
|
||||||
|
const TOAST_KEY = "reviewsessions";
|
||||||
|
|
||||||
|
export const showToast = (deviceIds: Set<string>) => {
|
||||||
|
const onAccept = () => {
|
||||||
|
DeviceListener.sharedInstance().dismissUnverifiedSessions(deviceIds);
|
||||||
|
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'view_user_info',
|
||||||
|
userId: MatrixClientPeg.get().getUserId(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onReject = () => {
|
||||||
|
DeviceListener.sharedInstance().dismissUnverifiedSessions(deviceIds);
|
||||||
|
};
|
||||||
|
|
||||||
|
ToastStore.sharedInstance().addOrReplaceToast({
|
||||||
|
key: TOAST_KEY,
|
||||||
|
title: _t("Review where you’re logged in"),
|
||||||
|
icon: "verification_warning",
|
||||||
|
props: {
|
||||||
|
description: _t("Verify all your sessions to ensure your account & messages are safe"),
|
||||||
|
acceptLabel: _t("Review"),
|
||||||
|
onAccept,
|
||||||
|
rejectLabel: _t("Later"),
|
||||||
|
onReject,
|
||||||
|
},
|
||||||
|
component: GenericToast,
|
||||||
|
priority: 50,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const hideToast = () => {
|
||||||
|
ToastStore.sharedInstance().dismissToast(TOAST_KEY);
|
||||||
|
};
|
106
src/toasts/SetupEncryptionToast.ts
Normal file
106
src/toasts/SetupEncryptionToast.ts
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
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 Modal from "../Modal";
|
||||||
|
import * as sdk from "../index";
|
||||||
|
import { _t } from "../languageHandler";
|
||||||
|
import DeviceListener from "../DeviceListener";
|
||||||
|
import SetupEncryptionDialog from "../components/views/dialogs/SetupEncryptionDialog";
|
||||||
|
import { accessSecretStorage } from "../CrossSigningManager";
|
||||||
|
import ToastStore from "../stores/ToastStore";
|
||||||
|
import GenericToast from "../components/views/toasts/GenericToast";
|
||||||
|
|
||||||
|
const TOAST_KEY = "setupencryption";
|
||||||
|
|
||||||
|
const getTitle = (kind: Kind) => {
|
||||||
|
switch (kind) {
|
||||||
|
case Kind.SET_UP_ENCRYPTION:
|
||||||
|
return _t("Set up encryption");
|
||||||
|
case Kind.UPGRADE_ENCRYPTION:
|
||||||
|
return _t("Encryption upgrade available");
|
||||||
|
case Kind.VERIFY_THIS_SESSION:
|
||||||
|
return _t("Verify this session");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSetupCaption = (kind: Kind) => {
|
||||||
|
switch (kind) {
|
||||||
|
case Kind.SET_UP_ENCRYPTION:
|
||||||
|
return _t("Set up");
|
||||||
|
case Kind.UPGRADE_ENCRYPTION:
|
||||||
|
return _t("Upgrade");
|
||||||
|
case Kind.VERIFY_THIS_SESSION:
|
||||||
|
return _t("Verify");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDescription = (kind: Kind) => {
|
||||||
|
switch (kind) {
|
||||||
|
case Kind.SET_UP_ENCRYPTION:
|
||||||
|
case Kind.UPGRADE_ENCRYPTION:
|
||||||
|
return _t("Verify yourself & others to keep your chats safe");
|
||||||
|
case Kind.VERIFY_THIS_SESSION:
|
||||||
|
return _t("Other users may not trust it");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum Kind {
|
||||||
|
SET_UP_ENCRYPTION = "set_up_encryption",
|
||||||
|
UPGRADE_ENCRYPTION = "upgrade_encryption",
|
||||||
|
VERIFY_THIS_SESSION = "verify_this_session",
|
||||||
|
}
|
||||||
|
|
||||||
|
const onReject = () => {
|
||||||
|
DeviceListener.sharedInstance().dismissEncryptionSetup();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const showToast = (kind: Kind) => {
|
||||||
|
const onAccept = async () => {
|
||||||
|
if (kind === Kind.VERIFY_THIS_SESSION) {
|
||||||
|
Modal.createTrackedDialog("Verify session", "Verify session", SetupEncryptionDialog,
|
||||||
|
{}, null, /* priority = */ false, /* static = */ true);
|
||||||
|
} else {
|
||||||
|
const Spinner = sdk.getComponent("elements.Spinner");
|
||||||
|
const modal = Modal.createDialog(
|
||||||
|
Spinner, null, "mx_Dialog_spinner", /* priority */ false, /* static */ true,
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
await accessSecretStorage();
|
||||||
|
} finally {
|
||||||
|
modal.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ToastStore.sharedInstance().addOrReplaceToast({
|
||||||
|
key: TOAST_KEY,
|
||||||
|
title: getTitle(kind),
|
||||||
|
icon: "verification_warning",
|
||||||
|
props: {
|
||||||
|
description: getDescription(kind),
|
||||||
|
acceptLabel: getSetupCaption(kind),
|
||||||
|
onAccept,
|
||||||
|
rejectLabel: _t("Later"),
|
||||||
|
onReject,
|
||||||
|
},
|
||||||
|
component: GenericToast,
|
||||||
|
priority: kind === Kind.VERIFY_THIS_SESSION ? 95 : 40,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const hideToast = () => {
|
||||||
|
ToastStore.sharedInstance().dismissToast(TOAST_KEY);
|
||||||
|
};
|
70
src/toasts/UnverifiedSessionToast.ts
Normal file
70
src/toasts/UnverifiedSessionToast.ts
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
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 { _t } from '../languageHandler';
|
||||||
|
import { MatrixClientPeg } from '../MatrixClientPeg';
|
||||||
|
import Modal from '../Modal';
|
||||||
|
import DeviceListener from '../DeviceListener';
|
||||||
|
import NewSessionReviewDialog from '../components/views/dialogs/NewSessionReviewDialog';
|
||||||
|
import ToastStore from "../stores/ToastStore";
|
||||||
|
import GenericToast from "../components/views/toasts/GenericToast";
|
||||||
|
|
||||||
|
function toastKey(deviceId: string) {
|
||||||
|
return "unverified_session_" + deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const showToast = (deviceId: string) => {
|
||||||
|
const cli = MatrixClientPeg.get();
|
||||||
|
|
||||||
|
const onAccept = () => {
|
||||||
|
Modal.createTrackedDialog('New Session Review', 'Starting dialog', NewSessionReviewDialog, {
|
||||||
|
userId: cli.getUserId(),
|
||||||
|
device: cli.getStoredDevice(cli.getUserId(), deviceId),
|
||||||
|
onFinished: (r) => {
|
||||||
|
if (!r) {
|
||||||
|
/* This'll come back false if the user clicks "this wasn't me" and saw a warning dialog */
|
||||||
|
DeviceListener.sharedInstance().dismissUnverifiedSessions([deviceId]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}, null, /* priority = */ false, /* static = */ true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onReject = () => {
|
||||||
|
DeviceListener.sharedInstance().dismissUnverifiedSessions([deviceId]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const device = cli.getStoredDevice(cli.getUserId(), deviceId);
|
||||||
|
|
||||||
|
ToastStore.sharedInstance().addOrReplaceToast({
|
||||||
|
key: toastKey(deviceId),
|
||||||
|
title: _t("New login. Was this you?"),
|
||||||
|
icon: "verification_warning",
|
||||||
|
props: {
|
||||||
|
description: _t(
|
||||||
|
"Verify the new login accessing your account: %(name)s", { name: device.getDisplayName()}),
|
||||||
|
acceptLabel: _t("Verify"),
|
||||||
|
onAccept,
|
||||||
|
rejectLabel: _t("Later"),
|
||||||
|
onReject,
|
||||||
|
},
|
||||||
|
component: GenericToast,
|
||||||
|
priority: 80,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const hideToast = (deviceId: string) => {
|
||||||
|
ToastStore.sharedInstance().dismissToast(deviceId);
|
||||||
|
};
|
Loading…
Add table
Add a link
Reference in a new issue