Ensure that call events don't override existing activeCallState
This commit is contained in:
@@ -2274,12 +2274,18 @@ const _startCallLinkLobby = async ({
|
||||
dispatch(togglePip());
|
||||
} else {
|
||||
log.warn(
|
||||
`${logId}: Attempted to start lobby while already waiting for it!`
|
||||
`${logId}: Attempted to start lobby while already waiting for this call!`
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (activeCallState) {
|
||||
if (activeCallState.state !== 'Active') {
|
||||
log.warn(
|
||||
`${logId}: Call wasn't active; still showing leave call modal`,
|
||||
activeCallState
|
||||
);
|
||||
}
|
||||
dispatch(
|
||||
toggleConfirmLeaveCallModal({
|
||||
type: 'adhoc-rootKey',
|
||||
@@ -2462,12 +2468,18 @@ function startCallingLobby({
|
||||
dispatch(togglePip());
|
||||
} else {
|
||||
log.warn(
|
||||
`${logId}: Attempted to start lobby while already waiting for it!`
|
||||
`${logId}: Attempted to start lobby while already waiting for this call!`
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (activeCallState) {
|
||||
if (activeCallState.state !== 'Active') {
|
||||
log.warn(
|
||||
`${logId}: Call wasn't active; still showing leave call modal`,
|
||||
activeCallState
|
||||
);
|
||||
}
|
||||
dispatch(
|
||||
toggleConfirmLeaveCallModal({
|
||||
type: 'conversation',
|
||||
@@ -2550,8 +2562,12 @@ function startCall(
|
||||
|
||||
log.info(`${logId}: starting, mode ${callMode}`);
|
||||
|
||||
if (activeCallState?.state === 'Waiting') {
|
||||
log.error(`${logId}: Call is not ready; `);
|
||||
if (
|
||||
!activeCallState ||
|
||||
activeCallState?.state === 'Waiting' ||
|
||||
activeCallState?.conversationId !== conversationId
|
||||
) {
|
||||
log.error(`${logId}: Call is not ready`, activeCallState);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2840,6 +2856,14 @@ export function reducer(
|
||||
if (action.type === WAITING_FOR_CALLING_LOBBY) {
|
||||
const { conversationId } = action.payload;
|
||||
|
||||
if (state.activeCallState) {
|
||||
log.warn(
|
||||
`${action.type}: Already have an active call!`,
|
||||
state.activeCallState
|
||||
);
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
activeCallState: {
|
||||
@@ -2848,9 +2872,18 @@ export function reducer(
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === WAITING_FOR_CALL_LINK_LOBBY) {
|
||||
const { roomId } = action.payload;
|
||||
|
||||
if (state.activeCallState) {
|
||||
log.warn(
|
||||
`${action.type}: Already have an active call!`,
|
||||
state.activeCallState
|
||||
);
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
activeCallState: {
|
||||
@@ -2859,24 +2892,45 @@ export function reducer(
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === CALL_LOBBY_FAILED) {
|
||||
const { conversationId } = action.payload;
|
||||
|
||||
const { activeCallState } = state;
|
||||
if (!activeCallState || activeCallState.conversationId !== conversationId) {
|
||||
if (
|
||||
!activeCallState ||
|
||||
activeCallState.conversationId !== conversationId ||
|
||||
activeCallState.state !== 'Waiting'
|
||||
) {
|
||||
log.warn(
|
||||
`${action.type}: Active call does not match target conversation`
|
||||
`${action.type}: Active call does not match target conversation`,
|
||||
activeCallState
|
||||
);
|
||||
return state;
|
||||
}
|
||||
|
||||
return removeConversationFromState(state, conversationId);
|
||||
}
|
||||
|
||||
if (
|
||||
action.type === START_CALLING_LOBBY ||
|
||||
action.type === START_CALL_LINK_LOBBY
|
||||
) {
|
||||
const { callMode, conversationId } = action.payload;
|
||||
|
||||
const { activeCallState } = state;
|
||||
if (
|
||||
!activeCallState ||
|
||||
activeCallState.conversationId !== conversationId ||
|
||||
activeCallState.state !== 'Waiting'
|
||||
) {
|
||||
log.warn(
|
||||
`${action.type}: Active call does not match target conversation`,
|
||||
activeCallState
|
||||
);
|
||||
return state;
|
||||
}
|
||||
|
||||
let call: DirectCallStateType | GroupCallStateType;
|
||||
let newAdhocCalls: AdhocCallsType;
|
||||
let outgoingRing: boolean;
|
||||
@@ -2987,13 +3041,28 @@ export function reducer(
|
||||
}
|
||||
|
||||
if (action.type === START_DIRECT_CALL) {
|
||||
const { conversationId } = action.payload;
|
||||
|
||||
const { activeCallState } = state;
|
||||
if (
|
||||
activeCallState &&
|
||||
(activeCallState.state === 'Waiting' ||
|
||||
activeCallState.conversationId !== conversationId)
|
||||
) {
|
||||
log.warn(
|
||||
`${action.type}: Cannot start call; activeCall doesn't match conversation`,
|
||||
activeCallState
|
||||
);
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
callsByConversation: {
|
||||
...callsByConversation,
|
||||
[action.payload.conversationId]: {
|
||||
[conversationId]: {
|
||||
callMode: CallMode.Direct,
|
||||
conversationId: action.payload.conversationId,
|
||||
conversationId,
|
||||
callState: CallState.Prering,
|
||||
isIncoming: false,
|
||||
isVideoCall: action.payload.hasLocalVideo,
|
||||
@@ -3002,7 +3071,7 @@ export function reducer(
|
||||
activeCallState: {
|
||||
state: 'Active',
|
||||
callMode: CallMode.Direct,
|
||||
conversationId: action.payload.conversationId,
|
||||
conversationId,
|
||||
hasLocalAudio: action.payload.hasLocalAudio,
|
||||
hasLocalVideo: action.payload.hasLocalVideo,
|
||||
localAudioLevel: 0,
|
||||
@@ -3017,21 +3086,24 @@ export function reducer(
|
||||
}
|
||||
|
||||
if (action.type === ACCEPT_CALL_PENDING) {
|
||||
const call = getOwn(
|
||||
state.callsByConversation,
|
||||
action.payload.conversationId
|
||||
);
|
||||
const { conversationId } = action.payload;
|
||||
const call = getOwn(state.callsByConversation, conversationId);
|
||||
if (!call) {
|
||||
log.warn('Unable to accept a non-existent call');
|
||||
return state;
|
||||
}
|
||||
|
||||
const { activeCallState } = state;
|
||||
if (!activeCallState || activeCallState.conversationId !== conversationId) {
|
||||
log.warn(`${action.type}: Active call didn't match:`, activeCallState);
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
activeCallState: {
|
||||
state: 'Active',
|
||||
callMode: call.callMode,
|
||||
conversationId: action.payload.conversationId,
|
||||
conversationId,
|
||||
hasLocalAudio: true,
|
||||
hasLocalVideo: action.payload.asVideoCall,
|
||||
localAudioLevel: 0,
|
||||
@@ -3129,13 +3201,27 @@ export function reducer(
|
||||
}
|
||||
|
||||
if (action.type === INCOMING_DIRECT_CALL) {
|
||||
const { conversationId } = action.payload;
|
||||
|
||||
const { activeCallState } = state;
|
||||
if (activeCallState && activeCallState.conversationId !== conversationId) {
|
||||
log.warn(
|
||||
`${action.type}: activeCallState didn't match conversation; overriding.`,
|
||||
activeCallState
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
activeCallState: {
|
||||
state: 'Waiting',
|
||||
conversationId,
|
||||
},
|
||||
callsByConversation: {
|
||||
...callsByConversation,
|
||||
[action.payload.conversationId]: {
|
||||
[conversationId]: {
|
||||
callMode: CallMode.Direct,
|
||||
conversationId: action.payload.conversationId,
|
||||
conversationId,
|
||||
callState: CallState.Prering,
|
||||
isIncoming: true,
|
||||
isVideoCall: action.payload.isVideoCall,
|
||||
@@ -3197,13 +3283,27 @@ export function reducer(
|
||||
}
|
||||
|
||||
if (action.type === OUTGOING_CALL) {
|
||||
const { conversationId } = action.payload;
|
||||
|
||||
const { activeCallState } = state;
|
||||
if (
|
||||
activeCallState &&
|
||||
(activeCallState.state === 'Waiting' ||
|
||||
activeCallState.conversationId !== conversationId)
|
||||
) {
|
||||
log.warn(
|
||||
`${action.type}: Cannot start call; activeCall doesn't match conversation`
|
||||
);
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
callsByConversation: {
|
||||
...callsByConversation,
|
||||
[action.payload.conversationId]: {
|
||||
[conversationId]: {
|
||||
callMode: CallMode.Direct,
|
||||
conversationId: action.payload.conversationId,
|
||||
conversationId,
|
||||
callState: CallState.Prering,
|
||||
isIncoming: false,
|
||||
isVideoCall: action.payload.hasLocalVideo,
|
||||
@@ -3212,7 +3312,7 @@ export function reducer(
|
||||
activeCallState: {
|
||||
state: 'Active',
|
||||
callMode: CallMode.Direct,
|
||||
conversationId: action.payload.conversationId,
|
||||
conversationId,
|
||||
hasLocalAudio: action.payload.hasLocalAudio,
|
||||
hasLocalVideo: action.payload.hasLocalVideo,
|
||||
localAudioLevel: 0,
|
||||
|
@@ -5,7 +5,10 @@ import { assert } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import { cloneDeep, noop } from 'lodash';
|
||||
import type { PeekInfo } from '@signalapp/ringrtc';
|
||||
import type { StateType as RootStateType } from '../../../state/reducer';
|
||||
import type {
|
||||
StateType as RootStateType,
|
||||
StateType,
|
||||
} from '../../../state/reducer';
|
||||
import { reducer as rootReducer } from '../../../state/reducer';
|
||||
import { noopAction } from '../../../state/ducks/noop';
|
||||
import type {
|
||||
@@ -199,7 +202,7 @@ describe('calling duck', () => {
|
||||
|
||||
const ourAci = generateAci();
|
||||
|
||||
const getEmptyRootState = () => {
|
||||
const getEmptyRootState = (): StateType => {
|
||||
const rootState = rootReducer(undefined, noopAction());
|
||||
return {
|
||||
...rootState,
|
||||
@@ -2274,10 +2277,11 @@ describe('calling duck', () => {
|
||||
|
||||
const waitingAction = dispatch.getCall(0).args[0];
|
||||
assert.equal(waitingAction.type, 'calling/WAITING_FOR_CALLING_LOBBY');
|
||||
const waitingState = reducer(callingState, waitingAction);
|
||||
|
||||
const action = dispatch.getCall(1).args[0];
|
||||
|
||||
return reducer(callingState, action);
|
||||
const startLobbyAction = dispatch.getCall(1).args[0];
|
||||
assert.equal(startLobbyAction.type, 'calling/START_CALLING_LOBBY');
|
||||
return reducer(waitingState, startLobbyAction);
|
||||
};
|
||||
|
||||
it('saves a direct call and makes it active', async () => {
|
||||
@@ -2563,17 +2567,40 @@ describe('calling duck', () => {
|
||||
|
||||
it('asks the calling service to start an outgoing direct call', async function (this: Mocha.Context) {
|
||||
const dispatch = sinon.spy();
|
||||
await startCall({
|
||||
const emptyState = getEmptyRootState();
|
||||
const conversationId = '123';
|
||||
|
||||
const startState: StateType = {
|
||||
...emptyState,
|
||||
calling: {
|
||||
...emptyState.calling,
|
||||
activeCallState: {
|
||||
state: 'Active',
|
||||
callMode: CallMode.Direct,
|
||||
conversationId: '123',
|
||||
conversationId,
|
||||
hasLocalAudio: true,
|
||||
hasLocalVideo: false,
|
||||
})(dispatch, getEmptyRootState, null);
|
||||
localAudioLevel: 0,
|
||||
viewMode: CallViewMode.Sidebar,
|
||||
joinedAt: null,
|
||||
outgoingRing: true,
|
||||
pip: false,
|
||||
settingsDialogOpen: false,
|
||||
showParticipantsList: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
await startCall({
|
||||
callMode: CallMode.Direct,
|
||||
conversationId,
|
||||
hasLocalAudio: true,
|
||||
hasLocalVideo: false,
|
||||
})(dispatch, () => startState, null);
|
||||
|
||||
sinon.assert.calledOnce(this.callingStartOutgoingDirectCall);
|
||||
sinon.assert.calledWith(
|
||||
this.callingStartOutgoingDirectCall,
|
||||
'123',
|
||||
conversationId,
|
||||
true,
|
||||
false
|
||||
);
|
||||
@@ -2583,34 +2610,87 @@ describe('calling duck', () => {
|
||||
|
||||
it('asks the calling service to join a group call', async function (this: Mocha.Context) {
|
||||
const dispatch = sinon.spy();
|
||||
await startCall({
|
||||
const emptyState = getEmptyRootState();
|
||||
const conversationId = '123';
|
||||
|
||||
const startState: StateType = {
|
||||
...emptyState,
|
||||
calling: {
|
||||
...emptyState.calling,
|
||||
activeCallState: {
|
||||
state: 'Active',
|
||||
callMode: CallMode.Group,
|
||||
conversationId: '123',
|
||||
conversationId,
|
||||
hasLocalAudio: true,
|
||||
hasLocalVideo: false,
|
||||
})(dispatch, getEmptyRootState, null);
|
||||
localAudioLevel: 0,
|
||||
viewMode: CallViewMode.Sidebar,
|
||||
joinedAt: null,
|
||||
outgoingRing: true,
|
||||
pip: false,
|
||||
settingsDialogOpen: false,
|
||||
showParticipantsList: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
await startCall({
|
||||
callMode: CallMode.Group,
|
||||
conversationId,
|
||||
hasLocalAudio: true,
|
||||
hasLocalVideo: false,
|
||||
})(dispatch, () => startState, null);
|
||||
|
||||
sinon.assert.calledOnce(this.callingJoinGroupCall);
|
||||
sinon.assert.calledWith(this.callingJoinGroupCall, '123', true, false);
|
||||
sinon.assert.calledWith(
|
||||
this.callingJoinGroupCall,
|
||||
conversationId,
|
||||
true,
|
||||
false
|
||||
);
|
||||
|
||||
sinon.assert.notCalled(this.callingStartOutgoingDirectCall);
|
||||
});
|
||||
|
||||
it('saves direct calls and makes them active', async () => {
|
||||
const dispatch = sinon.spy();
|
||||
await startCall({
|
||||
const emptyState = getEmptyRootState();
|
||||
const conversationId = '123';
|
||||
|
||||
const startState: StateType = {
|
||||
...emptyState,
|
||||
calling: {
|
||||
...emptyState.calling,
|
||||
activeCallState: {
|
||||
state: 'Active',
|
||||
callMode: CallMode.Direct,
|
||||
conversationId: 'fake-conversation-id',
|
||||
conversationId,
|
||||
hasLocalAudio: true,
|
||||
hasLocalVideo: false,
|
||||
})(dispatch, getEmptyRootState, null);
|
||||
localAudioLevel: 0,
|
||||
viewMode: CallViewMode.Sidebar,
|
||||
joinedAt: null,
|
||||
outgoingRing: true,
|
||||
pip: false,
|
||||
settingsDialogOpen: false,
|
||||
showParticipantsList: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
await startCall({
|
||||
callMode: CallMode.Direct,
|
||||
conversationId,
|
||||
hasLocalAudio: true,
|
||||
hasLocalVideo: false,
|
||||
})(dispatch, () => startState, null);
|
||||
const action = dispatch.getCall(0).args[0];
|
||||
|
||||
const result = reducer(getEmptyState(), action);
|
||||
const result = reducer(startState.calling, action);
|
||||
|
||||
assert.deepEqual(result.callsByConversation['fake-conversation-id'], {
|
||||
assert.deepEqual(result.callsByConversation[conversationId], {
|
||||
callMode: CallMode.Direct,
|
||||
conversationId: 'fake-conversation-id',
|
||||
conversationId,
|
||||
callState: CallState.Prering,
|
||||
isIncoming: false,
|
||||
isVideoCall: false,
|
||||
@@ -2618,7 +2698,7 @@ describe('calling duck', () => {
|
||||
assert.deepEqual(result.activeCallState, {
|
||||
state: 'Active',
|
||||
callMode: CallMode.Direct,
|
||||
conversationId: 'fake-conversation-id',
|
||||
conversationId,
|
||||
hasLocalAudio: true,
|
||||
hasLocalVideo: false,
|
||||
localAudioLevel: 0,
|
||||
@@ -2633,12 +2713,36 @@ describe('calling duck', () => {
|
||||
|
||||
it("doesn't dispatch any actions for group calls", async () => {
|
||||
const dispatch = sinon.spy();
|
||||
await startCall({
|
||||
const emptyState = getEmptyRootState();
|
||||
const conversationId = '123';
|
||||
|
||||
const startState: StateType = {
|
||||
...emptyState,
|
||||
calling: {
|
||||
...emptyState.calling,
|
||||
activeCallState: {
|
||||
state: 'Active',
|
||||
callMode: CallMode.Group,
|
||||
conversationId: '123',
|
||||
conversationId,
|
||||
hasLocalAudio: true,
|
||||
hasLocalVideo: false,
|
||||
})(dispatch, getEmptyRootState, null);
|
||||
localAudioLevel: 0,
|
||||
viewMode: CallViewMode.Sidebar,
|
||||
joinedAt: null,
|
||||
outgoingRing: true,
|
||||
pip: false,
|
||||
settingsDialogOpen: false,
|
||||
showParticipantsList: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
await startCall({
|
||||
callMode: CallMode.Group,
|
||||
conversationId,
|
||||
hasLocalAudio: true,
|
||||
hasLocalVideo: false,
|
||||
})(dispatch, () => startState, null);
|
||||
|
||||
sinon.assert.notCalled(dispatch);
|
||||
});
|
||||
|
Reference in New Issue
Block a user