Files
element-web/src/components/views/dialogs/IncomingSasDialog.tsx
renovate[bot] 4a381c2a10 Update all non-major dependencies (#29194)
* Update all non-major dependencies

* Delint

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Prettier

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2025-02-05 13:25:06 +00:00

263 lines
8.3 KiB
TypeScript

/*
Copyright 2019-2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import React, { type ReactNode } from "react";
import { type GeneratedSas, type ShowSasCallbacks, type Verifier, VerifierEvent } from "matrix-js-sdk/src/crypto-api";
import { logger } from "matrix-js-sdk/src/logger";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import { _t } from "../../../languageHandler";
import { mediaFromMxc } from "../../../customisations/Media";
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";
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;
}
interface IState {
phase: number;
sasVerified: boolean;
opponentProfile: {
// eslint-disable-next-line camelcase
avatar_url?: string;
displayname?: string;
} | null;
opponentProfileError: Error | null;
sas: GeneratedSas | null;
}
export default class IncomingSasDialog extends React.Component<IProps, IState> {
private showSasEvent: ShowSasCallbacks | null;
public constructor(props: IProps) {
super(props);
let phase = PHASE_START;
if (this.props.verifier.hasBeenCancelled) {
logger.log("Verifier was cancelled in the background.");
phase = PHASE_CANCELLED;
}
this.showSasEvent = null;
this.state = {
phase: phase,
sasVerified: false,
opponentProfile: null,
opponentProfileError: null,
sas: null,
};
}
public componentDidMount(): void {
this.props.verifier.on(VerifierEvent.ShowSas, this.onVerifierShowSas);
this.props.verifier.on(VerifierEvent.Cancel, this.onVerifierCancel);
this.fetchOpponentProfile();
}
public componentWillUnmount(): void {
if (this.state.phase !== PHASE_CANCELLED && this.state.phase !== PHASE_VERIFIED) {
this.props.verifier.cancel(new Error("User cancel"));
}
this.props.verifier.removeListener(VerifierEvent.ShowSas, this.onVerifierShowSas);
}
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,
});
}
}
private onFinished = (): void => {
this.props.onFinished(this.state.phase === PHASE_VERIFIED);
};
private onCancelClick = (): void => {
this.props.onFinished(this.state.phase === PHASE_VERIFIED);
};
private onContinueClick = (): void => {
this.setState({ phase: PHASE_WAIT_FOR_PARTNER_TO_CONFIRM });
this.props.verifier
.verify()
.then(() => {
this.setState({ phase: PHASE_VERIFIED });
})
.catch((e) => {
logger.log("Verification failed", e);
});
};
private onVerifierShowSas = (e: ShowSasCallbacks): void => {
this.showSasEvent = e;
this.setState({
phase: PHASE_SHOW_SAS,
sas: e.sas,
});
};
private onVerifierCancel = (): void => {
this.setState({
phase: PHASE_CANCELLED,
});
};
private onSasMatchesClick = (): void => {
this.showSasEvent?.confirm();
this.setState({
phase: PHASE_WAIT_FOR_PARTNER_TO_CONFIRM,
});
};
private onVerifiedDoneClick = (): void => {
this.props.onFinished(true);
};
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">
<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>
<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 />;
}
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>,
];
return (
<div>
{profile}
{isSelf ? selfDetailText : userDetailText}
<DialogButtons
primaryButton={_t("action|continue")}
hasCancel={true}
onPrimaryButtonClick={this.onContinueClick}
onCancel={this.onCancelClick}
/>
</div>
);
}
private renderPhaseShowSas(): ReactNode {
if (!this.showSasEvent) return null;
return (
<VerificationShowSas
sas={this.showSasEvent.sas}
onCancel={this.onCancelClick}
onDone={this.onSasMatchesClick}
isSelf={this.props.verifier.userId === MatrixClientPeg.safeGet().getUserId()}
inDialog={true}
/>
);
}
private renderPhaseWaitForPartnerToConfirm(): ReactNode {
return (
<div>
<Spinner />
<p>{_t("encryption|verification|incoming_sas_dialog_waiting")}</p>
</div>
);
}
private renderPhaseVerified(): ReactNode {
return <VerificationComplete onDone={this.onVerifiedDoneClick} />;
}
private renderPhaseCancelled(): ReactNode {
return <VerificationCancelled onDone={this.onCancelClick} />;
}
public render(): ReactNode {
let body;
switch (this.state.phase) {
case PHASE_START:
body = this.renderPhaseStart();
break;
case PHASE_SHOW_SAS:
body = this.renderPhaseShowSas();
break;
case PHASE_WAIT_FOR_PARTNER_TO_CONFIRM:
body = this.renderPhaseWaitForPartnerToConfirm();
break;
case PHASE_VERIFIED:
body = this.renderPhaseVerified();
break;
case PHASE_CANCELLED:
body = this.renderPhaseCancelled();
break;
}
return (
<BaseDialog
title={_t("encryption|verification|incoming_sas_dialog_title")}
onFinished={this.onFinished}
fixedWidth={false}
>
{body}
</BaseDialog>
);
}
}