Detect startup after recent crashes

This commit is contained in:
Fedor Indutny 2022-01-11 12:02:46 -08:00 committed by GitHub
parent 02a732c511
commit 91f1b62bc7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 650 additions and 101 deletions

View file

@ -0,0 +1,33 @@
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React, { useState } from 'react';
import { action } from '@storybook/addon-actions';
import { storiesOf } from '@storybook/react';
import { CrashReportDialog } from './CrashReportDialog';
import { setupI18n } from '../util/setupI18n';
import { sleep } from '../util/sleep';
import enMessages from '../../_locales/en/messages.json';
const story = storiesOf('Components/CrashReportDialog', module);
const i18n = setupI18n('en', enMessages);
story.add('CrashReportDialog', () => {
const [isPending, setIsPending] = useState(false);
return (
<CrashReportDialog
i18n={i18n}
isPending={isPending}
uploadCrashReports={async () => {
setIsPending(true);
action('uploadCrashReports')();
await sleep(5000);
setIsPending(false);
}}
eraseCrashReports={action('eraseCrashReports')}
/>
);
});

View file

@ -0,0 +1,68 @@
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import type { LocalizerType } from '../types/Util';
import { Button, ButtonVariant } from './Button';
import { Modal } from './Modal';
import { Spinner } from './Spinner';
type PropsActionsType = {
uploadCrashReports: () => void;
eraseCrashReports: () => void;
};
type PropsType = {
i18n: LocalizerType;
isPending: boolean;
} & PropsActionsType;
export function CrashReportDialog(props: Readonly<PropsType>): JSX.Element {
const { i18n, isPending, uploadCrashReports, eraseCrashReports } = props;
const onEraseClick = (event: React.MouseEvent) => {
event.preventDefault();
eraseCrashReports();
};
const onSubmitClick = (event: React.MouseEvent) => {
event.preventDefault();
uploadCrashReports();
};
return (
<Modal
moduleClassName="module-Modal--important"
i18n={i18n}
title={i18n('CrashReportDialog__title')}
hasXButton
onClose={eraseCrashReports}
>
<section>{i18n('CrashReportDialog__body')}</section>
<Modal.ButtonFooter>
<Button
disabled={isPending}
onClick={onEraseClick}
variant={ButtonVariant.Secondary}
>
{i18n('CrashReportDialog__erase')}
</Button>
<Button
disabled={isPending}
onClick={onSubmitClick}
ref={button => button?.focus()}
variant={ButtonVariant.Primary}
>
{isPending ? (
<Spinner size="22px" svgSize="small" />
) : (
i18n('CrashReportDialog__submit')
)}
</Button>
</Modal.ButtonFooter>
</Modal>
);
}

View file

@ -10,6 +10,7 @@ import { storiesOf } from '@storybook/react';
import type { PropsType } from './LeftPane';
import { LeftPane, LeftPaneMode } from './LeftPane';
import { CaptchaDialog } from './CaptchaDialog';
import { CrashReportDialog } from './CrashReportDialog';
import type { ConversationType } from '../state/ducks/conversations';
import { MessageSearchResult } from './conversationList/MessageSearchResult';
import { setupI18n } from '../util/setupI18n';
@ -104,6 +105,7 @@ const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
['idle', 'required', 'pending'],
'idle'
),
crashReportCount: select('challengeReportCount', [0, 1], 0),
setChallengeStatus: action('setChallengeStatus'),
renderExpiredBuildDialog: () => <div />,
renderMainHeader: () => <div />,
@ -134,6 +136,14 @@ const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
onSkip={action('onCaptchaSkip')}
/>
),
renderCrashReportDialog: () => (
<CrashReportDialog
i18n={i18n}
isPending={false}
uploadCrashReports={action('uploadCrashReports')}
eraseCrashReports={action('eraseCrashReports')}
/>
),
selectedConversationId: undefined,
selectedMessageId: undefined,
savePreferredLeftPaneWidth: action('savePreferredLeftPaneWidth'),
@ -633,6 +643,24 @@ story.add('Captcha dialog: pending', () => (
/>
));
// Crash report flow
story.add('Crash report dialog', () => (
<LeftPane
{...useProps({
modeSpecificProps: {
mode: LeftPaneMode.Inbox,
pinnedConversations,
conversations: defaultConversations,
archivedConversations: [],
isAboutToSearchInAConversation: false,
startSearchCounter: 0,
},
crashReportCount: 42,
})}
/>
));
// Set group metadata
story.add('Group Metadata: No Timer', () => (

View file

@ -92,6 +92,7 @@ export type PropsType = {
canResizeLeftPane: boolean;
challengeStatus: 'idle' | 'required' | 'pending';
setChallengeStatus: (status: 'idle') => void;
crashReportCount: number;
theme: ThemeType;
// Action Creators
@ -144,12 +145,14 @@ export type PropsType = {
_: Readonly<{ containerWidthBreakpoint: WidthBreakpoint }>
) => JSX.Element;
renderCaptchaDialog: (props: { onSkip(): void }) => JSX.Element;
renderCrashReportDialog: () => JSX.Element;
};
export const LeftPane: React.FC<PropsType> = ({
cantAddContactToGroup,
canResizeLeftPane,
challengeStatus,
crashReportCount,
clearGroupCreationError,
clearSearch,
closeCantAddContactToGroupModal,
@ -165,6 +168,7 @@ export const LeftPane: React.FC<PropsType> = ({
openConversationInternal,
preferredWidthFromStorage,
renderCaptchaDialog,
renderCrashReportDialog,
renderExpiredBuildDialog,
renderMainHeader,
renderMessageSearchResult,
@ -641,6 +645,7 @@ export const LeftPane: React.FC<PropsType> = ({
setChallengeStatus('idle');
},
})}
{crashReportCount > 0 && renderCrashReportDialog()}
</div>
);
};