Adds ErrorBoundary around stories
This commit is contained in:
parent
37f9346f57
commit
d7ec2e9d82
|
@ -2377,6 +2377,14 @@
|
||||||
"message": "No conversations found",
|
"message": "No conversations found",
|
||||||
"description": "Label shown when there are no conversations to compose to"
|
"description": "Label shown when there are no conversations to compose to"
|
||||||
},
|
},
|
||||||
|
"Toast--error": {
|
||||||
|
"message": "An error has occurred",
|
||||||
|
"description": "Toast for general errors"
|
||||||
|
},
|
||||||
|
"Toast--error--action": {
|
||||||
|
"message": "Submit log",
|
||||||
|
"description": "Label for the error toast button"
|
||||||
|
},
|
||||||
"Toast--failed-to-fetch-username": {
|
"Toast--failed-to-fetch-username": {
|
||||||
"message": "Failed to fetch username. Check your connection and try again.",
|
"message": "Failed to fetch username. Check your connection and try again.",
|
||||||
"description": "Shown if request to Signal servers to find username fails"
|
"description": "Shown if request to Signal servers to find username fails"
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import * as Errors from '../types/errors';
|
||||||
|
import * as log from '../logging/log';
|
||||||
|
import { ToastType } from '../state/ducks/toast';
|
||||||
|
|
||||||
|
export type Props = {
|
||||||
|
children: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type State = {
|
||||||
|
error?: Error;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class ErrorBoundary extends React.PureComponent<Props, State> {
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = { error: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getDerivedStateFromError(error: Error): State {
|
||||||
|
log.error(
|
||||||
|
'ErrorBoundary: captured rendering error',
|
||||||
|
Errors.toLogFormat(error)
|
||||||
|
);
|
||||||
|
if (window.reduxActions) {
|
||||||
|
window.reduxActions.toast.showToast(ToastType.Error);
|
||||||
|
}
|
||||||
|
return { error };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override render(): ReactNode {
|
||||||
|
const { error } = this.state;
|
||||||
|
const { children } = this.props;
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,6 +20,21 @@ export const ToastManager = ({
|
||||||
i18n,
|
i18n,
|
||||||
toastType,
|
toastType,
|
||||||
}: PropsType): JSX.Element | null => {
|
}: PropsType): JSX.Element | null => {
|
||||||
|
if (toastType === ToastType.Error) {
|
||||||
|
return (
|
||||||
|
<Toast
|
||||||
|
autoDismissDisabled
|
||||||
|
onClose={hideToast}
|
||||||
|
toastAction={{
|
||||||
|
label: i18n('Toast--error--action'),
|
||||||
|
onClick: () => window.showDebugLog(),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{i18n('Toast--error')}
|
||||||
|
</Toast>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (toastType === ToastType.MessageBodyTooLong) {
|
if (toastType === ToastType.MessageBodyTooLong) {
|
||||||
return <ToastMessageBodyTooLong i18n={i18n} onClose={hideToast} />;
|
return <ToastMessageBodyTooLong i18n={i18n} onClose={hideToast} />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import { useBoundActions } from '../../hooks/useBoundActions';
|
import { useBoundActions } from '../../hooks/useBoundActions';
|
||||||
|
|
||||||
export enum ToastType {
|
export enum ToastType {
|
||||||
|
Error = 'Error',
|
||||||
MessageBodyTooLong = 'MessageBodyTooLong',
|
MessageBodyTooLong = 'MessageBodyTooLong',
|
||||||
StoryReact = 'StoryReact',
|
StoryReact = 'StoryReact',
|
||||||
StoryReply = 'StoryReply',
|
StoryReply = 'StoryReply',
|
||||||
|
|
|
@ -33,13 +33,16 @@ import { getConversationsStoppingSend } from '../selectors/conversations';
|
||||||
import { getIsCustomizingPreferredReactions } from '../selectors/preferredReactions';
|
import { getIsCustomizingPreferredReactions } from '../selectors/preferredReactions';
|
||||||
import { mapDispatchToProps } from '../actions';
|
import { mapDispatchToProps } from '../actions';
|
||||||
import type { SafetyNumberProps } from '../../components/SafetyNumberChangeDialog';
|
import type { SafetyNumberProps } from '../../components/SafetyNumberChangeDialog';
|
||||||
|
import { ErrorBoundary } from '../../components/ErrorBoundary';
|
||||||
|
|
||||||
const mapStateToProps = (state: StateType) => {
|
const mapStateToProps = (state: StateType) => {
|
||||||
|
const i18n = getIntl(state);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state.app,
|
...state.app,
|
||||||
conversationsStoppingSend: getConversationsStoppingSend(state),
|
conversationsStoppingSend: getConversationsStoppingSend(state),
|
||||||
getPreferredBadge: getPreferredBadgeSelector(state),
|
getPreferredBadge: getPreferredBadgeSelector(state),
|
||||||
i18n: getIntl(state),
|
i18n,
|
||||||
localeMessages: getLocaleMessages(state),
|
localeMessages: getLocaleMessages(state),
|
||||||
isCustomizingPreferredReactions: getIsCustomizingPreferredReactions(state),
|
isCustomizingPreferredReactions: getIsCustomizingPreferredReactions(state),
|
||||||
isMaximized: getIsMainWindowMaximized(state),
|
isMaximized: getIsMainWindowMaximized(state),
|
||||||
|
@ -57,9 +60,17 @@ const mapStateToProps = (state: StateType) => {
|
||||||
<SmartSafetyNumberViewer {...props} />
|
<SmartSafetyNumberViewer {...props} />
|
||||||
),
|
),
|
||||||
isShowingStoriesView: shouldShowStoriesView(state),
|
isShowingStoriesView: shouldShowStoriesView(state),
|
||||||
renderStories: () => <SmartStories />,
|
renderStories: () => (
|
||||||
|
<ErrorBoundary>
|
||||||
|
<SmartStories />
|
||||||
|
</ErrorBoundary>
|
||||||
|
),
|
||||||
selectedStoryData: getSelectedStoryData(state),
|
selectedStoryData: getSelectedStoryData(state),
|
||||||
renderStoryViewer: () => <SmartStoryViewer />,
|
renderStoryViewer: () => (
|
||||||
|
<ErrorBoundary>
|
||||||
|
<SmartStoryViewer />
|
||||||
|
</ErrorBoundary>
|
||||||
|
),
|
||||||
requestVerification: (
|
requestVerification: (
|
||||||
type: 'sms' | 'voice',
|
type: 'sms' | 'voice',
|
||||||
number: string,
|
number: string,
|
||||||
|
|
Loading…
Reference in New Issue