2023-07-18 23:57:38 +00:00
|
|
|
// Copyright 2023 Signal Messenger, LLC
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
|
|
|
// Note that this file should not important any binary addons or Node.js modules
|
|
|
|
// because it can be imported by storybook
|
2024-10-25 00:46:54 +00:00
|
|
|
import {
|
|
|
|
CallState,
|
|
|
|
GroupCallConnectionState,
|
|
|
|
GroupCallJoinState,
|
|
|
|
} from '../../types/Calling';
|
2024-08-06 19:29:13 +00:00
|
|
|
import { CallMode } from '../../types/CallDisposition';
|
2024-10-25 00:46:54 +00:00
|
|
|
|
|
|
|
import type { CallingConversationType } from '../../types/Calling';
|
2023-08-10 16:43:33 +00:00
|
|
|
import type { AciString } from '../../types/ServiceId';
|
2023-07-18 23:57:38 +00:00
|
|
|
import type {
|
|
|
|
DirectCallStateType,
|
|
|
|
CallsByConversationType,
|
|
|
|
GroupCallPeekInfoType,
|
|
|
|
GroupCallStateType,
|
2024-10-25 00:46:54 +00:00
|
|
|
GroupCallParticipantInfoType,
|
|
|
|
ActiveCallStateType,
|
2023-07-18 23:57:38 +00:00
|
|
|
} from './calling';
|
|
|
|
|
2024-09-10 21:26:34 +00:00
|
|
|
export const MAX_CALL_PARTICIPANTS_FOR_DEFAULT_MUTE = 8;
|
|
|
|
|
2023-07-18 23:57:38 +00:00
|
|
|
// In theory, there could be multiple incoming calls, or an incoming call while there's
|
|
|
|
// an active call. In practice, the UI is not ready for this, and RingRTC doesn't
|
|
|
|
// support it for direct calls.
|
2024-10-25 00:46:54 +00:00
|
|
|
// Adhoc calls can not be incoming, so we don't look for them here.
|
|
|
|
export const getRingingCall = (
|
2023-07-18 23:57:38 +00:00
|
|
|
callsByConversation: Readonly<CallsByConversationType>,
|
2024-10-25 00:46:54 +00:00
|
|
|
activeCallState: ActiveCallStateType | undefined,
|
2023-08-10 16:43:33 +00:00
|
|
|
ourAci: AciString
|
2024-10-25 00:46:54 +00:00
|
|
|
): DirectCallStateType | GroupCallStateType | undefined => {
|
|
|
|
const callList = Object.values(callsByConversation);
|
|
|
|
const ringingDirect = callList.find(call => {
|
|
|
|
if (call.callMode !== CallMode.Direct) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-11-04 18:05:53 +00:00
|
|
|
if (
|
|
|
|
activeCallState?.state === 'Active' &&
|
|
|
|
activeCallState.conversationId !== call.conversationId
|
|
|
|
) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-10-25 00:46:54 +00:00
|
|
|
return isRinging(call.callState) && call.callEndedReason == null;
|
|
|
|
});
|
|
|
|
|
|
|
|
if (ringingDirect) {
|
|
|
|
return ringingDirect;
|
|
|
|
}
|
|
|
|
|
|
|
|
return callList.find(call => {
|
|
|
|
if (call.callMode !== CallMode.Group) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-11-04 18:05:53 +00:00
|
|
|
if (
|
|
|
|
activeCallState?.state === 'Active' &&
|
|
|
|
activeCallState.conversationId !== call.conversationId
|
|
|
|
) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-10-25 00:46:54 +00:00
|
|
|
// Outgoing - ringerAci is not set for outgoing group calls
|
|
|
|
if (
|
|
|
|
activeCallState?.state === 'Active' &&
|
|
|
|
activeCallState.conversationId === call.conversationId &&
|
2024-11-04 18:05:53 +00:00
|
|
|
activeCallState.outgoingRing &&
|
2024-10-25 00:46:54 +00:00
|
|
|
isConnected(call.connectionState) &&
|
|
|
|
isJoined(call.joinState) &&
|
|
|
|
!hasRemoteParticipants(call.remoteParticipants)
|
|
|
|
) {
|
|
|
|
return true;
|
2023-07-18 23:57:38 +00:00
|
|
|
}
|
2024-10-25 00:46:54 +00:00
|
|
|
|
|
|
|
// Incoming
|
|
|
|
return (
|
|
|
|
call.ringerAci &&
|
|
|
|
call.ringerAci !== ourAci &&
|
|
|
|
!isConnected(call.connectionState) &&
|
|
|
|
!isJoined(call.joinState) &&
|
|
|
|
isAnybodyElseInGroupCall(call.peekInfo, ourAci)
|
|
|
|
);
|
2023-07-18 23:57:38 +00:00
|
|
|
});
|
2024-10-25 00:46:54 +00:00
|
|
|
};
|
2023-07-18 23:57:38 +00:00
|
|
|
|
|
|
|
export const isAnybodyElseInGroupCall = (
|
2023-08-10 16:43:33 +00:00
|
|
|
peekInfo: undefined | Readonly<Pick<GroupCallPeekInfoType, 'acis'>>,
|
|
|
|
ourAci: AciString
|
|
|
|
): boolean => Boolean(peekInfo?.acis.some(id => id !== ourAci));
|
2024-05-17 22:02:07 +00:00
|
|
|
|
|
|
|
export const isAnybodyInGroupCall = (
|
|
|
|
peekInfo: undefined | Readonly<Pick<GroupCallPeekInfoType, 'acis'>>
|
|
|
|
): boolean => {
|
|
|
|
if (!peekInfo?.acis) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return peekInfo.acis.length > 0;
|
|
|
|
};
|
2024-10-18 20:19:45 +00:00
|
|
|
|
|
|
|
export const isGroupCallActiveOnServer = (
|
|
|
|
peekInfo: undefined | Readonly<Pick<GroupCallPeekInfoType, 'eraId'>>
|
|
|
|
): boolean => {
|
|
|
|
return Boolean(peekInfo?.eraId);
|
|
|
|
};
|
2024-10-25 00:46:54 +00:00
|
|
|
|
|
|
|
export function isLonelyGroup(conversation: CallingConversationType): boolean {
|
|
|
|
return (conversation.sortedGroupMembers?.length ?? 0) < 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isRinging(callState: CallState | undefined): boolean {
|
|
|
|
return callState === CallState.Prering || callState === CallState.Ringing;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isConnected(connectionState: GroupCallConnectionState): boolean {
|
|
|
|
return (
|
|
|
|
connectionState === GroupCallConnectionState.Connecting ||
|
|
|
|
connectionState === GroupCallConnectionState.Connected
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function isJoined(joinState: GroupCallJoinState): boolean {
|
|
|
|
return joinState !== GroupCallJoinState.NotJoined;
|
|
|
|
}
|
|
|
|
|
|
|
|
function hasRemoteParticipants(
|
|
|
|
remoteParticipants: Array<GroupCallParticipantInfoType>
|
|
|
|
): boolean {
|
|
|
|
return remoteParticipants.length > 0;
|
|
|
|
}
|