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
|
@ -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