Speaking indicator for group calls
Co-authored-by: Peter Thatcher <peter@signal.org> Co-authored-by: Jim Gustafson <jim@signal.org> Co-authored-by: Josh Perez <60019601+josh-signal@users.noreply.github.com>
This commit is contained in:
parent
cb5131420f
commit
5ce26eb91a
35 changed files with 482 additions and 42 deletions
|
@ -39,6 +39,7 @@ import type { UUIDStringType } from '../../types/UUID';
|
|||
import type { ConversationChangedActionType } from './conversations';
|
||||
import * as log from '../../logging/log';
|
||||
import { strictAssert } from '../../util/assert';
|
||||
import * as setUtil from '../../util/setUtil';
|
||||
|
||||
// State
|
||||
|
||||
|
@ -89,6 +90,7 @@ export type GroupCallStateType = {
|
|||
joinState: GroupCallJoinState;
|
||||
peekInfo: GroupCallPeekInfoType;
|
||||
remoteParticipants: Array<GroupCallParticipantInfoType>;
|
||||
speakingDemuxIds?: Set<number>;
|
||||
} & GroupCallRingStateType;
|
||||
|
||||
export type ActiveCallStateType = {
|
||||
|
@ -305,6 +307,7 @@ const CALL_STATE_CHANGE_FULFILLED = 'calling/CALL_STATE_CHANGE_FULFILLED';
|
|||
const CHANGE_IO_DEVICE_FULFILLED = 'calling/CHANGE_IO_DEVICE_FULFILLED';
|
||||
const CLOSE_NEED_PERMISSION_SCREEN = 'calling/CLOSE_NEED_PERMISSION_SCREEN';
|
||||
const DECLINE_DIRECT_CALL = 'calling/DECLINE_DIRECT_CALL';
|
||||
const GROUP_CALL_AUDIO_LEVELS_CHANGE = 'calling/GROUP_CALL_AUDIO_LEVELS_CHANGE';
|
||||
const GROUP_CALL_STATE_CHANGE = 'calling/GROUP_CALL_STATE_CHANGE';
|
||||
const HANG_UP = 'calling/HANG_UP';
|
||||
const INCOMING_DIRECT_CALL = 'calling/INCOMING_DIRECT_CALL';
|
||||
|
@ -370,6 +373,16 @@ type DeclineCallActionType = {
|
|||
payload: DeclineCallType;
|
||||
};
|
||||
|
||||
type GroupCallAudioLevelsChangeActionPayloadType = Readonly<{
|
||||
conversationId: string;
|
||||
remoteDeviceStates: ReadonlyArray<{ audioLevel: number; demuxId: number }>;
|
||||
}>;
|
||||
|
||||
type GroupCallAudioLevelsChangeActionType = {
|
||||
type: 'calling/GROUP_CALL_AUDIO_LEVELS_CHANGE';
|
||||
payload: GroupCallAudioLevelsChangeActionPayloadType;
|
||||
};
|
||||
|
||||
export type GroupCallStateChangeActionType = {
|
||||
type: 'calling/GROUP_CALL_STATE_CHANGE';
|
||||
payload: GroupCallStateChangeActionPayloadType;
|
||||
|
@ -500,6 +513,7 @@ export type CallingActionType =
|
|||
| CloseNeedPermissionScreenActionType
|
||||
| ConversationChangedActionType
|
||||
| DeclineCallActionType
|
||||
| GroupCallAudioLevelsChangeActionType
|
||||
| GroupCallStateChangeActionType
|
||||
| HangUpActionType
|
||||
| IncomingDirectCallActionType
|
||||
|
@ -706,6 +720,12 @@ function getPresentingSources(): ThunkAction<
|
|||
};
|
||||
}
|
||||
|
||||
function groupCallAudioLevelsChange(
|
||||
payload: GroupCallAudioLevelsChangeActionPayloadType
|
||||
): GroupCallAudioLevelsChangeActionType {
|
||||
return { type: GROUP_CALL_AUDIO_LEVELS_CHANGE, payload };
|
||||
}
|
||||
|
||||
function groupCallStateChange(
|
||||
payload: GroupCallStateChangeArgumentType
|
||||
): ThunkAction<void, RootStateType, unknown, GroupCallStateChangeActionType> {
|
||||
|
@ -1242,6 +1262,7 @@ export const actions = {
|
|||
closeNeedPermissionScreen,
|
||||
declineCall,
|
||||
getPresentingSources,
|
||||
groupCallAudioLevelsChange,
|
||||
groupCallStateChange,
|
||||
hangUp,
|
||||
hangUpActiveCall,
|
||||
|
@ -1631,6 +1652,40 @@ export function reducer(
|
|||
};
|
||||
}
|
||||
|
||||
if (action.type === GROUP_CALL_AUDIO_LEVELS_CHANGE) {
|
||||
const { conversationId, remoteDeviceStates } = action.payload;
|
||||
|
||||
const existingCall = getGroupCall(conversationId, state);
|
||||
if (!existingCall) {
|
||||
return state;
|
||||
}
|
||||
|
||||
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) {
|
||||
speakingDemuxIds.add(demuxId);
|
||||
}
|
||||
});
|
||||
|
||||
// This action is dispatched frequently. This equality check helps avoid re-renders.
|
||||
const oldSpeakingDemuxIds = existingCall.speakingDemuxIds;
|
||||
if (
|
||||
oldSpeakingDemuxIds &&
|
||||
setUtil.isEqual(oldSpeakingDemuxIds, speakingDemuxIds)
|
||||
) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
callsByConversation: {
|
||||
...callsByConversation,
|
||||
[conversationId]: { ...existingCall, speakingDemuxIds },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === GROUP_CALL_STATE_CHANGE) {
|
||||
const {
|
||||
connectionState,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue