Page MenuHomePhorge

D15163.1765090646.diff
No OneTemporary

Size
8 KB
Referenced Files
None
Subscribers
None

D15163.1765090646.diff

diff --git a/web/account/log-in-form.react.js b/web/account/log-in-form.react.js
--- a/web/account/log-in-form.react.js
+++ b/web/account/log-in-form.react.js
@@ -167,11 +167,17 @@
state => state.restoreBackupState.status !== 'no_backup',
);
- if (fullBackupSupport && (qrAuthInProgress || userDataRestoreStarted)) {
+ const [errorUIShown, setErrorUIShown] = React.useState(false);
+
+ if (
+ fullBackupSupport &&
+ (qrAuthInProgress || userDataRestoreStarted || errorUIShown)
+ ) {
return (
<RestorationProgress
qrAuthInProgress={qrAuthInProgress}
userDataRestoreStarted={userDataRestoreStarted}
+ onErrorUIToggle={setErrorUIShown}
/>
);
}
diff --git a/web/account/restoration.css b/web/account/restoration.css
--- a/web/account/restoration.css
+++ b/web/account/restoration.css
@@ -177,6 +177,7 @@
flex-direction: column;
align-items: stretch;
row-gap: 8px;
+ margin-top: auto;
}
.errorButtons button {
diff --git a/web/account/restoration.react.js b/web/account/restoration.react.js
--- a/web/account/restoration.react.js
+++ b/web/account/restoration.react.js
@@ -10,8 +10,12 @@
logOutActionTypes,
useSecondaryDeviceLogOut,
} from 'lib/actions/user-actions.js';
+import { useSecondaryDeviceQRAuthContext } from 'lib/components/secondary-device-qr-auth-context-provider.react.js';
import { isLoggedIn } from 'lib/selectors/user-selectors.js';
-import { getMessageForException } from 'lib/utils/errors.js';
+import {
+ BackupIsNewerError,
+ getMessageForException,
+} from 'lib/utils/errors.js';
import { useDispatchActionPromise } from 'lib/utils/redux-promise-utils.js';
import { useDispatch } from 'lib/utils/redux-utils.js';
@@ -21,6 +25,10 @@
import LoadingIndicator from '../loading-indicator.react.js';
import { useSelector } from '../redux/redux-utils.js';
import { useStaffCanSee } from '../utils/staff-utils.js';
+import {
+ getBackupIsNewerThanAppError,
+ getVersionUnsupportedError,
+} from '../utils/version-utils.js';
type RestorationStep = 'authenticating' | 'restoring';
@@ -118,13 +126,13 @@
);
}
-type RestorationErrorProps = {
+type RestorationFailedErrorProps = {
+error: Error,
- +step: RestorationStep,
};
-
-function RestorationError(props: RestorationErrorProps): React.Node {
- const { step, error } = props;
+function RestorationFailedError(
+ props: RestorationFailedErrorProps,
+): React.Node {
+ const { error } = props;
const dispatch = useDispatch();
const dispatchActionPromise = useDispatchActionPromise();
@@ -155,7 +163,7 @@
return (
<RestorationViewContainer
title="Restoration failed"
- step={step}
+ step="restoring"
isError={true}
>
<div className={css.errorMessageContainer}>
@@ -193,31 +201,59 @@
);
}
-export type RestorationProgressProps = {
- +qrAuthInProgress: boolean,
- +userDataRestoreStarted: boolean,
+type SimpleErrorProps = {
+ +title: string,
+ +rawErrorMessage?: ?string,
+ +step: RestorationStep,
+ +onTryAgain?: () => void,
+ +children?: React.Node,
};
+function SimpleError(props: SimpleErrorProps): React.Node {
+ const { title, step, children, rawErrorMessage, onTryAgain } = props;
-function RestorationProgress(props: RestorationProgressProps): React.Node {
- const { qrAuthInProgress, userDataRestoreStarted } = props;
+ let rawErrorContents;
+ if (rawErrorMessage) {
+ rawErrorContents = (
+ <div className={css.errorDetailsContainer}>
+ <div className={css.errorDetailsHeader}>Error message:</div>
+ <div className={css.errorDetails}>{rawErrorMessage}</div>
+ </div>
+ );
+ }
- const restorationError = useSelector(state =>
- state.restoreBackupState.status === 'user_data_restore_failed'
- ? state.restoreBackupState.payload.error
- : null,
- );
+ let tryAgainButton;
+ if (onTryAgain) {
+ tryAgainButton = (
+ <Button
+ variant="filled"
+ buttonColor={buttonThemes.primary}
+ onClick={onTryAgain}
+ >
+ Try again
+ </Button>
+ );
+ }
- const step: RestorationStep =
- qrAuthInProgress && !userDataRestoreStarted
- ? 'authenticating'
- : 'restoring';
+ return (
+ <RestorationViewContainer title={title} step={step} isError={true}>
+ <div className={css.errorMessageContainer}>
+ <div className={css.modalText}>{children}</div>
+ {rawErrorContents}
+ </div>
+ <div className={css.errorButtons}>{tryAgainButton}</div>
+ </RestorationViewContainer>
+ );
+}
- if (restorationError) {
- return <RestorationError step={step} error={restorationError} />;
- }
+export type ProgressViewProps = {
+ +step: RestorationStep,
+};
+function ProgressView(props: ProgressViewProps): React.Node {
+ const { step } = props;
const title =
step === 'authenticating' ? 'Authenticating device' : 'Restoring your data';
+
return (
<RestorationViewContainer title={title} step={step} isError={false}>
<div className={css.restorationSpinnerWrapper}>
@@ -227,4 +263,114 @@
);
}
-export default RestorationProgress;
+type AuthErrorProps = {
+ +error: mixed,
+ +handleRetry: () => void,
+};
+function AuthErrorView(props: AuthErrorProps): React.Node {
+ const { error, handleRetry } = props;
+
+ const rawErrorMessage = React.useMemo(
+ () => getMessageForException(error),
+ [error],
+ );
+
+ if (
+ rawErrorMessage === 'client_version_unsupported' ||
+ rawErrorMessage === 'unsupported_version'
+ ) {
+ return (
+ <SimpleError title="App version unsupported" step="authenticating">
+ {getVersionUnsupportedError()}
+ </SimpleError>
+ );
+ } else if (rawErrorMessage === 'network_error') {
+ return (
+ <SimpleError
+ title="Network error"
+ step="authenticating"
+ onTryAgain={handleRetry}
+ >
+ Failed to contact Comm services. Please check your network connection.
+ </SimpleError>
+ );
+ } else {
+ return (
+ <SimpleError
+ title="Unknown error"
+ step="authenticating"
+ onTryAgain={handleRetry}
+ rawErrorMessage={rawErrorMessage}
+ >
+ Uhh... try again?
+ </SimpleError>
+ );
+ }
+}
+
+type RestoreErrorProps = {
+ +error: Error,
+};
+function RestoreErrorView(props: RestoreErrorProps): React.Node {
+ const { error } = props;
+
+ if (error instanceof BackupIsNewerError) {
+ return (
+ <SimpleError step="restoring" title="App out of date">
+ {getBackupIsNewerThanAppError()}
+ </SimpleError>
+ );
+ } else {
+ return <RestorationFailedError error={error} />;
+ }
+}
+
+export type RestorationViewProps = {
+ +qrAuthInProgress: boolean,
+ +userDataRestoreStarted: boolean,
+ +onErrorUIToggle?: (errorShown: boolean) => void,
+};
+function RestorationView(props: RestorationViewProps): React.Node {
+ const { qrAuthInProgress, userDataRestoreStarted, onErrorUIToggle } = props;
+
+ const { registerErrorListener } = useSecondaryDeviceQRAuthContext();
+ const [qrAuthError, setQRAuthError] = React.useState<?mixed>(null);
+ const userDataError = useSelector(state =>
+ state.restoreBackupState.status === 'user_data_restore_failed'
+ ? state.restoreBackupState.payload.error
+ : null,
+ );
+
+ React.useEffect(() => {
+ const subscription = registerErrorListener((error, isUserDataError) => {
+ if (isUserDataError) {
+ // user data errors are handled by selector
+ return;
+ }
+ setQRAuthError(error);
+ onErrorUIToggle?.(true);
+ });
+
+ return () => subscription.remove();
+ }, [registerErrorListener, onErrorUIToggle]);
+
+ const retryQRAuth = React.useCallback(() => {
+ setQRAuthError(null);
+ onErrorUIToggle?.(false);
+ }, [onErrorUIToggle]);
+
+ if (userDataError) {
+ return <RestoreErrorView error={userDataError} />;
+ } else if (qrAuthError) {
+ return <AuthErrorView error={qrAuthError} handleRetry={retryQRAuth} />;
+ }
+
+ const step: RestorationStep =
+ qrAuthInProgress && !userDataRestoreStarted
+ ? 'authenticating'
+ : 'restoring';
+
+ return <ProgressView step={step} />;
+}
+
+export default RestorationView;
diff --git a/web/utils/qr-code-utils.js b/web/utils/qr-code-utils.js
--- a/web/utils/qr-code-utils.js
+++ b/web/utils/qr-code-utils.js
@@ -22,6 +22,7 @@
BackupIsNewerError,
getMessageForException,
} from 'lib/utils/errors.js';
+import { fullBackupSupport } from 'lib/utils/services-utils.js';
import { base64DecodeBuffer, base64EncodeBuffer } from './base64-utils.js';
import { getBackupIsNewerThanAppError } from './version-utils.js';
@@ -68,6 +69,11 @@
return React.useCallback(
(error: mixed, isUserDataRestoreError?: boolean) => {
console.error('Secondary device log in error:', error);
+ if (fullBackupSupport) {
+ // for full backup, errors are handled in the restore UI
+ return;
+ }
+
const messageForException = getMessageForException(error);
if (
messageForException === 'client_version_unsupported' ||

File Metadata

Mime Type
text/plain
Expires
Sun, Dec 7, 6:57 AM (17 h, 12 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5842346
Default Alt Text
D15163.1765090646.diff (8 KB)

Event Timeline