diff --git a/src/components/views/elements/QRCode.tsx b/src/components/views/elements/QRCode.tsx index 4ba8af2250..c58f9a0449 100644 --- a/src/components/views/elements/QRCode.tsx +++ b/src/components/views/elements/QRCode.tsx @@ -22,7 +22,8 @@ import { _t } from "../../../languageHandler"; import Spinner from "./Spinner"; interface IProps extends QRCodeRenderersOptions { - data: string | QRCodeSegment[]; + /** The data for the QR code. If `null`, a spinner is shown. */ + data: null | string | QRCodeSegment[]; className?: string; } @@ -33,6 +34,10 @@ const defaultOptions: QRCodeToDataURLOptions = { const QRCode: React.FC = ({ data, className, ...options }) => { const [dataUri, setUri] = React.useState(null); React.useEffect(() => { + if (data === null) { + setUri(null); + return; + } let cancelled = false; toDataURL(data, { ...defaultOptions, ...options }).then((uri) => { if (cancelled) return; diff --git a/src/components/views/elements/crypto/VerificationQRCode.tsx b/src/components/views/elements/crypto/VerificationQRCode.tsx index 193134cd93..1f25472341 100644 --- a/src/components/views/elements/crypto/VerificationQRCode.tsx +++ b/src/components/views/elements/crypto/VerificationQRCode.tsx @@ -19,14 +19,15 @@ import React from "react"; import QRCode from "../QRCode"; interface IProps { - qrCodeBytes: Buffer; + /** The data for the QR code. If `undefined`, a spinner is shown. */ + qrCodeBytes: undefined | Buffer; } export default class VerificationQRCode extends React.PureComponent { public render(): React.ReactNode { return ( diff --git a/src/components/views/right_panel/VerificationPanel.tsx b/src/components/views/right_panel/VerificationPanel.tsx index f82c974ec2..f07569e684 100644 --- a/src/components/views/right_panel/VerificationPanel.tsx +++ b/src/components/views/right_panel/VerificationPanel.tsx @@ -49,6 +49,14 @@ interface IProps { } interface IState { + /** + * The data for the QR code to display. + * + * We attempt to calculate this once the verification request transitions into the "Ready" phase. If the other + * side cannot scan QR codes, it will remain `undefined`. + */ + qrCodeBytes: Buffer | undefined; + sasEvent: ShowSasCallbacks | null; emojiButtonClicked?: boolean; reciprocateButtonClicked?: boolean; @@ -68,9 +76,12 @@ export default class VerificationPanel extends React.PureComponent

{_t("Scan this unique code")}

- + ); } @@ -145,7 +155,7 @@ export default class VerificationPanel extends React.PureComponent

{_t("Verify by scanning")}

@@ -156,7 +166,7 @@ export default class VerificationPanel extends React.PureComponent
- +
); @@ -426,6 +436,20 @@ export default class VerificationPanel extends React.PureComponent { + this.setState({ qrCodeBytes: buf }); + }, + (error) => { + console.error("Error generating QR code:", error); + }, + ); + } + const hadVerifier = this.hasVerifier; this.hasVerifier = !!request.verifier; if (!hadVerifier && this.hasVerifier) { diff --git a/test/components/views/elements/QRCode-test.tsx b/test/components/views/elements/QRCode-test.tsx index 4148366063..8c27604182 100644 --- a/test/components/views/elements/QRCode-test.tsx +++ b/test/components/views/elements/QRCode-test.tsx @@ -22,6 +22,11 @@ describe("", () => { cleanup(); }); + it("shows a spinner when data is null", async () => { + const { container } = render(); + expect(container.querySelector(".mx_Spinner")).toBeDefined(); + }); + it("renders a QR with defaults", async () => { const { container, getAllByAltText } = render(); await waitFor(() => getAllByAltText("QR Code").length === 1); diff --git a/test/components/views/right_panel/UserInfo-test.tsx b/test/components/views/right_panel/UserInfo-test.tsx index 28b072a5d8..2875f7691c 100644 --- a/test/components/views/right_panel/UserInfo-test.tsx +++ b/test/components/views/right_panel/UserInfo-test.tsx @@ -179,6 +179,7 @@ describe("", () => { Object.assign(this, { channel: { transactionId: 1 }, otherPartySupportsMethod: jest.fn(), + generateQRCode: jest.fn().mockReturnValue(new Promise(() => {})), ...opts, }); } diff --git a/test/components/views/right_panel/VerificationPanel-test.tsx b/test/components/views/right_panel/VerificationPanel-test.tsx index 73f927afcb..1dc252c304 100644 --- a/test/components/views/right_panel/VerificationPanel-test.tsx +++ b/test/components/views/right_panel/VerificationPanel-test.tsx @@ -56,7 +56,7 @@ describe("", () => { const request = makeMockVerificationRequest({ phase: Phase.Ready, }); - request.getQRCodeBytes.mockReturnValue(Buffer.from("test", "utf-8")); + request.generateQRCode.mockResolvedValue(Buffer.from("test", "utf-8")); const container = renderComponent({ request: request, layout: "dialog", @@ -81,7 +81,7 @@ describe("", () => { const request = makeMockVerificationRequest({ phase: Phase.Ready, }); - request.getQRCodeBytes.mockReturnValue(Buffer.from("test", "utf-8")); + request.generateQRCode.mockResolvedValue(Buffer.from("test", "utf-8")); const container = renderComponent({ request: request, member: new User("@other:user"), @@ -198,7 +198,7 @@ function makeMockVerificationRequest(props: Partial = {}): Object.assign(request, { cancel: jest.fn(), otherPartySupportsMethod: jest.fn().mockReturnValue(true), - getQRCodeBytes: jest.fn(), + generateQRCode: jest.fn().mockResolvedValue(undefined), ...props, }); return request as unknown as Mocked;