Allow joining a call when already in a call, confirming first
This commit is contained in:
		
					parent
					
						
							
								f4a18414f1
							
						
					
				
			
			
				commit
				
					
						4ec3b98293
					
				
			
		
					 14 changed files with 290 additions and 137 deletions
				
			
		| 
						 | 
				
			
			@ -7242,12 +7242,12 @@
 | 
			
		|||
    "description": "On the Calls Tab, when trying to join a different call when you're already in another one, this is the title of the confirmation dialog to leave the other call."
 | 
			
		||||
  },
 | 
			
		||||
  "icu:CallsList__LeaveCallDialogBody": {
 | 
			
		||||
    "messageformat": "You must leave the current call before joining a new call.",
 | 
			
		||||
    "description": "On the Calls Tab, when trying to join a different call when you're already in another one, this is the body of the confirmation dialog to leave the other call."
 | 
			
		||||
    "messageformat": "You must leave the current call before starting or joining a new call.",
 | 
			
		||||
    "description": "When trying to join a different call when you're already in another one, this is the body of the confirmation dialog to leave the other call."
 | 
			
		||||
  },
 | 
			
		||||
  "icu:CallsList__LeaveCallDialogButton--leave": {
 | 
			
		||||
    "messageformat": "Leave call",
 | 
			
		||||
    "description": "On the Calls Tab, when trying to join a different call when you're already in another one, this is the button to confirm leaving the other call."
 | 
			
		||||
    "description": "When trying to join a different call when you're already in another one, this is the button to confirm leaving the other call."
 | 
			
		||||
  },
 | 
			
		||||
  "icu:CallsNewCall__EmptyState--noQuery": {
 | 
			
		||||
    "messageformat": "No recent conversations.",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -376,7 +376,6 @@
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
.CallsNewCall__ItemActionButton--join-call-disabled {
 | 
			
		||||
  cursor: default;
 | 
			
		||||
  opacity: 0.5;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -65,7 +65,7 @@ import type {
 | 
			
		|||
  PeekNotConnectedGroupCallType,
 | 
			
		||||
} from '../state/ducks/calling';
 | 
			
		||||
import { DAY, MINUTE, SECOND } from '../util/durations';
 | 
			
		||||
import { ConfirmationDialog } from './ConfirmationDialog';
 | 
			
		||||
import type { StartCallData } from './ConfirmLeaveCallModal';
 | 
			
		||||
 | 
			
		||||
function Timestamp({
 | 
			
		||||
  i18n,
 | 
			
		||||
| 
						 | 
				
			
			@ -148,7 +148,8 @@ type CallsListProps = Readonly<{
 | 
			
		|||
  onOutgoingVideoCallInConversation: (conversationId: string) => void;
 | 
			
		||||
  onChangeCallsTabSelectedView: (selectedView: CallsTabSelectedView) => void;
 | 
			
		||||
  peekNotConnectedGroupCall: (options: PeekNotConnectedGroupCallType) => void;
 | 
			
		||||
  startCallLinkLobbyByRoomId: (roomId: string) => void;
 | 
			
		||||
  startCallLinkLobbyByRoomId: (options: { roomId: string }) => void;
 | 
			
		||||
  toggleConfirmLeaveCallModal: (options: StartCallData | null) => void;
 | 
			
		||||
  togglePip: () => void;
 | 
			
		||||
}>;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -179,7 +180,6 @@ export function CallsList({
 | 
			
		|||
  getCall,
 | 
			
		||||
  getCallLink,
 | 
			
		||||
  getConversation,
 | 
			
		||||
  hangUpActiveCall,
 | 
			
		||||
  i18n,
 | 
			
		||||
  selectedCallHistoryGroup,
 | 
			
		||||
  onCreateCallLink,
 | 
			
		||||
| 
						 | 
				
			
			@ -188,6 +188,7 @@ export function CallsList({
 | 
			
		|||
  onChangeCallsTabSelectedView,
 | 
			
		||||
  peekNotConnectedGroupCall,
 | 
			
		||||
  startCallLinkLobbyByRoomId,
 | 
			
		||||
  toggleConfirmLeaveCallModal,
 | 
			
		||||
  togglePip,
 | 
			
		||||
}: CallsListProps): JSX.Element {
 | 
			
		||||
  const infiniteLoaderRef = useRef<InfiniteLoader>(null);
 | 
			
		||||
| 
						 | 
				
			
			@ -195,8 +196,6 @@ export function CallsList({
 | 
			
		|||
  const [queryInput, setQueryInput] = useState('');
 | 
			
		||||
  const [statusInput, setStatusInput] = useState(CallHistoryFilterStatus.All);
 | 
			
		||||
  const [searchState, setSearchState] = useState(defaultInitState);
 | 
			
		||||
  const [isLeaveCallDialogVisible, setIsLeaveCallDialogVisible] =
 | 
			
		||||
    useState(false);
 | 
			
		||||
 | 
			
		||||
  const prevOptionsRef = useRef<CallHistoryFilterOptions | null>(null);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -330,7 +329,7 @@ export function CallsList({
 | 
			
		|||
        return true;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Direct is not supported currently
 | 
			
		||||
      // We can't tell from CallHistory alone whether a 1:1 call is active
 | 
			
		||||
      return false;
 | 
			
		||||
    },
 | 
			
		||||
    [getCallByPeerId]
 | 
			
		||||
| 
						 | 
				
			
			@ -358,12 +357,14 @@ export function CallsList({
 | 
			
		|||
        return peerId === activeCallConversationId;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Not supported currently
 | 
			
		||||
      // For direct conversations, we know the call is active if it's the active call!
 | 
			
		||||
      if (mode === CallMode.Direct) {
 | 
			
		||||
        return false;
 | 
			
		||||
        return Boolean(
 | 
			
		||||
          conversation && conversation?.id === activeCallConversationId
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Group
 | 
			
		||||
      // For group and adhoc calls, a call has to have members in it (see getIsCallActive)
 | 
			
		||||
      return Boolean(
 | 
			
		||||
        isActive &&
 | 
			
		||||
          conversation &&
 | 
			
		||||
| 
						 | 
				
			
			@ -773,6 +774,43 @@ export function CallsList({
 | 
			
		|||
        strictAssert(false, 'Cannot format call');
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const inCallAndNotThisOne = !isInCall && activeCall;
 | 
			
		||||
      const callButton = (
 | 
			
		||||
        <CallsNewCallButton
 | 
			
		||||
          callType={item.type}
 | 
			
		||||
          isActive={isActiveVisible}
 | 
			
		||||
          isInCall={isInCall}
 | 
			
		||||
          isEnabled={!inCallAndNotThisOne}
 | 
			
		||||
          onClick={() => {
 | 
			
		||||
            if (isInCall) {
 | 
			
		||||
              togglePip();
 | 
			
		||||
            } else if (activeCall) {
 | 
			
		||||
              if (isAdhoc) {
 | 
			
		||||
                toggleConfirmLeaveCallModal({
 | 
			
		||||
                  type: 'adhoc-roomId',
 | 
			
		||||
                  roomId: item.peerId,
 | 
			
		||||
                });
 | 
			
		||||
              } else {
 | 
			
		||||
                toggleConfirmLeaveCallModal({
 | 
			
		||||
                  type: 'conversation',
 | 
			
		||||
                  conversationId: conversation.id,
 | 
			
		||||
                  isVideoCall: item.type !== CallType.Audio,
 | 
			
		||||
                });
 | 
			
		||||
              }
 | 
			
		||||
            } else if (isAdhoc) {
 | 
			
		||||
              startCallLinkLobbyByRoomId({ roomId: item.peerId });
 | 
			
		||||
            } else if (conversation) {
 | 
			
		||||
              if (item.type === CallType.Audio) {
 | 
			
		||||
                onOutgoingAudioCallInConversation(conversation.id);
 | 
			
		||||
              } else {
 | 
			
		||||
                onOutgoingVideoCallInConversation(conversation.id);
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }}
 | 
			
		||||
          i18n={i18n}
 | 
			
		||||
        />
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      return (
 | 
			
		||||
        <div
 | 
			
		||||
          key={key}
 | 
			
		||||
| 
						 | 
				
			
			@ -801,34 +839,7 @@ export function CallsList({
 | 
			
		|||
                className="CallsList__ItemAvatar"
 | 
			
		||||
              />
 | 
			
		||||
            }
 | 
			
		||||
            trailing={
 | 
			
		||||
              isCallButtonVisible ? (
 | 
			
		||||
                <CallsNewCallButton
 | 
			
		||||
                  callType={item.type}
 | 
			
		||||
                  isActive={isActiveVisible}
 | 
			
		||||
                  isInCall={isInCall}
 | 
			
		||||
                  isEnabled={isInCall || !activeCall}
 | 
			
		||||
                  onClick={() => {
 | 
			
		||||
                    if (isInCall) {
 | 
			
		||||
                      togglePip();
 | 
			
		||||
                    } else if (activeCall) {
 | 
			
		||||
                      if (isActiveVisible) {
 | 
			
		||||
                        setIsLeaveCallDialogVisible(true);
 | 
			
		||||
                      }
 | 
			
		||||
                    } else if (isAdhoc) {
 | 
			
		||||
                      startCallLinkLobbyByRoomId(item.peerId);
 | 
			
		||||
                    } else if (conversation) {
 | 
			
		||||
                      if (item.type === CallType.Audio) {
 | 
			
		||||
                        onOutgoingAudioCallInConversation(conversation.id);
 | 
			
		||||
                      } else {
 | 
			
		||||
                        onOutgoingVideoCallInConversation(conversation.id);
 | 
			
		||||
                      }
 | 
			
		||||
                    }
 | 
			
		||||
                  }}
 | 
			
		||||
                  i18n={i18n}
 | 
			
		||||
                />
 | 
			
		||||
              ) : undefined
 | 
			
		||||
            }
 | 
			
		||||
            trailing={isCallButtonVisible ? callButton : undefined}
 | 
			
		||||
            title={
 | 
			
		||||
              <span
 | 
			
		||||
                className="CallsList__ItemTitle"
 | 
			
		||||
| 
						 | 
				
			
			@ -887,6 +898,7 @@ export function CallsList({
 | 
			
		|||
      onOutgoingAudioCallInConversation,
 | 
			
		||||
      onOutgoingVideoCallInConversation,
 | 
			
		||||
      startCallLinkLobbyByRoomId,
 | 
			
		||||
      toggleConfirmLeaveCallModal,
 | 
			
		||||
      togglePip,
 | 
			
		||||
      i18n,
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -913,31 +925,6 @@ export function CallsList({
 | 
			
		|||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      {isLeaveCallDialogVisible && (
 | 
			
		||||
        <ConfirmationDialog
 | 
			
		||||
          dialogName="GroupCallRemoteParticipant.blockInfo"
 | 
			
		||||
          cancelText={i18n('icu:cancel')}
 | 
			
		||||
          i18n={i18n}
 | 
			
		||||
          onClose={() => {
 | 
			
		||||
            setIsLeaveCallDialogVisible(false);
 | 
			
		||||
          }}
 | 
			
		||||
          title={i18n('icu:CallsList__LeaveCallDialogTitle')}
 | 
			
		||||
          actions={[
 | 
			
		||||
            {
 | 
			
		||||
              text: i18n('icu:CallsList__LeaveCallDialogButton--leave'),
 | 
			
		||||
              style: 'affirmative',
 | 
			
		||||
              action: () => {
 | 
			
		||||
                hangUpActiveCall(
 | 
			
		||||
                  'Calls Tab leave active call to join different call'
 | 
			
		||||
                );
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          ]}
 | 
			
		||||
        >
 | 
			
		||||
          {i18n('icu:CallsList__LeaveCallDialogBody')}
 | 
			
		||||
        </ConfirmationDialog>
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      <NavSidebarSearchHeader>
 | 
			
		||||
        <SearchInput
 | 
			
		||||
          i18n={i18n}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,6 +21,7 @@ import { SizeObserver } from '../hooks/useSizeObserver';
 | 
			
		|||
import { CallType } from '../types/CallDisposition';
 | 
			
		||||
import type { CallsTabSelectedView } from './CallsTab';
 | 
			
		||||
import { Tooltip, TooltipPlacement } from './Tooltip';
 | 
			
		||||
import { offsetDistanceModifier } from '../util/popperUtil';
 | 
			
		||||
 | 
			
		||||
type CallsNewCallProps = Readonly<{
 | 
			
		||||
  hasActiveCall: boolean;
 | 
			
		||||
| 
						 | 
				
			
			@ -53,17 +54,18 @@ export function CallsNewCallButton({
 | 
			
		|||
}): JSX.Element {
 | 
			
		||||
  let innerContent: React.ReactNode | string;
 | 
			
		||||
  let tooltipContent = '';
 | 
			
		||||
  if (callType === CallType.Audio) {
 | 
			
		||||
    innerContent = (
 | 
			
		||||
      <span className="CallsNewCall__ItemIcon CallsNewCall__ItemIcon--Phone" />
 | 
			
		||||
    );
 | 
			
		||||
  } else if (isActive) {
 | 
			
		||||
  if (!isEnabled) {
 | 
			
		||||
    tooltipContent = i18n('icu:ContactModal--already-in-call');
 | 
			
		||||
  }
 | 
			
		||||
  // Note: isActive is only set for groups and adhoc calls
 | 
			
		||||
  if (isActive) {
 | 
			
		||||
    innerContent = isInCall
 | 
			
		||||
      ? i18n('icu:CallsNewCallButton--return')
 | 
			
		||||
      : i18n('icu:joinOngoingCall');
 | 
			
		||||
    if (!isEnabled) {
 | 
			
		||||
      tooltipContent = i18n('icu:CallsNewCallButtonTooltip--in-another-call');
 | 
			
		||||
    }
 | 
			
		||||
  } else if (callType === CallType.Audio) {
 | 
			
		||||
    innerContent = (
 | 
			
		||||
      <span className="CallsNewCall__ItemIcon CallsNewCall__ItemIcon--Phone" />
 | 
			
		||||
    );
 | 
			
		||||
  } else {
 | 
			
		||||
    innerContent = (
 | 
			
		||||
      <span className="CallsNewCall__ItemIcon CallsNewCall__ItemIcon--Video" />
 | 
			
		||||
| 
						 | 
				
			
			@ -97,6 +99,7 @@ export function CallsNewCallButton({
 | 
			
		|||
      className="CallsNewCall__ItemActionButtonTooltip"
 | 
			
		||||
      content={tooltipContent}
 | 
			
		||||
      direction={TooltipPlacement.Top}
 | 
			
		||||
      popperModifiers={[offsetDistanceModifier(15)]}
 | 
			
		||||
    >
 | 
			
		||||
      {buttonContent}
 | 
			
		||||
    </Tooltip>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,7 @@ 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';
 | 
			
		||||
 | 
			
		||||
enum CallsTabSidebarView {
 | 
			
		||||
  CallsListView,
 | 
			
		||||
| 
						 | 
				
			
			@ -72,7 +73,8 @@ type CallsTabProps = Readonly<{
 | 
			
		|||
  }) => JSX.Element;
 | 
			
		||||
  regionCode: string | undefined;
 | 
			
		||||
  savePreferredLeftPaneWidth: (preferredLeftPaneWidth: number) => void;
 | 
			
		||||
  startCallLinkLobbyByRoomId: (roomId: string) => void;
 | 
			
		||||
  startCallLinkLobbyByRoomId: (options: { roomId: string }) => void;
 | 
			
		||||
  toggleConfirmLeaveCallModal: (options: StartCallData | null) => void;
 | 
			
		||||
  togglePip: () => void;
 | 
			
		||||
}>;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -119,6 +121,7 @@ export function CallsTab({
 | 
			
		|||
  regionCode,
 | 
			
		||||
  savePreferredLeftPaneWidth,
 | 
			
		||||
  startCallLinkLobbyByRoomId,
 | 
			
		||||
  toggleConfirmLeaveCallModal,
 | 
			
		||||
  togglePip,
 | 
			
		||||
}: CallsTabProps): JSX.Element {
 | 
			
		||||
  const [sidebarView, setSidebarView] = useState(
 | 
			
		||||
| 
						 | 
				
			
			@ -282,6 +285,7 @@ export function CallsTab({
 | 
			
		|||
              }
 | 
			
		||||
              peekNotConnectedGroupCall={peekNotConnectedGroupCall}
 | 
			
		||||
              startCallLinkLobbyByRoomId={startCallLinkLobbyByRoomId}
 | 
			
		||||
              toggleConfirmLeaveCallModal={toggleConfirmLeaveCallModal}
 | 
			
		||||
              togglePip={togglePip}
 | 
			
		||||
            />
 | 
			
		||||
          )}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										59
									
								
								ts/components/ConfirmLeaveCallModal.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								ts/components/ConfirmLeaveCallModal.tsx
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,59 @@
 | 
			
		|||
// Copyright 2024 Signal Messenger, LLC
 | 
			
		||||
// SPDX-License-Identifier: AGPL-3.0-only
 | 
			
		||||
 | 
			
		||||
import React from 'react';
 | 
			
		||||
 | 
			
		||||
import { ConfirmationDialog } from './ConfirmationDialog';
 | 
			
		||||
 | 
			
		||||
import type { LocalizerType } from '../types/Util';
 | 
			
		||||
import type {
 | 
			
		||||
  StartCallingLobbyType,
 | 
			
		||||
  StartCallLinkLobbyByRoomIdType,
 | 
			
		||||
  StartCallLinkLobbyType,
 | 
			
		||||
} from '../state/ducks/calling';
 | 
			
		||||
 | 
			
		||||
export type StartCallData =
 | 
			
		||||
  | ({
 | 
			
		||||
      type: 'conversation';
 | 
			
		||||
    } & StartCallingLobbyType)
 | 
			
		||||
  | ({ type: 'adhoc-roomId' } & StartCallLinkLobbyByRoomIdType)
 | 
			
		||||
  | ({ type: 'adhoc-rootKey' } & StartCallLinkLobbyType);
 | 
			
		||||
type HousekeepingProps = {
 | 
			
		||||
  i18n: LocalizerType;
 | 
			
		||||
};
 | 
			
		||||
type DispatchProps = {
 | 
			
		||||
  toggleConfirmLeaveCallModal: (options: StartCallData | null) => void;
 | 
			
		||||
  leaveCurrentCallAndStartCallingLobby: (options: StartCallData) => void;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type Props = { data: StartCallData } & HousekeepingProps & DispatchProps;
 | 
			
		||||
 | 
			
		||||
export function ConfirmLeaveCallModal({
 | 
			
		||||
  i18n,
 | 
			
		||||
  data,
 | 
			
		||||
  leaveCurrentCallAndStartCallingLobby,
 | 
			
		||||
  toggleConfirmLeaveCallModal,
 | 
			
		||||
}: Props): JSX.Element | null {
 | 
			
		||||
  return (
 | 
			
		||||
    <ConfirmationDialog
 | 
			
		||||
      dialogName="GroupCallRemoteParticipant.blockInfo"
 | 
			
		||||
      cancelText={i18n('icu:cancel')}
 | 
			
		||||
      i18n={i18n}
 | 
			
		||||
      onClose={() => {
 | 
			
		||||
        toggleConfirmLeaveCallModal(null);
 | 
			
		||||
      }}
 | 
			
		||||
      title={i18n('icu:CallsList__LeaveCallDialogTitle')}
 | 
			
		||||
      actions={[
 | 
			
		||||
        {
 | 
			
		||||
          text: i18n('icu:CallsList__LeaveCallDialogButton--leave'),
 | 
			
		||||
          style: 'affirmative',
 | 
			
		||||
          action: () => {
 | 
			
		||||
            leaveCurrentCallAndStartCallingLobby(data);
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      ]}
 | 
			
		||||
    >
 | 
			
		||||
      {i18n('icu:CallsList__LeaveCallDialogBody')}
 | 
			
		||||
    </ConfirmationDialog>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -20,6 +20,7 @@ import { ButtonVariant } from './Button';
 | 
			
		|||
import { ConfirmationDialog } from './ConfirmationDialog';
 | 
			
		||||
import { SignalConnectionsModal } from './SignalConnectionsModal';
 | 
			
		||||
import { WhatsNewModal } from './WhatsNewModal';
 | 
			
		||||
import type { StartCallData } from './ConfirmLeaveCallModal';
 | 
			
		||||
 | 
			
		||||
// NOTE: All types should be required for this component so that the smart
 | 
			
		||||
// component gives you type errors when adding/removing props.
 | 
			
		||||
| 
						 | 
				
			
			@ -35,6 +36,9 @@ export type PropsType = {
 | 
			
		|||
  // CallLinkEditModal
 | 
			
		||||
  callLinkEditModalRoomId: string | null;
 | 
			
		||||
  renderCallLinkEditModal: () => JSX.Element;
 | 
			
		||||
  // ConfirmLeaveCallModal
 | 
			
		||||
  confirmLeaveCallModalState: StartCallData | null;
 | 
			
		||||
  renderConfirmLeaveCallModal: () => JSX.Element;
 | 
			
		||||
  // ContactModal
 | 
			
		||||
  contactModalState: ContactModalStateType | undefined;
 | 
			
		||||
  renderContactModal: () => JSX.Element;
 | 
			
		||||
| 
						 | 
				
			
			@ -114,6 +118,9 @@ export function GlobalModalContainer({
 | 
			
		|||
  // CallLinkEditModal
 | 
			
		||||
  callLinkEditModalRoomId,
 | 
			
		||||
  renderCallLinkEditModal,
 | 
			
		||||
  // ConfirmLeaveCallModal
 | 
			
		||||
  confirmLeaveCallModalState,
 | 
			
		||||
  renderConfirmLeaveCallModal,
 | 
			
		||||
  // ContactModal
 | 
			
		||||
  contactModalState,
 | 
			
		||||
  renderContactModal,
 | 
			
		||||
| 
						 | 
				
			
			@ -196,6 +203,10 @@ export function GlobalModalContainer({
 | 
			
		|||
 | 
			
		||||
  // The Rest
 | 
			
		||||
 | 
			
		||||
  if (confirmLeaveCallModalState) {
 | 
			
		||||
    return renderConfirmLeaveCallModal();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (addUserToAnotherGroupModalContactId) {
 | 
			
		||||
    return renderAddUserToAnotherGroup();
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -424,14 +424,14 @@ export function ConversationDetails({
 | 
			
		|||
        {!conversation.isMe && (
 | 
			
		||||
          <>
 | 
			
		||||
            <ConversationDetailsCallButton
 | 
			
		||||
              disabled={hasActiveCall}
 | 
			
		||||
              hasActiveCall={hasActiveCall}
 | 
			
		||||
              i18n={i18n}
 | 
			
		||||
              onClick={() => onOutgoingVideoCallInConversation(conversation.id)}
 | 
			
		||||
              type="video"
 | 
			
		||||
            />
 | 
			
		||||
            {!isGroup && (
 | 
			
		||||
              <ConversationDetailsCallButton
 | 
			
		||||
                disabled={hasActiveCall}
 | 
			
		||||
                hasActiveCall={hasActiveCall}
 | 
			
		||||
                i18n={i18n}
 | 
			
		||||
                onClick={() =>
 | 
			
		||||
                  onOutgoingAudioCallInConversation(conversation.id)
 | 
			
		||||
| 
						 | 
				
			
			@ -733,19 +733,18 @@ export function ConversationDetails({
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
function ConversationDetailsCallButton({
 | 
			
		||||
  disabled,
 | 
			
		||||
  hasActiveCall,
 | 
			
		||||
  i18n,
 | 
			
		||||
  onClick,
 | 
			
		||||
  type,
 | 
			
		||||
}: Readonly<{
 | 
			
		||||
  disabled: boolean;
 | 
			
		||||
  hasActiveCall: boolean;
 | 
			
		||||
  i18n: LocalizerType;
 | 
			
		||||
  onClick: () => unknown;
 | 
			
		||||
  type: 'audio' | 'video';
 | 
			
		||||
}>) {
 | 
			
		||||
  const button = (
 | 
			
		||||
    <Button
 | 
			
		||||
      disabled={disabled}
 | 
			
		||||
      icon={ButtonIconType[type]}
 | 
			
		||||
      onClick={onClick}
 | 
			
		||||
      variant={ButtonVariant.Details}
 | 
			
		||||
| 
						 | 
				
			
			@ -754,7 +753,7 @@ function ConversationDetailsCallButton({
 | 
			
		|||
    </Button>
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  if (disabled) {
 | 
			
		||||
  if (hasActiveCall) {
 | 
			
		||||
    return (
 | 
			
		||||
      <Tooltip content={i18n('icu:calling__in-another-call-tooltip')}>
 | 
			
		||||
        {button}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -82,8 +82,11 @@ import {
 | 
			
		|||
  isGroupOrAdhocCallMode,
 | 
			
		||||
  isGroupOrAdhocCallState,
 | 
			
		||||
} from '../../util/isGroupOrAdhocCall';
 | 
			
		||||
import type { ShowErrorModalActionType } from './globalModals';
 | 
			
		||||
import { SHOW_ERROR_MODAL } from './globalModals';
 | 
			
		||||
import type {
 | 
			
		||||
  ShowErrorModalActionType,
 | 
			
		||||
  ToggleConfirmLeaveCallModalActionType,
 | 
			
		||||
} from './globalModals';
 | 
			
		||||
import { SHOW_ERROR_MODAL, toggleConfirmLeaveCallModal } from './globalModals';
 | 
			
		||||
import { ButtonVariant } from '../../components/Button';
 | 
			
		||||
import { getConversationIdForLogging } from '../../util/idForLogging';
 | 
			
		||||
import { DataReader, DataWriter } from '../../sql/Client';
 | 
			
		||||
| 
						 | 
				
			
			@ -92,6 +95,7 @@ import type { CallHistoryAdd } from './callHistory';
 | 
			
		|||
import { addCallHistory } from './callHistory';
 | 
			
		||||
import { saveDraftRecordingIfNeeded } from './composer';
 | 
			
		||||
import type { CallHistoryDetails } from '../../types/CallDisposition';
 | 
			
		||||
import type { StartCallData } from '../../components/ConfirmLeaveCallModal';
 | 
			
		||||
 | 
			
		||||
// State
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1464,48 +1468,6 @@ function handleCallLinkUpdate(
 | 
			
		|||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * When starting a lobby and there's an active call, if we're already in call then
 | 
			
		||||
 * focus it (toggle pip), otherwise show an error.
 | 
			
		||||
 * @returns {boolean} `true` if there was an active call and we handled it.
 | 
			
		||||
 */
 | 
			
		||||
function handleActiveCallOnStartLobby({
 | 
			
		||||
  conversationId,
 | 
			
		||||
  state,
 | 
			
		||||
  dispatch,
 | 
			
		||||
}: {
 | 
			
		||||
  conversationId: string;
 | 
			
		||||
  state: RootStateType;
 | 
			
		||||
  dispatch: ThunkDispatch<
 | 
			
		||||
    RootStateType,
 | 
			
		||||
    unknown,
 | 
			
		||||
    ShowErrorModalActionType | TogglePipActionType
 | 
			
		||||
  >;
 | 
			
		||||
}): boolean {
 | 
			
		||||
  const { activeCallState } = state.calling;
 | 
			
		||||
  if (!activeCallState) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (activeCallState.conversationId === conversationId) {
 | 
			
		||||
    dispatch({
 | 
			
		||||
      type: TOGGLE_PIP,
 | 
			
		||||
    });
 | 
			
		||||
  } else {
 | 
			
		||||
    const i18n = getIntl(state);
 | 
			
		||||
    dispatch({
 | 
			
		||||
      type: SHOW_ERROR_MODAL,
 | 
			
		||||
      payload: {
 | 
			
		||||
        title: i18n('icu:calling__cant-join'),
 | 
			
		||||
        description: i18n('icu:calling__dialog-already-in-call'),
 | 
			
		||||
        buttonVariant: ButtonVariant.Primary,
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function hangUpActiveCall(
 | 
			
		||||
  reason: string
 | 
			
		||||
): ThunkAction<void, RootStateType, unknown, HangUpActionType> {
 | 
			
		||||
| 
						 | 
				
			
			@ -2046,9 +2008,9 @@ function updateCallLinkRestrictions(
 | 
			
		|||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function startCallLinkLobbyByRoomId(
 | 
			
		||||
  roomId: string
 | 
			
		||||
): StartCallLinkLobbyThunkActionType {
 | 
			
		||||
function startCallLinkLobbyByRoomId({
 | 
			
		||||
  roomId,
 | 
			
		||||
}: StartCallLinkLobbyByRoomIdType): StartCallLinkLobbyThunkActionType {
 | 
			
		||||
  return async (dispatch, getState) => {
 | 
			
		||||
    const state = getState();
 | 
			
		||||
    const callLink = getOwn(state.calling.callLinks, roomId);
 | 
			
		||||
| 
						 | 
				
			
			@ -2082,6 +2044,7 @@ const _startCallLinkLobby = async ({
 | 
			
		|||
    unknown,
 | 
			
		||||
    | StartCallLinkLobbyActionType
 | 
			
		||||
    | ShowErrorModalActionType
 | 
			
		||||
    | ToggleConfirmLeaveCallModalActionType
 | 
			
		||||
    | TogglePipActionType
 | 
			
		||||
  >;
 | 
			
		||||
  getState: () => RootStateType;
 | 
			
		||||
| 
						 | 
				
			
			@ -2090,9 +2053,20 @@ const _startCallLinkLobby = async ({
 | 
			
		|||
  const roomId = getRoomIdFromRootKey(callLinkRootKey);
 | 
			
		||||
  const state = getState();
 | 
			
		||||
 | 
			
		||||
  if (
 | 
			
		||||
    handleActiveCallOnStartLobby({ conversationId: roomId, state, dispatch })
 | 
			
		||||
  ) {
 | 
			
		||||
  const { activeCallState } = state.calling;
 | 
			
		||||
  if (activeCallState && activeCallState.conversationId === roomId) {
 | 
			
		||||
    dispatch({
 | 
			
		||||
      type: TOGGLE_PIP,
 | 
			
		||||
    });
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (activeCallState) {
 | 
			
		||||
    dispatch(
 | 
			
		||||
      toggleConfirmLeaveCallModal({
 | 
			
		||||
        type: 'adhoc-rootKey',
 | 
			
		||||
        rootKey,
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2181,6 +2155,34 @@ const _startCallLinkLobby = async ({
 | 
			
		|||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function leaveCurrentCallAndStartCallingLobby(
 | 
			
		||||
  data: StartCallData
 | 
			
		||||
): ThunkAction<void, RootStateType, unknown, HangUpActionType> {
 | 
			
		||||
  return async (dispatch, getState) => {
 | 
			
		||||
    hangUpActiveCall(
 | 
			
		||||
      'Leave call button pressed in ConfirmLeaveCurrentCallModal'
 | 
			
		||||
    )(dispatch, getState, undefined);
 | 
			
		||||
 | 
			
		||||
    const { type } = data;
 | 
			
		||||
    if (type === 'conversation') {
 | 
			
		||||
      const { conversationId, isVideoCall } = data;
 | 
			
		||||
      startCallingLobby({ conversationId, isVideoCall })(
 | 
			
		||||
        dispatch,
 | 
			
		||||
        getState,
 | 
			
		||||
        undefined
 | 
			
		||||
      );
 | 
			
		||||
    } else if (type === 'adhoc-roomId') {
 | 
			
		||||
      const { roomId } = data;
 | 
			
		||||
      startCallLinkLobbyByRoomId({ roomId })(dispatch, getState, undefined);
 | 
			
		||||
    } else if (type === 'adhoc-rootKey') {
 | 
			
		||||
      const { rootKey } = data;
 | 
			
		||||
      startCallLinkLobby({ rootKey })(dispatch, getState, undefined);
 | 
			
		||||
    } else {
 | 
			
		||||
      throw missingCaseError(type);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function startCallingLobby({
 | 
			
		||||
  conversationId,
 | 
			
		||||
  isVideoCall,
 | 
			
		||||
| 
						 | 
				
			
			@ -2188,7 +2190,9 @@ function startCallingLobby({
 | 
			
		|||
  void,
 | 
			
		||||
  RootStateType,
 | 
			
		||||
  unknown,
 | 
			
		||||
  StartCallingLobbyActionType | TogglePipActionType
 | 
			
		||||
  | StartCallingLobbyActionType
 | 
			
		||||
  | ToggleConfirmLeaveCallModalActionType
 | 
			
		||||
  | TogglePipActionType
 | 
			
		||||
> {
 | 
			
		||||
  return async (dispatch, getState) => {
 | 
			
		||||
    const state = getState();
 | 
			
		||||
| 
						 | 
				
			
			@ -2201,10 +2205,16 @@ function startCallingLobby({
 | 
			
		|||
      "startCallingLobby: can't start lobby without a conversation"
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    strictAssert(
 | 
			
		||||
      !state.calling.activeCallState,
 | 
			
		||||
      "startCallingLobby: can't start lobby if a call is active"
 | 
			
		||||
    );
 | 
			
		||||
    if (state.calling.activeCallState) {
 | 
			
		||||
      dispatch(
 | 
			
		||||
        toggleConfirmLeaveCallModal({
 | 
			
		||||
          type: 'conversation',
 | 
			
		||||
          conversationId,
 | 
			
		||||
          isVideoCall,
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // The group call device count is considered 0 for a direct call.
 | 
			
		||||
    const groupCall = getGroupCall(
 | 
			
		||||
| 
						 | 
				
			
			@ -2374,6 +2384,7 @@ export const actions = {
 | 
			
		|||
  hangUpActiveCall,
 | 
			
		||||
  handleCallLinkUpdate,
 | 
			
		||||
  joinedAdhocCall,
 | 
			
		||||
  leaveCurrentCallAndStartCallingLobby,
 | 
			
		||||
  onOutgoingVideoCallInConversation,
 | 
			
		||||
  onOutgoingAudioCallInConversation,
 | 
			
		||||
  openSystemPreferencesAction,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,6 +48,7 @@ import { ForwardMessagesModalType } from '../../components/ForwardMessagesModal'
 | 
			
		|||
import type { CallLinkType } from '../../types/CallLink';
 | 
			
		||||
import type { LocalizerType } from '../../types/I18N';
 | 
			
		||||
import { linkCallRoute } from '../../util/signalRoutes';
 | 
			
		||||
import type { StartCallData } from '../../components/ConfirmLeaveCallModal';
 | 
			
		||||
 | 
			
		||||
// State
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -93,6 +94,7 @@ export type GlobalModalsStateType = ReadonlyDeep<{
 | 
			
		|||
  aboutContactModalContactId?: string;
 | 
			
		||||
  callLinkAddNameModalRoomId: string | null;
 | 
			
		||||
  callLinkEditModalRoomId: string | null;
 | 
			
		||||
  confirmLeaveCallModalState: StartCallData | null;
 | 
			
		||||
  contactModalState?: ContactModalStateType;
 | 
			
		||||
  deleteMessagesProps?: DeleteMessagesPropsType;
 | 
			
		||||
  editHistoryMessages?: EditHistoryMessagesType;
 | 
			
		||||
| 
						 | 
				
			
			@ -168,6 +170,8 @@ const TOGGLE_CONFIRMATION_MODAL = 'globalModals/TOGGLE_CONFIRMATION_MODAL';
 | 
			
		|||
const SHOW_EDIT_HISTORY_MODAL = 'globalModals/SHOW_EDIT_HISTORY_MODAL';
 | 
			
		||||
const CLOSE_EDIT_HISTORY_MODAL = 'globalModals/CLOSE_EDIT_HISTORY_MODAL';
 | 
			
		||||
const TOGGLE_USERNAME_ONBOARDING = 'globalModals/TOGGLE_USERNAME_ONBOARDING';
 | 
			
		||||
const TOGGLE_CONFIRM_LEAVE_CALL_MODAL =
 | 
			
		||||
  'globalModals/TOGGLE_CONFIRM_LEAVE_CALL_MODAL';
 | 
			
		||||
 | 
			
		||||
export type ContactModalStateType = ReadonlyDeep<{
 | 
			
		||||
  contactId: string;
 | 
			
		||||
| 
						 | 
				
			
			@ -221,6 +225,11 @@ type ToggleForwardMessagesModalActionType = ReadonlyDeep<{
 | 
			
		|||
  payload: ForwardMessagesPropsType | undefined;
 | 
			
		||||
}>;
 | 
			
		||||
 | 
			
		||||
export type ToggleConfirmLeaveCallModalActionType = ReadonlyDeep<{
 | 
			
		||||
  type: typeof TOGGLE_CONFIRM_LEAVE_CALL_MODAL;
 | 
			
		||||
  payload: StartCallData | null;
 | 
			
		||||
}>;
 | 
			
		||||
 | 
			
		||||
type ToggleNotePreviewModalActionType = ReadonlyDeep<{
 | 
			
		||||
  type: typeof TOGGLE_NOTE_PREVIEW_MODAL;
 | 
			
		||||
  payload: NotePreviewModalPropsType | null;
 | 
			
		||||
| 
						 | 
				
			
			@ -385,6 +394,7 @@ export type GlobalModalsActionType = ReadonlyDeep<
 | 
			
		|||
  | ToggleCallLinkAddNameModalActionType
 | 
			
		||||
  | ToggleCallLinkEditModalActionType
 | 
			
		||||
  | ToggleConfirmationModalActionType
 | 
			
		||||
  | ToggleConfirmLeaveCallModalActionType
 | 
			
		||||
  | ToggleDeleteMessagesModalActionType
 | 
			
		||||
  | ToggleForwardMessagesModalActionType
 | 
			
		||||
  | ToggleNotePreviewModalActionType
 | 
			
		||||
| 
						 | 
				
			
			@ -426,6 +436,7 @@ export const actions = {
 | 
			
		|||
  toggleCallLinkAddNameModal,
 | 
			
		||||
  toggleCallLinkEditModal,
 | 
			
		||||
  toggleConfirmationModal,
 | 
			
		||||
  toggleConfirmLeaveCallModal,
 | 
			
		||||
  toggleDeleteMessagesModal,
 | 
			
		||||
  toggleForwardMessagesModal,
 | 
			
		||||
  toggleNotePreviewModal,
 | 
			
		||||
| 
						 | 
				
			
			@ -682,6 +693,15 @@ function showShareCallLinkViaSignal(
 | 
			
		|||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function toggleConfirmLeaveCallModal(
 | 
			
		||||
  payload: StartCallData | null
 | 
			
		||||
): ToggleConfirmLeaveCallModalActionType {
 | 
			
		||||
  return {
 | 
			
		||||
    type: TOGGLE_CONFIRM_LEAVE_CALL_MODAL,
 | 
			
		||||
    payload,
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function toggleNotePreviewModal(
 | 
			
		||||
  payload: NotePreviewModalPropsType | null
 | 
			
		||||
): ToggleNotePreviewModalActionType {
 | 
			
		||||
| 
						 | 
				
			
			@ -954,6 +974,7 @@ export function getEmptyState(): GlobalModalsStateType {
 | 
			
		|||
    hasConfirmationModal: false,
 | 
			
		||||
    callLinkAddNameModalRoomId: null,
 | 
			
		||||
    callLinkEditModalRoomId: null,
 | 
			
		||||
    confirmLeaveCallModalState: null,
 | 
			
		||||
    editNicknameAndNoteModalProps: null,
 | 
			
		||||
    isProfileEditorVisible: false,
 | 
			
		||||
    isShortcutGuideModalVisible: false,
 | 
			
		||||
| 
						 | 
				
			
			@ -979,6 +1000,13 @@ export function reducer(
 | 
			
		|||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (action.type === TOGGLE_CONFIRM_LEAVE_CALL_MODAL) {
 | 
			
		||||
    return {
 | 
			
		||||
      ...state,
 | 
			
		||||
      confirmLeaveCallModalState: action.payload,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (action.type === TOGGLE_NOTE_PREVIEW_MODAL) {
 | 
			
		||||
    return {
 | 
			
		||||
      ...state,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,6 +32,11 @@ export const getCallLinkAddNameModalRoomId = createSelector(
 | 
			
		|||
  ({ callLinkAddNameModalRoomId }) => callLinkAddNameModalRoomId
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export const getConfirmLeaveCallModalState = createSelector(
 | 
			
		||||
  getGlobalModalsState,
 | 
			
		||||
  ({ confirmLeaveCallModalState }) => confirmLeaveCallModalState
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export const getContactModalState = createSelector(
 | 
			
		||||
  getGlobalModalsState,
 | 
			
		||||
  ({ contactModalState }) => contactModalState
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -172,7 +172,8 @@ export const SmartCallsTab = memo(function SmartCallsTab() {
 | 
			
		|||
    markCallHistoryRead,
 | 
			
		||||
    markCallsTabViewed,
 | 
			
		||||
  } = useCallHistoryActions();
 | 
			
		||||
  const { toggleCallLinkEditModal } = useGlobalModalActions();
 | 
			
		||||
  const { toggleCallLinkEditModal, toggleConfirmLeaveCallModal } =
 | 
			
		||||
    useGlobalModalActions();
 | 
			
		||||
 | 
			
		||||
  const getCallHistoryGroupsCount = useCallback(
 | 
			
		||||
    async (options: CallHistoryFilterOptions) => {
 | 
			
		||||
| 
						 | 
				
			
			@ -257,6 +258,7 @@ export const SmartCallsTab = memo(function SmartCallsTab() {
 | 
			
		|||
      regionCode={regionCode}
 | 
			
		||||
      savePreferredLeftPaneWidth={savePreferredLeftPaneWidth}
 | 
			
		||||
      startCallLinkLobbyByRoomId={startCallLinkLobbyByRoomId}
 | 
			
		||||
      toggleConfirmLeaveCallModal={toggleConfirmLeaveCallModal}
 | 
			
		||||
      togglePip={togglePip}
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										37
									
								
								ts/state/smart/ConfirmLeaveCallModal.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								ts/state/smart/ConfirmLeaveCallModal.tsx
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
// Copyright 2024 Signal Messenger, LLC
 | 
			
		||||
// SPDX-License-Identifier: AGPL-3.0-only
 | 
			
		||||
 | 
			
		||||
import React, { memo } from 'react';
 | 
			
		||||
import { useSelector } from 'react-redux';
 | 
			
		||||
import { useCallingActions } from '../ducks/calling';
 | 
			
		||||
import { getIntl } from '../selectors/user';
 | 
			
		||||
import { useGlobalModalActions } from '../ducks/globalModals';
 | 
			
		||||
import { getConfirmLeaveCallModalState } from '../selectors/globalModals';
 | 
			
		||||
import { ConfirmLeaveCallModal } from '../../components/ConfirmLeaveCallModal';
 | 
			
		||||
 | 
			
		||||
export const SmartConfirmLeaveCallModal = memo(
 | 
			
		||||
  function SmartConfirmLeaveCallModal(): JSX.Element | null {
 | 
			
		||||
    const i18n = useSelector(getIntl);
 | 
			
		||||
    const confirmLeaveCallModalState = useSelector(
 | 
			
		||||
      getConfirmLeaveCallModalState
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const { leaveCurrentCallAndStartCallingLobby } = useCallingActions();
 | 
			
		||||
    const { toggleConfirmLeaveCallModal } = useGlobalModalActions();
 | 
			
		||||
 | 
			
		||||
    if (!confirmLeaveCallModalState) {
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <ConfirmLeaveCallModal
 | 
			
		||||
        i18n={i18n}
 | 
			
		||||
        data={confirmLeaveCallModalState}
 | 
			
		||||
        leaveCurrentCallAndStartCallingLobby={
 | 
			
		||||
          leaveCurrentCallAndStartCallingLobby
 | 
			
		||||
        }
 | 
			
		||||
        toggleConfirmLeaveCallModal={toggleConfirmLeaveCallModal}
 | 
			
		||||
      />
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
);
 | 
			
		||||
| 
						 | 
				
			
			@ -28,6 +28,7 @@ import { SmartEditNicknameAndNoteModal } from './EditNicknameAndNoteModal';
 | 
			
		|||
import { SmartNotePreviewModal } from './NotePreviewModal';
 | 
			
		||||
import { SmartCallLinkEditModal } from './CallLinkEditModal';
 | 
			
		||||
import { SmartCallLinkAddNameModal } from './CallLinkAddNameModal';
 | 
			
		||||
import { SmartConfirmLeaveCallModal } from './ConfirmLeaveCallModal';
 | 
			
		||||
 | 
			
		||||
function renderCallLinkAddNameModal(): JSX.Element {
 | 
			
		||||
  return <SmartCallLinkAddNameModal />;
 | 
			
		||||
| 
						 | 
				
			
			@ -37,6 +38,10 @@ function renderCallLinkEditModal(): JSX.Element {
 | 
			
		|||
  return <SmartCallLinkEditModal />;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function renderConfirmLeaveCallModal(): JSX.Element {
 | 
			
		||||
  return <SmartConfirmLeaveCallModal />;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function renderEditHistoryMessagesModal(): JSX.Element {
 | 
			
		||||
  return <SmartEditHistoryMessagesModal />;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -102,6 +107,7 @@ export const SmartGlobalModalContainer = memo(
 | 
			
		|||
      addUserToAnotherGroupModalContactId,
 | 
			
		||||
      callLinkAddNameModalRoomId,
 | 
			
		||||
      callLinkEditModalRoomId,
 | 
			
		||||
      confirmLeaveCallModalState,
 | 
			
		||||
      contactModalState,
 | 
			
		||||
      deleteMessagesProps,
 | 
			
		||||
      editHistoryMessages,
 | 
			
		||||
| 
						 | 
				
			
			@ -182,6 +188,7 @@ export const SmartGlobalModalContainer = memo(
 | 
			
		|||
        }
 | 
			
		||||
        callLinkAddNameModalRoomId={callLinkAddNameModalRoomId}
 | 
			
		||||
        callLinkEditModalRoomId={callLinkEditModalRoomId}
 | 
			
		||||
        confirmLeaveCallModalState={confirmLeaveCallModalState}
 | 
			
		||||
        contactModalState={contactModalState}
 | 
			
		||||
        editHistoryMessages={editHistoryMessages}
 | 
			
		||||
        editNicknameAndNoteModalProps={editNicknameAndNoteModalProps}
 | 
			
		||||
| 
						 | 
				
			
			@ -206,6 +213,7 @@ export const SmartGlobalModalContainer = memo(
 | 
			
		|||
        renderAddUserToAnotherGroup={renderAddUserToAnotherGroup}
 | 
			
		||||
        renderCallLinkAddNameModal={renderCallLinkAddNameModal}
 | 
			
		||||
        renderCallLinkEditModal={renderCallLinkEditModal}
 | 
			
		||||
        renderConfirmLeaveCallModal={renderConfirmLeaveCallModal}
 | 
			
		||||
        renderContactModal={renderContactModal}
 | 
			
		||||
        renderEditHistoryMessagesModal={renderEditHistoryMessagesModal}
 | 
			
		||||
        renderEditNicknameAndNoteModal={renderEditNicknameAndNoteModal}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue