// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React, { useEffect, useState } from 'react'; import type { ReactNode } from 'react'; import * as log from '../../logging/log'; import { missingCaseError } from '../../util/missingCaseError'; import { About } from './About'; import { Avatar } from '../Avatar'; import { AvatarLightbox } from '../AvatarLightbox'; import type { ConversationType } from '../../state/ducks/conversations'; import { Modal } from '../Modal'; import type { LocalizerType, ThemeType } from '../../types/Util'; import { BadgeDialog } from '../BadgeDialog'; import type { BadgeType } from '../../badges/types'; import { SharedGroupNames } from '../SharedGroupNames'; import { ConfirmationDialog } from '../ConfirmationDialog'; import { RemoveGroupMemberConfirmationDialog } from './RemoveGroupMemberConfirmationDialog'; export type PropsDataType = { areWeASubscriber: boolean; areWeAdmin: boolean; badges: ReadonlyArray; contact?: ConversationType; conversation?: ConversationType; readonly i18n: LocalizerType; isAdmin: boolean; isMember: boolean; theme: ThemeType; }; type PropsActionType = { hideContactModal: () => void; openConversationInternal: ( options: Readonly<{ conversationId: string; messageId?: string; switchToAssociatedView?: boolean; }> ) => void; removeMemberFromGroup: (conversationId: string, contactId: string) => void; toggleAdmin: (conversationId: string, contactId: string) => void; toggleSafetyNumberModal: (conversationId: string) => unknown; updateConversationModelSharedGroups: (conversationId: string) => void; }; export type PropsType = PropsDataType & PropsActionType; enum ContactModalView { Default, ShowingAvatar, ShowingBadges, } enum SubModalState { None = 'None', ToggleAdmin = 'ToggleAdmin', MemberRemove = 'MemberRemove', } export const ContactModal = ({ areWeASubscriber, areWeAdmin, badges, contact, conversation, hideContactModal, i18n, isAdmin, isMember, openConversationInternal, removeMemberFromGroup, theme, toggleAdmin, toggleSafetyNumberModal, updateConversationModelSharedGroups, }: PropsType): JSX.Element => { if (!contact) { throw new Error('Contact modal opened without a matching contact'); } const [view, setView] = useState(ContactModalView.Default); const [subModalState, setSubModalState] = useState( SubModalState.None ); useEffect(() => { if (contact?.id) { // Kick off the expensive hydration of the current sharedGroupNames updateConversationModelSharedGroups(contact.id); } }, [contact?.id, updateConversationModelSharedGroups]); let modalNode: ReactNode; switch (subModalState) { case SubModalState.None: modalNode = undefined; break; case SubModalState.ToggleAdmin: if (!conversation?.id) { log.warn('ContactModal: ToggleAdmin state - missing conversationId'); modalNode = undefined; break; } modalNode = ( toggleAdmin(conversation.id, contact.id), text: isAdmin ? i18n('ContactModal--rm-admin') : i18n('ContactModal--make-admin'), }, ]} i18n={i18n} onClose={() => setSubModalState(SubModalState.None)} > {isAdmin ? i18n('ContactModal--rm-admin-info', [contact.title]) : i18n('ContactModal--make-admin-info', [contact.title])} ); break; case SubModalState.MemberRemove: if (!contact || !conversation?.id) { log.warn( 'ContactModal: MemberRemove state - missing contact or conversationId' ); modalNode = undefined; break; } modalNode = ( { setSubModalState(SubModalState.None); }} onRemove={() => { removeMemberFromGroup(conversation?.id, contact.id); }} /> ); break; default: { const state: never = subModalState; log.warn(`ContactModal: unexpected ${state}!`); modalNode = undefined; break; } } switch (view) { case ContactModalView.Default: { const preferredBadge: undefined | BadgeType = badges[0]; return (
setView(ContactModalView.ShowingAvatar)} onClickBadge={() => setView(ContactModalView.ShowingBadges)} />
{contact.title}
{contact.phoneNumber && (
{contact.phoneNumber}
)} {!contact.isMe && (
)}
{!contact.isMe && ( )} {!contact.isMe && areWeAdmin && isMember && conversation?.id && ( <> )}
{modalNode}
); } case ContactModalView.ShowingAvatar: return ( setView(ContactModalView.Default)} /> ); case ContactModalView.ShowingBadges: return ( setView(ContactModalView.Default)} title={contact.title} /> ); default: throw missingCaseError(view); } };