// Copyright 2023 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React, { useCallback, useEffect, useState } from 'react'; import type { LocalizerType } from '../types/I18N'; import { NavSidebar, NavSidebarActionButton } from './NavSidebar'; import { CallsList } from './CallsList'; import type { ConversationType } from '../state/ducks/conversations'; import type { CallHistoryFilterOptions, CallHistoryGroup, CallHistoryPagination, } from '../types/CallDisposition'; import { CallsNewCall } from './CallsNewCallButton'; import { useEscapeHandling } from '../hooks/useEscapeHandling'; import type { ActiveCallStateType, PeekNotConnectedGroupCallType, } from '../state/ducks/calling'; import { ContextMenu } from './ContextMenu'; import { ConfirmationDialog } from './ConfirmationDialog'; import type { UnreadStats } from '../util/countUnreadStats'; import type { WidthBreakpoint } from './_util'; import type { CallLinkType } from '../types/CallLink'; import type { CallStateType } from '../state/selectors/calling'; import type { StartCallData } from './ConfirmLeaveCallModal'; import { I18n } from './I18n'; enum CallsTabSidebarView { CallsListView, NewCallView, } type CallsTabProps = Readonly<{ activeCall: ActiveCallStateType | undefined; allConversations: ReadonlyArray; otherTabsUnreadStats: UnreadStats; getCallHistoryGroupsCount: ( options: CallHistoryFilterOptions ) => Promise; getCallHistoryGroups: ( options: CallHistoryFilterOptions, pagination: CallHistoryPagination ) => Promise>; callHistoryEdition: number; canCreateCallLinks: boolean; getAdhocCall: (roomId: string) => CallStateType | undefined; getCall: (id: string) => CallStateType | undefined; getCallLink: (id: string) => CallLinkType | undefined; getConversation: (id: string) => ConversationType | void; hangUpActiveCall: (reason: string) => void; hasFailedStorySends: boolean; hasPendingUpdate: boolean; i18n: LocalizerType; navTabsCollapsed: boolean; onClearCallHistory: () => void; onMarkCallHistoryRead: (conversationId: string, callId: string) => void; onToggleNavTabsCollapse: (navTabsCollapsed: boolean) => void; onCreateCallLink: () => void; onOutgoingAudioCallInConversation: (conversationId: string) => void; onOutgoingVideoCallInConversation: (conversationId: string) => void; peekNotConnectedGroupCall: (options: PeekNotConnectedGroupCallType) => void; preferredLeftPaneWidth: number; renderCallLinkDetails: ( roomId: string, callHistoryGroup: CallHistoryGroup, onClose: () => void ) => JSX.Element; renderConversationDetails: ( conversationId: string, callHistoryGroup: CallHistoryGroup | null ) => JSX.Element; renderToastManager: (_: { containerWidthBreakpoint: WidthBreakpoint; }) => JSX.Element; regionCode: string | undefined; savePreferredLeftPaneWidth: (preferredLeftPaneWidth: number) => void; startCallLinkLobbyByRoomId: (options: { roomId: string }) => void; toggleConfirmLeaveCallModal: (options: StartCallData | null) => void; togglePip: () => void; }>; export type CallsTabSelectedView = | { type: 'conversation'; conversationId: string; callHistoryGroup: CallHistoryGroup | null; } | { type: 'callLink'; roomId: string; callHistoryGroup: CallHistoryGroup; }; export function CallsTab({ activeCall, allConversations, otherTabsUnreadStats, getCallHistoryGroupsCount, getCallHistoryGroups, callHistoryEdition, canCreateCallLinks, getAdhocCall, getCall, getCallLink, getConversation, hangUpActiveCall, hasFailedStorySends, hasPendingUpdate, i18n, navTabsCollapsed, onClearCallHistory, onMarkCallHistoryRead, onToggleNavTabsCollapse, onCreateCallLink, onOutgoingAudioCallInConversation, onOutgoingVideoCallInConversation, peekNotConnectedGroupCall, preferredLeftPaneWidth, renderCallLinkDetails, renderConversationDetails, renderToastManager, regionCode, savePreferredLeftPaneWidth, startCallLinkLobbyByRoomId, toggleConfirmLeaveCallModal, togglePip, }: CallsTabProps): JSX.Element { const [sidebarView, setSidebarView] = useState( CallsTabSidebarView.CallsListView ); const [selectedView, setSelectedViewInner] = useState(null); const [selectedViewKey, setSelectedViewKey] = useState(() => 1); const [ confirmClearCallHistoryDialogOpen, setConfirmClearCallHistoryDialogOpen, ] = useState(false); const updateSelectedView = useCallback( (nextSelected: CallsTabSelectedView | null) => { setSelectedViewInner(nextSelected); setSelectedViewKey(key => key + 1); }, [] ); const updateSidebarView = useCallback( (newSidebarView: CallsTabSidebarView) => { setSidebarView(newSidebarView); updateSelectedView(null); }, [updateSelectedView] ); const onCloseSelectedView = useCallback(() => { updateSelectedView(null); }, [updateSelectedView]); useEscapeHandling( sidebarView === CallsTabSidebarView.NewCallView ? () => { updateSidebarView(CallsTabSidebarView.CallsListView); } : undefined ); const handleOpenClearCallHistoryDialog = useCallback(() => { setConfirmClearCallHistoryDialogOpen(true); }, []); const handleCloseClearCallHistoryDialog = useCallback(() => { setConfirmClearCallHistoryDialogOpen(false); }, []); const handleOutgoingAudioCallInConversation = useCallback( (conversationId: string) => { onOutgoingAudioCallInConversation(conversationId); updateSidebarView(CallsTabSidebarView.CallsListView); }, [updateSidebarView, onOutgoingAudioCallInConversation] ); const handleOutgoingVideoCallInConversation = useCallback( (conversationId: string) => { onOutgoingVideoCallInConversation(conversationId); updateSidebarView(CallsTabSidebarView.CallsListView); }, [updateSidebarView, onOutgoingVideoCallInConversation] ); useEffect(() => { if (selectedView?.type === 'conversation') { selectedView.callHistoryGroup?.children.forEach(child => { onMarkCallHistoryRead(selectedView.conversationId, child.callId); }); } }, [selectedView, onMarkCallHistoryRead]); return ( <>
{ updateSidebarView(CallsTabSidebarView.CallsListView); } : null } onToggleNavTabsCollapse={onToggleNavTabsCollapse} requiresFullWidth preferredLeftPaneWidth={preferredLeftPaneWidth} savePreferredLeftPaneWidth={savePreferredLeftPaneWidth} renderToastManager={renderToastManager} actions={ <> {sidebarView === CallsTabSidebarView.CallsListView && ( <> } label={i18n('icu:CallsTab__NewCallActionLabel')} onClick={() => { updateSidebarView(CallsTabSidebarView.NewCallView); }} /> {({ onClick, onKeyDown, ref }) => { return ( } label={i18n('icu:CallsTab__MoreActionsLabel')} /> ); }} )} } > {sidebarView === CallsTabSidebarView.CallsListView && ( )} {sidebarView === CallsTabSidebarView.NewCallView && ( )} {selectedView == null ? (

{ return ( ); }, }} />

) : (
{selectedView.type === 'conversation' && renderConversationDetails( selectedView.conversationId, selectedView.callHistoryGroup )} {selectedView.type === 'callLink' && renderCallLinkDetails( selectedView.roomId, selectedView.callHistoryGroup, onCloseSelectedView )}
)}
{confirmClearCallHistoryDialogOpen && ( {i18n('icu:CallsTab__ConfirmClearCallHistory__Body')} )} ); }