// 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 './CallsNewCall'; import { useEscapeHandling } from '../hooks/useEscapeHandling'; import type { ActiveCallStateType } from '../state/ducks/calling'; import { ContextMenu } from './ContextMenu'; import { ConfirmationDialog } from './ConfirmationDialog'; import type { UnreadStats } from '../util/countUnreadStats'; 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; getConversation: (id: string) => ConversationType | void; hasFailedStorySends: boolean; hasPendingUpdate: boolean; i18n: LocalizerType; navTabsCollapsed: boolean; onClearCallHistory: () => void; onMarkCallHistoryRead: (conversationId: string, callId: string) => void; onToggleNavTabsCollapse: (navTabsCollapsed: boolean) => void; onOutgoingAudioCallInConversation: (conversationId: string) => void; onOutgoingVideoCallInConversation: (conversationId: string) => void; preferredLeftPaneWidth: number; renderConversationDetails: ( conversationId: string, callHistoryGroup: CallHistoryGroup | null ) => JSX.Element; regionCode: string | undefined; savePreferredLeftPaneWidth: (preferredLeftPaneWidth: number) => void; }>; export function CallsTab({ activeCall, allConversations, otherTabsUnreadStats, getCallHistoryGroupsCount, getCallHistoryGroups, callHistoryEdition, getConversation, hasFailedStorySends, hasPendingUpdate, i18n, navTabsCollapsed, onClearCallHistory, onMarkCallHistoryRead, onToggleNavTabsCollapse, onOutgoingAudioCallInConversation, onOutgoingVideoCallInConversation, preferredLeftPaneWidth, renderConversationDetails, regionCode, savePreferredLeftPaneWidth, }: CallsTabProps): JSX.Element { const [sidebarView, setSidebarView] = useState( CallsTabSidebarView.CallsListView ); const [selected, setSelected] = useState<{ conversationId: string; callHistoryGroup: CallHistoryGroup | null; } | null>(null); const [ confirmClearCallHistoryDialogOpen, setConfirmClearCallHistoryDialogOpen, ] = useState(false); const updateSidebarView = useCallback( (newSidebarView: CallsTabSidebarView) => { setSidebarView(newSidebarView); setSelected(null); }, [] ); const handleSelectCallHistoryGroup = useCallback( (conversationId: string, callHistoryGroup: CallHistoryGroup) => { setSelected({ conversationId, callHistoryGroup, }); }, [] ); const handleSelectConversation = useCallback((conversationId: string) => { setSelected({ conversationId, callHistoryGroup: null }); }, []); 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 (selected?.callHistoryGroup != null) { selected.callHistoryGroup.children.forEach(child => { onMarkCallHistoryRead(selected.conversationId, child.callId); }); } }, [selected, onMarkCallHistoryRead]); return ( <>
{ updateSidebarView(CallsTabSidebarView.CallsListView); } : null } onToggleNavTabsCollapse={onToggleNavTabsCollapse} requiresFullWidth preferredLeftPaneWidth={preferredLeftPaneWidth} savePreferredLeftPaneWidth={savePreferredLeftPaneWidth} actions={ <> {sidebarView === CallsTabSidebarView.CallsListView && ( <> } label={i18n('icu:CallsTab__NewCallActionLabel')} onClick={() => { updateSidebarView(CallsTabSidebarView.NewCallView); }} /> {({ openMenu, onKeyDown }) => { return ( } label={i18n('icu:CallsTab__MoreActionsLabel')} /> ); }} )} } > {sidebarView === CallsTabSidebarView.CallsListView && ( )} {sidebarView === CallsTabSidebarView.NewCallView && ( )} {selected == null ? (

{i18n('icu:CallsTab__EmptyStateText')}

) : (
{renderConversationDetails( selected.conversationId, selected.callHistoryGroup )}
)}
{confirmClearCallHistoryDialogOpen && ( {i18n('icu:CallsTab__ConfirmClearCallHistory__Body')} )} ); }