Make startCallLobby resilient to re-entrant calls
This commit is contained in:
parent
5367b26320
commit
2d892de3b2
6 changed files with 498 additions and 150 deletions
|
@ -167,6 +167,7 @@ export type GroupCallStateType = {
|
||||||
|
|
||||||
// eslint-disable-next-line local-rules/type-alias-readonlydeep
|
// eslint-disable-next-line local-rules/type-alias-readonlydeep
|
||||||
export type ActiveCallStateType = {
|
export type ActiveCallStateType = {
|
||||||
|
state: 'Active';
|
||||||
callMode: CallMode;
|
callMode: CallMode;
|
||||||
conversationId: string;
|
conversationId: string;
|
||||||
hasLocalAudio: boolean;
|
hasLocalAudio: boolean;
|
||||||
|
@ -184,6 +185,10 @@ export type ActiveCallStateType = {
|
||||||
showParticipantsList: boolean;
|
showParticipantsList: boolean;
|
||||||
reactions?: ActiveCallReactionsType;
|
reactions?: ActiveCallReactionsType;
|
||||||
};
|
};
|
||||||
|
export type WaitingCallStateType = ReadonlyDeep<{
|
||||||
|
state: 'Waiting';
|
||||||
|
conversationId: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
// eslint-disable-next-line local-rules/type-alias-readonlydeep
|
// eslint-disable-next-line local-rules/type-alias-readonlydeep
|
||||||
export type CallsByConversationType = {
|
export type CallsByConversationType = {
|
||||||
|
@ -204,7 +209,7 @@ export type CallingStateType = MediaDeviceSettings & {
|
||||||
callsByConversation: CallsByConversationType;
|
callsByConversation: CallsByConversationType;
|
||||||
adhocCalls: AdhocCallsType;
|
adhocCalls: AdhocCallsType;
|
||||||
callLinks: CallLinksByRoomIdType;
|
callLinks: CallLinksByRoomIdType;
|
||||||
activeCallState?: ActiveCallStateType;
|
activeCallState?: ActiveCallStateType | WaitingCallStateType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AcceptCallType = ReadonlyDeep<{
|
export type AcceptCallType = ReadonlyDeep<{
|
||||||
|
@ -430,8 +435,12 @@ export const getActiveCall = ({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { callMode, conversationId } = activeCallState;
|
const { state, conversationId } = activeCallState;
|
||||||
return callMode === CallMode.Adhoc
|
if (state === 'Waiting') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return activeCallState.callMode === CallMode.Adhoc
|
||||||
? getOwn(adhocCalls, conversationId)
|
? getOwn(adhocCalls, conversationId)
|
||||||
: getOwn(callsByConversation, conversationId);
|
: getOwn(callsByConversation, conversationId);
|
||||||
};
|
};
|
||||||
|
@ -600,7 +609,10 @@ const CANCEL_INCOMING_GROUP_CALL_RING =
|
||||||
const CHANGE_CALL_VIEW = 'calling/CHANGE_CALL_VIEW';
|
const CHANGE_CALL_VIEW = 'calling/CHANGE_CALL_VIEW';
|
||||||
const DENY_USER = 'calling/DENY_USER';
|
const DENY_USER = 'calling/DENY_USER';
|
||||||
const START_CALLING_LOBBY = 'calling/START_CALLING_LOBBY';
|
const START_CALLING_LOBBY = 'calling/START_CALLING_LOBBY';
|
||||||
|
const WAITING_FOR_CALLING_LOBBY = 'calling/WAITING_FOR_CALLING_LOBBY';
|
||||||
const START_CALL_LINK_LOBBY = 'calling/START_CALL_LINK_LOBBY';
|
const START_CALL_LINK_LOBBY = 'calling/START_CALL_LINK_LOBBY';
|
||||||
|
const WAITING_FOR_CALL_LINK_LOBBY = 'calling/WAITING_FOR_CALL_LINK_LOBBY';
|
||||||
|
const CALL_LOBBY_FAILED = 'calling/CALL_LOBBY_FAILED';
|
||||||
const CALL_STATE_CHANGE_FULFILLED = 'calling/CALL_STATE_CHANGE_FULFILLED';
|
const CALL_STATE_CHANGE_FULFILLED = 'calling/CALL_STATE_CHANGE_FULFILLED';
|
||||||
const CHANGE_IO_DEVICE_FULFILLED = 'calling/CHANGE_IO_DEVICE_FULFILLED';
|
const CHANGE_IO_DEVICE_FULFILLED = 'calling/CHANGE_IO_DEVICE_FULFILLED';
|
||||||
const CLOSE_NEED_PERMISSION_SCREEN = 'calling/CLOSE_NEED_PERMISSION_SCREEN';
|
const CLOSE_NEED_PERMISSION_SCREEN = 'calling/CLOSE_NEED_PERMISSION_SCREEN';
|
||||||
|
@ -663,16 +675,30 @@ type DenyUserActionType = ReadonlyDeep<{
|
||||||
|
|
||||||
// eslint-disable-next-line local-rules/type-alias-readonlydeep
|
// eslint-disable-next-line local-rules/type-alias-readonlydeep
|
||||||
type StartCallingLobbyActionType = {
|
type StartCallingLobbyActionType = {
|
||||||
type: 'calling/START_CALLING_LOBBY';
|
type: typeof START_CALLING_LOBBY;
|
||||||
payload: StartCallingLobbyPayloadType;
|
payload: StartCallingLobbyPayloadType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type WaitingForCallingLobbyActionType = ReadonlyDeep<{
|
||||||
|
type: typeof WAITING_FOR_CALLING_LOBBY;
|
||||||
|
payload: { conversationId: string };
|
||||||
|
}>;
|
||||||
|
|
||||||
// eslint-disable-next-line local-rules/type-alias-readonlydeep
|
// eslint-disable-next-line local-rules/type-alias-readonlydeep
|
||||||
type StartCallLinkLobbyActionType = {
|
type StartCallLinkLobbyActionType = {
|
||||||
type: 'calling/START_CALL_LINK_LOBBY';
|
type: typeof START_CALL_LINK_LOBBY;
|
||||||
payload: StartCallLinkLobbyPayloadType;
|
payload: StartCallLinkLobbyPayloadType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type WaitingForCallLinkLobbyActionType = ReadonlyDeep<{
|
||||||
|
type: typeof WAITING_FOR_CALL_LINK_LOBBY;
|
||||||
|
payload: { roomId: string };
|
||||||
|
}>;
|
||||||
|
type CallLobbyFailedActionType = ReadonlyDeep<{
|
||||||
|
type: typeof CALL_LOBBY_FAILED;
|
||||||
|
payload: { conversationId: string };
|
||||||
|
}>;
|
||||||
|
|
||||||
type CallStateChangeFulfilledActionType = ReadonlyDeep<{
|
type CallStateChangeFulfilledActionType = ReadonlyDeep<{
|
||||||
type: 'calling/CALL_STATE_CHANGE_FULFILLED';
|
type: 'calling/CALL_STATE_CHANGE_FULFILLED';
|
||||||
payload: CallStateChangeType;
|
payload: CallStateChangeType;
|
||||||
|
@ -904,6 +930,7 @@ type SwitchFromPresentationViewActionType = ReadonlyDeep<{
|
||||||
export type CallingActionType =
|
export type CallingActionType =
|
||||||
| ApproveUserActionType
|
| ApproveUserActionType
|
||||||
| AcceptCallPendingActionType
|
| AcceptCallPendingActionType
|
||||||
|
| CallLobbyFailedActionType
|
||||||
| CancelCallActionType
|
| CancelCallActionType
|
||||||
| CancelIncomingGroupCallRingActionType
|
| CancelIncomingGroupCallRingActionType
|
||||||
| ChangeCallViewActionType
|
| ChangeCallViewActionType
|
||||||
|
@ -946,7 +973,9 @@ export type CallingActionType =
|
||||||
| SetPresentingFulfilledActionType
|
| SetPresentingFulfilledActionType
|
||||||
| ToggleSettingsActionType
|
| ToggleSettingsActionType
|
||||||
| SwitchToPresentationViewActionType
|
| SwitchToPresentationViewActionType
|
||||||
| SwitchFromPresentationViewActionType;
|
| SwitchFromPresentationViewActionType
|
||||||
|
| WaitingForCallingLobbyActionType
|
||||||
|
| WaitingForCallLinkLobbyActionType;
|
||||||
|
|
||||||
// Action Creators
|
// Action Creators
|
||||||
|
|
||||||
|
@ -1851,7 +1880,11 @@ function setPresenting(
|
||||||
|
|
||||||
const { activeCallState } = callingState;
|
const { activeCallState } = callingState;
|
||||||
const activeCall = getActiveCall(callingState);
|
const activeCall = getActiveCall(callingState);
|
||||||
if (!activeCall || !activeCallState) {
|
if (
|
||||||
|
!activeCall ||
|
||||||
|
!activeCallState ||
|
||||||
|
activeCallState.state === 'Waiting'
|
||||||
|
) {
|
||||||
log.warn('Trying to present when no call is active');
|
log.warn('Trying to present when no call is active');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1939,7 +1972,7 @@ function onOutgoingVideoCallInConversation(
|
||||||
|
|
||||||
if (await isCallSafe(conversation.attributes, source)) {
|
if (await isCallSafe(conversation.attributes, source)) {
|
||||||
log.info(
|
log.info(
|
||||||
'onOutgoingVideoCallInConversation: call is deemed "safe". Making call'
|
'onOutgoingVideoCallInConversation: call is deemed "safe". Starting lobby'
|
||||||
);
|
);
|
||||||
dispatch(
|
dispatch(
|
||||||
startCallingLobby({
|
startCallingLobby({
|
||||||
|
@ -1947,7 +1980,6 @@ function onOutgoingVideoCallInConversation(
|
||||||
isVideoCall: true,
|
isVideoCall: true,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
log.info('onOutgoingVideoCallInConversation: started the call');
|
|
||||||
} else {
|
} else {
|
||||||
log.info(
|
log.info(
|
||||||
'onOutgoingVideoCallInConversation: call is deemed "unsafe". Stopping'
|
'onOutgoingVideoCallInConversation: call is deemed "unsafe". Stopping'
|
||||||
|
@ -1980,13 +2012,12 @@ function onOutgoingAudioCallInConversation(
|
||||||
|
|
||||||
if (await isCallSafe(conversation.attributes, source)) {
|
if (await isCallSafe(conversation.attributes, source)) {
|
||||||
log.info(
|
log.info(
|
||||||
'onOutgoingAudioCallInConversation: call is deemed "safe". Making call'
|
'onOutgoingAudioCallInConversation: call is deemed "safe". Starting lobby'
|
||||||
);
|
);
|
||||||
startCallingLobby({
|
startCallingLobby({
|
||||||
conversationId,
|
conversationId,
|
||||||
isVideoCall: false,
|
isVideoCall: false,
|
||||||
})(dispatch, getState, undefined);
|
})(dispatch, getState, undefined);
|
||||||
log.info('onOutgoingAudioCallInConversation: started the call');
|
|
||||||
} else {
|
} else {
|
||||||
log.info(
|
log.info(
|
||||||
'onOutgoingAudioCallInConversation: call is deemed "unsafe". Stopping'
|
'onOutgoingAudioCallInConversation: call is deemed "unsafe". Stopping'
|
||||||
|
@ -2118,10 +2149,12 @@ const _startCallLinkLobby = async ({
|
||||||
dispatch: ThunkDispatch<
|
dispatch: ThunkDispatch<
|
||||||
RootStateType,
|
RootStateType,
|
||||||
unknown,
|
unknown,
|
||||||
|
| CallLobbyFailedActionType
|
||||||
| StartCallLinkLobbyActionType
|
| StartCallLinkLobbyActionType
|
||||||
| ShowErrorModalActionType
|
| ShowErrorModalActionType
|
||||||
| ToggleConfirmLeaveCallModalActionType
|
| ToggleConfirmLeaveCallModalActionType
|
||||||
| TogglePipActionType
|
| TogglePipActionType
|
||||||
|
| WaitingForCallLinkLobbyActionType
|
||||||
>;
|
>;
|
||||||
getState: () => RootStateType;
|
getState: () => RootStateType;
|
||||||
}) => {
|
}) => {
|
||||||
|
@ -2129,9 +2162,17 @@ const _startCallLinkLobby = async ({
|
||||||
const roomId = getRoomIdFromRootKey(callLinkRootKey);
|
const roomId = getRoomIdFromRootKey(callLinkRootKey);
|
||||||
const state = getState();
|
const state = getState();
|
||||||
|
|
||||||
|
const logId = `_startCallLinkLobby(${roomId})`;
|
||||||
|
|
||||||
const { activeCallState } = state.calling;
|
const { activeCallState } = state.calling;
|
||||||
if (activeCallState && activeCallState.conversationId === roomId) {
|
if (activeCallState && activeCallState.conversationId === roomId) {
|
||||||
dispatch(togglePip());
|
if (activeCallState.state === 'Active') {
|
||||||
|
dispatch(togglePip());
|
||||||
|
} else {
|
||||||
|
log.warn(
|
||||||
|
`${logId}: Attempted to start lobby while already waiting for it!`
|
||||||
|
);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (activeCallState) {
|
if (activeCallState) {
|
||||||
|
@ -2144,50 +2185,51 @@ const _startCallLinkLobby = async ({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let callLinkState: CallLinkStateType | null = null;
|
|
||||||
try {
|
try {
|
||||||
|
dispatch({
|
||||||
|
type: WAITING_FOR_CALL_LINK_LOBBY,
|
||||||
|
payload: {
|
||||||
|
roomId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let callLinkState: CallLinkStateType | null = null;
|
||||||
callLinkState = await calling.readCallLink(callLinkRootKey);
|
callLinkState = await calling.readCallLink(callLinkRootKey);
|
||||||
} catch (error) {
|
|
||||||
log.error(
|
|
||||||
'startCallLinkLobby: Error fetching call link state',
|
|
||||||
Errors.toLogFormat(error)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (callLinkState == null) {
|
if (callLinkState == null) {
|
||||||
const i18n = getIntl(getState());
|
const i18n = getIntl(getState());
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SHOW_ERROR_MODAL,
|
type: SHOW_ERROR_MODAL,
|
||||||
payload: {
|
payload: {
|
||||||
title: i18n('icu:calling__cant-join'),
|
title: i18n('icu:calling__cant-join'),
|
||||||
description: i18n('icu:calling__call-link-connection-issues'),
|
description: i18n('icu:calling__call-link-connection-issues'),
|
||||||
buttonVariant: ButtonVariant.Primary,
|
buttonVariant: ButtonVariant.Primary,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (
|
|
||||||
callLinkState.revoked ||
|
if (
|
||||||
callLinkState.expiration == null ||
|
callLinkState.revoked ||
|
||||||
callLinkState.expiration < new Date().getTime()
|
callLinkState.expiration == null ||
|
||||||
) {
|
callLinkState.expiration < new Date().getTime()
|
||||||
const i18n = getIntl(getState());
|
) {
|
||||||
dispatch({
|
const i18n = getIntl(getState());
|
||||||
type: SHOW_ERROR_MODAL,
|
dispatch({
|
||||||
payload: {
|
type: SHOW_ERROR_MODAL,
|
||||||
title: i18n('icu:calling__cant-join'),
|
payload: {
|
||||||
description: i18n('icu:calling__call-link-no-longer-valid'),
|
title: i18n('icu:calling__cant-join'),
|
||||||
buttonVariant: ButtonVariant.Primary,
|
description: i18n('icu:calling__call-link-no-longer-valid'),
|
||||||
},
|
buttonVariant: ButtonVariant.Primary,
|
||||||
});
|
},
|
||||||
return;
|
});
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
const callLinkExists = await DataReader.callLinkExists(roomId);
|
const callLinkExists = await DataReader.callLinkExists(roomId);
|
||||||
if (callLinkExists) {
|
if (callLinkExists) {
|
||||||
await DataWriter.updateCallLinkState(roomId, callLinkState);
|
await DataWriter.updateCallLinkState(roomId, callLinkState);
|
||||||
log.info('startCallLinkLobby: Updated existing call link', roomId);
|
log.info(`${logId}: Updated existing call link`);
|
||||||
} else {
|
} else {
|
||||||
const { name, restrictions, expiration, revoked } = callLinkState;
|
const { name, restrictions, expiration, revoked } = callLinkState;
|
||||||
await DataWriter.insertCallLink({
|
await DataWriter.insertCallLink({
|
||||||
|
@ -2200,44 +2242,56 @@ const _startCallLinkLobby = async ({
|
||||||
expiration,
|
expiration,
|
||||||
storageNeedsSync: false,
|
storageNeedsSync: false,
|
||||||
});
|
});
|
||||||
log.info('startCallLinkLobby: Saved new call link', roomId);
|
log.info(`${logId}: Saved new call link`);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
log.error(
|
const groupCall = getGroupCall(roomId, state.calling, CallMode.Adhoc);
|
||||||
'startCallLinkLobby: Call link DB error',
|
const groupCallDeviceCount =
|
||||||
Errors.toLogFormat(err)
|
groupCall?.peekInfo?.deviceCount ||
|
||||||
);
|
groupCall?.remoteParticipants.length ||
|
||||||
|
0;
|
||||||
|
|
||||||
|
const { adminKey } = getOwn(state.calling.callLinks, roomId) ?? {};
|
||||||
|
const adminPasskey = adminKey ? toAdminKeyBytes(adminKey) : undefined;
|
||||||
|
|
||||||
|
const callLobbyData = await calling.startCallLinkLobby({
|
||||||
|
callLinkRootKey,
|
||||||
|
adminPasskey,
|
||||||
|
hasLocalAudio:
|
||||||
|
groupCallDeviceCount < MAX_CALL_PARTICIPANTS_FOR_DEFAULT_MUTE,
|
||||||
|
});
|
||||||
|
if (!callLobbyData) {
|
||||||
|
throw new Error('Failed to start call lobby');
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: START_CALL_LINK_LOBBY,
|
||||||
|
payload: {
|
||||||
|
...callLobbyData,
|
||||||
|
callLinkState,
|
||||||
|
callLinkRoomId: roomId,
|
||||||
|
callLinkRootKey: rootKey,
|
||||||
|
conversationId: roomId,
|
||||||
|
isConversationTooBigToRing: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`${logId}: Failed to start lobby`, Errors.toLogFormat(error));
|
||||||
|
|
||||||
|
try {
|
||||||
|
calling.stopCallingLobby(roomId);
|
||||||
|
} catch (innerError) {
|
||||||
|
log.error(
|
||||||
|
`${logId}: Failed to stop calling lobby`,
|
||||||
|
Errors.toLogFormat(innerError)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: CALL_LOBBY_FAILED,
|
||||||
|
payload: { conversationId: roomId },
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const groupCall = getGroupCall(roomId, state.calling, CallMode.Adhoc);
|
|
||||||
const groupCallDeviceCount =
|
|
||||||
groupCall?.peekInfo?.deviceCount ||
|
|
||||||
groupCall?.remoteParticipants.length ||
|
|
||||||
0;
|
|
||||||
|
|
||||||
const { adminKey } = getOwn(state.calling.callLinks, roomId) ?? {};
|
|
||||||
const adminPasskey = adminKey ? toAdminKeyBytes(adminKey) : undefined;
|
|
||||||
const callLobbyData = await calling.startCallLinkLobby({
|
|
||||||
callLinkRootKey,
|
|
||||||
adminPasskey,
|
|
||||||
hasLocalAudio:
|
|
||||||
groupCallDeviceCount < MAX_CALL_PARTICIPANTS_FOR_DEFAULT_MUTE,
|
|
||||||
});
|
|
||||||
if (!callLobbyData) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: START_CALL_LINK_LOBBY,
|
|
||||||
payload: {
|
|
||||||
...callLobbyData,
|
|
||||||
callLinkState,
|
|
||||||
callLinkRoomId: roomId,
|
|
||||||
callLinkRootKey: rootKey,
|
|
||||||
conversationId: roomId,
|
|
||||||
isConversationTooBigToRing: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function leaveCurrentCallAndStartCallingLobby(
|
function leaveCurrentCallAndStartCallingLobby(
|
||||||
|
@ -2275,9 +2329,11 @@ function startCallingLobby({
|
||||||
void,
|
void,
|
||||||
RootStateType,
|
RootStateType,
|
||||||
unknown,
|
unknown,
|
||||||
|
| CallLobbyFailedActionType
|
||||||
| StartCallingLobbyActionType
|
| StartCallingLobbyActionType
|
||||||
| ToggleConfirmLeaveCallModalActionType
|
| ToggleConfirmLeaveCallModalActionType
|
||||||
| TogglePipActionType
|
| TogglePipActionType
|
||||||
|
| WaitingForCallingLobbyActionType
|
||||||
> {
|
> {
|
||||||
return async (dispatch, getState) => {
|
return async (dispatch, getState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
|
@ -2290,10 +2346,17 @@ function startCallingLobby({
|
||||||
"startCallingLobby: can't start lobby without a conversation"
|
"startCallingLobby: can't start lobby without a conversation"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const logId = `startCallingLobby(${getConversationIdForLogging(conversation)})`;
|
||||||
const { activeCallState } = state.calling;
|
const { activeCallState } = state.calling;
|
||||||
|
|
||||||
if (activeCallState && activeCallState.conversationId === conversationId) {
|
if (activeCallState && activeCallState.conversationId === conversationId) {
|
||||||
dispatch(togglePip());
|
if (activeCallState.state === 'Active') {
|
||||||
|
dispatch(togglePip());
|
||||||
|
} else {
|
||||||
|
log.warn(
|
||||||
|
`${logId}: Attempted to start lobby while already waiting for it!`
|
||||||
|
);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (activeCallState) {
|
if (activeCallState) {
|
||||||
|
@ -2307,35 +2370,59 @@ function startCallingLobby({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The group call device count is considered 0 for a direct call.
|
try {
|
||||||
const groupCall = getGroupCall(
|
dispatch({
|
||||||
conversationId,
|
type: WAITING_FOR_CALLING_LOBBY,
|
||||||
state.calling,
|
payload: {
|
||||||
CallMode.Group
|
conversationId,
|
||||||
);
|
},
|
||||||
const groupCallDeviceCount =
|
});
|
||||||
groupCall?.peekInfo?.deviceCount ||
|
|
||||||
groupCall?.remoteParticipants.length ||
|
|
||||||
0;
|
|
||||||
|
|
||||||
const callLobbyData = await calling.startCallingLobby({
|
// The group call device count is considered 0 for a direct call.
|
||||||
conversation,
|
const groupCall = getGroupCall(
|
||||||
hasLocalAudio:
|
|
||||||
groupCallDeviceCount < MAX_CALL_PARTICIPANTS_FOR_DEFAULT_MUTE,
|
|
||||||
hasLocalVideo: isVideoCall,
|
|
||||||
});
|
|
||||||
if (!callLobbyData) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: START_CALLING_LOBBY,
|
|
||||||
payload: {
|
|
||||||
...callLobbyData,
|
|
||||||
conversationId,
|
conversationId,
|
||||||
isConversationTooBigToRing: isConversationTooBigToRing(conversation),
|
state.calling,
|
||||||
},
|
CallMode.Group
|
||||||
});
|
);
|
||||||
|
const groupCallDeviceCount =
|
||||||
|
groupCall?.peekInfo?.deviceCount ||
|
||||||
|
groupCall?.remoteParticipants.length ||
|
||||||
|
0;
|
||||||
|
const callLobbyData = await calling.startCallingLobby({
|
||||||
|
conversation,
|
||||||
|
hasLocalAudio:
|
||||||
|
groupCallDeviceCount < MAX_CALL_PARTICIPANTS_FOR_DEFAULT_MUTE,
|
||||||
|
hasLocalVideo: isVideoCall,
|
||||||
|
});
|
||||||
|
if (!callLobbyData) {
|
||||||
|
throw new Error('Failed to start call lobby');
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: START_CALLING_LOBBY,
|
||||||
|
payload: {
|
||||||
|
...callLobbyData,
|
||||||
|
conversationId,
|
||||||
|
isConversationTooBigToRing: isConversationTooBigToRing(conversation),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`${logId}: Failed to start lobby`, Errors.toLogFormat(error));
|
||||||
|
|
||||||
|
try {
|
||||||
|
calling.stopCallingLobby(conversationId);
|
||||||
|
} catch (innerError) {
|
||||||
|
log.error(
|
||||||
|
`${logId}: Failed to stop calling lobby`,
|
||||||
|
Errors.toLogFormat(innerError)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: CALL_LOBBY_FAILED,
|
||||||
|
payload: { conversationId },
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2345,10 +2432,16 @@ function startCall(
|
||||||
return async (dispatch, getState) => {
|
return async (dispatch, getState) => {
|
||||||
const { callMode, conversationId, hasLocalAudio, hasLocalVideo } = payload;
|
const { callMode, conversationId, hasLocalAudio, hasLocalVideo } = payload;
|
||||||
|
|
||||||
|
const logId = `startCall(${conversationId})`;
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const { activeCallState } = state.calling;
|
const { activeCallState } = state.calling;
|
||||||
|
|
||||||
log.info(`startCall for conversation ${conversationId}, mode ${callMode}`);
|
log.info(`${logId}: starting, mode ${callMode}`);
|
||||||
|
|
||||||
|
if (activeCallState?.state === 'Waiting') {
|
||||||
|
log.error(`${logId}: Call is not ready; `);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (callMode) {
|
switch (callMode) {
|
||||||
case CallMode.Direct:
|
case CallMode.Direct:
|
||||||
|
@ -2609,6 +2702,40 @@ export function reducer(
|
||||||
): CallingStateType {
|
): CallingStateType {
|
||||||
const { callsByConversation, adhocCalls } = state;
|
const { callsByConversation, adhocCalls } = state;
|
||||||
|
|
||||||
|
if (action.type === WAITING_FOR_CALLING_LOBBY) {
|
||||||
|
const { conversationId } = action.payload;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
activeCallState: {
|
||||||
|
state: 'Waiting',
|
||||||
|
conversationId,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (action.type === WAITING_FOR_CALL_LINK_LOBBY) {
|
||||||
|
const { roomId } = action.payload;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
activeCallState: {
|
||||||
|
state: 'Waiting',
|
||||||
|
conversationId: roomId,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (action.type === CALL_LOBBY_FAILED) {
|
||||||
|
const { conversationId } = action.payload;
|
||||||
|
|
||||||
|
const { activeCallState } = state;
|
||||||
|
if (!activeCallState || activeCallState.conversationId !== conversationId) {
|
||||||
|
log.warn(
|
||||||
|
`${action.type}: Active call does not match target conversation`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return removeConversationFromState(state, conversationId);
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
action.type === START_CALLING_LOBBY ||
|
action.type === START_CALLING_LOBBY ||
|
||||||
action.type === START_CALL_LINK_LOBBY
|
action.type === START_CALL_LINK_LOBBY
|
||||||
|
@ -2708,6 +2835,7 @@ export function reducer(
|
||||||
}
|
}
|
||||||
: callLinks,
|
: callLinks,
|
||||||
activeCallState: {
|
activeCallState: {
|
||||||
|
state: 'Active',
|
||||||
callMode,
|
callMode,
|
||||||
conversationId,
|
conversationId,
|
||||||
hasLocalAudio: action.payload.hasLocalAudio,
|
hasLocalAudio: action.payload.hasLocalAudio,
|
||||||
|
@ -2737,6 +2865,7 @@ export function reducer(
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
activeCallState: {
|
activeCallState: {
|
||||||
|
state: 'Active',
|
||||||
callMode: CallMode.Direct,
|
callMode: CallMode.Direct,
|
||||||
conversationId: action.payload.conversationId,
|
conversationId: action.payload.conversationId,
|
||||||
hasLocalAudio: action.payload.hasLocalAudio,
|
hasLocalAudio: action.payload.hasLocalAudio,
|
||||||
|
@ -2765,6 +2894,7 @@ export function reducer(
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
activeCallState: {
|
activeCallState: {
|
||||||
|
state: 'Active',
|
||||||
callMode: call.callMode,
|
callMode: call.callMode,
|
||||||
conversationId: action.payload.conversationId,
|
conversationId: action.payload.conversationId,
|
||||||
hasLocalAudio: true,
|
hasLocalAudio: true,
|
||||||
|
@ -2787,7 +2917,7 @@ export function reducer(
|
||||||
) {
|
) {
|
||||||
const activeCall = getActiveCall(state);
|
const activeCall = getActiveCall(state);
|
||||||
if (!activeCall) {
|
if (!activeCall) {
|
||||||
log.warn('No active call to remove');
|
log.warn(`${action.type}: No active call to remove`);
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
switch (activeCall.callMode) {
|
switch (activeCall.callMode) {
|
||||||
|
@ -2822,6 +2952,7 @@ export function reducer(
|
||||||
const activeCall = getActiveCall(state);
|
const activeCall = getActiveCall(state);
|
||||||
const { activeCallState } = state;
|
const { activeCallState } = state;
|
||||||
if (
|
if (
|
||||||
|
activeCallState?.state === 'Waiting' ||
|
||||||
!activeCallState?.outgoingRing ||
|
!activeCallState?.outgoingRing ||
|
||||||
activeCallState.conversationId !== action.payload.id ||
|
activeCallState.conversationId !== action.payload.id ||
|
||||||
!isGroupOrAdhocCallState(activeCall) ||
|
!isGroupOrAdhocCallState(activeCall) ||
|
||||||
|
@ -2927,6 +3058,7 @@ export function reducer(
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
activeCallState: {
|
activeCallState: {
|
||||||
|
state: 'Active',
|
||||||
callMode: CallMode.Direct,
|
callMode: CallMode.Direct,
|
||||||
conversationId: action.payload.conversationId,
|
conversationId: action.payload.conversationId,
|
||||||
hasLocalAudio: action.payload.hasLocalAudio,
|
hasLocalAudio: action.payload.hasLocalAudio,
|
||||||
|
@ -2956,7 +3088,9 @@ export function reducer(
|
||||||
calling.notifyScreenShareStatus({
|
calling.notifyScreenShareStatus({
|
||||||
callMode: CallMode.Direct,
|
callMode: CallMode.Direct,
|
||||||
callState: action.payload.callState,
|
callState: action.payload.callState,
|
||||||
isPresenting: state.activeCallState?.presentingSource != null,
|
isPresenting:
|
||||||
|
state.activeCallState?.state === 'Active' &&
|
||||||
|
state.activeCallState?.presentingSource != null,
|
||||||
conversationId: state.activeCallState?.conversationId,
|
conversationId: state.activeCallState?.conversationId,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -2977,9 +3111,10 @@ export function reducer(
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
let activeCallState: undefined | ActiveCallStateType;
|
let activeCallState: undefined | ActiveCallStateType | WaitingCallStateType;
|
||||||
if (
|
if (
|
||||||
state.activeCallState?.conversationId === action.payload.conversationId
|
state.activeCallState?.conversationId === action.payload.conversationId &&
|
||||||
|
state.activeCallState.state === 'Active'
|
||||||
) {
|
) {
|
||||||
activeCallState = {
|
activeCallState = {
|
||||||
...state.activeCallState,
|
...state.activeCallState,
|
||||||
|
@ -3011,7 +3146,12 @@ export function reducer(
|
||||||
|
|
||||||
// The PiP check is an optimization. We don't need to update audio levels if the user
|
// The PiP check is an optimization. We don't need to update audio levels if the user
|
||||||
// cannot see them.
|
// cannot see them.
|
||||||
if (!activeCallState || activeCallState.pip || !existingCall) {
|
if (
|
||||||
|
!activeCallState ||
|
||||||
|
activeCallState.state === 'Waiting' ||
|
||||||
|
activeCallState.pip ||
|
||||||
|
!existingCall
|
||||||
|
) {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3113,8 +3253,14 @@ export function reducer(
|
||||||
deviceCount: remoteParticipants.length,
|
deviceCount: remoteParticipants.length,
|
||||||
};
|
};
|
||||||
|
|
||||||
let newActiveCallState: ActiveCallStateType | undefined;
|
let newActiveCallState:
|
||||||
if (state.activeCallState?.conversationId === conversationId) {
|
| undefined
|
||||||
|
| ActiveCallStateType
|
||||||
|
| WaitingCallStateType;
|
||||||
|
if (
|
||||||
|
state.activeCallState?.state === 'Active' &&
|
||||||
|
state.activeCallState?.conversationId === conversationId
|
||||||
|
) {
|
||||||
newActiveCallState =
|
newActiveCallState =
|
||||||
connectionState === GroupCallConnectionState.NotConnected
|
connectionState === GroupCallConnectionState.NotConnected
|
||||||
? undefined
|
? undefined
|
||||||
|
@ -3140,6 +3286,7 @@ export function reducer(
|
||||||
|
|
||||||
if (
|
if (
|
||||||
newActiveCallState &&
|
newActiveCallState &&
|
||||||
|
newActiveCallState.state === 'Active' &&
|
||||||
newActiveCallState.outgoingRing &&
|
newActiveCallState.outgoingRing &&
|
||||||
newActiveCallState.conversationId === conversationId &&
|
newActiveCallState.conversationId === conversationId &&
|
||||||
isAnybodyElseInGroupCall(newPeekInfo, ourAci)
|
isAnybodyElseInGroupCall(newPeekInfo, ourAci)
|
||||||
|
@ -3174,7 +3321,9 @@ export function reducer(
|
||||||
calling.notifyScreenShareStatus({
|
calling.notifyScreenShareStatus({
|
||||||
callMode,
|
callMode,
|
||||||
connectionState,
|
connectionState,
|
||||||
isPresenting: state.activeCallState?.presentingSource != null,
|
isPresenting:
|
||||||
|
state.activeCallState?.state === 'Active' &&
|
||||||
|
state.activeCallState?.presentingSource != null,
|
||||||
conversationId: state.activeCallState?.conversationId,
|
conversationId: state.activeCallState?.conversationId,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -3246,7 +3395,10 @@ export function reducer(
|
||||||
action.type === GROUP_CALL_REACTIONS_RECEIVED
|
action.type === GROUP_CALL_REACTIONS_RECEIVED
|
||||||
) {
|
) {
|
||||||
const { callMode, conversationId, timestamp } = action.payload;
|
const { callMode, conversationId, timestamp } = action.payload;
|
||||||
if (state.activeCallState?.conversationId !== conversationId) {
|
if (
|
||||||
|
state.activeCallState?.state === 'Waiting' ||
|
||||||
|
state.activeCallState?.conversationId !== conversationId
|
||||||
|
) {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3300,6 +3452,7 @@ export function reducer(
|
||||||
if (action.type === GROUP_CALL_REACTIONS_EXPIRED) {
|
if (action.type === GROUP_CALL_REACTIONS_EXPIRED) {
|
||||||
const { conversationId, timestamp: receivedAt } = action.payload;
|
const { conversationId, timestamp: receivedAt } = action.payload;
|
||||||
if (
|
if (
|
||||||
|
state.activeCallState?.state === 'Waiting' ||
|
||||||
state.activeCallState?.conversationId !== conversationId ||
|
state.activeCallState?.conversationId !== conversationId ||
|
||||||
!state.activeCallState?.reactions
|
!state.activeCallState?.reactions
|
||||||
) {
|
) {
|
||||||
|
@ -3386,7 +3539,7 @@ export function reducer(
|
||||||
|
|
||||||
if (action.type === RETURN_TO_ACTIVE_CALL) {
|
if (action.type === RETURN_TO_ACTIVE_CALL) {
|
||||||
const { activeCallState } = state;
|
const { activeCallState } = state;
|
||||||
if (!activeCallState) {
|
if (activeCallState?.state !== 'Active') {
|
||||||
log.warn('Cannot return to active call if there is no active call');
|
log.warn('Cannot return to active call if there is no active call');
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -3401,7 +3554,7 @@ export function reducer(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.type === SET_LOCAL_AUDIO_FULFILLED) {
|
if (action.type === SET_LOCAL_AUDIO_FULFILLED) {
|
||||||
if (!state.activeCallState) {
|
if (state.activeCallState?.state !== 'Active') {
|
||||||
log.warn('Cannot set local audio with no active call');
|
log.warn('Cannot set local audio with no active call');
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -3416,7 +3569,7 @@ export function reducer(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.type === SET_LOCAL_VIDEO_FULFILLED) {
|
if (action.type === SET_LOCAL_VIDEO_FULFILLED) {
|
||||||
if (!state.activeCallState) {
|
if (state.activeCallState?.state !== 'Active') {
|
||||||
log.warn('Cannot set local video with no active call');
|
log.warn('Cannot set local video with no active call');
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -3471,7 +3624,7 @@ export function reducer(
|
||||||
|
|
||||||
if (action.type === TOGGLE_SETTINGS) {
|
if (action.type === TOGGLE_SETTINGS) {
|
||||||
const { activeCallState } = state;
|
const { activeCallState } = state;
|
||||||
if (!activeCallState) {
|
if (activeCallState?.state !== 'Active') {
|
||||||
log.warn('Cannot toggle settings when there is no active call');
|
log.warn('Cannot toggle settings when there is no active call');
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -3487,7 +3640,7 @@ export function reducer(
|
||||||
|
|
||||||
if (action.type === TOGGLE_PARTICIPANTS) {
|
if (action.type === TOGGLE_PARTICIPANTS) {
|
||||||
const { activeCallState } = state;
|
const { activeCallState } = state;
|
||||||
if (!activeCallState) {
|
if (activeCallState?.state !== 'Active') {
|
||||||
log.warn('Cannot toggle participants list when there is no active call');
|
log.warn('Cannot toggle participants list when there is no active call');
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -3503,7 +3656,7 @@ export function reducer(
|
||||||
|
|
||||||
if (action.type === TOGGLE_PIP) {
|
if (action.type === TOGGLE_PIP) {
|
||||||
const { activeCallState } = state;
|
const { activeCallState } = state;
|
||||||
if (!activeCallState) {
|
if (activeCallState?.state !== 'Active') {
|
||||||
log.warn('Cannot toggle PiP when there is no active call');
|
log.warn('Cannot toggle PiP when there is no active call');
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -3519,7 +3672,7 @@ export function reducer(
|
||||||
|
|
||||||
if (action.type === SET_PRESENTING) {
|
if (action.type === SET_PRESENTING) {
|
||||||
const { activeCallState } = state;
|
const { activeCallState } = state;
|
||||||
if (!activeCallState) {
|
if (activeCallState?.state !== 'Active') {
|
||||||
log.warn('Cannot toggle presenting when there is no active call');
|
log.warn('Cannot toggle presenting when there is no active call');
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -3536,7 +3689,7 @@ export function reducer(
|
||||||
|
|
||||||
if (action.type === SET_PRESENTING_SOURCES) {
|
if (action.type === SET_PRESENTING_SOURCES) {
|
||||||
const { activeCallState } = state;
|
const { activeCallState } = state;
|
||||||
if (!activeCallState) {
|
if (activeCallState?.state !== 'Active') {
|
||||||
log.warn('Cannot set presenting sources when there is no active call');
|
log.warn('Cannot set presenting sources when there is no active call');
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -3552,7 +3705,7 @@ export function reducer(
|
||||||
|
|
||||||
if (action.type === SET_OUTGOING_RING) {
|
if (action.type === SET_OUTGOING_RING) {
|
||||||
const { activeCallState } = state;
|
const { activeCallState } = state;
|
||||||
if (!activeCallState) {
|
if (activeCallState?.state !== 'Active') {
|
||||||
log.warn('Cannot set outgoing ring when there is no active call');
|
log.warn('Cannot set outgoing ring when there is no active call');
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -3568,7 +3721,7 @@ export function reducer(
|
||||||
|
|
||||||
if (action.type === TOGGLE_NEEDS_SCREEN_RECORDING_PERMISSIONS) {
|
if (action.type === TOGGLE_NEEDS_SCREEN_RECORDING_PERMISSIONS) {
|
||||||
const { activeCallState } = state;
|
const { activeCallState } = state;
|
||||||
if (!activeCallState) {
|
if (activeCallState?.state !== 'Active') {
|
||||||
log.warn('Cannot set presenting sources when there is no active call');
|
log.warn('Cannot set presenting sources when there is no active call');
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -3585,7 +3738,7 @@ export function reducer(
|
||||||
|
|
||||||
if (action.type === CHANGE_CALL_VIEW) {
|
if (action.type === CHANGE_CALL_VIEW) {
|
||||||
const { activeCallState } = state;
|
const { activeCallState } = state;
|
||||||
if (!activeCallState) {
|
if (activeCallState?.state !== 'Active') {
|
||||||
log.warn('Cannot change call view when there is no active call');
|
log.warn('Cannot change call view when there is no active call');
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -3609,7 +3762,7 @@ export function reducer(
|
||||||
|
|
||||||
if (action.type === SWITCH_TO_PRESENTATION_VIEW) {
|
if (action.type === SWITCH_TO_PRESENTATION_VIEW) {
|
||||||
const { activeCallState } = state;
|
const { activeCallState } = state;
|
||||||
if (!activeCallState) {
|
if (activeCallState?.state !== 'Active') {
|
||||||
log.warn('Cannot switch to speaker view when there is no active call');
|
log.warn('Cannot switch to speaker view when there is no active call');
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -3630,7 +3783,7 @@ export function reducer(
|
||||||
|
|
||||||
if (action.type === SWITCH_FROM_PRESENTATION_VIEW) {
|
if (action.type === SWITCH_FROM_PRESENTATION_VIEW) {
|
||||||
const { activeCallState } = state;
|
const { activeCallState } = state;
|
||||||
if (!activeCallState) {
|
if (activeCallState?.state !== 'Active') {
|
||||||
log.warn('Cannot switch to speaker view when there is no active call');
|
log.warn('Cannot switch to speaker view when there is no active call');
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import type {
|
||||||
CallLinksByRoomIdType,
|
CallLinksByRoomIdType,
|
||||||
DirectCallStateType,
|
DirectCallStateType,
|
||||||
GroupCallStateType,
|
GroupCallStateType,
|
||||||
|
ActiveCallStateType,
|
||||||
} from '../ducks/calling';
|
} from '../ducks/calling';
|
||||||
import { getIncomingCall as getIncomingCallHelper } from '../ducks/callingHelpers';
|
import { getIncomingCall as getIncomingCallHelper } from '../ducks/callingHelpers';
|
||||||
import { CallMode } from '../../types/CallDisposition';
|
import { CallMode } from '../../types/CallDisposition';
|
||||||
|
@ -55,7 +56,13 @@ export const getSelectedCamera = createSelector(
|
||||||
|
|
||||||
export const getActiveCallState = createSelector(
|
export const getActiveCallState = createSelector(
|
||||||
getCalling,
|
getCalling,
|
||||||
(state: CallingStateType) => state.activeCallState
|
(state: CallingStateType) => {
|
||||||
|
if (state.activeCallState?.state !== 'Active') {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return state.activeCallState;
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getCallsByConversation = createSelector(
|
export const getCallsByConversation = createSelector(
|
||||||
|
@ -134,9 +141,9 @@ export const isInCall = createSelector(
|
||||||
);
|
);
|
||||||
|
|
||||||
export const isInFullScreenCall = createSelector(
|
export const isInFullScreenCall = createSelector(
|
||||||
getCalling,
|
getActiveCallState,
|
||||||
(state: CallingStateType): boolean =>
|
(activeCallState: undefined | ActiveCallStateType): boolean =>
|
||||||
Boolean(state.activeCallState && !state.activeCallState.pip)
|
Boolean(activeCallState?.pip)
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getIncomingCall = createSelector(
|
export const getIncomingCall = createSelector(
|
||||||
|
|
|
@ -90,7 +90,9 @@ export const getPreferredTheme = createSelector(
|
||||||
const getIsInFullScreenCall = createSelector(
|
const getIsInFullScreenCall = createSelector(
|
||||||
(state: StateType): CallingStateType => state.calling,
|
(state: StateType): CallingStateType => state.calling,
|
||||||
(state: CallingStateType): boolean =>
|
(state: CallingStateType): boolean =>
|
||||||
Boolean(state.activeCallState && !state.activeCallState.pip)
|
Boolean(
|
||||||
|
state.activeCallState?.state === 'Active' && !state.activeCallState.pip
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getTheme = createSelector(
|
export const getTheme = createSelector(
|
||||||
|
|
|
@ -46,6 +46,7 @@ import type { ConversationType } from '../ducks/conversations';
|
||||||
import type { StateType } from '../reducer';
|
import type { StateType } from '../reducer';
|
||||||
import { getHasInitialLoadCompleted } from '../selectors/app';
|
import { getHasInitialLoadCompleted } from '../selectors/app';
|
||||||
import {
|
import {
|
||||||
|
getActiveCallState,
|
||||||
getAvailableCameras,
|
getAvailableCameras,
|
||||||
getCallLinkSelector,
|
getCallLinkSelector,
|
||||||
getIncomingCall,
|
getIncomingCall,
|
||||||
|
@ -122,7 +123,7 @@ const mapStateToActiveCallProp = (
|
||||||
state: StateType
|
state: StateType
|
||||||
): undefined | ActiveCallType => {
|
): undefined | ActiveCallType => {
|
||||||
const { calling } = state;
|
const { calling } = state;
|
||||||
const { activeCallState } = calling;
|
const activeCallState = getActiveCallState(state);
|
||||||
|
|
||||||
if (!activeCallState) {
|
if (!activeCallState) {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|
|
@ -44,6 +44,7 @@ import {
|
||||||
FAKE_CALL_LINK_WITH_ADMIN_KEY,
|
FAKE_CALL_LINK_WITH_ADMIN_KEY,
|
||||||
getCallLinkState,
|
getCallLinkState,
|
||||||
} from '../../../test-both/helpers/fakeCallLink';
|
} from '../../../test-both/helpers/fakeCallLink';
|
||||||
|
import { strictAssert } from '../../../util/assert';
|
||||||
|
|
||||||
const ACI_1 = generateAci();
|
const ACI_1 = generateAci();
|
||||||
const NOW = new Date('2020-01-23T04:56:00.000');
|
const NOW = new Date('2020-01-23T04:56:00.000');
|
||||||
|
@ -71,6 +72,7 @@ describe('calling duck', () => {
|
||||||
const stateWithActiveDirectCall: CallingStateTypeWithActiveCall = {
|
const stateWithActiveDirectCall: CallingStateTypeWithActiveCall = {
|
||||||
...stateWithDirectCall,
|
...stateWithDirectCall,
|
||||||
activeCallState: {
|
activeCallState: {
|
||||||
|
state: 'Active',
|
||||||
callMode: CallMode.Direct,
|
callMode: CallMode.Direct,
|
||||||
conversationId: directCallState.conversationId,
|
conversationId: directCallState.conversationId,
|
||||||
hasLocalAudio: true,
|
hasLocalAudio: true,
|
||||||
|
@ -174,6 +176,7 @@ describe('calling duck', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const groupCallActiveCallState: ActiveCallStateType = {
|
const groupCallActiveCallState: ActiveCallStateType = {
|
||||||
|
state: 'Active',
|
||||||
callMode: CallMode.Group,
|
callMode: CallMode.Group,
|
||||||
conversationId: 'fake-group-call-conversation-id',
|
conversationId: 'fake-group-call-conversation-id',
|
||||||
hasLocalAudio: true,
|
hasLocalAudio: true,
|
||||||
|
@ -383,6 +386,10 @@ describe('calling duck', () => {
|
||||||
const nextState = reducer(getState().calling, action);
|
const nextState = reducer(getState().calling, action);
|
||||||
|
|
||||||
assert.isDefined(nextState.activeCallState);
|
assert.isDefined(nextState.activeCallState);
|
||||||
|
strictAssert(
|
||||||
|
nextState.activeCallState?.state === 'Active',
|
||||||
|
'state is active'
|
||||||
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
nextState.activeCallState?.presentingSource,
|
nextState.activeCallState?.presentingSource,
|
||||||
presentedSource
|
presentedSource
|
||||||
|
@ -410,6 +417,10 @@ describe('calling duck', () => {
|
||||||
const nextState = reducer(getState().calling, action);
|
const nextState = reducer(getState().calling, action);
|
||||||
|
|
||||||
assert.isDefined(nextState.activeCallState);
|
assert.isDefined(nextState.activeCallState);
|
||||||
|
strictAssert(
|
||||||
|
nextState.activeCallState?.state === 'Active',
|
||||||
|
'state is active'
|
||||||
|
);
|
||||||
assert.isUndefined(nextState.activeCallState?.presentingSource);
|
assert.isUndefined(nextState.activeCallState?.presentingSource);
|
||||||
assert.isUndefined(
|
assert.isUndefined(
|
||||||
nextState.activeCallState?.presentingSourcesAvailable
|
nextState.activeCallState?.presentingSourcesAvailable
|
||||||
|
@ -506,6 +517,7 @@ describe('calling duck', () => {
|
||||||
const result = reducer(stateWithIncomingDirectCall, action);
|
const result = reducer(stateWithIncomingDirectCall, action);
|
||||||
|
|
||||||
assert.deepEqual(result.activeCallState, {
|
assert.deepEqual(result.activeCallState, {
|
||||||
|
state: 'Active',
|
||||||
callMode: CallMode.Direct,
|
callMode: CallMode.Direct,
|
||||||
conversationId: 'fake-direct-call-conversation-id',
|
conversationId: 'fake-direct-call-conversation-id',
|
||||||
hasLocalAudio: true,
|
hasLocalAudio: true,
|
||||||
|
@ -600,6 +612,7 @@ describe('calling duck', () => {
|
||||||
const result = reducer(stateWithIncomingGroupCall, action);
|
const result = reducer(stateWithIncomingGroupCall, action);
|
||||||
|
|
||||||
assert.deepEqual(result.activeCallState, {
|
assert.deepEqual(result.activeCallState, {
|
||||||
|
state: 'Active',
|
||||||
callMode: CallMode.Group,
|
callMode: CallMode.Group,
|
||||||
conversationId: 'fake-group-call-conversation-id',
|
conversationId: 'fake-group-call-conversation-id',
|
||||||
hasLocalAudio: true,
|
hasLocalAudio: true,
|
||||||
|
@ -893,6 +906,10 @@ describe('calling duck', () => {
|
||||||
});
|
});
|
||||||
const result = reducer(stateWithActiveGroupCall, action);
|
const result = reducer(stateWithActiveGroupCall, action);
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
result.activeCallState?.state === 'Active',
|
||||||
|
'state is active'
|
||||||
|
);
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
result.activeCallState?.localAudioLevel,
|
result.activeCallState?.localAudioLevel,
|
||||||
truncateAudioLevel(0.8)
|
truncateAudioLevel(0.8)
|
||||||
|
@ -1228,6 +1245,7 @@ describe('calling duck', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.deepEqual(result.activeCallState, {
|
assert.deepEqual(result.activeCallState, {
|
||||||
|
state: 'Active',
|
||||||
callMode: CallMode.Group,
|
callMode: CallMode.Group,
|
||||||
conversationId: 'fake-group-call-conversation-id',
|
conversationId: 'fake-group-call-conversation-id',
|
||||||
hasLocalAudio: true,
|
hasLocalAudio: true,
|
||||||
|
@ -1278,6 +1296,10 @@ describe('calling duck', () => {
|
||||||
result.activeCallState?.conversationId,
|
result.activeCallState?.conversationId,
|
||||||
'fake-group-call-conversation-id'
|
'fake-group-call-conversation-id'
|
||||||
);
|
);
|
||||||
|
strictAssert(
|
||||||
|
result.activeCallState?.state === 'Active',
|
||||||
|
'state is active'
|
||||||
|
);
|
||||||
assert.isTrue(result.activeCallState?.hasLocalAudio);
|
assert.isTrue(result.activeCallState?.hasLocalAudio);
|
||||||
assert.isTrue(result.activeCallState?.hasLocalVideo);
|
assert.isTrue(result.activeCallState?.hasLocalVideo);
|
||||||
});
|
});
|
||||||
|
@ -1310,6 +1332,10 @@ describe('calling duck', () => {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
result.activeCallState?.state === 'Active',
|
||||||
|
'state is active'
|
||||||
|
);
|
||||||
assert.isTrue(result.activeCallState?.outgoingRing);
|
assert.isTrue(result.activeCallState?.outgoingRing);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1341,6 +1367,10 @@ describe('calling duck', () => {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
result.activeCallState?.state === 'Active',
|
||||||
|
'state is active'
|
||||||
|
);
|
||||||
assert.isFalse(result.activeCallState?.outgoingRing);
|
assert.isFalse(result.activeCallState?.outgoingRing);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1368,6 +1398,10 @@ describe('calling duck', () => {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
result.activeCallState?.state === 'Active',
|
||||||
|
'state is active'
|
||||||
|
);
|
||||||
assert.isFalse(result.activeCallState?.hasLocalAudio);
|
assert.isFalse(result.activeCallState?.hasLocalAudio);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1395,6 +1429,10 @@ describe('calling duck', () => {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
result.activeCallState?.state === 'Active',
|
||||||
|
'state is active'
|
||||||
|
);
|
||||||
assert.isTrue(result.activeCallState?.hasLocalAudio);
|
assert.isTrue(result.activeCallState?.hasLocalAudio);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1419,6 +1457,10 @@ describe('calling duck', () => {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
result.activeCallState?.state === 'Active',
|
||||||
|
'state is active'
|
||||||
|
);
|
||||||
assert.isTrue(result.activeCallState?.hasLocalAudio);
|
assert.isTrue(result.activeCallState?.hasLocalAudio);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1549,7 +1591,13 @@ describe('calling duck', () => {
|
||||||
const { roomId, rootKey } = FAKE_CALL_LINK;
|
const { roomId, rootKey } = FAKE_CALL_LINK;
|
||||||
const { dispatch } = await doAction({ rootKey });
|
const { dispatch } = await doAction({ rootKey });
|
||||||
|
|
||||||
sinon.assert.calledOnce(dispatch);
|
sinon.assert.calledTwice(dispatch);
|
||||||
|
sinon.assert.calledWith(dispatch, {
|
||||||
|
type: 'calling/WAITING_FOR_CALL_LINK_LOBBY',
|
||||||
|
payload: {
|
||||||
|
roomId,
|
||||||
|
},
|
||||||
|
});
|
||||||
sinon.assert.calledWith(dispatch, {
|
sinon.assert.calledWith(dispatch, {
|
||||||
type: 'calling/START_CALL_LINK_LOBBY',
|
type: 'calling/START_CALL_LINK_LOBBY',
|
||||||
payload: {
|
payload: {
|
||||||
|
@ -1789,6 +1837,11 @@ describe('calling duck', () => {
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
const firstResult = reducer(getState().calling, firstAction);
|
const firstResult = reducer(getState().calling, firstAction);
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
firstResult.activeCallState?.state === 'Active',
|
||||||
|
'state is active'
|
||||||
|
);
|
||||||
assert.deepEqual(firstResult.activeCallState?.reactions, [
|
assert.deepEqual(firstResult.activeCallState?.reactions, [
|
||||||
{
|
{
|
||||||
timestamp: NOW.getTime(),
|
timestamp: NOW.getTime(),
|
||||||
|
@ -1810,6 +1863,11 @@ describe('calling duck', () => {
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
const secondResult = reducer(firstResult, secondAction);
|
const secondResult = reducer(firstResult, secondAction);
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
secondResult.activeCallState?.state === 'Active',
|
||||||
|
'state is active'
|
||||||
|
);
|
||||||
assert.deepEqual(secondResult.activeCallState?.reactions, [
|
assert.deepEqual(secondResult.activeCallState?.reactions, [
|
||||||
{
|
{
|
||||||
timestamp: NOW.getTime(),
|
timestamp: NOW.getTime(),
|
||||||
|
@ -1840,6 +1898,11 @@ describe('calling duck', () => {
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
const result = reducer(getState().calling, action);
|
const result = reducer(getState().calling, action);
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
result.activeCallState?.state === 'Active',
|
||||||
|
'state is active'
|
||||||
|
);
|
||||||
assert.deepEqual(result.activeCallState?.reactions, [
|
assert.deepEqual(result.activeCallState?.reactions, [
|
||||||
{
|
{
|
||||||
timestamp: NOW.getTime(),
|
timestamp: NOW.getTime(),
|
||||||
|
@ -1892,6 +1955,10 @@ describe('calling duck', () => {
|
||||||
});
|
});
|
||||||
const result = reducer(getState().calling, action);
|
const result = reducer(getState().calling, action);
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
result.activeCallState?.state === 'Active',
|
||||||
|
'state is active'
|
||||||
|
);
|
||||||
assert.deepEqual(result.activeCallState?.reactions, [
|
assert.deepEqual(result.activeCallState?.reactions, [
|
||||||
{
|
{
|
||||||
timestamp: NOW.getTime(),
|
timestamp: NOW.getTime(),
|
||||||
|
@ -1981,6 +2048,10 @@ describe('calling duck', () => {
|
||||||
|
|
||||||
const result = reducer(stateWithActiveDirectCall, action);
|
const result = reducer(stateWithActiveDirectCall, action);
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
result.activeCallState?.state === 'Active',
|
||||||
|
'state is active'
|
||||||
|
);
|
||||||
assert.isFalse(result.activeCallState?.hasLocalAudio);
|
assert.isFalse(result.activeCallState?.hasLocalAudio);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1992,6 +2063,10 @@ describe('calling duck', () => {
|
||||||
const action = setOutgoingRing(true);
|
const action = setOutgoingRing(true);
|
||||||
const result = reducer(stateWithActiveGroupCall, action);
|
const result = reducer(stateWithActiveGroupCall, action);
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
result.activeCallState?.state === 'Active',
|
||||||
|
'state is active'
|
||||||
|
);
|
||||||
assert.isTrue(result.activeCallState?.outgoingRing);
|
assert.isTrue(result.activeCallState?.outgoingRing);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1999,6 +2074,10 @@ describe('calling duck', () => {
|
||||||
const action = setOutgoingRing(false);
|
const action = setOutgoingRing(false);
|
||||||
const result = reducer(stateWithActiveDirectCall, action);
|
const result = reducer(stateWithActiveDirectCall, action);
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
result.activeCallState?.state === 'Active',
|
||||||
|
'state is active'
|
||||||
|
);
|
||||||
assert.isFalse(result.activeCallState?.outgoingRing);
|
assert.isFalse(result.activeCallState?.outgoingRing);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -2092,7 +2171,7 @@ describe('calling duck', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('dispatches an action if the calling lobby returns something', async () => {
|
it('dispatches two actions if the calling lobby returns something', async () => {
|
||||||
startCallingLobbyStub.resolves({
|
startCallingLobbyStub.resolves({
|
||||||
callMode: CallMode.Direct,
|
callMode: CallMode.Direct,
|
||||||
hasLocalAudio: true,
|
hasLocalAudio: true,
|
||||||
|
@ -2101,23 +2180,55 @@ describe('calling duck', () => {
|
||||||
|
|
||||||
const dispatch = sinon.stub();
|
const dispatch = sinon.stub();
|
||||||
|
|
||||||
|
const conversationId = 'fake-conversation-id';
|
||||||
await startCallingLobby({
|
await startCallingLobby({
|
||||||
conversationId: 'fake-conversation-id',
|
conversationId,
|
||||||
isVideoCall: true,
|
isVideoCall: true,
|
||||||
})(dispatch, () => rootState, null);
|
})(dispatch, () => rootState, null);
|
||||||
|
|
||||||
sinon.assert.calledOnce(dispatch);
|
sinon.assert.calledTwice(dispatch);
|
||||||
|
|
||||||
|
sinon.assert.calledWith(dispatch, {
|
||||||
|
type: 'calling/WAITING_FOR_CALLING_LOBBY',
|
||||||
|
payload: {
|
||||||
|
conversationId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
sinon.assert.calledWith(dispatch, {
|
||||||
|
type: 'calling/START_CALLING_LOBBY',
|
||||||
|
payload: {
|
||||||
|
callMode: 'Direct',
|
||||||
|
hasLocalAudio: true,
|
||||||
|
hasLocalVideo: true,
|
||||||
|
conversationId,
|
||||||
|
isConversationTooBigToRing: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("doesn't dispatch an action if the calling lobby returns nothing", async () => {
|
it('dispatches two actions if the calling lobby returns nothing', async () => {
|
||||||
const dispatch = sinon.stub();
|
const dispatch = sinon.stub();
|
||||||
|
|
||||||
|
const conversationId = 'fake-conversation-id';
|
||||||
await startCallingLobby({
|
await startCallingLobby({
|
||||||
conversationId: 'fake-conversation-id',
|
conversationId,
|
||||||
isVideoCall: true,
|
isVideoCall: true,
|
||||||
})(dispatch, () => rootState, null);
|
})(dispatch, () => rootState, null);
|
||||||
|
|
||||||
sinon.assert.notCalled(dispatch);
|
sinon.assert.calledTwice(dispatch);
|
||||||
|
|
||||||
|
sinon.assert.calledWith(dispatch, {
|
||||||
|
type: 'calling/WAITING_FOR_CALLING_LOBBY',
|
||||||
|
payload: {
|
||||||
|
conversationId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
sinon.assert.calledWith(dispatch, {
|
||||||
|
type: 'calling/CALL_LOBBY_FAILED',
|
||||||
|
payload: {
|
||||||
|
conversationId,
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2138,7 +2249,10 @@ describe('calling duck', () => {
|
||||||
isVideoCall: true,
|
isVideoCall: true,
|
||||||
})(dispatch, () => ({ ...rootState, calling: callingState }), null);
|
})(dispatch, () => ({ ...rootState, calling: callingState }), null);
|
||||||
|
|
||||||
const action = dispatch.getCall(0).args[0];
|
const waitingAction = dispatch.getCall(0).args[0];
|
||||||
|
assert.equal(waitingAction.type, 'calling/WAITING_FOR_CALLING_LOBBY');
|
||||||
|
|
||||||
|
const action = dispatch.getCall(1).args[0];
|
||||||
|
|
||||||
return reducer(callingState, action);
|
return reducer(callingState, action);
|
||||||
};
|
};
|
||||||
|
@ -2157,6 +2271,7 @@ describe('calling duck', () => {
|
||||||
isVideoCall: true,
|
isVideoCall: true,
|
||||||
});
|
});
|
||||||
assert.deepEqual(result.activeCallState, {
|
assert.deepEqual(result.activeCallState, {
|
||||||
|
state: 'Active',
|
||||||
callMode: CallMode.Direct,
|
callMode: CallMode.Direct,
|
||||||
conversationId: 'fake-conversation-id',
|
conversationId: 'fake-conversation-id',
|
||||||
hasLocalAudio: true,
|
hasLocalAudio: true,
|
||||||
|
@ -2231,6 +2346,10 @@ describe('calling duck', () => {
|
||||||
result.activeCallState?.conversationId,
|
result.activeCallState?.conversationId,
|
||||||
'fake-conversation-id'
|
'fake-conversation-id'
|
||||||
);
|
);
|
||||||
|
strictAssert(
|
||||||
|
result.activeCallState?.state === 'Active',
|
||||||
|
'state is active'
|
||||||
|
);
|
||||||
assert.isFalse(result.activeCallState?.outgoingRing);
|
assert.isFalse(result.activeCallState?.outgoingRing);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2397,6 +2516,10 @@ describe('calling duck', () => {
|
||||||
remoteParticipants: [],
|
remoteParticipants: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
result.activeCallState?.state === 'Active',
|
||||||
|
'state is active'
|
||||||
|
);
|
||||||
assert.isTrue(result.activeCallState?.outgoingRing);
|
assert.isTrue(result.activeCallState?.outgoingRing);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -2470,6 +2593,7 @@ describe('calling duck', () => {
|
||||||
isVideoCall: false,
|
isVideoCall: false,
|
||||||
});
|
});
|
||||||
assert.deepEqual(result.activeCallState, {
|
assert.deepEqual(result.activeCallState, {
|
||||||
|
state: 'Active',
|
||||||
callMode: CallMode.Direct,
|
callMode: CallMode.Direct,
|
||||||
conversationId: 'fake-conversation-id',
|
conversationId: 'fake-conversation-id',
|
||||||
hasLocalAudio: true,
|
hasLocalAudio: true,
|
||||||
|
@ -2508,8 +2632,22 @@ describe('calling duck', () => {
|
||||||
const afterTwoToggles = reducer(afterOneToggle, toggleSettings());
|
const afterTwoToggles = reducer(afterOneToggle, toggleSettings());
|
||||||
const afterThreeToggles = reducer(afterTwoToggles, toggleSettings());
|
const afterThreeToggles = reducer(afterTwoToggles, toggleSettings());
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
afterOneToggle.activeCallState?.state === 'Active',
|
||||||
|
'state is active #1'
|
||||||
|
);
|
||||||
assert.isTrue(afterOneToggle.activeCallState?.settingsDialogOpen);
|
assert.isTrue(afterOneToggle.activeCallState?.settingsDialogOpen);
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
afterTwoToggles.activeCallState?.state === 'Active',
|
||||||
|
'state is active #2'
|
||||||
|
);
|
||||||
assert.isFalse(afterTwoToggles.activeCallState?.settingsDialogOpen);
|
assert.isFalse(afterTwoToggles.activeCallState?.settingsDialogOpen);
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
afterThreeToggles.activeCallState?.state === 'Active',
|
||||||
|
'state is active #3'
|
||||||
|
);
|
||||||
assert.isTrue(afterThreeToggles.activeCallState?.settingsDialogOpen);
|
assert.isTrue(afterThreeToggles.activeCallState?.settingsDialogOpen);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -2528,8 +2666,22 @@ describe('calling duck', () => {
|
||||||
toggleParticipants()
|
toggleParticipants()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
afterOneToggle.activeCallState?.state === 'Active',
|
||||||
|
'state is active #1'
|
||||||
|
);
|
||||||
assert.isTrue(afterOneToggle.activeCallState?.showParticipantsList);
|
assert.isTrue(afterOneToggle.activeCallState?.showParticipantsList);
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
afterTwoToggles.activeCallState?.state === 'Active',
|
||||||
|
'state is active #2'
|
||||||
|
);
|
||||||
assert.isFalse(afterTwoToggles.activeCallState?.showParticipantsList);
|
assert.isFalse(afterTwoToggles.activeCallState?.showParticipantsList);
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
afterThreeToggles.activeCallState?.state === 'Active',
|
||||||
|
'state is active #3'
|
||||||
|
);
|
||||||
assert.isTrue(afterThreeToggles.activeCallState?.showParticipantsList);
|
assert.isTrue(afterThreeToggles.activeCallState?.showParticipantsList);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -2542,8 +2694,22 @@ describe('calling duck', () => {
|
||||||
const afterTwoToggles = reducer(afterOneToggle, togglePip());
|
const afterTwoToggles = reducer(afterOneToggle, togglePip());
|
||||||
const afterThreeToggles = reducer(afterTwoToggles, togglePip());
|
const afterThreeToggles = reducer(afterTwoToggles, togglePip());
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
afterOneToggle.activeCallState?.state === 'Active',
|
||||||
|
'state is active #1'
|
||||||
|
);
|
||||||
assert.isTrue(afterOneToggle.activeCallState?.pip);
|
assert.isTrue(afterOneToggle.activeCallState?.pip);
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
afterTwoToggles.activeCallState?.state === 'Active',
|
||||||
|
'state is active #2'
|
||||||
|
);
|
||||||
assert.isFalse(afterTwoToggles.activeCallState?.pip);
|
assert.isFalse(afterTwoToggles.activeCallState?.pip);
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
afterThreeToggles.activeCallState?.state === 'Active',
|
||||||
|
'state is active #3'
|
||||||
|
);
|
||||||
assert.isTrue(afterThreeToggles.activeCallState?.pip);
|
assert.isTrue(afterThreeToggles.activeCallState?.pip);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -2569,14 +2735,28 @@ describe('calling duck', () => {
|
||||||
switchFromPresentationView()
|
switchFromPresentationView()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
afterOneToggle.activeCallState?.state === 'Active',
|
||||||
|
'state is active #1'
|
||||||
|
);
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
afterOneToggle.activeCallState?.viewMode,
|
afterOneToggle.activeCallState?.viewMode,
|
||||||
CallViewMode.Presentation
|
CallViewMode.Presentation
|
||||||
);
|
);
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
afterTwoToggles.activeCallState?.state === 'Active',
|
||||||
|
'state is active #2'
|
||||||
|
);
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
afterTwoToggles.activeCallState?.viewMode,
|
afterTwoToggles.activeCallState?.viewMode,
|
||||||
CallViewMode.Presentation
|
CallViewMode.Presentation
|
||||||
);
|
);
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
afterThreeToggles.activeCallState?.state === 'Active',
|
||||||
|
'state is active #3'
|
||||||
|
);
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
afterThreeToggles.activeCallState?.viewMode,
|
afterThreeToggles.activeCallState?.viewMode,
|
||||||
CallViewMode.Paginated
|
CallViewMode.Paginated
|
||||||
|
@ -2597,6 +2777,10 @@ describe('calling duck', () => {
|
||||||
switchFromPresentationView()
|
switchFromPresentationView()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
strictAssert(
|
||||||
|
stateAfterPresentation.activeCallState?.state === 'Active',
|
||||||
|
'state is active'
|
||||||
|
);
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
stateAfterPresentation.activeCallState?.viewMode,
|
stateAfterPresentation.activeCallState?.viewMode,
|
||||||
CallViewMode.Overflow
|
CallViewMode.Overflow
|
||||||
|
|
|
@ -62,6 +62,7 @@ describe('state/selectors/calling', () => {
|
||||||
const stateWithActiveDirectCall: CallingStateType = {
|
const stateWithActiveDirectCall: CallingStateType = {
|
||||||
...stateWithDirectCall,
|
...stateWithDirectCall,
|
||||||
activeCallState: {
|
activeCallState: {
|
||||||
|
state: 'Active',
|
||||||
callMode: CallMode.Direct,
|
callMode: CallMode.Direct,
|
||||||
conversationId: 'fake-direct-call-conversation-id',
|
conversationId: 'fake-direct-call-conversation-id',
|
||||||
hasLocalAudio: true,
|
hasLocalAudio: true,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue