// Copyright 2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React, { useState } from 'react'; import classNames from 'classnames'; import type { ConversationStoryType, StoryViewType } from '../types/Stories'; import type { ConversationType } from '../state/ducks/conversations'; import type { LocalizerType, ThemeType } 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 { SIGNAL_ACI } from '../types/SignalConversation'; import { StoryViewTargetType, HasStories } from '../types/Stories'; import { MessageTimestamp } from './conversation/MessageTimestamp'; import { StoryImage } from './StoryImage'; import { getAvatarColor } from '../types/Colors'; export type PropsType = Pick & { conversationId: string; getPreferredBadge: PreferredBadgeSelectorType; hasReplies?: boolean; hasRepliesFromSelf?: boolean; i18n: LocalizerType; onGoToConversation: (conversationId: string) => unknown; onHideStory: (conversationId: string) => unknown; queueStoryDownload: (storyId: string) => unknown; onMediaPlaybackStart: () => void; story: StoryViewType; theme: ThemeType; viewUserStories: ViewUserStoriesActionCreatorType; }; function StoryListItemAvatar({ acceptedMessageRequest, avatarPath, avatarStoryRing, badges, color, getPreferredBadge, i18n, isMe, profileName, sharedGroupNames, title, theme, }: Pick< ConversationType, | 'acceptedMessageRequest' | 'avatarPath' | 'color' | 'profileName' | 'sharedGroupNames' | 'title' > & { avatarStoryRing?: HasStories; badges?: ConversationType['badges']; getPreferredBadge: PreferredBadgeSelectorType; i18n: LocalizerType; isMe?: boolean; theme: ThemeType; }): JSX.Element { return ( ); } export function StoryListItem({ conversationId, getPreferredBadge, group, hasReplies, hasRepliesFromSelf, i18n, isHidden, onGoToConversation, onHideStory, onMediaPlaybackStart, queueStoryDownload, story, theme, viewUserStories, }: PropsType): JSX.Element { const [hasConfirmHideStory, setHasConfirmHideStory] = useState(false); const { attachment, isUnread, sender, timestamp } = story; const { firstName, title } = sender; const isSignalOfficial = sender.serviceId === SIGNAL_ACI; let avatarStoryRing: HasStories | undefined; if (attachment) { avatarStoryRing = isUnread ? HasStories.Unread : HasStories.Read; } let repliesElement: JSX.Element | undefined; if (group === undefined && hasRepliesFromSelf) { repliesElement =
; } else if (group && (hasReplies || hasRepliesFromSelf)) { repliesElement =
; } const menuOptions = [ { icon: 'StoryListItem__icon--hide', label: isHidden ? i18n('icu:StoryListItem__unhide') : i18n('icu:StoryListItem__hide'), onClick: () => { if (isHidden) { onHideStory(conversationId); } else { setHasConfirmHideStory(true); } }, }, ]; if (!isSignalOfficial) { menuOptions.push({ icon: 'StoryListItem__icon--info', label: i18n('icu:StoryListItem__info'), onClick: () => viewUserStories({ conversationId, viewTarget: StoryViewTargetType.Details, }), }); menuOptions.push({ icon: 'StoryListItem__icon--chat', label: i18n('icu:StoryListItem__go-to-chat'), onClick: () => onGoToConversation(conversationId), }); } return ( <> viewUserStories({ conversationId })} popperOptions={{ placement: 'bottom', strategy: 'absolute', }} >
{group ? group.title : title} {isSignalOfficial && ( )}
{!isSignalOfficial && ( )} {repliesElement}
{hasConfirmHideStory && ( onHideStory(conversationId), style: 'affirmative', text: i18n('icu:StoryListItem__hide-modal--confirm'), }, ]} i18n={i18n} onClose={() => { setHasConfirmHideStory(false); }} > {i18n('icu:StoryListItem__hide-modal--body', { name: String(firstName), })} )} ); }