Raise Hand in Group Calls

This commit is contained in:
ayumi-signal 2023-12-06 13:52:29 -08:00 committed by GitHub
parent 45aeaeefd4
commit d6db3f7943
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 1050 additions and 51 deletions

View file

@ -116,6 +116,7 @@ export type GroupCallStateType = {
localDemuxId: number | undefined;
joinState: GroupCallJoinState;
peekInfo?: GroupCallPeekInfoType;
raisedHands?: Array<number>;
remoteParticipants: Array<GroupCallParticipantInfoType>;
remoteAudioLevels?: Map<number, number>;
} & GroupCallRingStateType;
@ -222,11 +223,15 @@ type IncomingGroupCallType = ReadonlyDeep<{
ringerAci: AciString;
}>;
export type SendGroupCallRaiseHandType = ReadonlyDeep<{
conversationId: string;
raise: boolean;
}>;
export type SendGroupCallReactionType = ReadonlyDeep<{
conversationId: string;
value: string;
}>;
type SendGroupCallReactionLocalCopyType = ReadonlyDeep<{
conversationId: string;
value: string;
@ -445,6 +450,7 @@ 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_RAISED_HANDS_CHANGE = 'calling/GROUP_CALL_RAISED_HANDS_CHANGE';
const GROUP_CALL_STATE_CHANGE = 'calling/GROUP_CALL_STATE_CHANGE';
const GROUP_CALL_REACTIONS_RECEIVED = 'calling/GROUP_CALL_REACTIONS_RECEIVED';
const GROUP_CALL_REACTIONS_EXPIRED = 'calling/GROUP_CALL_REACTIONS_EXPIRED';
@ -455,6 +461,7 @@ const MARK_CALL_TRUSTED = 'calling/MARK_CALL_TRUSTED';
const MARK_CALL_UNTRUSTED = 'calling/MARK_CALL_UNTRUSTED';
const OUTGOING_CALL = 'calling/OUTGOING_CALL';
const PEEK_GROUP_CALL_FULFILLED = 'calling/PEEK_GROUP_CALL_FULFILLED';
const RAISE_HAND_GROUP_CALL = 'calling/RAISE_HAND_GROUP_CALL';
const REFRESH_IO_DEVICES = 'calling/REFRESH_IO_DEVICES';
const REMOTE_SHARING_SCREEN_CHANGE = 'calling/REMOTE_SHARING_SCREEN_CHANGE';
const REMOTE_VIDEO_CHANGE = 'calling/REMOTE_VIDEO_CHANGE';
@ -525,6 +532,16 @@ type GroupCallAudioLevelsChangeActionType = ReadonlyDeep<{
payload: GroupCallAudioLevelsChangeActionPayloadType;
}>;
type GroupCallRaisedHandsChangeActionPayloadType = ReadonlyDeep<{
conversationId: string;
raisedHands: ReadonlyArray<number>;
}>;
type GroupCallRaisedHandsChangeActionType = ReadonlyDeep<{
type: 'calling/GROUP_CALL_RAISED_HANDS_CHANGE';
payload: GroupCallRaisedHandsChangeActionPayloadType;
}>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type GroupCallStateChangeActionType = {
type: 'calling/GROUP_CALL_STATE_CHANGE';
@ -580,6 +597,11 @@ type KeyChangeOkActionType = ReadonlyDeep<{
payload: null;
}>;
type SendGroupCallRaiseHandActionType = ReadonlyDeep<{
type: 'calling/RAISE_HAND_GROUP_CALL';
payload: SendGroupCallRaiseHandType;
}>;
export type SendGroupCallReactionActionType = ReadonlyDeep<{
type: 'calling/SEND_GROUP_CALL_REACTION';
payload: SendGroupCallReactionLocalCopyType;
@ -692,6 +714,7 @@ export type CallingActionType =
| ConversationRemovedActionType
| DeclineCallActionType
| GroupCallAudioLevelsChangeActionType
| GroupCallRaisedHandsChangeActionType
| GroupCallStateChangeActionType
| GroupCallReactionsReceivedActionType
| GroupCallReactionsExpiredActionType
@ -950,6 +973,12 @@ function receiveGroupCallReactions(
};
}
function groupCallRaisedHandsChange(
payload: GroupCallRaisedHandsChangeActionPayloadType
): GroupCallRaisedHandsChangeActionType {
return { type: GROUP_CALL_RAISED_HANDS_CHANGE, payload };
}
function groupCallStateChange(
payload: GroupCallStateChangeArgumentType
): ThunkAction<void, RootStateType, unknown, GroupCallStateChangeActionType> {
@ -1075,6 +1104,19 @@ function keyChangeOk(
};
}
function sendGroupCallRaiseHand(
payload: SendGroupCallRaiseHandType
): ThunkAction<void, RootStateType, unknown, SendGroupCallRaiseHandActionType> {
return dispatch => {
calling.sendGroupCallRaiseHand(payload.conversationId, payload.raise);
dispatch({
type: RAISE_HAND_GROUP_CALL,
payload,
});
};
}
function sendGroupCallReaction(
payload: SendGroupCallReactionType
): ThunkAction<
@ -1612,6 +1654,7 @@ export const actions = {
declineCall,
getPresentingSources,
groupCallAudioLevelsChange,
groupCallRaisedHandsChange,
groupCallStateChange,
hangUpActiveCall,
keyChangeOk,
@ -1630,6 +1673,7 @@ export const actions = {
remoteSharingScreenChange,
remoteVideoChange,
returnToActiveCall,
sendGroupCallRaiseHand,
sendGroupCallReaction,
setGroupCallVideoRequest,
setIsCallActive,
@ -2137,6 +2181,7 @@ export function reducer(
localDemuxId,
peekInfo: newPeekInfo,
remoteParticipants,
raisedHands: existingCall?.raisedHands ?? [],
...newRingState,
},
},
@ -2267,6 +2312,29 @@ export function reducer(
};
}
if (action.type === GROUP_CALL_RAISED_HANDS_CHANGE) {
const { conversationId, raisedHands } = action.payload;
const { activeCallState } = state;
const existingCall = getGroupCall(conversationId, state);
if (
state.activeCallState?.conversationId !== conversationId ||
!activeCallState ||
!existingCall
) {
return state;
}
return {
...state,
callsByConversation: {
...callsByConversation,
[conversationId]: { ...existingCall, raisedHands: [...raisedHands] },
},
};
}
if (action.type === REMOTE_SHARING_SCREEN_CHANGE) {
const { conversationId, isSharingScreen } = action.payload;
const call = getOwn(state.callsByConversation, conversationId);

View file

@ -13,6 +13,7 @@ import { getActiveCall } from '../ducks/calling';
import type { ConversationType } from '../ducks/conversations';
import { getIncomingCall } from '../selectors/calling';
import { isGroupCallOutboundRingEnabled } from '../../util/isGroupCallOutboundRingEnabled';
import { isGroupCallRaiseHandEnabled } from '../../util/isGroupCallRaiseHandEnabled';
import { isGroupCallReactionsEnabled } from '../../util/isGroupCallReactionsEnabled';
import type {
ActiveCallBaseType,
@ -201,6 +202,8 @@ const mapStateToActiveCallProp = (
const remoteParticipants: Array<GroupCallRemoteParticipantType> = [];
const peekedParticipants: Array<ConversationType> = [];
const conversationsByDemuxId: ConversationsByDemuxIdType = new Map();
const { localDemuxId } = call;
const raisedHands: Set<number> = new Set(call.raisedHands ?? []);
const { memberships = [] } = conversation;
@ -243,6 +246,7 @@ const mapStateToActiveCallProp = (
demuxId: remoteParticipant.demuxId,
hasRemoteAudio: remoteParticipant.hasRemoteAudio,
hasRemoteVideo: remoteParticipant.hasRemoteVideo,
isHandRaised: raisedHands.has(remoteParticipant.demuxId),
presenting: remoteParticipant.presenting,
sharingScreen: remoteParticipant.sharingScreen,
speakerTime: remoteParticipant.speakerTime,
@ -254,6 +258,17 @@ const mapStateToActiveCallProp = (
);
}
if (localDemuxId !== undefined) {
conversationsByDemuxId.set(localDemuxId, getMe(state));
}
// Filter raisedHands to ensure valid demuxIds.
raisedHands.forEach(demuxId => {
if (!conversationsByDemuxId.has(demuxId)) {
raisedHands.delete(demuxId);
}
});
for (
let i = 0;
i < activeCallState.safetyNumberChangedAcis.length;
@ -293,9 +308,10 @@ const mapStateToActiveCallProp = (
groupMembers,
isConversationTooBigToRing: isConversationTooBigToRing(conversation),
joinState: call.joinState,
localDemuxId: call.localDemuxId,
localDemuxId,
maxDevices: peekInfo.maxDevices,
peekedParticipants,
raisedHands,
remoteParticipants,
remoteAudioLevels: call.remoteAudioLevels || new Map<number, number>(),
} satisfies ActiveGroupCallType;
@ -360,6 +376,7 @@ const mapStateToProps = (state: StateType) => {
getPreferredBadge: getPreferredBadgeSelector(state),
i18n: getIntl(state),
isGroupCallOutboundRingEnabled: isGroupCallOutboundRingEnabled(),
isGroupCallRaiseHandEnabled: isGroupCallRaiseHandEnabled(),
isGroupCallReactionsEnabled: isGroupCallReactionsEnabled(),
incomingCall,
me: getMe(state),