// Copyright 2020 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React, { type ReactNode, useEffect, useState } from 'react'; import classNames from 'classnames'; import type { Props as AvatarProps } from '../Avatar'; import { Avatar, AvatarSize, AvatarBlur } from '../Avatar'; import { ContactName } from './ContactName'; import { About } from './About'; import { GroupDescription } from './GroupDescription'; import { SharedGroupNames } from '../SharedGroupNames'; import { GroupMembersNames } from '../GroupMembersNames'; import type { LocalizerType, ThemeType } from '../../types/Util'; import type { HasStories } from '../../types/Stories'; import type { ViewUserStoriesActionCreatorType } from '../../state/ducks/stories'; import type { GroupV2Membership } from './conversation-details/ConversationDetailsMembershipList'; import { StoryViewModeType } from '../../types/Stories'; import { Button, ButtonVariant } from '../Button'; import { SafetyTipsModal } from '../SafetyTipsModal'; import { I18n } from '../I18n'; export type Props = { about?: string; acceptedMessageRequest?: boolean; fromOrAddedByTrustedContact?: boolean; groupDescription?: string; hasAvatar?: boolean; hasStories?: HasStories; id: string; i18n: LocalizerType; isDirectConvoAndHasNickname?: boolean; isMe: boolean; invitesCount?: number; isSignalConversation?: boolean; membersCount?: number; memberships: ReadonlyArray; openConversationDetails?: () => unknown; pendingAvatarDownload?: boolean; phoneNumber?: string; sharedGroupNames?: ReadonlyArray; startAvatarDownload: () => void; updateSharedGroups: (conversationId: string) => unknown; theme: ThemeType; viewUserStories: ViewUserStoriesActionCreatorType; toggleAboutContactModal: (conversationId: string) => unknown; toggleProfileNameWarningModal: (conversationType?: string) => unknown; } & Omit; const renderExtraInformation = ({ acceptedMessageRequest, conversationType, fromOrAddedByTrustedContact, i18n, isDirectConvoAndHasNickname, isMe, invitesCount, memberships, onClickProfileNameWarning, onToggleSafetyTips, openConversationDetails, phoneNumber, sharedGroupNames, }: Pick< Props, | 'avatarPlaceholderGradient' | 'acceptedMessageRequest' | 'conversationType' | 'fromOrAddedByTrustedContact' | 'i18n' | 'isDirectConvoAndHasNickname' | 'isMe' | 'invitesCount' | 'membersCount' | 'memberships' | 'openConversationDetails' | 'phoneNumber' > & Required> & { onClickProfileNameWarning: () => void; onToggleSafetyTips: (showSafetyTips: boolean) => void; }) => { if (conversationType !== 'direct' && conversationType !== 'group') { return null; } if (isMe) { return (
{i18n('icu:noteToSelfHero')}
); } const safetyTipsButton = !acceptedMessageRequest ? (
) : null; const shouldShowReviewCarefully = !acceptedMessageRequest && (conversationType === 'group' || sharedGroupNames.length <= 1); const reviewCarefullyLabel = shouldShowReviewCarefully ? (
{i18n('icu:ConversationHero--review-carefully')}
) : null; const sharedGroupsLabel = conversationType === 'direct' ? (
) : null; const nameNotVerifiedLabel = !fromOrAddedByTrustedContact && !isDirectConvoAndHasNickname ? (
( ), }} i18n={i18n} id={ conversationType === 'group' ? 'icu:ConversationHero--group-names' : 'icu:ConversationHero--profile-names' } />
) : null; const membersCountLabel = conversationType === 'group' ? (
) : null; if ( conversationType === 'direct' && sharedGroupNames.length === 0 && acceptedMessageRequest && phoneNumber ) { return null; } // Check if we should show anything at all const shouldShowAnything = Boolean(reviewCarefullyLabel) || Boolean(nameNotVerifiedLabel) || Boolean(sharedGroupsLabel) || Boolean(safetyTipsButton) || Boolean(membersCountLabel); if (!shouldShowAnything) { return null; } return (
{reviewCarefullyLabel} {nameNotVerifiedLabel} {sharedGroupsLabel} {membersCountLabel} {safetyTipsButton}
); }; function ReleaseNotesExtraInformation({ i18n, }: { i18n: LocalizerType; }): JSX.Element { return (
{i18n('icu:ConversationHero--signal-official-chat')}
{i18n('icu:ConversationHero--release-notes')}
); } export function ConversationHero({ avatarPlaceholderGradient, i18n, about, acceptedMessageRequest, avatarUrl, badge, color, conversationType, fromOrAddedByTrustedContact, groupDescription, hasAvatar, hasStories, id, isDirectConvoAndHasNickname, isMe, invitesCount, openConversationDetails, isSignalConversation, membersCount, memberships, pendingAvatarDownload, sharedGroupNames = [], phoneNumber, profileName, startAvatarDownload, theme, title, updateSharedGroups, viewUserStories, toggleAboutContactModal, toggleProfileNameWarningModal, }: Props): JSX.Element { const [isShowingSafetyTips, setIsShowingSafetyTips] = useState(false); useEffect(() => { // Kick off the expensive hydration of the current sharedGroupNames updateSharedGroups(id); }, [id, updateSharedGroups]); let avatarBlur: AvatarBlur = AvatarBlur.NoBlur; let avatarOnClick: undefined | (() => void); if (!avatarUrl && !isMe && hasAvatar) { avatarBlur = AvatarBlur.BlurPictureWithClickToView; avatarOnClick = () => { if (!pendingAvatarDownload) { startAvatarDownload(); } }; } else if (hasStories) { avatarOnClick = () => { viewUserStories({ conversationId: id, storyViewMode: StoryViewModeType.User, }); }; } let titleElem: JSX.Element | undefined; if (isMe) { titleElem = ( ); } else if (isSignalConversation || conversationType !== 'direct') { titleElem = ( ); } else if (title) { titleElem = ( ); } return ( <>

{titleElem}

{about && !isMe && (
)} {!isMe && groupDescription ? (
) : null} {!isSignalConversation && renderExtraInformation({ acceptedMessageRequest, conversationType, fromOrAddedByTrustedContact, i18n, isDirectConvoAndHasNickname, isMe, invitesCount, membersCount, memberships, onClickProfileNameWarning() { toggleProfileNameWarningModal(conversationType); }, onToggleSafetyTips(showSafetyTips: boolean) { setIsShowingSafetyTips(showSafetyTips); }, openConversationDetails, phoneNumber, sharedGroupNames, })} {isSignalConversation && }
{isShowingSafetyTips && ( { setIsShowingSafetyTips(false); }} /> )} ); }