Re-generate QR code if the channel expires before scan (#33303)
* Re-generate QR code if the channel expires before scan * Tweak styling * Remove unused state variable * Update tests * Add tests
This commit is contained in:
@@ -19,6 +19,7 @@ import {
|
||||
} from "matrix-js-sdk/src/rendezvous";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { type MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { sleep } from "matrix-js-sdk/src/utils";
|
||||
|
||||
import { Click, Mode, Phase } from "./LoginWithQR-types";
|
||||
import LoginWithQRFlow from "./LoginWithQRFlow";
|
||||
@@ -32,7 +33,6 @@ interface IProps {
|
||||
interface IState {
|
||||
phase: Phase;
|
||||
rendezvous?: MSC4108SignInWithQR;
|
||||
mediaPermissionError?: boolean;
|
||||
verificationUri?: string;
|
||||
userCode?: string;
|
||||
checkCode?: string;
|
||||
@@ -78,13 +78,15 @@ export default class LoginWithQR extends React.Component<IProps, IState> {
|
||||
}
|
||||
}
|
||||
|
||||
private async updateMode(mode: Mode): Promise<void> {
|
||||
this.setState({ phase: Phase.Loading });
|
||||
private async updateMode(mode: Mode, showLoading = true): Promise<void> {
|
||||
if (this.state.rendezvous) {
|
||||
const rendezvous = this.state.rendezvous;
|
||||
rendezvous.onFailure = undefined;
|
||||
this.setState({ rendezvous: undefined });
|
||||
}
|
||||
if (showLoading) {
|
||||
this.setState({ phase: Phase.Loading });
|
||||
}
|
||||
if (mode === Mode.Show) {
|
||||
await this.generateAndShowCode();
|
||||
}
|
||||
@@ -187,9 +189,23 @@ export default class LoginWithQR extends React.Component<IProps, IState> {
|
||||
}
|
||||
};
|
||||
|
||||
private onFailure = (reason: RendezvousFailureReason): void => {
|
||||
private onFailure = async (reason: RendezvousFailureReason): Promise<void> => {
|
||||
if (this.state.phase === Phase.Error) return; // Already in failed state
|
||||
logger.info(`Rendezvous failed: ${reason}`);
|
||||
|
||||
// Generate a new rendezvous channel & qr code if we hit expiry whilst still showing the QR code
|
||||
if (reason === ClientRendezvousFailureReason.Expired && this.state.phase === Phase.ShowingQR) {
|
||||
try {
|
||||
this.reset();
|
||||
// Add a sleep to make the UX looks less flickery and more intentional
|
||||
await sleep(1000);
|
||||
await this.updateMode(Mode.Show, false);
|
||||
return;
|
||||
} catch (e) {
|
||||
logger.warn("Failed to re-roll qr code on expiry", e);
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({ phase: Phase.Error, failureReason: reason });
|
||||
};
|
||||
|
||||
@@ -200,7 +216,6 @@ export default class LoginWithQR extends React.Component<IProps, IState> {
|
||||
failureReason: undefined,
|
||||
userCode: undefined,
|
||||
checkCode: undefined,
|
||||
mediaPermissionError: false,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -226,39 +226,39 @@ export default class LoginWithQRFlow extends React.Component<Props> {
|
||||
</>
|
||||
);
|
||||
break;
|
||||
case Phase.ShowingQR:
|
||||
if (this.props.code) {
|
||||
const data = this.props.code;
|
||||
case Phase.ShowingQR: {
|
||||
const steps = [
|
||||
_t("auth|qr_code_login|open_element_other_device", {
|
||||
brand: SdkConfig.get().brand,
|
||||
}),
|
||||
_t("auth|qr_code_login|select_qr_code", {
|
||||
scanQRCode: <strong>{_t("auth|qr_code_login|scan_qr_code")}</strong>,
|
||||
}),
|
||||
_t("auth|qr_code_login|point_the_camera"),
|
||||
_t("auth|qr_code_login|follow_remaining_instructions"),
|
||||
];
|
||||
|
||||
main = (
|
||||
<>
|
||||
<Heading as="h1" size="sm" weight="semibold">
|
||||
{_t("auth|qr_code_login|scan_code_instruction")}
|
||||
</Heading>
|
||||
<div className="mx_LoginWithQR_qrWrapper">
|
||||
<QRCode data={[{ data, mode: "byte" }]} className="mx_QRCode" />
|
||||
</div>
|
||||
<ol>
|
||||
<li>
|
||||
{_t("auth|qr_code_login|open_element_other_device", {
|
||||
brand: SdkConfig.get().brand,
|
||||
})}
|
||||
</li>
|
||||
<li>
|
||||
{_t("auth|qr_code_login|select_qr_code", {
|
||||
scanQRCode: <strong>{_t("auth|qr_code_login|scan_qr_code")}</strong>,
|
||||
})}
|
||||
</li>
|
||||
<li>{_t("auth|qr_code_login|point_the_camera")}</li>
|
||||
<li>{_t("auth|qr_code_login|follow_remaining_instructions")}</li>
|
||||
</ol>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
main = this.simpleSpinner();
|
||||
buttons = this.cancelButton();
|
||||
}
|
||||
main = (
|
||||
<>
|
||||
<Heading as="h1" size="sm" weight="semibold">
|
||||
{_t("auth|qr_code_login|scan_code_instruction")}
|
||||
</Heading>
|
||||
<div className="mx_LoginWithQR_qrWrapper">
|
||||
{this.props.code ? (
|
||||
<QRCode data={[{ data: this.props.code, mode: "byte" }]} width={200} />
|
||||
) : (
|
||||
<Spinner />
|
||||
)}
|
||||
</div>
|
||||
<ol>
|
||||
{steps.map((step, i) => (
|
||||
<li key={i}>{step}</li>
|
||||
))}
|
||||
</ol>
|
||||
</>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case Phase.Loading:
|
||||
main = this.simpleSpinner();
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user