Show error state when QR code times out loading
This commit is contained in:
parent
48545d6a83
commit
b34ea60d34
7 changed files with 107 additions and 22 deletions
|
@ -2664,6 +2664,10 @@
|
|||
"message": "Scan this code in the Signal app on your phone",
|
||||
"description": "(deleted 03/29/2023) Title of the device link screen. Also used as alt text for the QR code on the device link screen"
|
||||
},
|
||||
"icu:Install__learn-more": {
|
||||
"messageformat": "Learn more",
|
||||
"description": "Button text for learn more button during error screen"
|
||||
},
|
||||
"icu:Install__scan-this-code": {
|
||||
"messageformat": "Scan this code in the Signal app on your phone",
|
||||
"description": "Title of the device link screen. Also used as alt text for the QR code on the device link screen"
|
||||
|
@ -2701,7 +2705,11 @@
|
|||
"description": "Instructions on the device link screen"
|
||||
},
|
||||
"icu:Install__qr-failed": {
|
||||
"messageformat": "The QR code couldn't load. Check your internet and try again. <learnMoreLink>Learn more</learnMoreLink>",
|
||||
"messageformat": "(deleted 05/09/2023) The QR code couldn't load. Check your internet and try again. <learnMoreLink>Learn more</learnMoreLink>",
|
||||
"description": "Shown on the install screen if the QR code fails to load"
|
||||
},
|
||||
"icu:Install__qr-failed-load": {
|
||||
"messageformat": "The QR code couldn't load. Check your internet and try again. <retry>Retry</retry>",
|
||||
"description": "Shown on the install screen if the QR code fails to load"
|
||||
},
|
||||
"Install__support-link": {
|
||||
|
|
|
@ -55,6 +55,11 @@
|
|||
color: $color-gray-60;
|
||||
}
|
||||
|
||||
&__link {
|
||||
@include button-reset;
|
||||
color: $color-ultramarine;
|
||||
}
|
||||
|
||||
&__code {
|
||||
height: $size;
|
||||
width: $size;
|
||||
|
|
|
@ -9,12 +9,14 @@ import { openLinkInWebBrowser } from '../../util/openLinkInWebBrowser';
|
|||
import { Button, ButtonVariant } from '../Button';
|
||||
import { TitlebarDragArea } from '../TitlebarDragArea';
|
||||
import { InstallScreenSignalLogo } from './InstallScreenSignalLogo';
|
||||
import { LINK_SIGNAL_DESKTOP } from '../../types/support';
|
||||
|
||||
export enum InstallError {
|
||||
TooManyDevices,
|
||||
TooOld,
|
||||
ConnectionFailed,
|
||||
UnknownError,
|
||||
QRCodeFailed,
|
||||
}
|
||||
|
||||
export function InstallScreenErrorStep({
|
||||
|
@ -51,6 +53,14 @@ export function InstallScreenErrorStep({
|
|||
case InstallError.UnknownError:
|
||||
errorMessage = i18n('icu:installUnknownError');
|
||||
break;
|
||||
case InstallError.QRCodeFailed:
|
||||
buttonText = i18n('icu:Install__learn-more');
|
||||
errorMessage = i18n('icu:installUnknownError');
|
||||
onClickButton = () => {
|
||||
openLinkInWebBrowser(LINK_SIGNAL_DESKTOP);
|
||||
};
|
||||
shouldShowQuitButton = true;
|
||||
break;
|
||||
default:
|
||||
throw missingCaseError(error);
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ function Simulation({ finalResult }: { finalResult: Loadable<string> }) {
|
|||
OS="macOS"
|
||||
startUpdate={action('startUpdate')}
|
||||
currentVersion="v6.0.0"
|
||||
retryGetQrCode={action('retryGetQrCode')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -71,6 +72,7 @@ export function QrCodeLoading(): JSX.Element {
|
|||
OS="macOS"
|
||||
startUpdate={action('startUpdate')}
|
||||
currentVersion="v6.0.0"
|
||||
retryGetQrCode={action('retryGetQrCode')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -91,6 +93,7 @@ export function QrCodeFailedToLoad(): JSX.Element {
|
|||
OS="macOS"
|
||||
startUpdate={action('startUpdate')}
|
||||
currentVersion="v6.0.0"
|
||||
retryGetQrCode={action('retryGetQrCode')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -108,6 +111,7 @@ export function QrCodeLoaded(): JSX.Element {
|
|||
OS="macOS"
|
||||
startUpdate={action('startUpdate')}
|
||||
currentVersion="v6.0.0"
|
||||
retryGetQrCode={action('retryGetQrCode')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -158,6 +162,7 @@ export function WithUpdateKnobs({
|
|||
OS="macOS"
|
||||
startUpdate={action('startUpdate')}
|
||||
currentVersion={currentVersion}
|
||||
retryGetQrCode={action('retryGetQrCode')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -29,24 +29,23 @@ type PropsType = Readonly<{
|
|||
updates: UpdatesStateType;
|
||||
currentVersion: string;
|
||||
OS: string;
|
||||
retryGetQrCode: () => void;
|
||||
startUpdate: () => void;
|
||||
}>;
|
||||
|
||||
const QR_CODE_FAILED_LINK =
|
||||
'https://support.signal.org/hc/articles/360007320451#desktop_multiple_device';
|
||||
|
||||
const getQrCodeClassName = getClassNamesFor(
|
||||
'module-InstallScreenQrCodeNotScannedStep__qr-code'
|
||||
);
|
||||
|
||||
export function InstallScreenQrCodeNotScannedStep({
|
||||
i18n,
|
||||
provisioningUrl,
|
||||
hasExpired,
|
||||
updates,
|
||||
startUpdate,
|
||||
currentVersion,
|
||||
hasExpired,
|
||||
i18n,
|
||||
OS,
|
||||
provisioningUrl,
|
||||
retryGetQrCode,
|
||||
startUpdate,
|
||||
updates,
|
||||
}: Readonly<PropsType>): ReactElement {
|
||||
return (
|
||||
<div className="module-InstallScreenQrCodeNotScannedStep">
|
||||
|
@ -65,7 +64,11 @@ export function InstallScreenQrCodeNotScannedStep({
|
|||
)}
|
||||
|
||||
<div className="module-InstallScreenQrCodeNotScannedStep__contents">
|
||||
<InstallScreenQrCode i18n={i18n} {...provisioningUrl} />
|
||||
<InstallScreenQrCode
|
||||
i18n={i18n}
|
||||
{...provisioningUrl}
|
||||
retryGetQrCode={retryGetQrCode}
|
||||
/>
|
||||
<div className="module-InstallScreenQrCodeNotScannedStep__instructions">
|
||||
<h1>{i18n('icu:Install__scan-this-code')}</h1>
|
||||
<ol>
|
||||
|
@ -110,7 +113,7 @@ export function InstallScreenQrCodeNotScannedStep({
|
|||
}
|
||||
|
||||
function InstallScreenQrCode(
|
||||
props: Loadable<string> & { i18n: LocalizerType }
|
||||
props: Loadable<string> & { i18n: LocalizerType; retryGetQrCode: () => void }
|
||||
): ReactElement {
|
||||
const { i18n } = props;
|
||||
|
||||
|
@ -124,11 +127,24 @@ function InstallScreenQrCode(
|
|||
<span className={classNames(getQrCodeClassName('__error-message'))}>
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id="icu:Install__qr-failed"
|
||||
id="icu:Install__qr-failed-load"
|
||||
components={{
|
||||
// eslint-disable-next-line react/no-unstable-nested-components
|
||||
learnMoreLink: children => (
|
||||
<a href={QR_CODE_FAILED_LINK}>{children}</a>
|
||||
retry: children => (
|
||||
<button
|
||||
className={getQrCodeClassName('__link')}
|
||||
onClick={props.retryGetQrCode}
|
||||
onKeyDown={ev => {
|
||||
if (ev.key === 'Enter') {
|
||||
props.retryGetQrCode();
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
}
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
import type { ComponentProps, ReactElement } from 'react';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import pTimeout, { TimeoutError } from 'p-timeout';
|
||||
|
||||
import { getIntl } from '../selectors/user';
|
||||
import { getUpdatesState } from '../selectors/updates';
|
||||
|
@ -28,6 +29,9 @@ import { isRecord } from '../../util/isRecord';
|
|||
import * as Errors from '../../types/errors';
|
||||
import { normalizeDeviceName } from '../../util/normalizeDeviceName';
|
||||
import OS from '../../util/os/osMain';
|
||||
import { SECOND } from '../../util/durations';
|
||||
import { BackOff } from '../../util/BackOff';
|
||||
import { drop } from '../../util/drop';
|
||||
|
||||
type PropsType = ComponentProps<typeof InstallScreen>;
|
||||
|
||||
|
@ -53,6 +57,13 @@ const INITIAL_STATE: StateType = {
|
|||
provisioningUrl: { loadingState: LoadingState.Loading },
|
||||
};
|
||||
|
||||
const qrCodeBackOff = new BackOff([
|
||||
10 * SECOND,
|
||||
20 * SECOND,
|
||||
30 * SECOND,
|
||||
60 * SECOND,
|
||||
]);
|
||||
|
||||
function getInstallError(err: unknown): InstallError {
|
||||
if (err instanceof HTTPError) {
|
||||
switch (err.code) {
|
||||
|
@ -83,6 +94,7 @@ export function SmartInstallScreen(): ReactElement {
|
|||
const chooseDeviceNamePromiseWrapperRef = useRef(explodePromise<string>());
|
||||
|
||||
const [state, setState] = useState<StateType>(INITIAL_STATE);
|
||||
const [retryCounter, setRetryCounter] = useState(0);
|
||||
|
||||
const setProvisioningUrl = useCallback(
|
||||
(value: string) => {
|
||||
|
@ -206,12 +218,16 @@ export function SmartInstallScreen(): ReactElement {
|
|||
return result;
|
||||
};
|
||||
|
||||
void (async () => {
|
||||
async function getQRCode(): Promise<void> {
|
||||
const sleepError = new TimeoutError();
|
||||
try {
|
||||
await accountManager.registerSecondDevice(
|
||||
const qrCodePromise = accountManager.registerSecondDevice(
|
||||
updateProvisioningUrl,
|
||||
confirmNumber
|
||||
);
|
||||
const sleepMs = qrCodeBackOff.getAndIncrement();
|
||||
log.info(`InstallScreen/getQRCode: race to ${sleepMs}ms`);
|
||||
await pTimeout(qrCodePromise, sleepMs, sleepError);
|
||||
|
||||
window.IPC.removeSetupMenuItems();
|
||||
} catch (error) {
|
||||
|
@ -222,17 +238,36 @@ export function SmartInstallScreen(): ReactElement {
|
|||
if (hasCleanedUp) {
|
||||
return;
|
||||
}
|
||||
setState({
|
||||
step: InstallScreenStep.Error,
|
||||
error: getInstallError(error),
|
||||
});
|
||||
|
||||
if (qrCodeBackOff.isFull()) {
|
||||
log.error('InstallScreen/getQRCode: too many tries');
|
||||
setState({
|
||||
step: InstallScreenStep.Error,
|
||||
error: InstallError.QRCodeFailed,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (error === sleepError) {
|
||||
setState({
|
||||
step: InstallScreenStep.QrCodeNotScanned,
|
||||
provisioningUrl: { loadingState: LoadingState.LoadFailed, error },
|
||||
});
|
||||
} else {
|
||||
setState({
|
||||
step: InstallScreenStep.Error,
|
||||
error: getInstallError(error),
|
||||
});
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
drop(getQRCode());
|
||||
|
||||
return () => {
|
||||
hasCleanedUp = true;
|
||||
};
|
||||
}, [setProvisioningUrl, onQrCodeScanned]);
|
||||
}, [setProvisioningUrl, retryCounter, onQrCodeScanned]);
|
||||
|
||||
let props: PropsType;
|
||||
|
||||
|
@ -258,6 +293,10 @@ export function SmartInstallScreen(): ReactElement {
|
|||
updates,
|
||||
currentVersion: window.getVersion(),
|
||||
startUpdate,
|
||||
retryGetQrCode: () => {
|
||||
setRetryCounter(count => count + 1);
|
||||
setState(INITIAL_STATE);
|
||||
},
|
||||
OS: OS.getName(),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -5,3 +5,5 @@ export const PRODUCTION_DOWNLOAD_URL = 'https://signal.org/download/';
|
|||
export const BETA_DOWNLOAD_URL = 'https://support.signal.org/beta';
|
||||
export const UNSUPPORTED_OS_URL =
|
||||
'https://support.signal.org/hc/articles/5109141421850';
|
||||
export const LINK_SIGNAL_DESKTOP =
|
||||
'https://support.signal.org/hc/articles/360007320451#desktop_multiple_device';
|
||||
|
|
Loading…
Reference in a new issue