Call link admin key fix and in-call approve, deny, remove

This commit is contained in:
ayumi-signal 2024-04-30 09:36:34 -07:00 committed by GitHub
parent 5df8924197
commit 8ec585d54c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 599 additions and 43 deletions

View file

@ -49,11 +49,12 @@ import { requestCameraPermissions } from '../../util/callingPermissions';
import {
CALL_LINK_DEFAULT_STATE,
getRoomIdFromRootKey,
toAdminKeyBytes,
} from '../../util/callLinks';
import { sendCallLinkUpdateSync } from '../../util/sendCallLinkUpdateSync';
import { sleep } from '../../util/sleep';
import { LatestQueue } from '../../util/LatestQueue';
import type { AciString } from '../../types/ServiceId';
import type { AciString, ServiceIdString } from '../../types/ServiceId';
import type {
ConversationChangedActionType,
ConversationRemovedActionType,
@ -81,11 +82,13 @@ import { SHOW_ERROR_MODAL } from './globalModals';
import { ButtonVariant } from '../../components/Button';
import { getConversationIdForLogging } from '../../util/idForLogging';
import dataInterface from '../../sql/Client';
import { isAciString } from '../../util/isAciString';
// State
export type GroupCallPeekInfoType = ReadonlyDeep<{
acis: Array<AciString>;
pendingAcis: Array<AciString>;
creatorAci?: AciString;
eraId?: string;
maxDevices: number;
@ -250,7 +253,7 @@ type HangUpActionPayloadType = ReadonlyDeep<{
conversationId: string;
}>;
type HandleCallLinkUpdateType = ReadonlyDeep<{
export type HandleCallLinkUpdateType = ReadonlyDeep<{
rootKey: string;
adminKey: string | null;
}>;
@ -309,6 +312,10 @@ type RemoteSharingScreenChangeType = ReadonlyDeep<{
isSharingScreen: boolean;
}>;
export type RemoveClientType = ReadonlyDeep<{
demuxId: number;
}>;
export type SetLocalAudioType = ReadonlyDeep<{
enabled: boolean;
}>;
@ -558,10 +565,12 @@ const doGroupCallPeek = ({
// Actions
const ACCEPT_CALL_PENDING = 'calling/ACCEPT_CALL_PENDING';
const APPROVE_USER = 'calling/APPROVE_USER';
const CANCEL_CALL = 'calling/CANCEL_CALL';
const CANCEL_INCOMING_GROUP_CALL_RING =
'calling/CANCEL_INCOMING_GROUP_CALL_RING';
const CHANGE_CALL_VIEW = 'calling/CHANGE_CALL_VIEW';
const DENY_USER = 'calling/DENY_USER';
const START_CALLING_LOBBY = 'calling/START_CALLING_LOBBY';
const START_CALL_LINK_LOBBY = 'calling/START_CALL_LINK_LOBBY';
const CALL_STATE_CHANGE_FULFILLED = 'calling/CALL_STATE_CHANGE_FULFILLED';
@ -584,6 +593,7 @@ 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';
const REMOVE_CLIENT = 'calling/REMOVE_CLIENT';
const RETURN_TO_ACTIVE_CALL = 'calling/RETURN_TO_ACTIVE_CALL';
const SEND_GROUP_CALL_REACTION = 'calling/SEND_GROUP_CALL_REACTION';
const SET_LOCAL_AUDIO_FULFILLED = 'calling/SET_LOCAL_AUDIO_FULFILLED';
@ -605,6 +615,10 @@ type AcceptCallPendingActionType = ReadonlyDeep<{
payload: AcceptCallType;
}>;
type ApproveUserActionType = ReadonlyDeep<{
type: 'calling/APPROVE_USER';
}>;
type CancelCallActionType = ReadonlyDeep<{
type: 'calling/CANCEL_CALL';
}>;
@ -614,6 +628,10 @@ type CancelIncomingGroupCallRingActionType = ReadonlyDeep<{
payload: CancelIncomingGroupCallRingType;
}>;
type DenyUserActionType = ReadonlyDeep<{
type: 'calling/DENY_USER';
}>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep
type StartCallingLobbyActionType = {
type: 'calling/START_CALLING_LOBBY';
@ -751,6 +769,10 @@ export type PeekGroupCallFulfilledActionType = ReadonlyDeep<{
};
}>;
export type PendingUserActionPayloadType = ReadonlyDeep<{
serviceId: ServiceIdString | undefined;
}>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep
type RefreshIODevicesActionType = {
type: 'calling/REFRESH_IO_DEVICES';
@ -767,6 +789,10 @@ type RemoteVideoChangeActionType = ReadonlyDeep<{
payload: RemoteVideoChangeType;
}>;
type RemoveClientActionType = ReadonlyDeep<{
type: 'calling/REMOVE_CLIENT';
}>;
type ReturnToActiveCallActionType = ReadonlyDeep<{
type: 'calling/RETURN_TO_ACTIVE_CALL';
}>;
@ -833,10 +859,12 @@ type SwitchFromPresentationViewActionType = ReadonlyDeep<{
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type CallingActionType =
| ApproveUserActionType
| AcceptCallPendingActionType
| CancelCallActionType
| CancelIncomingGroupCallRingActionType
| ChangeCallViewActionType
| DenyUserActionType
| StartCallingLobbyActionType
| StartCallLinkLobbyActionType
| CallStateChangeFulfilledActionType
@ -860,6 +888,7 @@ export type CallingActionType =
| RefreshIODevicesActionType
| RemoteSharingScreenChangeActionType
| RemoteVideoChangeActionType
| RemoveClientActionType
| ReturnToActiveCallActionType
| SendGroupCallReactionActionType
| SetLocalAudioActionType
@ -911,6 +940,68 @@ function acceptCall(
};
}
function approveUser(
payload: PendingUserActionPayloadType
): ThunkAction<void, RootStateType, unknown, ApproveUserActionType> {
return (dispatch, getState) => {
const activeCall = getActiveCall(getState().calling);
if (!activeCall || !isGroupOrAdhocCallMode(activeCall.callMode)) {
log.warn(
'approveUser: Trying to approve pending user without active group or adhoc call'
);
return;
}
if (!isAciString(payload.serviceId)) {
log.warn(
'approveUser: Trying to approve pending user without valid aci serviceid'
);
return;
}
calling.approveUser(activeCall.conversationId, payload.serviceId);
dispatch({ type: APPROVE_USER });
};
}
function denyUser(
payload: PendingUserActionPayloadType
): ThunkAction<void, RootStateType, unknown, DenyUserActionType> {
return (dispatch, getState) => {
const activeCall = getActiveCall(getState().calling);
if (!activeCall || !isGroupOrAdhocCallMode(activeCall.callMode)) {
log.warn(
'approveUser: Trying to approve pending user without active group or adhoc call'
);
return;
}
if (!isAciString(payload.serviceId)) {
log.warn(
'approveUser: Trying to approve pending user without valid aci serviceid'
);
return;
}
calling.denyUser(activeCall.conversationId, payload.serviceId);
dispatch({ type: DENY_USER });
};
}
function removeClient(
payload: RemoveClientType
): ThunkAction<void, RootStateType, unknown, RemoveClientActionType> {
return (dispatch, getState) => {
const activeCall = getActiveCall(getState().calling);
if (!activeCall || !isGroupOrAdhocCallMode(activeCall.callMode)) {
log.warn(
'approveUser: Trying to approve pending user without active group or adhoc call'
);
return;
}
calling.removeClient(activeCall.conversationId, payload.demuxId);
dispatch({ type: REMOVE_CLIENT });
};
}
function callStateChange(
payload: CallStateChangeType
): ThunkAction<
@ -1869,8 +1960,13 @@ const _startCallLinkLobby = async ({
groupCall?.remoteParticipants.length ||
0;
const { adminKey } = getOwn(state.calling.callLinks, roomId) ?? {};
const adminPasskey = adminKey
? Buffer.from(toAdminKeyBytes(adminKey))
: undefined;
const callLobbyData = await calling.startCallLinkLobby({
callLinkRootKey,
adminPasskey,
hasLocalAudio: groupCallDeviceCount < 8,
});
if (!callLobbyData) {
@ -2003,6 +2099,7 @@ function startCall(
await calling.joinCallLinkCall({
roomId: conversationId,
rootKey: callLink.rootKey,
adminKey: callLink.adminKey ?? undefined,
hasLocalAudio,
hasLocalVideo,
});
@ -2061,6 +2158,7 @@ function switchFromPresentationView(): SwitchFromPresentationViewActionType {
}
export const actions = {
acceptCall,
approveUser,
callStateChange,
cancelCall,
cancelIncomingGroupCallRing,
@ -2068,6 +2166,7 @@ export const actions = {
changeIODevice,
closeNeedPermissionScreen,
declineCall,
denyUser,
getPresentingSources,
groupCallAudioLevelsChange,
groupCallEnded,
@ -2089,6 +2188,7 @@ export const actions = {
refreshIODevices,
remoteSharingScreenChange,
remoteVideoChange,
removeClient,
returnToActiveCall,
sendGroupCallRaiseHand,
sendGroupCallReaction,
@ -2237,6 +2337,7 @@ export function reducer(
peekInfo: peekInfo ||
existingCall?.peekInfo || {
acis: remoteParticipants.map(({ aci }) => aci),
pendingAcis: [],
maxDevices: Infinity,
deviceCount: remoteParticipants.length,
},
@ -2286,8 +2387,10 @@ export function reducer(
...callLinks,
[conversationId]: {
...action.payload.callLinkState,
rootKey: action.payload.callLinkRootKey,
adminKey: null,
rootKey:
callLinks[conversationId]?.rootKey ??
action.payload.callLinkRootKey,
adminKey: callLinks[conversationId]?.adminKey,
},
}
: callLinks,
@ -2478,6 +2581,7 @@ export function reducer(
localDemuxId: undefined,
peekInfo: {
acis: [],
pendingAcis: [],
maxDevices: Infinity,
deviceCount: 0,
},
@ -2676,6 +2780,7 @@ export function reducer(
const newPeekInfo = peekInfo ||
existingCall?.peekInfo || {
acis: remoteParticipants.map(({ aci }) => aci),
pendingAcis: [],
maxDevices: Infinity,
deviceCount: remoteParticipants.length,
};
@ -2755,6 +2860,7 @@ export function reducer(
localDemuxId: undefined,
peekInfo: {
acis: [],
pendingAcis: [],
maxDevices: Infinity,
deviceCount: 0,
},

View file

@ -211,6 +211,7 @@ const mapStateToActiveCallProp = (
const groupMembers: Array<ConversationType> = [];
const remoteParticipants: Array<GroupCallRemoteParticipantType> = [];
const peekedParticipants: Array<ConversationType> = [];
const pendingParticipants: Array<ConversationType> = [];
const conversationsByDemuxId: ConversationsByDemuxIdType = new Map();
const { localDemuxId } = call;
const raisedHands: Set<number> = new Set(call.raisedHands ?? []);
@ -224,6 +225,7 @@ const mapStateToActiveCallProp = (
deviceCount: 0,
maxDevices: Infinity,
acis: [],
pendingAcis: [],
},
} = call;
@ -294,6 +296,20 @@ const mapStateToActiveCallProp = (
peekedParticipants.push(peekedConversation);
}
for (let i = 0; i < peekInfo.pendingAcis.length; i += 1) {
const aci = peekInfo.pendingAcis[i];
// In call links, pending users may be unknown until they share profile keys.
// conversationSelectorByAci should create conversations for new contacts.
const pendingConversation = conversationSelectorByAci(aci);
if (!pendingConversation) {
log.error('Pending participant has no corresponding conversation');
continue;
}
pendingParticipants.push(pendingConversation);
}
return {
...baseResult,
callMode: call.callMode,
@ -306,6 +322,7 @@ const mapStateToActiveCallProp = (
localDemuxId,
maxDevices: peekInfo.maxDevices,
peekedParticipants,
pendingParticipants,
raisedHands,
remoteParticipants,
remoteAudioLevels: call.remoteAudioLevels || new Map<number, number>(),
@ -407,6 +424,8 @@ export const SmartCallManager = memo(function SmartCallManager() {
: false;
const {
approveUser,
denyUser,
changeCallView,
closeNeedPermissionScreen,
getPresentingSources,
@ -416,6 +435,7 @@ export const SmartCallManager = memo(function SmartCallManager() {
acceptCall,
declineCall,
openSystemPreferencesAction,
removeClient,
sendGroupCallRaiseHand,
sendGroupCallReaction,
setGroupCallVideoRequest,
@ -440,6 +460,7 @@ export const SmartCallManager = memo(function SmartCallManager() {
<CallManager
acceptCall={acceptCall}
activeCall={activeCall}
approveUser={approveUser}
availableCameras={availableCameras}
bounceAppIconStart={bounceAppIconStart}
bounceAppIconStop={bounceAppIconStop}
@ -448,6 +469,7 @@ export const SmartCallManager = memo(function SmartCallManager() {
changeCallView={changeCallView}
closeNeedPermissionScreen={closeNeedPermissionScreen}
declineCall={declineCall}
denyUser={denyUser}
getGroupCallVideoFrameSource={getGroupCallVideoFrameSource}
getPresentingSources={getPresentingSources}
hangUpActiveCall={hangUpActiveCall}
@ -461,6 +483,7 @@ export const SmartCallManager = memo(function SmartCallManager() {
openSystemPreferencesAction={openSystemPreferencesAction}
pauseVoiceNotePlayer={pauseVoiceNotePlayer}
playRingtone={playRingtone}
removeClient={removeClient}
renderDeviceSelection={renderDeviceSelection}
renderEmojiPicker={renderEmojiPicker}
renderReactionPicker={renderReactionPicker}