From 36c81f641619d43f9c3ed4c7567e512db5934502 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Fri, 23 Jun 2023 13:38:06 +0100 Subject: [PATCH] VerificationPanel: avoid use of `getStoredDevice` (#11129) * VerificationPanel: avoid use of `getStoredDevice` This is deprecated and doesn't work with the rust-sdk. * fix types --- .../views/right_panel/VerificationPanel.tsx | 49 +++++++++++---- .../verification/VerificationShowSas.tsx | 14 +++-- .../right_panel/VerificationPanel-test.tsx | 60 +++++++++++++++++-- 3 files changed, 100 insertions(+), 23 deletions(-) diff --git a/src/components/views/right_panel/VerificationPanel.tsx b/src/components/views/right_panel/VerificationPanel.tsx index 0abee5e81f..29af0672fb 100644 --- a/src/components/views/right_panel/VerificationPanel.tsx +++ b/src/components/views/right_panel/VerificationPanel.tsx @@ -18,15 +18,15 @@ import React from "react"; import { verificationMethods } from "matrix-js-sdk/src/crypto"; import { SCAN_QR_CODE_METHOD } from "matrix-js-sdk/src/crypto/verification/QRCode"; import { - VerificationRequest, VerificationPhase as Phase, + VerificationRequest, VerificationRequestEvent, } from "matrix-js-sdk/src/crypto-api"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { User } from "matrix-js-sdk/src/models/user"; import { logger } from "matrix-js-sdk/src/logger"; -import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo"; import { ShowQrCodeCallbacks, ShowSasCallbacks, VerifierEvent } from "matrix-js-sdk/src/crypto-api/verification"; +import { Device } from "matrix-js-sdk/src/matrix"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import VerificationQRCode from "../elements/crypto/VerificationQRCode"; @@ -52,11 +52,21 @@ interface IState { emojiButtonClicked?: boolean; reciprocateButtonClicked?: boolean; reciprocateQREvent: ShowQrCodeCallbacks | null; + + /** + * Details of the other device involved in the transaction. + * + * `undefined` if there is not (yet) another device in the transaction, or if we do not know about it. + */ + otherDeviceDetails?: Device; } export default class VerificationPanel extends React.PureComponent { private hasVerifier: boolean; + /** have we yet tried to check the other device's info */ + private haveCheckedDevice = false; + public constructor(props: IProps) { super(props); this.state = { sasEvent: null, reciprocateQREvent: null }; @@ -201,14 +211,25 @@ export default class VerificationPanel extends React.PureComponent { + if (this.haveCheckedDevice) return; + + const client = MatrixClientPeg.safeGet(); const deviceId = this.props.request?.otherDeviceId; - const userId = MatrixClientPeg.safeGet().getUserId(); - if (deviceId && userId) { - return MatrixClientPeg.safeGet().getStoredDevice(userId, deviceId); - } else { - return null; + const userId = client.getUserId(); + if (!deviceId || !userId) { + return; } + this.haveCheckedDevice = true; + + const deviceMap = await client.getCrypto()?.getUserDeviceInfo([userId]); + if (!deviceMap) return; + const userDevices = deviceMap.get(userId); + if (!userDevices) return; + this.setState({ otherDeviceDetails: userDevices.get(deviceId) }); } private renderQRReciprocatePhase(): JSX.Element { @@ -272,7 +293,7 @@ export default class VerificationPanel extends React.PureComponent => { const { request } = this.props; + + // if we have a device ID and did not have one before, fetch the device's details + this.maybeGetOtherDevice(); + const hadVerifier = this.hasVerifier; this.hasVerifier = !!request.verifier; if (!hadVerifier && this.hasVerifier) { diff --git a/src/components/views/verification/VerificationShowSas.tsx b/src/components/views/verification/VerificationShowSas.tsx index 2a429d720c..27ec1f0e18 100644 --- a/src/components/views/verification/VerificationShowSas.tsx +++ b/src/components/views/verification/VerificationShowSas.tsx @@ -15,7 +15,7 @@ limitations under the License. */ import React from "react"; -import { DeviceInfo } from "matrix-js-sdk/src//crypto/deviceinfo"; +import { Device } from "matrix-js-sdk/src/matrix"; import { GeneratedSas } from "matrix-js-sdk/src/crypto-api/verification"; import { _t, _td } from "../../../languageHandler"; @@ -26,7 +26,10 @@ import { fixupColorFonts } from "../../../utils/FontManager"; interface IProps { pending?: boolean; displayName?: string; // required if pending is true - device?: DeviceInfo; + + /** Details of the other device involved in the verification, if known */ + otherDeviceDetails?: Device; + onDone: () => void; onCancel: () => void; sas: GeneratedSas; @@ -111,10 +114,11 @@ export default class VerificationShowSas extends React.Component let text; // device shouldn't be null in this situation but it can be, eg. if the device is // logged out during verification - if (this.props.device) { + const otherDevice = this.props.otherDeviceDetails; + if (otherDevice) { text = _t("Waiting for you to verify on your other device, %(deviceName)s (%(deviceId)s)…", { - deviceName: this.props.device ? this.props.device.getDisplayName() : "", - deviceId: this.props.device ? this.props.device.deviceId : "", + deviceName: otherDevice.displayName, + deviceId: otherDevice.deviceId, }); } else { text = _t("Waiting for you to verify on your other device…"); diff --git a/test/components/views/right_panel/VerificationPanel-test.tsx b/test/components/views/right_panel/VerificationPanel-test.tsx index 99a6404acb..bee4611c6d 100644 --- a/test/components/views/right_panel/VerificationPanel-test.tsx +++ b/test/components/views/right_panel/VerificationPanel-test.tsx @@ -18,24 +18,27 @@ import { act, render, waitFor } from "@testing-library/react"; import React, { ComponentProps } from "react"; import { TypedEventEmitter } from "matrix-js-sdk/src/models/typed-event-emitter"; import { User } from "matrix-js-sdk/src/models/user"; -import { Mocked } from "jest-mock"; +import { mocked, Mocked } from "jest-mock"; import { EmojiMapping, ShowSasCallbacks, - Verifier, - VerifierEvent, - VerifierEventHandlerMap, VerificationPhase as Phase, VerificationRequest, VerificationRequestEvent, + Verifier, + VerifierEvent, + VerifierEventHandlerMap, } from "matrix-js-sdk/src/crypto-api/verification"; +import { Device, MatrixClient } from "matrix-js-sdk/src/matrix"; import VerificationPanel from "../../../../src/components/views/right_panel/VerificationPanel"; -import { stubClient } from "../../../test-utils"; +import { flushPromises, stubClient } from "../../../test-utils"; describe("", () => { + let client: MatrixClient; + beforeEach(() => { - stubClient(); + client = stubClient(); }); describe("'Ready' phase (dialog mode)", () => { @@ -130,6 +133,51 @@ describe("", () => { expect(emoji).toHaveTextContent("🦄Unicorn"); } }); + + describe("'Verify own device' flow", () => { + beforeEach(() => { + Object.defineProperty(mockRequest, "isSelfVerification", { get: () => true }); + Object.defineProperty(mockRequest, "otherDeviceId", { get: () => "other_device" }); + + const otherDeviceDetails = new Device({ + algorithms: [], + deviceId: "other_device", + keys: new Map(), + userId: "", + displayName: "my other device", + }); + + mocked(client.getCrypto()!).getUserDeviceInfo.mockResolvedValue( + new Map([[client.getSafeUserId(), new Map([["other_device", otherDeviceDetails]])]]), + ); + }); + + it("should show 'Waiting for you to verify' after confirming", async () => { + const rendered = renderComponent({ + request: mockRequest, + phase: Phase.Started, + }); + + // wait for the device to be looked up + await act(() => flushPromises()); + + // fire the ShowSas event + const sasEvent = makeMockSasCallbacks(); + mockVerifier.getShowSasCallbacks.mockReturnValue(sasEvent); + act(() => { + mockVerifier.emit(VerifierEvent.ShowSas, sasEvent); + }); + + // confirm + act(() => { + rendered.getByRole("button", { name: "They match" }).click(); + }); + + expect(rendered.container).toHaveTextContent( + "Waiting for you to verify on your other device, my other device (other_device)…", + ); + }); + }); }); });