Files
ThreadNet-Web/src/components/views/dialogs/IncomingSasDialog.tsx
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

263 lines
8.3 KiB
TypeScript
Raw Normal View History

2019-01-15 18:08:13 +00:00
/*
2024-09-09 14:57:16 +01:00
Copyright 2019-2024 New Vector Ltd.
2019-01-15 18:08:13 +00:00
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
2024-09-09 14:57:16 +01:00
Please see LICENSE files in the repository root for full details.
2019-01-15 18:08:13 +00:00
*/
2025-02-05 13:25:06 +00:00
import React, { type ReactNode } from "react";
import { type GeneratedSas, type ShowSasCallbacks, type Verifier, VerifierEvent } from "matrix-js-sdk/src/crypto-api";
2021-10-22 17:23:32 -05:00
import { logger } from "matrix-js-sdk/src/logger";
2021-06-29 13:11:58 +01:00
import { MatrixClientPeg } from "../../../MatrixClientPeg";
2019-01-15 18:08:13 +00:00
import { _t } from "../../../languageHandler";
2021-06-29 13:11:58 +01:00
import { mediaFromMxc } from "../../../customisations/Media";
2021-09-05 17:15:33 +02:00
import VerificationComplete from "../verification/VerificationComplete";
import VerificationCancelled from "../verification/VerificationCancelled";
import BaseAvatar from "../avatars/BaseAvatar";
import Spinner from "../elements/Spinner";
import VerificationShowSas from "../verification/VerificationShowSas";
import BaseDialog from "./BaseDialog";
import DialogButtons from "../elements/DialogButtons";
2019-01-15 18:08:13 +00:00
const PHASE_START = 0;
const PHASE_SHOW_SAS = 1;
const PHASE_WAIT_FOR_PARTNER_TO_CONFIRM = 2;
const PHASE_VERIFIED = 3;
const PHASE_CANCELLED = 4;
interface IProps {
verifier: Verifier;
onFinished(verified?: boolean): void;
2021-09-05 17:15:33 +02:00
}
2019-01-15 18:08:13 +00:00
2021-09-05 17:15:33 +02:00
interface IState {
phase: number;
sasVerified: boolean;
opponentProfile: {
// eslint-disable-next-line camelcase
avatar_url?: string;
displayname?: string;
} | null;
opponentProfileError: Error | null;
sas: GeneratedSas | null;
2021-09-05 17:15:33 +02:00
}
export default class IncomingSasDialog extends React.Component<IProps, IState> {
private showSasEvent: ShowSasCallbacks | null;
2021-09-05 17:15:33 +02:00
public constructor(props: IProps) {
2019-01-15 18:08:13 +00:00
super(props);
2019-06-20 14:17:06 -06:00
let phase = PHASE_START;
2021-09-13 18:03:05 +02:00
if (this.props.verifier.hasBeenCancelled) {
logger.log("Verifier was cancelled in the background.");
2019-06-20 14:17:06 -06:00
phase = PHASE_CANCELLED;
}
2021-09-05 17:15:33 +02:00
this.showSasEvent = null;
2019-01-15 18:08:13 +00:00
this.state = {
2019-06-20 14:17:06 -06:00
phase: phase,
2019-01-18 16:56:49 +00:00
sasVerified: false,
opponentProfile: null,
opponentProfileError: null,
2021-09-05 17:15:33 +02:00
sas: null,
2019-01-15 18:08:13 +00:00
};
}
public componentDidMount(): void {
this.props.verifier.on(VerifierEvent.ShowSas, this.onVerifierShowSas);
this.props.verifier.on(VerifierEvent.Cancel, this.onVerifierCancel);
2021-09-05 17:15:33 +02:00
this.fetchOpponentProfile();
2019-01-15 18:08:13 +00:00
}
2021-09-05 17:15:33 +02:00
public componentWillUnmount(): void {
2019-01-15 18:08:13 +00:00
if (this.state.phase !== PHASE_CANCELLED && this.state.phase !== PHASE_VERIFIED) {
2021-09-13 18:03:05 +02:00
this.props.verifier.cancel(new Error("User cancel"));
2019-01-15 18:08:13 +00:00
}
this.props.verifier.removeListener(VerifierEvent.ShowSas, this.onVerifierShowSas);
2019-01-15 18:08:13 +00:00
}
2021-09-05 17:15:33 +02:00
private async fetchOpponentProfile(): Promise<void> {
try {
const prof = await MatrixClientPeg.safeGet().getProfileInfo(this.props.verifier.userId);
this.setState({
opponentProfile: prof,
});
} catch (e) {
this.setState({
opponentProfileError: e as Error,
});
}
}
2021-09-05 17:15:33 +02:00
private onFinished = (): void => {
2019-01-15 18:08:13 +00:00
this.props.onFinished(this.state.phase === PHASE_VERIFIED);
2021-09-05 17:15:33 +02:00
};
2019-01-15 18:08:13 +00:00
2021-09-05 17:15:33 +02:00
private onCancelClick = (): void => {
2019-01-15 18:08:13 +00:00
this.props.onFinished(this.state.phase === PHASE_VERIFIED);
2021-09-05 17:15:33 +02:00
};
2019-01-15 18:08:13 +00:00
2021-09-05 17:15:33 +02:00
private onContinueClick = (): void => {
2021-06-29 13:11:58 +01:00
this.setState({ phase: PHASE_WAIT_FOR_PARTNER_TO_CONFIRM });
2019-01-15 18:08:13 +00:00
this.props.verifier
.verify()
.then(() => {
2021-06-29 13:11:58 +01:00
this.setState({ phase: PHASE_VERIFIED });
2019-01-15 18:08:13 +00:00
})
.catch((e) => {
logger.log("Verification failed", e);
2019-01-15 18:08:13 +00:00
});
2021-09-05 17:15:33 +02:00
};
2019-01-15 18:08:13 +00:00
private onVerifierShowSas = (e: ShowSasCallbacks): void => {
2021-09-05 17:15:33 +02:00
this.showSasEvent = e;
2019-01-15 18:08:13 +00:00
this.setState({
phase: PHASE_SHOW_SAS,
sas: e.sas,
});
2021-09-05 17:15:33 +02:00
};
2019-01-15 18:08:13 +00:00
2021-09-10 19:19:29 +02:00
private onVerifierCancel = (): void => {
2019-01-15 18:08:13 +00:00
this.setState({
phase: PHASE_CANCELLED,
});
2021-09-05 17:15:33 +02:00
};
2019-01-15 18:08:13 +00:00
2021-09-05 17:15:33 +02:00
private onSasMatchesClick = (): void => {
this.showSasEvent?.confirm();
2019-01-15 18:08:13 +00:00
this.setState({
phase: PHASE_WAIT_FOR_PARTNER_TO_CONFIRM,
});
2021-09-05 17:15:33 +02:00
};
2019-01-15 18:08:13 +00:00
2021-09-05 17:15:33 +02:00
private onVerifiedDoneClick = (): void => {
2019-01-15 18:08:13 +00:00
this.props.onFinished(true);
2021-09-05 17:15:33 +02:00
};
private renderPhaseStart(): ReactNode {
const isSelf = this.props.verifier.userId === MatrixClientPeg.safeGet().getUserId();
let profile;
const oppProfile = this.state.opponentProfile;
if (oppProfile) {
const url = oppProfile.avatar_url ? mediaFromMxc(oppProfile.avatar_url).getSquareThumbnailHttp(48) : null;
profile = (
<div className="mx_IncomingSasDialog_opponentProfile">
2021-07-23 10:23:45 +01:00
<BaseAvatar
name={oppProfile.displayname}
idName={this.props.verifier.userId}
url={url}
size="48px"
/>
<h2>{oppProfile.displayname}</h2>
</div>
);
} else if (this.state.opponentProfileError) {
profile = (
<div>
2021-07-23 10:23:45 +01:00
<BaseAvatar
name={this.props.verifier.userId.slice(1)}
idName={this.props.verifier.userId}
size="48px"
/>
<h2>{this.props.verifier.userId}</h2>
</div>
);
} else {
profile = <Spinner />;
}
2019-01-15 18:08:13 +00:00
const userDetailText = [
<p key="p1">{_t("encryption|verification|incoming_sas_user_dialog_text_1")}</p>,
<p key="p2">
{_t(
// NB. Below wording adjusted to singular 'session' until we have
// cross-signing
"encryption|verification|incoming_sas_user_dialog_text_2",
)}
</p>,
];
const selfDetailText = [
<p key="p1">{_t("encryption|verification|incoming_sas_device_dialog_text_1")}</p>,
<p key="p2">{_t("encryption|verification|incoming_sas_device_dialog_text_2")}</p>,
];
2019-01-15 18:08:13 +00:00
return (
<div>
{profile}
{isSelf ? selfDetailText : userDetailText}
2019-01-15 18:08:13 +00:00
<DialogButtons
primaryButton={_t("action|continue")}
2019-01-15 18:08:13 +00:00
hasCancel={true}
2021-09-05 17:15:33 +02:00
onPrimaryButtonClick={this.onContinueClick}
onCancel={this.onCancelClick}
2019-01-15 18:08:13 +00:00
/>
</div>
);
}
private renderPhaseShowSas(): ReactNode {
if (!this.showSasEvent) return null;
return (
<VerificationShowSas
2021-09-05 17:15:33 +02:00
sas={this.showSasEvent.sas}
onCancel={this.onCancelClick}
onDone={this.onSasMatchesClick}
isSelf={this.props.verifier.userId === MatrixClientPeg.safeGet().getUserId()}
2020-04-01 12:21:18 +01:00
inDialog={true}
2019-01-18 18:43:40 +00:00
/>
);
2019-01-15 18:08:13 +00:00
}
private renderPhaseWaitForPartnerToConfirm(): ReactNode {
2019-01-15 18:08:13 +00:00
return (
<div>
<Spinner />
<p>{_t("encryption|verification|incoming_sas_dialog_waiting")}</p>
2019-01-15 18:08:13 +00:00
</div>
);
}
private renderPhaseVerified(): ReactNode {
2021-09-05 17:15:33 +02:00
return <VerificationComplete onDone={this.onVerifiedDoneClick} />;
2019-01-15 18:08:13 +00:00
}
private renderPhaseCancelled(): ReactNode {
2021-09-05 17:15:33 +02:00
return <VerificationCancelled onDone={this.onCancelClick} />;
2019-01-15 18:08:13 +00:00
}
public render(): ReactNode {
2019-01-15 18:08:13 +00:00
let body;
switch (this.state.phase) {
case PHASE_START:
2021-09-05 17:15:33 +02:00
body = this.renderPhaseStart();
2019-01-15 18:08:13 +00:00
break;
case PHASE_SHOW_SAS:
2021-09-05 17:15:33 +02:00
body = this.renderPhaseShowSas();
2019-01-15 18:08:13 +00:00
break;
case PHASE_WAIT_FOR_PARTNER_TO_CONFIRM:
2021-09-05 17:15:33 +02:00
body = this.renderPhaseWaitForPartnerToConfirm();
2019-01-15 18:08:13 +00:00
break;
case PHASE_VERIFIED:
2021-09-05 17:15:33 +02:00
body = this.renderPhaseVerified();
2019-01-15 18:08:13 +00:00
break;
case PHASE_CANCELLED:
2021-09-05 17:15:33 +02:00
body = this.renderPhaseCancelled();
2019-01-15 18:08:13 +00:00
break;
}
return (
<BaseDialog
title={_t("encryption|verification|incoming_sas_dialog_title")}
onFinished={this.onFinished}
fixedWidth={false}
>
{body}
2019-01-15 18:08:13 +00:00
</BaseDialog>
);
}
}