// Copyright 2024 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React, { type ReactNode, useCallback, useMemo } from 'react'; import { isInSystemContacts } from '../../util/isInSystemContacts.std.js'; import { Avatar, AvatarBlur, AvatarSize } from '../Avatar.dom.js'; import { Modal } from '../Modal.dom.js'; import { UserText } from '../UserText.dom.js'; import { SharedGroupNames } from '../SharedGroupNames.dom.js'; import { About } from './About.dom.js'; import { I18n } from '../I18n.dom.js'; import { canHaveNicknameAndNote } from '../../util/nicknames.dom.js'; import { Tooltip, TooltipPlacement } from '../Tooltip.dom.js'; import { useFunEmojiLocalizer } from '../fun/useFunEmojiLocalizer.dom.js'; import { getEmojiVariantByKey, getEmojiVariantKeyByValue, isEmojiVariantValue, } from '../fun/data/emojis.std.js'; import { FunStaticEmoji } from '../fun/FunEmoji.dom.js'; import { missingEmojiPlaceholder } from '../../types/GroupMemberLabels.std.js'; import type { ConversationType } from '../../state/ducks/conversations.preload.js'; import type { LocalizerType } from '../../types/Util.std.js'; function muted(parts: Array) { return ( {parts} ); } export type PropsType = Readonly<{ i18n: LocalizerType; canAddLabel: boolean; contact: ConversationType; contactLabelEmoji: string | undefined; contactLabelString: string | undefined; contactNameColor: string | undefined; fromOrAddedByTrustedContact?: boolean; isEditMemberLabelEnabled: boolean; isSignalConnection: boolean; onClose: () => void; onOpenNotePreviewModal: () => void; pendingAvatarDownload?: boolean; sharedGroupNames: ReadonlyArray; startAvatarDownload?: (id: string) => unknown; toggleSignalConnectionsModal: () => void; toggleSafetyNumberModal: (id: string) => void; toggleProfileNameWarningModal: () => void; }>; export function AboutContactModal({ i18n, canAddLabel, contact, contactLabelEmoji, contactLabelString, contactNameColor, fromOrAddedByTrustedContact, isEditMemberLabelEnabled, isSignalConnection, pendingAvatarDownload, sharedGroupNames, startAvatarDownload, toggleSignalConnectionsModal, toggleSafetyNumberModal, toggleProfileNameWarningModal, onClose, onOpenNotePreviewModal, }: PropsType): React.JSX.Element { const { avatarUrl, hasAvatar, isMe } = contact; // If hasAvatar is true, we show the download button instead of blur const enableClickToLoad = !avatarUrl && !isMe && hasAvatar; const avatarBlur = enableClickToLoad ? AvatarBlur.BlurPictureWithClickToView : AvatarBlur.NoBlur; const avatarOnClick = useMemo(() => { if (!enableClickToLoad) { return undefined; } return () => { if (!pendingAvatarDownload && startAvatarDownload) { startAvatarDownload(contact.id); } }; }, [ contact.id, startAvatarDownload, enableClickToLoad, pendingAvatarDownload, ]); const onSignalConnectionClick = useCallback( (ev: React.MouseEvent) => { ev.preventDefault(); toggleSignalConnectionsModal(); }, [toggleSignalConnectionsModal] ); const onVerifiedClick = useCallback( (ev: React.MouseEvent) => { ev.preventDefault(); toggleSafetyNumberModal(contact.id); }, [toggleSafetyNumberModal, contact.id] ); const onProfileNameWarningClick = useCallback( (ev: React.MouseEvent) => { ev.preventDefault(); toggleProfileNameWarningModal(); }, [toggleProfileNameWarningModal] ); let statusRow: React.JSX.Element | undefined; const hasLabel = contactNameColor && contactLabelString; const shouldShowLabel = isMe && hasLabel; const shouldShowAddLabel = isMe && !hasLabel && canAddLabel && isEditMemberLabelEnabled; const emojiLocalizer = useFunEmojiLocalizer(); let labelEmojiElement; if ( shouldShowLabel && contactLabelEmoji && isEmojiVariantValue(contactLabelEmoji) ) { const emojiKey = getEmojiVariantKeyByValue(contactLabelEmoji); const labelEmojiData = getEmojiVariantByKey(emojiKey); labelEmojiElement = ( <> {' '} ); } else if (shouldShowLabel && contactLabelEmoji) { labelEmojiElement = `${missingEmojiPlaceholder} `; } if (isMe) { // No status for ourselves } else if (contact.isBlocked) { statusRow = (
{i18n('icu:AboutContactModal__blocked', { name: contact.title, })}
); } else if (!contact.acceptedMessageRequest) { statusRow = (
{i18n('icu:AboutContactModal__message-request')}
); } else if (!contact.hasMessages && !contact.profileSharing) { statusRow = (
{i18n('icu:AboutContactModal__no-dms', { name: contact.title, })}
); } return (

{isMe ? i18n('icu:AboutContactModal__title--myself') : i18n('icu:AboutContactModal__title')}

{canHaveNicknameAndNote(contact) && contact.titleNoNickname !== contact.title && contact.titleNoNickname ? ( , titleNoNickname: ( , }} /> } delay={0} > ), muted, }} /> ) : ( )}
{!isMe && !fromOrAddedByTrustedContact ? (
) : null} {!isMe && contact.isVerified ? (
) : null} {!isMe && contact.about ? (
) : null} {!isMe && isSignalConnection ? (
) : null} {!isMe && isInSystemContacts(contact) ? (
{i18n('icu:AboutContactModal__system-contact', { name: contact.systemGivenName || contact.firstName || contact.title, })}
) : null} {shouldShowLabel && (
{labelEmojiElement}
)} {shouldShowAddLabel && (
{i18n('icu:AboutContactModal__add-member-label')}
)} {contact.phoneNumber ? (
) : null} {!isMe && (
)} {contact.note && (
)} {statusRow}
); }