ErrorBoundary improvements, StickerCreator logging/resiliency

This commit is contained in:
Scott Nonnenberg 2022-08-19 11:35:40 -07:00 committed by GitHub
parent 0fb45f045d
commit 6dd6a64d6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 67 additions and 19 deletions

View File

@ -12,6 +12,7 @@ import { Button } from '../../elements/Button';
import { stickersDuck } from '../../store'; import { stickersDuck } from '../../store';
import { encryptAndUpload } from '../../util/preload'; import { encryptAndUpload } from '../../util/preload';
import { useI18n } from '../../util/i18n'; import { useI18n } from '../../util/i18n';
import * as Errors from '../../../ts/types/errors';
const handleCancel = () => { const handleCancel = () => {
history.push('/add-meta'); history.push('/add-meta');
@ -42,7 +43,10 @@ export const UploadStage: React.ComponentType = () => {
actions.setPackMeta(packMeta); actions.setPackMeta(packMeta);
history.push('/share'); history.push('/share');
} catch (e) { } catch (e) {
window.SignalContext.log.error('Error uploading image:', e); window.SignalContext.log.error(
'Error uploading pack:',
Errors.toLogFormat(e)
);
actions.addToast({ actions.addToast({
key: 'StickerCreator--Toasts--errorUploading', key: 'StickerCreator--Toasts--errorUploading',
subs: [e.message], subs: [e.message],

View File

@ -185,16 +185,25 @@ window.encryptAndUpload = async (
return s; return s;
}); });
const coverStickerId = const coverStickerId = 0;
uniqueStickers.length === stickers.length ? 0 : uniqueStickers.length - 1;
const coverStickerData = stickers[coverStickerId]; const coverStickerData = stickers[coverStickerId];
if (!coverStickerData) {
window.SignalContext.log.warn(
'encryptAndUpload: No coverStickerData with ' +
`index ${coverStickerId} and ${stickers.length} total stickers`
);
}
const coverSticker = new Proto.StickerPack.Sticker(); const coverSticker = new Proto.StickerPack.Sticker();
coverSticker.id = coverStickerId; coverSticker.id = coverStickerId;
if (coverStickerData.emoji) {
if (coverStickerData?.emoji && coverSticker) {
coverSticker.emoji = coverStickerData.emoji; coverSticker.emoji = coverStickerData.emoji;
} else { } else {
coverSticker.emoji = ''; coverSticker.emoji = '';
} }
manifestProto.cover = coverSticker; manifestProto.cover = coverSticker;
const encryptedManifest = await encrypt( const encryptedManifest = await encrypt(

View File

@ -10,6 +10,7 @@ import type { ExecuteMenuRoleType } from './TitleBarContainer';
import type { LocaleMessagesType } from '../types/I18N'; import type { LocaleMessagesType } from '../types/I18N';
import type { MenuOptionsType, MenuActionType } from '../types/menu'; import type { MenuOptionsType, MenuActionType } from '../types/menu';
import type { ToastType } from '../state/ducks/toast'; import type { ToastType } from '../state/ducks/toast';
import type { ViewStoryActionCreatorType } from '../state/ducks/stories';
import { AppViewType } from '../state/ducks/app'; import { AppViewType } from '../state/ducks/app';
import { Inbox } from './Inbox'; import { Inbox } from './Inbox';
import { SmartInstallScreen } from '../state/smart/InstallScreen'; import { SmartInstallScreen } from '../state/smart/InstallScreen';
@ -28,9 +29,9 @@ type PropsType = {
renderCallManager: () => JSX.Element; renderCallManager: () => JSX.Element;
renderGlobalModalContainer: () => JSX.Element; renderGlobalModalContainer: () => JSX.Element;
isShowingStoriesView: boolean; isShowingStoriesView: boolean;
renderStories: () => JSX.Element; renderStories: (closeView: () => unknown) => JSX.Element;
hasSelectedStoryData: boolean; hasSelectedStoryData: boolean;
renderStoryViewer: () => JSX.Element; renderStoryViewer: (closeView: () => unknown) => JSX.Element;
requestVerification: ( requestVerification: (
type: 'sms' | 'voice', type: 'sms' | 'voice',
number: string, number: string,
@ -48,6 +49,8 @@ type PropsType = {
titleBarDoubleClick: () => void; titleBarDoubleClick: () => void;
toastType?: ToastType; toastType?: ToastType;
hideToast: () => unknown; hideToast: () => unknown;
toggleStoriesView: () => unknown;
viewStory: ViewStoryActionCreatorType;
} & ComponentProps<typeof Inbox>; } & ComponentProps<typeof Inbox>;
export const App = ({ export const App = ({
@ -82,6 +85,8 @@ export const App = ({
theme, theme,
titleBarDoubleClick, titleBarDoubleClick,
toastType, toastType,
toggleStoriesView,
viewStory,
}: PropsType): JSX.Element => { }: PropsType): JSX.Element => {
let contents; let contents;
@ -168,8 +173,9 @@ export const App = ({
<ToastManager hideToast={hideToast} i18n={i18n} toastType={toastType} /> <ToastManager hideToast={hideToast} i18n={i18n} toastType={toastType} />
{renderGlobalModalContainer()} {renderGlobalModalContainer()}
{renderCallManager()} {renderCallManager()}
{isShowingStoriesView && renderStories()} {isShowingStoriesView && renderStories(toggleStoriesView)}
{hasSelectedStoryData && renderStoryViewer()} {hasSelectedStoryData &&
renderStoryViewer(() => viewStory({ closeViewer: true }))}
{contents} {contents}
</div> </div>
</TitleBarContainer> </TitleBarContainer>

View File

@ -1,7 +1,7 @@
// Copyright 2021 Signal Messenger, LLC // Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import type { ReactNode } from 'react'; import type { ReactNode, ErrorInfo } from 'react';
import React from 'react'; import React from 'react';
import * as Errors from '../types/errors'; import * as Errors from '../types/errors';
@ -10,6 +10,8 @@ import { ToastType } from '../state/ducks/toast';
export type Props = { export type Props = {
children: ReactNode; children: ReactNode;
name: string;
closeView?: () => unknown;
}; };
export type State = { export type State = {
@ -24,14 +26,23 @@ export class ErrorBoundary extends React.PureComponent<Props, State> {
} }
public static getDerivedStateFromError(error: Error): State { public static getDerivedStateFromError(error: Error): State {
return { error };
}
public override componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
const { closeView, name } = this.props;
log.error( log.error(
'ErrorBoundary: captured rendering error', `ErrorBoundary/${name}: ` +
Errors.toLogFormat(error) `captured rendering error ${Errors.toLogFormat(error)}` +
`\nerrorInfo: ${errorInfo.componentStack}`
); );
if (window.reduxActions) { if (window.reduxActions) {
window.reduxActions.toast.showToast(ToastType.Error); window.reduxActions.toast.showToast(ToastType.Error);
} }
return { error }; if (closeView) {
closeView();
}
} }
public override render(): ReactNode { public override render(): ReactNode {

View File

@ -972,7 +972,10 @@ export class Message extends React.PureComponent<Props, State> {
); );
} }
if (isImage(attachments) || isVideo(attachments)) { if (
isImage(attachments) ||
(isVideo(attachments) && hasVideoScreenshot(attachments))
) {
const bottomOverlay = !isSticker && !collapseMetadata; const bottomOverlay = !isSticker && !collapseMetadata;
// We only want users to tab into this if there's more than one // We only want users to tab into this if there's more than one
const tabIndex = attachments.length > 1 ? 0 : -1; const tabIndex = attachments.length > 1 ? 0 : -1;

View File

@ -4633,13 +4633,17 @@ function extractDiffs({
Boolean(current.expireTimer) && Boolean(current.expireTimer) &&
old.expireTimer !== current.expireTimer) old.expireTimer !== current.expireTimer)
) { ) {
const expireTimer = current.expireTimer || 0;
log.info(
`extractDiffs/${logId}: generating change notifcation for new ${expireTimer} timer`
);
timerNotification = { timerNotification = {
...generateBasicMessage(), ...generateBasicMessage(),
type: 'timer-notification', type: 'timer-notification',
sourceUuid, sourceUuid,
flags: Proto.DataMessage.Flags.EXPIRATION_TIMER_UPDATE, flags: Proto.DataMessage.Flags.EXPIRATION_TIMER_UPDATE,
expirationTimerUpdate: { expirationTimerUpdate: {
expireTimer: current.expireTimer || 0, expireTimer,
sourceUuid, sourceUuid,
}, },
}; };

View File

@ -5,6 +5,7 @@ import React from 'react';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import type { Store } from 'redux'; import type { Store } from 'redux';
import { ErrorBoundary } from '../../components/ErrorBoundary';
import type { PropsType } from '../smart/ConversationView'; import type { PropsType } from '../smart/ConversationView';
import { SmartConversationView } from '../smart/ConversationView'; import { SmartConversationView } from '../smart/ConversationView';
@ -14,6 +15,16 @@ export const createConversationView = (
props: PropsType props: PropsType
): React.ReactElement => ( ): React.ReactElement => (
<Provider store={store}> <Provider store={store}>
<SmartConversationView {...props} /> <ErrorBoundary
name="createConversationView"
closeView={() => {
window.reduxActions.conversations.showConversation({
conversationId: undefined,
messageId: undefined,
});
}}
>
<SmartConversationView {...props} />
</ErrorBoundary>
</Provider> </Provider>
); );

View File

@ -51,14 +51,14 @@ const mapStateToProps = (state: StateType) => {
renderGlobalModalContainer: () => <SmartGlobalModalContainer />, renderGlobalModalContainer: () => <SmartGlobalModalContainer />,
renderLeftPane: () => <SmartLeftPane />, renderLeftPane: () => <SmartLeftPane />,
isShowingStoriesView: shouldShowStoriesView(state), isShowingStoriesView: shouldShowStoriesView(state),
renderStories: () => ( renderStories: (closeView: () => unknown) => (
<ErrorBoundary> <ErrorBoundary name="App/renderStories" closeView={closeView}>
<SmartStories /> <SmartStories />
</ErrorBoundary> </ErrorBoundary>
), ),
hasSelectedStoryData: hasSelectedStoryData(state), hasSelectedStoryData: hasSelectedStoryData(state),
renderStoryViewer: () => ( renderStoryViewer: (closeView: () => unknown) => (
<ErrorBoundary> <ErrorBoundary name="App/renderStoryViewer" closeView={closeView}>
<SmartStoryViewer /> <SmartStoryViewer />
</ErrorBoundary> </ErrorBoundary>
), ),