Show toast when reacting/replying to a Story

This commit is contained in:
Josh Perez 2022-07-12 12:41:41 -04:00 committed by GitHub
parent fc98d54326
commit 9ce4b8977d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 228 additions and 9 deletions

View file

@ -6,18 +6,20 @@ import React, { useEffect } from 'react';
import { Globals } from '@react-spring/web';
import classNames from 'classnames';
import type { ExecuteMenuRoleType } from './TitleBarContainer';
import type { LocaleMessagesType } from '../types/I18N';
import type { MenuOptionsType, MenuActionType } from '../types/menu';
import type { SelectedStoryDataType } from '../state/ducks/stories';
import type { ToastType } from '../state/ducks/toast';
import { AppViewType } from '../state/ducks/app';
import { Inbox } from './Inbox';
import { SmartInstallScreen } from '../state/smart/InstallScreen';
import { StandaloneRegistration } from './StandaloneRegistration';
import { ThemeType } from '../types/Util';
import type { LocaleMessagesType } from '../types/I18N';
import { TitleBarContainer } from './TitleBarContainer';
import { ToastManager } from './ToastManager';
import { usePageVisibility } from '../hooks/usePageVisibility';
import { useReducedMotion } from '../hooks/useReducedMotion';
import type { MenuOptionsType, MenuActionType } from '../types/menu';
import { TitleBarContainer } from './TitleBarContainer';
import type { ExecuteMenuRoleType } from './TitleBarContainer';
import type { SelectedStoryDataType } from '../state/ducks/stories';
type PropsType = {
appView: AppViewType;
@ -45,6 +47,8 @@ type PropsType = {
executeMenuRole: ExecuteMenuRoleType;
executeMenuAction: (action: MenuActionType) => void;
titleBarDoubleClick: () => void;
toastType?: ToastType;
hideToast: () => unknown;
} & ComponentProps<typeof Inbox>;
export const App = ({
@ -56,6 +60,7 @@ export const App = ({
getPreferredBadge,
hasInitialLoadCompleted,
hideMenuBar,
hideToast,
i18n,
isCustomizingPreferredReactions,
isFullScreen,
@ -81,6 +86,7 @@ export const App = ({
showWhatsNewModal,
theme,
titleBarDoubleClick,
toastType,
verifyConversationsStoppingSend,
}: PropsType): JSX.Element => {
let contents;
@ -171,6 +177,7 @@ export const App = ({
'dark-theme': theme === ThemeType.dark,
})}
>
<ToastManager hideToast={hideToast} i18n={i18n} toastType={toastType} />
{renderGlobalModalContainer()}
{renderCallManager()}
{isShowingStoriesView && renderStories()}

View file

@ -11,6 +11,7 @@ import type { EmojiPickDataType } from './emoji/EmojiPicker';
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
import type { RenderEmojiPickerProps } from './conversation/ReactionPicker';
import type { ReplyStateType, StoryViewType } from '../types/Stories';
import type { ShowToastActionCreatorType } from '../state/ducks/toast';
import type { ViewStoryActionCreatorType } from '../state/ducks/stories';
import * as log from '../logging/log';
import { AnimatedEmojiGalore } from './AnimatedEmojiGalore';
@ -24,6 +25,7 @@ import { StoryImage } from './StoryImage';
import { StoryViewDirectionType, StoryViewModeType } from '../types/Stories';
import { StoryViewsNRepliesModal } from './StoryViewsNRepliesModal';
import { Theme } from '../util/theme';
import { ToastType } from '../state/ducks/toast';
import { getAvatarColor } from '../types/Colors';
import { getStoryBackground } from '../util/getStoryBackground';
import { getStoryDuration } from '../util/getStoryDuration';
@ -66,6 +68,7 @@ export type PropsType = {
recentEmojis?: Array<string>;
renderEmojiPicker: (props: RenderEmojiPickerProps) => JSX.Element;
replyState?: ReplyStateType;
showToast: ShowToastActionCreatorType;
skinTone?: number;
story: StoryViewType;
storyViewMode?: StoryViewModeType;
@ -105,6 +108,7 @@ export const StoryViewer = ({
recentEmojis,
renderEmojiPicker,
replyState,
showToast,
skinTone,
story,
storyViewMode,
@ -650,12 +654,14 @@ export const StoryViewer = ({
onReactToStory(emoji, story);
setHasReplyModal(false);
setReactionEmoji(emoji);
showToast(ToastType.StoryReact);
}}
onReply={(message, mentions, replyTimestamp) => {
if (!isGroupStory) {
setHasReplyModal(false);
}
onReplyToStory(message, mentions, replyTimestamp, story);
showToast(ToastType.StoryReply);
}}
onSetSkinTone={onSetSkinTone}
onTextTooLong={onTextTooLong}

View file

@ -0,0 +1,52 @@
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { Meta, Story } from '@storybook/react';
import React from 'react';
import type { PropsType } from './ToastManager';
import enMessages from '../../_locales/en/messages.json';
import { ToastManager } from './ToastManager';
import { ToastType } from '../state/ducks/toast';
import { setupI18n } from '../util/setupI18n';
const i18n = setupI18n('en', enMessages);
export default {
title: 'Components/ToastManager',
component: ToastManager,
argTypes: {
hideToast: { action: true },
i18n: {
defaultValue: i18n,
},
toastType: {
defaultValue: undefined,
},
},
} as Meta;
const Template: Story<PropsType> = args => <ToastManager {...args} />;
export const UndefinedToast = Template.bind({});
UndefinedToast.args = {};
export const InvalidToast = Template.bind({});
InvalidToast.args = {
toastType: 'this is a toast that does not exist' as ToastType,
};
export const StoryReact = Template.bind({});
StoryReact.args = {
toastType: ToastType.StoryReact,
};
export const StoryReply = Template.bind({});
StoryReply.args = {
toastType: ToastType.StoryReply,
};
export const MessageBodyTooLong = Template.bind({});
MessageBodyTooLong.args = {
toastType: ToastType.MessageBodyTooLong,
};

View file

@ -0,0 +1,49 @@
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import type { LocalizerType } from '../types/Util';
import { SECOND } from '../util/durations';
import { Toast } from './Toast';
import { ToastMessageBodyTooLong } from './ToastMessageBodyTooLong';
import { ToastType } from '../state/ducks/toast';
import { strictAssert } from '../util/assert';
export type PropsType = {
hideToast: () => unknown;
i18n: LocalizerType;
toastType?: ToastType;
};
export const ToastManager = ({
hideToast,
i18n,
toastType,
}: PropsType): JSX.Element | null => {
if (toastType === ToastType.MessageBodyTooLong) {
return <ToastMessageBodyTooLong i18n={i18n} onClose={hideToast} />;
}
if (toastType === ToastType.StoryReact) {
return (
<Toast onClose={hideToast} timeout={3 * SECOND}>
{i18n('Stories__toast--sending-reaction')}
</Toast>
);
}
if (toastType === ToastType.StoryReply) {
return (
<Toast onClose={hideToast} timeout={3 * SECOND}>
{i18n('Stories__toast--sending-reply')}
</Toast>
);
}
strictAssert(
toastType === undefined,
`Unhandled toast of type: ${toastType}`
);
return null;
};