diff --git a/ts/background.ts b/ts/background.ts index c68a740a4414..3d72fc4489bf 100644 --- a/ts/background.ts +++ b/ts/background.ts @@ -154,6 +154,7 @@ import { SeenStatus } from './MessageSeenStatus'; import MessageSender from './textsecure/SendMessage'; import type AccountManager from './textsecure/AccountManager'; import { onStoryRecipientUpdate } from './util/onStoryRecipientUpdate'; +import { StoryViewModeType, StoryViewTargetType } from './types/Stories'; const MAX_ATTACHMENT_DOWNLOAD_AGE = 3600 * 72 * 1000; @@ -1879,10 +1880,19 @@ export async function startApp(): Promise { activeWindowService.registerForActive(() => notificationService.clear()); window.addEventListener('unload', () => notificationService.fastClear()); - notificationService.on('click', (id, messageId) => { + notificationService.on('click', (id, messageId, storyId) => { window.showWindow(); + if (id) { - window.Whisper.events.trigger('showConversation', id, messageId); + if (storyId) { + window.reduxActions.stories.viewStory({ + storyId, + storyViewMode: StoryViewModeType.Single, + viewTarget: StoryViewTargetType.Replies, + }); + } else { + window.Whisper.events.trigger('showConversation', id, messageId); + } } else { window.reduxActions.app.openInbox(); } diff --git a/ts/components/MyStories.tsx b/ts/components/MyStories.tsx index c9fcddcd56c7..ff35f69d5634 100644 --- a/ts/components/MyStories.tsx +++ b/ts/components/MyStories.tsx @@ -3,11 +3,12 @@ import React, { useState } from 'react'; import type { MyStoryType, StoryViewType } from '../types/Stories'; +import { StoryViewTargetType, StoryViewModeType } from '../types/Stories'; import type { LocalizerType } from '../types/Util'; import type { ViewStoryActionCreatorType } from '../state/ducks/stories'; import { ConfirmationDialog } from './ConfirmationDialog'; import { ContextMenu } from './ContextMenu'; -import { StoryViewModeType } from '../types/Stories'; + import { MessageTimestamp } from './conversation/MessageTimestamp'; import { StoryDistributionListName } from './StoryDistributionListName'; import { StoryImage } from './StoryImage'; @@ -151,7 +152,7 @@ export const MyStories = ({ viewStory({ storyId: story.messageId, storyViewMode: StoryViewModeType.User, - shouldShowDetailsModal: true, + viewTarget: StoryViewTargetType.Details, }); }, }, diff --git a/ts/components/StoryListItem.tsx b/ts/components/StoryListItem.tsx index 39bce773723b..e9ace62fb755 100644 --- a/ts/components/StoryListItem.tsx +++ b/ts/components/StoryListItem.tsx @@ -5,13 +5,14 @@ import React, { useState } from 'react'; import classNames from 'classnames'; import type { ConversationType } from '../state/ducks/conversations'; import type { ConversationStoryType, StoryViewType } from '../types/Stories'; +import { StoryViewTargetType, HasStories } from '../types/Stories'; import type { LocalizerType } from '../types/Util'; import type { PreferredBadgeSelectorType } from '../state/selectors/badges'; import type { ViewUserStoriesActionCreatorType } from '../state/ducks/stories'; import { Avatar, AvatarSize } from './Avatar'; import { ConfirmationDialog } from './ConfirmationDialog'; import { ContextMenu } from './ContextMenu'; -import { HasStories } from '../types/Stories'; + import { MessageTimestamp } from './conversation/MessageTimestamp'; import { StoryImage } from './StoryImage'; import { ThemeType } from '../types/Util'; @@ -134,7 +135,10 @@ export const StoryListItem = ({ icon: 'StoryListItem__icon--info', label: i18n('StoryListItem__info'), onClick: () => - viewUserStories({ conversationId, shouldShowDetailsModal: true }), + viewUserStories({ + conversationId, + viewTarget: StoryViewTargetType.Details, + }), }, { icon: 'StoryListItem__icon--chat', diff --git a/ts/components/StoryViewer.tsx b/ts/components/StoryViewer.tsx index 4d066a1d241f..503f59a54aa3 100644 --- a/ts/components/StoryViewer.tsx +++ b/ts/components/StoryViewer.tsx @@ -31,7 +31,11 @@ import { SendStatus } from '../messages/MessageSendState'; import { StoryDetailsModal } from './StoryDetailsModal'; import { StoryDistributionListName } from './StoryDistributionListName'; import { StoryImage } from './StoryImage'; -import { StoryViewDirectionType, StoryViewModeType } from '../types/Stories'; +import { + StoryViewDirectionType, + StoryViewModeType, + StoryViewTargetType, +} from '../types/Stories'; import { StoryViewsNRepliesModal } from './StoryViewsNRepliesModal'; import { Theme } from '../util/theme'; import { ToastType } from '../state/ducks/toast'; @@ -83,7 +87,7 @@ export type PropsType = { recentEmojis?: Array; renderEmojiPicker: (props: RenderEmojiPickerProps) => JSX.Element; replyState?: ReplyStateType; - shouldShowDetailsModal?: boolean; + viewTarget?: StoryViewTargetType; showToast: ShowToastActionCreatorType; skinTone?: number; story: StoryViewType; @@ -128,7 +132,7 @@ export const StoryViewer = ({ recentEmojis, renderEmojiPicker, replyState, - shouldShowDetailsModal, + viewTarget, showToast, skinTone, story, @@ -167,12 +171,14 @@ export const StoryViewer = ({ const conversationId = group?.id || story.sender.id; - const [hasStoryViewsNRepliesModal, setHasStoryViewsNRepliesModal] = - useState(false); - const [hasStoryDetailsModal, setHasStoryDetailsModal] = useState( - Boolean(shouldShowDetailsModal) + const [currentViewTarget, setCurrentViewTarget] = useState( + viewTarget ?? null ); + useEffect(() => { + setCurrentViewTarget(viewTarget ?? null); + }, [viewTarget]); + const onClose = useCallback(() => { viewStory({ closeViewer: true, @@ -180,12 +186,12 @@ export const StoryViewer = ({ }, [viewStory]); const onEscape = useCallback(() => { - if (hasStoryViewsNRepliesModal) { - setHasStoryViewsNRepliesModal(false); + if (currentViewTarget != null) { + setCurrentViewTarget(null); } else { onClose(); } - }, [hasStoryViewsNRepliesModal, onClose]); + }, [currentViewTarget, onClose]); useEscapeHandling(onEscape); @@ -314,8 +320,7 @@ export const StoryViewer = ({ hasActiveCall || hasConfirmHideStory || hasExpandedCaption || - hasStoryDetailsModal || - hasStoryViewsNRepliesModal || + currentViewTarget != null || isShowingContextMenu || pauseStory || Boolean(reactionEmoji); @@ -351,7 +356,7 @@ export const StoryViewer = ({ (ev: KeyboardEvent) => { // the replies modal can consume arrow keys // we don't want to navigate while someone is typing a reply - if (hasStoryViewsNRepliesModal) { + if (currentViewTarget != null) { return; } @@ -374,7 +379,7 @@ export const StoryViewer = ({ } }, [ - hasStoryViewsNRepliesModal, + currentViewTarget, canNavigateLeft, canNavigateRight, story.messageId, @@ -466,7 +471,7 @@ export const StoryViewer = ({ { icon: 'StoryListItem__icon--info', label: i18n('StoryListItem__info'), - onClick: () => setHasStoryDetailsModal(true), + onClick: () => setCurrentViewTarget(StoryViewTargetType.Details), }, { icon: 'StoryListItem__icon--delete', @@ -478,7 +483,7 @@ export const StoryViewer = ({ { icon: 'StoryListItem__icon--info', label: i18n('StoryListItem__info'), - onClick: () => setHasStoryDetailsModal(true), + onClick: () => setCurrentViewTarget(StoryViewTargetType.Details), }, { icon: 'StoryListItem__icon--hide', @@ -726,7 +731,9 @@ export const StoryViewer = ({ {(canReply || isSent) && (