Show internal error toast on CDS errors
This commit is contained in:
parent
39354b11b7
commit
7632f31cf2
|
@ -643,6 +643,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"cdsMirroringErrorToast": {
|
||||||
|
"message": "Desktop ran into a Contact Discovery Service inconsistency.",
|
||||||
|
"description": "An error popup when we discovered an inconsistency between mirrored Contact Discovery Service requests."
|
||||||
|
},
|
||||||
"decryptionErrorToast": {
|
"decryptionErrorToast": {
|
||||||
"message": "Desktop ran into a decryption error from $name$, device $deviceId$",
|
"message": "Desktop ran into a decryption error from $name$, device $deviceId$",
|
||||||
"description": "An error popup when we haven't added an in-timeline error for decryption error, only for beta/internal users.",
|
"description": "An error popup when we haven't added an in-timeline error for decryption error, only for beta/internal users.",
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
// Copyright 2021 Signal Messenger, LLC
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import { action } from '@storybook/addon-actions';
|
|
||||||
import { ToastDecryptionError } from './ToastDecryptionError';
|
|
||||||
|
|
||||||
import { setupI18n } from '../util/setupI18n';
|
|
||||||
import enMessages from '../../_locales/en/messages.json';
|
|
||||||
|
|
||||||
const i18n = setupI18n('en', enMessages);
|
|
||||||
|
|
||||||
const defaultProps = {
|
|
||||||
deviceId: 3,
|
|
||||||
i18n,
|
|
||||||
name: 'Someone Somewhere',
|
|
||||||
onClose: action('onClose'),
|
|
||||||
onShowDebugLog: action('onShowDebugLog'),
|
|
||||||
};
|
|
||||||
|
|
||||||
export default {
|
|
||||||
title: 'Components/ToastDecryptionError',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const _ToastDecryptionError = (): JSX.Element => (
|
|
||||||
<ToastDecryptionError {...defaultProps} />
|
|
||||||
);
|
|
||||||
|
|
||||||
_ToastDecryptionError.story = {
|
|
||||||
name: 'ToastDecryptionError',
|
|
||||||
};
|
|
|
@ -1,43 +0,0 @@
|
||||||
// Copyright 2021 Signal Messenger, LLC
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import type { LocalizerType } from '../types/Util';
|
|
||||||
import { Toast } from './Toast';
|
|
||||||
|
|
||||||
export type ToastPropsType = {
|
|
||||||
deviceId: number;
|
|
||||||
name: string;
|
|
||||||
onShowDebugLog: () => unknown;
|
|
||||||
};
|
|
||||||
|
|
||||||
type PropsType = {
|
|
||||||
i18n: LocalizerType;
|
|
||||||
onClose: () => unknown;
|
|
||||||
} & ToastPropsType;
|
|
||||||
|
|
||||||
export const ToastDecryptionError = ({
|
|
||||||
deviceId,
|
|
||||||
i18n,
|
|
||||||
name,
|
|
||||||
onClose,
|
|
||||||
onShowDebugLog,
|
|
||||||
}: PropsType): JSX.Element => {
|
|
||||||
return (
|
|
||||||
<Toast
|
|
||||||
autoDismissDisabled
|
|
||||||
className="decryption-error"
|
|
||||||
onClose={onClose}
|
|
||||||
style={{ maxWidth: '500px' }}
|
|
||||||
toastAction={{
|
|
||||||
label: i18n('decryptionErrorToastAction'),
|
|
||||||
onClick: onShowDebugLog,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{i18n('decryptionErrorToast', {
|
|
||||||
name,
|
|
||||||
deviceId,
|
|
||||||
})}
|
|
||||||
</Toast>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { action } from '@storybook/addon-actions';
|
||||||
|
import {
|
||||||
|
ToastInternalError,
|
||||||
|
ToastInternalErrorKind,
|
||||||
|
} from './ToastInternalError';
|
||||||
|
|
||||||
|
import { setupI18n } from '../util/setupI18n';
|
||||||
|
import enMessages from '../../_locales/en/messages.json';
|
||||||
|
|
||||||
|
const i18n = setupI18n('en', enMessages);
|
||||||
|
|
||||||
|
const defaultProps = {
|
||||||
|
i18n,
|
||||||
|
onClose: action('onClose'),
|
||||||
|
onShowDebugLog: action('onShowDebugLog'),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Components/ToastInternalError',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ToastDecryptionError = (): JSX.Element => (
|
||||||
|
<ToastInternalError
|
||||||
|
kind={ToastInternalErrorKind.DecryptionError}
|
||||||
|
deviceId={3}
|
||||||
|
name="Someone Somewhere"
|
||||||
|
{...defaultProps}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
ToastDecryptionError.story = {
|
||||||
|
name: 'ToastDecryptionError',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ToastCDSMirroringError = (): JSX.Element => (
|
||||||
|
<ToastInternalError
|
||||||
|
kind={ToastInternalErrorKind.CDSMirroringError}
|
||||||
|
{...defaultProps}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
ToastDecryptionError.story = {
|
||||||
|
name: 'ToastCDSMirroringError',
|
||||||
|
};
|
|
@ -0,0 +1,63 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import type { LocalizerType } from '../types/Util';
|
||||||
|
import { missingCaseError } from '../util/missingCaseError';
|
||||||
|
import { Toast } from './Toast';
|
||||||
|
|
||||||
|
export enum ToastInternalErrorKind {
|
||||||
|
DecryptionError = 'DecryptionError',
|
||||||
|
CDSMirroringError = 'CDSMirroringError',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ToastPropsType = {
|
||||||
|
onShowDebugLog: () => unknown;
|
||||||
|
} & (
|
||||||
|
| {
|
||||||
|
kind: ToastInternalErrorKind.DecryptionError;
|
||||||
|
deviceId: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
kind: ToastInternalErrorKind.CDSMirroringError;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
type PropsType = {
|
||||||
|
i18n: LocalizerType;
|
||||||
|
onClose: () => unknown;
|
||||||
|
} & ToastPropsType;
|
||||||
|
|
||||||
|
export const ToastInternalError = (props: PropsType): JSX.Element => {
|
||||||
|
const { kind, i18n, onClose, onShowDebugLog } = props;
|
||||||
|
|
||||||
|
let body: string;
|
||||||
|
if (kind === ToastInternalErrorKind.DecryptionError) {
|
||||||
|
const { deviceId, name } = props;
|
||||||
|
|
||||||
|
body = i18n('decryptionErrorToast', {
|
||||||
|
name,
|
||||||
|
deviceId,
|
||||||
|
});
|
||||||
|
} else if (kind === ToastInternalErrorKind.CDSMirroringError) {
|
||||||
|
body = i18n('cdsMirroringErrorToast');
|
||||||
|
} else {
|
||||||
|
throw missingCaseError(kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Toast
|
||||||
|
autoDismissDisabled
|
||||||
|
className="internal-error-toast"
|
||||||
|
onClose={onClose}
|
||||||
|
style={{ maxWidth: '500px' }}
|
||||||
|
toastAction={{
|
||||||
|
label: i18n('decryptionErrorToastAction'),
|
||||||
|
onClick: onShowDebugLog,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{body}
|
||||||
|
</Toast>
|
||||||
|
);
|
||||||
|
};
|
|
@ -63,6 +63,12 @@ import type {
|
||||||
import { handleStatusCode, translateError } from './Utils';
|
import { handleStatusCode, translateError } from './Utils';
|
||||||
import * as log from '../logging/log';
|
import * as log from '../logging/log';
|
||||||
import { maybeParseUrl } from '../util/url';
|
import { maybeParseUrl } from '../util/url';
|
||||||
|
import {
|
||||||
|
ToastInternalError,
|
||||||
|
ToastInternalErrorKind,
|
||||||
|
} from '../components/ToastInternalError';
|
||||||
|
import { showToast } from '../util/showToast';
|
||||||
|
import { isProduction } from '../util/version';
|
||||||
|
|
||||||
// Note: this will break some code that expects to be able to use err.response when a
|
// Note: this will break some code that expects to be able to use err.response when a
|
||||||
// web request fails, because it will force it to text. But it is very useful for
|
// web request fails, because it will force it to text. But it is very useful for
|
||||||
|
@ -2860,6 +2866,7 @@ export function initialize({
|
||||||
|
|
||||||
const expectedMap = await expectedMapPromise;
|
const expectedMap = await expectedMapPromise;
|
||||||
let matched = 0;
|
let matched = 0;
|
||||||
|
let warnings = 0;
|
||||||
for (const [e164, { aci }] of actualMap) {
|
for (const [e164, { aci }] of actualMap) {
|
||||||
if (!aci) {
|
if (!aci) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -2869,6 +2876,7 @@ export function initialize({
|
||||||
if (expectedACI === aci) {
|
if (expectedACI === aci) {
|
||||||
matched += 1;
|
matched += 1;
|
||||||
} else {
|
} else {
|
||||||
|
warnings += 1;
|
||||||
log.warn(
|
log.warn(
|
||||||
`cdsLookup: mirrored request has aci=${aci} for ${e164}, while ` +
|
`cdsLookup: mirrored request has aci=${aci} for ${e164}, while ` +
|
||||||
`expected aci=${expectedACI}`
|
`expected aci=${expectedACI}`
|
||||||
|
@ -2876,6 +2884,14 @@ export function initialize({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (warnings !== 0 && !isProduction(window.getVersion())) {
|
||||||
|
log.info('cdsLookup: showing error toast');
|
||||||
|
showToast(ToastInternalError, {
|
||||||
|
kind: ToastInternalErrorKind.CDSMirroringError,
|
||||||
|
onShowDebugLog: () => window.showDebugLog(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
log.info(`cdsLookup: mirrored request success, matched=${matched}`);
|
log.info(`cdsLookup: mirrored request success, matched=${matched}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error('cdsLookup: mirrored request error', toLogFormat(error));
|
log.error('cdsLookup: mirrored request error', toLogFormat(error));
|
||||||
|
|
|
@ -20,7 +20,10 @@ import * as RemoteConfig from '../RemoteConfig';
|
||||||
import { Address } from '../types/Address';
|
import { Address } from '../types/Address';
|
||||||
import { QualifiedAddress } from '../types/QualifiedAddress';
|
import { QualifiedAddress } from '../types/QualifiedAddress';
|
||||||
import { UUID } from '../types/UUID';
|
import { UUID } from '../types/UUID';
|
||||||
import { ToastDecryptionError } from '../components/ToastDecryptionError';
|
import {
|
||||||
|
ToastInternalError,
|
||||||
|
ToastInternalErrorKind,
|
||||||
|
} from '../components/ToastInternalError';
|
||||||
import { showToast } from './showToast';
|
import { showToast } from './showToast';
|
||||||
import * as Errors from '../types/errors';
|
import * as Errors from '../types/errors';
|
||||||
|
|
||||||
|
@ -210,7 +213,8 @@ function maybeShowDecryptionToast(
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info(`maybeShowDecryptionToast/${logId}: Showing decryption error toast`);
|
log.info(`maybeShowDecryptionToast/${logId}: Showing decryption error toast`);
|
||||||
showToast(ToastDecryptionError, {
|
showToast(ToastInternalError, {
|
||||||
|
kind: ToastInternalErrorKind.DecryptionError,
|
||||||
deviceId,
|
deviceId,
|
||||||
name,
|
name,
|
||||||
onShowDebugLog: () => window.showDebugLog(),
|
onShowDebugLog: () => window.showDebugLog(),
|
||||||
|
|
|
@ -24,9 +24,9 @@ import type { ToastConversationMarkedUnread } from '../components/ToastConversat
|
||||||
import type { ToastConversationUnarchived } from '../components/ToastConversationUnarchived';
|
import type { ToastConversationUnarchived } from '../components/ToastConversationUnarchived';
|
||||||
import type { ToastDangerousFileType } from '../components/ToastDangerousFileType';
|
import type { ToastDangerousFileType } from '../components/ToastDangerousFileType';
|
||||||
import type {
|
import type {
|
||||||
ToastDecryptionError,
|
ToastInternalError,
|
||||||
ToastPropsType as ToastDecryptionErrorPropsType,
|
ToastPropsType as ToastInternalErrorPropsType,
|
||||||
} from '../components/ToastDecryptionError';
|
} from '../components/ToastInternalError';
|
||||||
import type { ToastDeleteForEveryoneFailed } from '../components/ToastDeleteForEveryoneFailed';
|
import type { ToastDeleteForEveryoneFailed } from '../components/ToastDeleteForEveryoneFailed';
|
||||||
import type { ToastExpired } from '../components/ToastExpired';
|
import type { ToastExpired } from '../components/ToastExpired';
|
||||||
import type {
|
import type {
|
||||||
|
@ -78,8 +78,8 @@ export function showToast(Toast: typeof ToastConversationMarkedUnread): void;
|
||||||
export function showToast(Toast: typeof ToastConversationUnarchived): void;
|
export function showToast(Toast: typeof ToastConversationUnarchived): void;
|
||||||
export function showToast(Toast: typeof ToastDangerousFileType): void;
|
export function showToast(Toast: typeof ToastDangerousFileType): void;
|
||||||
export function showToast(
|
export function showToast(
|
||||||
Toast: typeof ToastDecryptionError,
|
Toast: typeof ToastInternalError,
|
||||||
props: ToastDecryptionErrorPropsType
|
props: ToastInternalErrorPropsType
|
||||||
): void;
|
): void;
|
||||||
export function showToast(Toast: typeof ToastDeleteForEveryoneFailed): void;
|
export function showToast(Toast: typeof ToastDeleteForEveryoneFailed): void;
|
||||||
export function showToast(Toast: typeof ToastExpired): void;
|
export function showToast(Toast: typeof ToastExpired): void;
|
||||||
|
|
Loading…
Reference in New Issue