Show local speaking indicator for group calls

This commit is contained in:
Evan Hahn
2022-02-25 09:24:05 -06:00
committed by GitHub
parent dbb732e7cf
commit 41b4cce6ec
14 changed files with 87 additions and 32 deletions

View File

@@ -15,6 +15,7 @@ import { getPlatform } from '../selectors/user';
import { isConversationTooBigToRing } from '../../conversations/isConversationTooBigToRing';
import { missingCaseError } from '../../util/missingCaseError';
import { calling } from '../../services/calling';
import { AUDIO_LEVEL_FOR_SPEAKING } from '../../calling/constants';
import type { StateType as RootStateType } from '../reducer';
import type {
ChangeIODevicePayloadType,
@@ -102,6 +103,7 @@ export type ActiveCallStateType = {
conversationId: string;
hasLocalAudio: boolean;
hasLocalVideo: boolean;
amISpeaking: boolean;
isInSpeakerView: boolean;
joinedAt?: number;
outgoingRing: boolean;
@@ -463,6 +465,7 @@ type DeclineCallActionType = {
type GroupCallAudioLevelsChangeActionPayloadType = Readonly<{
conversationId: string;
localAudioLevel: number;
remoteDeviceStates: ReadonlyArray<{ audioLevel: number; demuxId: number }>;
}>;
@@ -1431,6 +1434,7 @@ export function reducer(
conversationId: action.payload.conversationId,
hasLocalAudio: action.payload.hasLocalAudio,
hasLocalVideo: action.payload.hasLocalVideo,
amISpeaking: false,
isInSpeakerView: false,
pip: false,
safetyNumberChangedUuids: [],
@@ -1458,6 +1462,7 @@ export function reducer(
conversationId: action.payload.conversationId,
hasLocalAudio: action.payload.hasLocalAudio,
hasLocalVideo: action.payload.hasLocalVideo,
amISpeaking: false,
isInSpeakerView: false,
pip: false,
safetyNumberChangedUuids: [],
@@ -1480,6 +1485,7 @@ export function reducer(
conversationId: action.payload.conversationId,
hasLocalAudio: true,
hasLocalVideo: action.payload.asVideoCall,
amISpeaking: false,
isInSpeakerView: false,
pip: false,
safetyNumberChangedUuids: [],
@@ -1633,6 +1639,7 @@ export function reducer(
conversationId: action.payload.conversationId,
hasLocalAudio: action.payload.hasLocalAudio,
hasLocalVideo: action.payload.hasLocalVideo,
amISpeaking: false,
isInSpeakerView: false,
pip: false,
safetyNumberChangedUuids: [],
@@ -1690,24 +1697,36 @@ export function reducer(
}
if (action.type === GROUP_CALL_AUDIO_LEVELS_CHANGE) {
const { conversationId, remoteDeviceStates } = action.payload;
const { conversationId, localAudioLevel, remoteDeviceStates } =
action.payload;
const { activeCallState } = state;
const existingCall = getGroupCall(conversationId, state);
if (!existingCall) {
// The PiP check is an optimization. We don't need to update audio levels if the user
// cannot see them.
if (!activeCallState || activeCallState.pip || !existingCall) {
return state;
}
const amISpeaking = localAudioLevel > AUDIO_LEVEL_FOR_SPEAKING;
const speakingDemuxIds = new Set<number>();
remoteDeviceStates.forEach(({ audioLevel, demuxId }) => {
// We expect `audioLevel` to be a number but have this check just in case.
if (typeof audioLevel === 'number' && audioLevel > 0.25) {
if (
typeof audioLevel === 'number' &&
audioLevel > AUDIO_LEVEL_FOR_SPEAKING
) {
speakingDemuxIds.add(demuxId);
}
});
// This action is dispatched frequently. This equality check helps avoid re-renders.
const oldAmISpeaking = activeCallState.amISpeaking;
const oldSpeakingDemuxIds = existingCall.speakingDemuxIds;
if (
oldAmISpeaking === amISpeaking &&
oldSpeakingDemuxIds &&
setUtil.isEqual(oldSpeakingDemuxIds, speakingDemuxIds)
) {
@@ -1716,6 +1735,7 @@ export function reducer(
return {
...state,
activeCallState: { ...activeCallState, amISpeaking },
callsByConversation: {
...callsByConversation,
[conversationId]: { ...existingCall, speakingDemuxIds },