Better handle group call ring race conditions
This commit is contained in:
parent
629b5c3f6a
commit
a88243f26d
9 changed files with 169 additions and 116 deletions
|
@ -38,7 +38,7 @@ import {
|
|||
import { uniqBy, noop } from 'lodash';
|
||||
|
||||
import type {
|
||||
ActionsType as UxActionsType,
|
||||
ActionsType as CallingReduxActionsType,
|
||||
GroupCallParticipantInfoType,
|
||||
GroupCallPeekInfoType,
|
||||
} from '../state/ducks/calling';
|
||||
|
@ -55,7 +55,6 @@ import {
|
|||
CallMode,
|
||||
GroupCallConnectionState,
|
||||
GroupCallJoinState,
|
||||
ProcessGroupCallRingRequestResult,
|
||||
} from '../types/Calling';
|
||||
import {
|
||||
AudioDeviceModule,
|
||||
|
@ -102,9 +101,9 @@ import * as log from '../logging/log';
|
|||
import { assertDev } from '../util/assert';
|
||||
|
||||
const {
|
||||
processGroupCallRingRequest,
|
||||
processGroupCallRingCancelation,
|
||||
cleanExpiredGroupCallRings,
|
||||
processGroupCallRingCancellation,
|
||||
cleanExpiredGroupCallRingCancellations,
|
||||
wasGroupCallRingPreviouslyCanceled,
|
||||
} = dataInterface;
|
||||
|
||||
const RINGRTC_HTTP_METHOD_TO_OUR_HTTP_METHOD: Map<
|
||||
|
@ -129,6 +128,24 @@ enum GroupCallUpdateMessageState {
|
|||
SentLeft,
|
||||
}
|
||||
|
||||
type CallingReduxInterface = Pick<
|
||||
CallingReduxActionsType,
|
||||
| 'callStateChange'
|
||||
| 'cancelIncomingGroupCallRing'
|
||||
| 'groupCallAudioLevelsChange'
|
||||
| 'groupCallStateChange'
|
||||
| 'outgoingCall'
|
||||
| 'receiveIncomingDirectCall'
|
||||
| 'receiveIncomingGroupCall'
|
||||
| 'refreshIODevices'
|
||||
| 'remoteSharingScreenChange'
|
||||
| 'remoteVideoChange'
|
||||
| 'setPresenting'
|
||||
| 'startCallingLobby'
|
||||
> & {
|
||||
areAnyCallsActiveOrRinging(): boolean;
|
||||
};
|
||||
|
||||
function isScreenSource(source: PresentedSource): boolean {
|
||||
return source.id.startsWith('screen');
|
||||
}
|
||||
|
@ -254,7 +271,7 @@ export class CallingClass {
|
|||
|
||||
readonly videoRenderer: CanvasVideoRenderer;
|
||||
|
||||
private uxActions?: UxActionsType;
|
||||
private reduxInterface?: CallingReduxInterface;
|
||||
|
||||
private sfuUrl?: string;
|
||||
|
||||
|
@ -281,9 +298,9 @@ export class CallingClass {
|
|||
this.callsByConversation = {};
|
||||
}
|
||||
|
||||
initialize(uxActions: UxActionsType, sfuUrl: string): void {
|
||||
this.uxActions = uxActions;
|
||||
if (!uxActions) {
|
||||
initialize(reduxInterface: CallingReduxInterface, sfuUrl: string): void {
|
||||
this.reduxInterface = reduxInterface;
|
||||
if (!reduxInterface) {
|
||||
throw new Error('CallingClass.initialize: Invalid uxActions.');
|
||||
}
|
||||
|
||||
|
@ -321,7 +338,7 @@ export class CallingClass {
|
|||
});
|
||||
|
||||
ipcRenderer.on('stop-screen-share', () => {
|
||||
uxActions.setPresenting();
|
||||
reduxInterface.setPresenting();
|
||||
});
|
||||
|
||||
ipcRenderer.on('quit', () => {
|
||||
|
@ -390,7 +407,7 @@ export class CallingClass {
|
|||
throw missingCaseError(callMode);
|
||||
}
|
||||
|
||||
if (!this.uxActions) {
|
||||
if (!this.reduxInterface) {
|
||||
log.error('Missing uxActions, new call not allowed.');
|
||||
return;
|
||||
}
|
||||
|
@ -492,7 +509,7 @@ export class CallingClass {
|
|||
): Promise<void> {
|
||||
log.info('CallingClass.startOutgoingDirectCall()');
|
||||
|
||||
if (!this.uxActions) {
|
||||
if (!this.reduxInterface) {
|
||||
throw new Error('Redux actions not available');
|
||||
}
|
||||
|
||||
|
@ -544,7 +561,7 @@ export class CallingClass {
|
|||
RingRTC.setVideoRenderer(call.callId, this.videoRenderer);
|
||||
this.attachToCall(conversation, call);
|
||||
|
||||
this.uxActions.outgoingCall({
|
||||
this.reduxInterface.outgoingCall({
|
||||
conversationId: conversation.id,
|
||||
hasLocalAudio,
|
||||
hasLocalVideo,
|
||||
|
@ -715,7 +732,7 @@ export class CallingClass {
|
|||
}
|
||||
const localAudioLevel = groupCall.getLocalDeviceState().audioLevel;
|
||||
|
||||
this.uxActions?.groupCallAudioLevelsChange({
|
||||
this.reduxInterface?.groupCallAudioLevelsChange({
|
||||
conversationId,
|
||||
localAudioLevel,
|
||||
remoteDeviceStates,
|
||||
|
@ -985,7 +1002,7 @@ export class CallingClass {
|
|||
conversationId: string,
|
||||
groupCall: GroupCall
|
||||
): void {
|
||||
this.uxActions?.groupCallStateChange({
|
||||
this.reduxInterface?.groupCallStateChange({
|
||||
conversationId,
|
||||
...this.formatGroupCallForRedux(groupCall),
|
||||
});
|
||||
|
@ -1247,8 +1264,8 @@ export class CallingClass {
|
|||
icon: 'images/icons/v2/video-solid-24.svg',
|
||||
message: window.i18n('calling__presenting--notification-body'),
|
||||
onNotificationClick: () => {
|
||||
if (this.uxActions) {
|
||||
this.uxActions.setPresenting();
|
||||
if (this.reduxInterface) {
|
||||
this.reduxInterface.setPresenting();
|
||||
}
|
||||
},
|
||||
silent: true,
|
||||
|
@ -1365,7 +1382,7 @@ export class CallingClass {
|
|||
|
||||
await this.selectPreferredDevices(newSettings);
|
||||
this.lastMediaDeviceSettings = newSettings;
|
||||
this.uxActions?.refreshIODevices(newSettings);
|
||||
this.reduxInterface?.refreshIODevices(newSettings);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1703,34 +1720,31 @@ export class CallingClass {
|
|||
let shouldRing = false;
|
||||
|
||||
if (update === RingUpdate.Requested) {
|
||||
const processResult = await processGroupCallRingRequest(ringId);
|
||||
switch (processResult) {
|
||||
case ProcessGroupCallRingRequestResult.ShouldRing:
|
||||
shouldRing = true;
|
||||
break;
|
||||
case ProcessGroupCallRingRequestResult.RingWasPreviouslyCanceled:
|
||||
RingRTC.cancelGroupRing(groupIdBytes, ringId, null);
|
||||
break;
|
||||
case ProcessGroupCallRingRequestResult.ThereIsAnotherActiveRing:
|
||||
RingRTC.cancelGroupRing(groupIdBytes, ringId, RingCancelReason.Busy);
|
||||
break;
|
||||
default:
|
||||
throw missingCaseError(processResult);
|
||||
if (await wasGroupCallRingPreviouslyCanceled(ringId)) {
|
||||
RingRTC.cancelGroupRing(groupIdBytes, ringId, null);
|
||||
} else if (this.areAnyCallsActiveOrRinging()) {
|
||||
RingRTC.cancelGroupRing(groupIdBytes, ringId, RingCancelReason.Busy);
|
||||
} else if (window.Events.getIncomingCallNotification()) {
|
||||
shouldRing = true;
|
||||
} else {
|
||||
log.info(
|
||||
'Incoming calls are disabled. Ignoring group call ring request'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
await processGroupCallRingCancelation(ringId);
|
||||
await processGroupCallRingCancellation(ringId);
|
||||
}
|
||||
|
||||
if (shouldRing) {
|
||||
log.info('handleGroupCallRingUpdate: ringing');
|
||||
this.uxActions?.receiveIncomingGroupCall({
|
||||
this.reduxInterface?.receiveIncomingGroupCall({
|
||||
conversationId,
|
||||
ringId,
|
||||
ringerUuid,
|
||||
});
|
||||
} else {
|
||||
log.info('handleGroupCallRingUpdate: canceling any existing ring');
|
||||
this.uxActions?.cancelIncomingGroupCallRing({
|
||||
this.reduxInterface?.cancelIncomingGroupCallRing({
|
||||
conversationId,
|
||||
ringId,
|
||||
});
|
||||
|
@ -1782,7 +1796,7 @@ export class CallingClass {
|
|||
private async handleIncomingCall(call: Call): Promise<CallSettings | null> {
|
||||
log.info('CallingClass.handleIncomingCall()');
|
||||
|
||||
if (!this.uxActions || !this.localDeviceId) {
|
||||
if (!this.reduxInterface || !this.localDeviceId) {
|
||||
log.error('Missing required objects, ignoring incoming call.');
|
||||
return null;
|
||||
}
|
||||
|
@ -1815,7 +1829,7 @@ export class CallingClass {
|
|||
|
||||
this.attachToCall(conversation, call);
|
||||
|
||||
this.uxActions.receiveIncomingDirectCall({
|
||||
this.reduxInterface.receiveIncomingDirectCall({
|
||||
conversationId: conversation.id,
|
||||
isVideoCall: call.isVideoCall,
|
||||
});
|
||||
|
@ -1866,8 +1880,8 @@ export class CallingClass {
|
|||
private attachToCall(conversation: ConversationModel, call: Call): void {
|
||||
this.callsByConversation[conversation.id] = call;
|
||||
|
||||
const { uxActions } = this;
|
||||
if (!uxActions) {
|
||||
const { reduxInterface } = this;
|
||||
if (!reduxInterface) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1883,7 +1897,7 @@ export class CallingClass {
|
|||
this.lastMediaDeviceSettings = undefined;
|
||||
delete this.callsByConversation[conversation.id];
|
||||
}
|
||||
uxActions.callStateChange({
|
||||
reduxInterface.callStateChange({
|
||||
conversationId: conversation.id,
|
||||
acceptedTime,
|
||||
callState: call.state,
|
||||
|
@ -1896,7 +1910,7 @@ export class CallingClass {
|
|||
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
call.handleRemoteVideoEnabled = () => {
|
||||
uxActions.remoteVideoChange({
|
||||
reduxInterface.remoteVideoChange({
|
||||
conversationId: conversation.id,
|
||||
hasVideo: call.remoteVideoEnabled,
|
||||
});
|
||||
|
@ -1904,7 +1918,7 @@ export class CallingClass {
|
|||
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
call.handleRemoteSharingScreen = () => {
|
||||
uxActions.remoteSharingScreenChange({
|
||||
reduxInterface.remoteSharingScreenChange({
|
||||
conversationId: conversation.id,
|
||||
isSharingScreen: Boolean(call.remoteSharingScreen),
|
||||
});
|
||||
|
@ -2189,7 +2203,7 @@ export class CallingClass {
|
|||
icon: 'images/icons/v2/video-solid-24.svg',
|
||||
message: notificationMessage,
|
||||
onNotificationClick: () => {
|
||||
this.uxActions?.startCallingLobby({
|
||||
this.reduxInterface?.startCallingLobby({
|
||||
conversationId: conversation.id,
|
||||
isVideoCall: true,
|
||||
});
|
||||
|
@ -2199,9 +2213,13 @@ export class CallingClass {
|
|||
});
|
||||
}
|
||||
|
||||
private areAnyCallsActiveOrRinging(): boolean {
|
||||
return this.reduxInterface?.areAnyCallsActiveOrRinging() ?? false;
|
||||
}
|
||||
|
||||
private async cleanExpiredGroupCallRingsAndLoop(): Promise<void> {
|
||||
try {
|
||||
await cleanExpiredGroupCallRings();
|
||||
await cleanExpiredGroupCallRingCancellations();
|
||||
} catch (err: unknown) {
|
||||
// These errors are ignored here. They should be logged elsewhere and it's okay if
|
||||
// we don't do a cleanup this time.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue