Add additional checks/logs to ringtones

This commit is contained in:
Jamie Kyle 2024-01-04 14:16:33 -08:00 committed by GitHub
parent 93e61a1a3a
commit 422ebf1bc8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 102 additions and 32 deletions

View file

@ -73,6 +73,7 @@ const createProps = (storyProps: Partial<PropsType> = {}): PropsType => ({
getPresentingSources: action('get-presenting-sources'), getPresentingSources: action('get-presenting-sources'),
hangUpActiveCall: action('hang-up-active-call'), hangUpActiveCall: action('hang-up-active-call'),
i18n, i18n,
incomingCall: null,
isGroupCallRaiseHandEnabled: true, isGroupCallRaiseHandEnabled: true,
isGroupCallReactionsEnabled: true, isGroupCallReactionsEnabled: true,
keyChangeOk: action('key-change-ok'), keyChangeOk: action('key-change-ok'),
@ -189,6 +190,8 @@ export function RingingGroupCall(): JSX.Element {
{...createProps({ {...createProps({
incomingCall: { incomingCall: {
callMode: CallMode.Group as const, callMode: CallMode.Group as const,
connectionState: GroupCallConnectionState.NotConnected,
joinState: GroupCallJoinState.NotJoined,
conversation: { conversation: {
...getConversation(), ...getConversation(),
type: 'group', type: 'group',
@ -201,6 +204,7 @@ export function RingingGroupCall(): JSX.Element {
{ firstName: 'Summer', title: 'Summer Smith' }, { firstName: 'Summer', title: 'Summer Smith' },
], ],
ringer: { firstName: 'Rick', title: 'Rick Sanchez' }, ringer: { firstName: 'Rick', title: 'Rick Sanchez' },
remoteParticipants: [],
}, },
})} })}
/> />

View file

@ -32,6 +32,7 @@ import type {
AcceptCallType, AcceptCallType,
CancelCallType, CancelCallType,
DeclineCallType, DeclineCallType,
GroupCallParticipantInfoType,
KeyChangeOkType, KeyChangeOkType,
SendGroupCallRaiseHandType, SendGroupCallRaiseHandType,
SendGroupCallReactionType, SendGroupCallReactionType,
@ -47,9 +48,28 @@ import { missingCaseError } from '../util/missingCaseError';
import { CallingToastProvider } from './CallingToast'; import { CallingToastProvider } from './CallingToast';
import type { SmartReactionPicker } from '../state/smart/ReactionPicker'; import type { SmartReactionPicker } from '../state/smart/ReactionPicker';
import type { Props as ReactionPickerProps } from './conversation/ReactionPicker'; import type { Props as ReactionPickerProps } from './conversation/ReactionPicker';
import * as log from '../logging/log';
const GROUP_CALL_RING_DURATION = 60 * 1000; const GROUP_CALL_RING_DURATION = 60 * 1000;
export type DirectIncomingCall = Readonly<{
callMode: CallMode.Direct;
callState?: CallState;
callEndedReason?: CallEndedReason;
conversation: ConversationType;
isVideoCall: boolean;
}>;
export type GroupIncomingCall = Readonly<{
callMode: CallMode.Group;
connectionState: GroupCallConnectionState;
joinState: GroupCallJoinState;
conversation: ConversationType;
otherMembersRung: Array<Pick<ConversationType, 'firstName' | 'title'>>;
ringer: Pick<ConversationType, 'firstName' | 'title'>;
remoteParticipants: Array<GroupCallParticipantInfoType>;
}>;
export type PropsType = { export type PropsType = {
activeCall?: ActiveCallType; activeCall?: ActiveCallType;
availableCameras: Array<MediaDeviceInfo>; availableCameras: Array<MediaDeviceInfo>;
@ -62,18 +82,7 @@ export type PropsType = {
) => VideoFrameSource; ) => VideoFrameSource;
getPreferredBadge: PreferredBadgeSelectorType; getPreferredBadge: PreferredBadgeSelectorType;
getPresentingSources: () => void; getPresentingSources: () => void;
incomingCall?: incomingCall: DirectIncomingCall | GroupIncomingCall | null;
| {
callMode: CallMode.Direct;
conversation: ConversationType;
isVideoCall: boolean;
}
| {
callMode: CallMode.Group;
conversation: ConversationType;
otherMembersRung: Array<Pick<ConversationType, 'firstName' | 'title'>>;
ringer: Pick<ConversationType, 'firstName' | 'title'>;
};
keyChangeOk: (_: KeyChangeOkType) => void; keyChangeOk: (_: KeyChangeOkType) => void;
renderDeviceSelection: () => JSX.Element; renderDeviceSelection: () => JSX.Element;
renderReactionPicker: ( renderReactionPicker: (
@ -431,8 +440,10 @@ export function CallManager(props: PropsType): JSX.Element | null {
const shouldRing = getShouldRing(props); const shouldRing = getShouldRing(props);
useEffect(() => { useEffect(() => {
if (shouldRing) { if (shouldRing) {
log.info('CallManager: Playing ringtone');
playRingtone(); playRingtone();
return () => { return () => {
log.info('CallManager: Stopping ringtone');
stopRingtone(); stopRingtone();
}; };
} }
@ -486,6 +497,31 @@ export function CallManager(props: PropsType): JSX.Element | null {
return null; return null;
} }
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;
}
function isLonelyGroup(conversation: ConversationType): boolean {
return (conversation.sortedGroupMembers?.length ?? 0) < 2;
}
function getShouldRing({ function getShouldRing({
activeCall, activeCall,
incomingCall, incomingCall,
@ -493,35 +529,54 @@ function getShouldRing({
}: Readonly< }: Readonly<
Pick<PropsType, 'activeCall' | 'incomingCall' | 'isConversationTooBigToRing'> Pick<PropsType, 'activeCall' | 'incomingCall' | 'isConversationTooBigToRing'>
>): boolean { >): boolean {
if (incomingCall) { if (incomingCall != null) {
// don't ring a large group // don't ring a large group
if (isConversationTooBigToRing) { if (isConversationTooBigToRing) {
return false; return false;
} }
return !activeCall; if (activeCall != null) {
return false;
}
if (incomingCall.callMode === CallMode.Direct) {
return (
isRinging(incomingCall.callState) &&
incomingCall.callEndedReason == null
);
}
if (incomingCall.callMode === CallMode.Group) {
return (
!isConnected(incomingCall.connectionState) &&
!isJoined(incomingCall.joinState) &&
!isLonelyGroup(incomingCall.conversation)
);
}
throw missingCaseError(incomingCall);
} }
if (!activeCall) { if (activeCall != null) {
return false; if (activeCall.callMode === CallMode.Direct) {
}
switch (activeCall.callMode) {
case CallMode.Direct:
return ( return (
activeCall.callState === CallState.Prering || activeCall.callState === CallState.Prering ||
activeCall.callState === CallState.Ringing activeCall.callState === CallState.Ringing
); );
case CallMode.Group: }
if (activeCall.callMode === CallMode.Group) {
return ( return (
activeCall.outgoingRing && activeCall.outgoingRing &&
(activeCall.connectionState === GroupCallConnectionState.Connecting || isConnected(activeCall.connectionState) &&
activeCall.connectionState === GroupCallConnectionState.Connected) && isJoined(activeCall.joinState) &&
activeCall.joinState !== GroupCallJoinState.NotJoined && !hasRemoteParticipants(activeCall.remoteParticipants) &&
!activeCall.remoteParticipants.length && !isLonelyGroup(activeCall.conversation)
(activeCall.conversation.sortedGroupMembers || []).length >= 2
); );
default: }
throw missingCaseError(activeCall);
throw missingCaseError(activeCall);
} }
return false;
} }

View file

@ -5,6 +5,10 @@ import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { memoize } from 'lodash'; import { memoize } from 'lodash';
import { mapDispatchToProps } from '../actions'; import { mapDispatchToProps } from '../actions';
import type {
DirectIncomingCall,
GroupIncomingCall,
} from '../../components/CallManager';
import { CallManager } from '../../components/CallManager'; import { CallManager } from '../../components/CallManager';
import { calling as callingService } from '../../services/calling'; import { calling as callingService } from '../../services/calling';
import { getIntl, getTheme } from '../selectors/user'; import { getIntl, getTheme } from '../selectors/user';
@ -320,29 +324,33 @@ const mapStateToActiveCallProp = (
} }
}; };
const mapStateToIncomingCallProp = (state: StateType) => { const mapStateToIncomingCallProp = (
state: StateType
): DirectIncomingCall | GroupIncomingCall | null => {
const call = getIncomingCall(state); const call = getIncomingCall(state);
if (!call) { if (!call) {
return undefined; return null;
} }
const conversation = getConversationSelector(state)(call.conversationId); const conversation = getConversationSelector(state)(call.conversationId);
if (!conversation) { if (!conversation) {
log.error('The incoming call has no corresponding conversation'); log.error('The incoming call has no corresponding conversation');
return undefined; return null;
} }
switch (call.callMode) { switch (call.callMode) {
case CallMode.Direct: case CallMode.Direct:
return { return {
callMode: CallMode.Direct as const, callMode: CallMode.Direct as const,
callState: call.callState,
callEndedReason: call.callEndedReason,
conversation, conversation,
isVideoCall: call.isVideoCall, isVideoCall: call.isVideoCall,
}; };
case CallMode.Group: { case CallMode.Group: {
if (!call.ringerAci) { if (!call.ringerAci) {
log.error('The incoming group call has no ring state'); log.error('The incoming group call has no ring state');
return undefined; return null;
} }
const conversationSelector = getConversationSelector(state); const conversationSelector = getConversationSelector(state);
@ -353,9 +361,12 @@ const mapStateToIncomingCallProp = (state: StateType) => {
return { return {
callMode: CallMode.Group as const, callMode: CallMode.Group as const,
connectionState: call.connectionState,
joinState: call.joinState,
conversation, conversation,
otherMembersRung, otherMembersRung,
ringer, ringer,
remoteParticipants: call.remoteParticipants,
}; };
} }
default: default: