// Copyright 2020 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React, { memo, useCallback, useMemo } from 'react'; import { useSelector } from 'react-redux'; import { useContactNameData } from '../../components/conversation/ContactName'; import { ConversationHeader, OutgoingCallButtonStyle, } from '../../components/conversation/ConversationHeader'; import { getCannotLeaveBecauseYouAreLastAdmin } from '../../components/conversation/conversation-details/ConversationDetails'; import { useMinimalConversation } from '../../hooks/useMinimalConversation'; import { CallMode } from '../../types/CallDisposition'; import { PanelType } from '../../types/Panels'; import { StoryViewModeType } from '../../types/Stories'; import { strictAssert } from '../../util/assert'; import { getAddedByForOurPendingInvitation } from '../../util/getAddedByForOurPendingInvitation'; import { getGroupMemberships } from '../../util/getGroupMemberships'; import { isConversationSMSOnly } from '../../util/isConversationSMSOnly'; import { isGroupOrAdhocCallState } from '../../util/isGroupOrAdhocCall'; import { isSignalConversation } from '../../util/isSignalConversation'; import { missingCaseError } from '../../util/missingCaseError'; import { useCallingActions } from '../ducks/calling'; import { isAnybodyElseInGroupCall } from '../ducks/callingHelpers'; import type { ConversationType } from '../ducks/conversations'; import { getConversationCallMode, useConversationsActions, } from '../ducks/conversations'; import { useSearchActions } from '../ducks/search'; import { useStoriesActions } from '../ducks/stories'; import { getPreferredBadgeSelector } from '../selectors/badges'; import { getActiveCallState, getCallSelector } from '../selectors/calling'; import { getConversationByServiceIdSelector, getConversationSelector, getHasPanelOpen, isMissingRequiredProfileSharing as getIsMissingRequiredProfileSharing, getSelectedMessageIds, } from '../selectors/conversations'; import { getHasStoriesSelector } from '../selectors/stories2'; import { getIntl, getTheme, getUserACI } from '../selectors/user'; import { useItemsActions } from '../ducks/items'; import { getLocalDeleteWarningShown } from '../selectors/items'; import { getDeleteSyncSendEnabled } from '../selectors/items-extra'; export type OwnProps = { id: string; }; const useOutgoingCallButtonStyle = ( conversation: ConversationType ): OutgoingCallButtonStyle => { const ourAci = useSelector(getUserACI); const activeCall = useSelector(getActiveCallState); const callSelector = useSelector(getCallSelector); strictAssert(ourAci, 'useOutgoingCallButtonStyle missing our uuid'); if (activeCall?.conversationId === conversation.id) { return OutgoingCallButtonStyle.None; } const conversationCallMode = getConversationCallMode(conversation); switch (conversationCallMode) { case null: return OutgoingCallButtonStyle.None; case CallMode.Direct: return OutgoingCallButtonStyle.Both; case CallMode.Group: case CallMode.Adhoc: { const call = callSelector(conversation.id); if ( isGroupOrAdhocCallState(call) && isAnybodyElseInGroupCall(call.peekInfo, ourAci) ) { return OutgoingCallButtonStyle.Join; } return OutgoingCallButtonStyle.JustVideo; } default: throw missingCaseError(conversationCallMode); } }; export const SmartConversationHeader = memo(function SmartConversationHeader({ id, }: OwnProps) { const conversationSelector = useSelector(getConversationSelector); const conversation = conversationSelector(id); if (!conversation) { throw new Error('Could not find conversation'); } const isAdmin = Boolean(conversation.areWeAdmin); const hasStoriesSelector = useSelector(getHasStoriesSelector); const hasStories = hasStoriesSelector(id); const badgeSelector = useSelector(getPreferredBadgeSelector); const badge = badgeSelector(conversation.badges); const i18n = useSelector(getIntl); const hasPanelShowing = useSelector(getHasPanelOpen); const outgoingCallButtonStyle = useOutgoingCallButtonStyle(conversation); const theme = useSelector(getTheme); const activeCall = useSelector(getActiveCallState); const hasActiveCall = Boolean(activeCall); const { destroyMessages, leaveGroup, onArchive, onMarkUnread, onMoveToInbox, pushPanelForConversation, setDisappearingMessages, setMuteExpiration, setPinned, toggleSelectMode, acceptConversation, blockAndReportSpam, blockConversation, reportSpam, deleteConversation, } = useConversationsActions(); const { onOutgoingAudioCallInConversation, onOutgoingVideoCallInConversation, } = useCallingActions(); const { searchInConversation } = useSearchActions(); const { viewUserStories } = useStoriesActions(); const conversationByServiceIdSelector = useSelector( getConversationByServiceIdSelector ); const groupMemberships = getGroupMemberships( conversation, conversationByServiceIdSelector ); const cannotLeaveBecauseYouAreLastAdmin = getCannotLeaveBecauseYouAreLastAdmin(groupMemberships.memberships, isAdmin); const selectedMessageIds = useSelector(getSelectedMessageIds); const isSelectMode = selectedMessageIds != null; const addedBy = useMemo(() => { if (conversation.type === 'group') { return getAddedByForOurPendingInvitation(conversation); } return null; }, [conversation]); const addedByName = useContactNameData(addedBy); const conversationName = useContactNameData(conversation); strictAssert(conversationName, 'conversationName is required'); const isDeleteSyncSendEnabled = useSelector(getDeleteSyncSendEnabled); const isMissingMandatoryProfileSharing = getIsMissingRequiredProfileSharing(conversation); const onConversationAccept = useCallback(() => { acceptConversation(conversation.id); }, [acceptConversation, conversation.id]); const onConversationArchive = useCallback(() => { onArchive(conversation.id); }, [onArchive, conversation.id]); const onConversationBlock = useCallback(() => { blockConversation(conversation.id); }, [blockConversation, conversation.id]); const onConversationBlockAndReportSpam = useCallback(() => { blockAndReportSpam(conversation.id); }, [blockAndReportSpam, conversation.id]); const onConversationDelete = useCallback(() => { deleteConversation(conversation.id); }, [deleteConversation, conversation.id]); const onConversationDeleteMessages = useCallback(() => { destroyMessages(conversation.id); }, [destroyMessages, conversation.id]); const onConversationDisappearingMessagesChange = useCallback( seconds => { setDisappearingMessages(conversation.id, seconds); }, [setDisappearingMessages, conversation.id] ); const onConversationLeaveGroup = useCallback(() => { leaveGroup(conversation.id); }, [leaveGroup, conversation.id]); const onConversationMarkUnread = useCallback(() => { onMarkUnread(conversation.id); }, [onMarkUnread, conversation.id]); const onConversationMuteExpirationChange = useCallback( seconds => { setMuteExpiration(conversation.id, seconds); }, [setMuteExpiration, conversation.id] ); const onConversationPin = useCallback(() => { setPinned(conversation.id, true); }, [setPinned, conversation.id]); const onConversationReportSpam = useCallback(() => { reportSpam(conversation.id); }, [reportSpam, conversation.id]); const onConversationUnarchive = useCallback(() => { onMoveToInbox(conversation.id); }, [onMoveToInbox, conversation.id]); const onConversationUnpin = useCallback(() => { setPinned(conversation.id, false); }, [setPinned, conversation.id]); const onOutgoingAudioCall = useCallback(() => { onOutgoingAudioCallInConversation(conversation.id); }, [onOutgoingAudioCallInConversation, conversation.id]); const onOutgoingVideoCall = useCallback(() => { onOutgoingVideoCallInConversation(conversation.id); }, [onOutgoingVideoCallInConversation, conversation.id]); const onSearchInConversation = useCallback(() => { searchInConversation(conversation.id); }, [searchInConversation, conversation.id]); const onSelectModeEnter = useCallback(() => { toggleSelectMode(true); }, [toggleSelectMode]); const onShowMembers = useCallback(() => { pushPanelForConversation({ type: PanelType.GroupV1Members }); }, [pushPanelForConversation]); const onViewConversationDetails = useCallback(() => { pushPanelForConversation({ type: PanelType.ConversationDetails }); }, [pushPanelForConversation]); const onViewAllMedia = useCallback(() => { pushPanelForConversation({ type: PanelType.AllMedia }); }, [pushPanelForConversation]); const onViewUserStories = useCallback(() => { viewUserStories({ conversationId: conversation.id, storyViewMode: StoryViewModeType.User, }); }, [viewUserStories, conversation.id]); const minimalConversation = useMinimalConversation(conversation); const localDeleteWarningShown = useSelector(getLocalDeleteWarningShown); const { putItem } = useItemsActions(); const setLocalDeleteWarningShown = () => putItem('localDeleteWarningShown', true); return ( ); });