-
- {ourServiceId && participant.serviceId === ourServiceId ? (
-
- {i18n('icu:you')}
-
- ) : (
- <>
-
- {isInSystemContacts(participant) ? (
-
- {' '}
-
-
- ) : null}
- >
- )}
+ );
+ }
+
+ const participant = participants[0];
+ return (
+
+
+
+
+
+
+
+ {isInSystemContacts(participant) ? (
+
+ ) : null}
-
-
-
- ))}
-
+
+ {i18n('icu:CallingPendingParticipants__WouldLikeToJoin')}
+
+
+
+ {renderApprovalButtons(participant)}
+
+ {participants.length > 1 && (
+
+
+
+ )}
);
}
diff --git a/ts/components/ToastManager.stories.tsx b/ts/components/ToastManager.stories.tsx
index 87910da565..2a73495d91 100644
--- a/ts/components/ToastManager.stories.tsx
+++ b/ts/components/ToastManager.stories.tsx
@@ -21,6 +21,8 @@ function getToast(toastType: ToastType): AnyToast {
switch (toastType) {
case ToastType.AddingUserToGroup:
return { toastType, parameters: { contact: 'Sam Mirete' } };
+ case ToastType.AddedUsersToCall:
+ return { toastType, parameters: { count: 6 } };
case ToastType.AlreadyGroupMember:
return { toastType: ToastType.AlreadyGroupMember };
case ToastType.AlreadyRequestedToJoin:
diff --git a/ts/components/ToastManager.tsx b/ts/components/ToastManager.tsx
index cc28558da7..6df79fe6b5 100644
--- a/ts/components/ToastManager.tsx
+++ b/ts/components/ToastManager.tsx
@@ -59,6 +59,16 @@ export function renderToast({
);
}
+ if (toastType === ToastType.AddedUsersToCall) {
+ return (
+
+ {i18n('icu:CallingPendingParticipants__Toast--added-users-to-call', {
+ count: toast.parameters.count,
+ })}
+
+ );
+ }
+
if (toastType === ToastType.AlreadyGroupMember) {
return (
diff --git a/ts/state/ducks/calling.ts b/ts/state/ducks/calling.ts
index 677c6cc38b..97d4b4f281 100644
--- a/ts/state/ducks/calling.ts
+++ b/ts/state/ducks/calling.ts
@@ -782,6 +782,11 @@ export type PendingUserActionPayloadType = ReadonlyDeep<{
serviceId: ServiceIdString | undefined;
}>;
+export type BatchUserActionPayloadType = ReadonlyDeep<{
+ action: 'approve' | 'deny';
+ serviceIds: Array;
+}>;
+
// eslint-disable-next-line local-rules/type-alias-readonlydeep
type RefreshIODevicesActionType = {
type: 'calling/REFRESH_IO_DEVICES';
@@ -1000,6 +1005,54 @@ function denyUser(
dispatch({ type: DENY_USER });
};
}
+
+function batchUserAction(
+ payload: BatchUserActionPayloadType
+): ThunkAction {
+ return (dispatch, getState) => {
+ const activeCall = getActiveCall(getState().calling);
+ if (!activeCall || !isGroupOrAdhocCallMode(activeCall.callMode)) {
+ log.warn(
+ 'batchUserAction: Trying to do pending user without active group or adhoc call'
+ );
+ return;
+ }
+
+ const { action, serviceIds } = payload;
+ let actionFn;
+ if (action === 'approve') {
+ actionFn = calling.approveUser;
+ } else if (action === 'deny') {
+ actionFn = calling.denyUser;
+ } else {
+ throw missingCaseError(action);
+ }
+
+ let count = 0;
+ for (const serviceId of serviceIds) {
+ if (!isAciString(serviceId)) {
+ log.warn(
+ 'batchUserAction: Trying to do user action without valid aci serviceid'
+ );
+ continue;
+ }
+
+ actionFn.call(calling, activeCall.conversationId, serviceId);
+ count += 1;
+ }
+
+ if (count > 0 && action === 'approve') {
+ dispatch({
+ type: SHOW_TOAST,
+ payload: {
+ toastType: ToastType.AddedUsersToCall,
+ parameters: { count },
+ },
+ });
+ }
+ };
+}
+
function removeClient(
payload: RemoveClientType
): ThunkAction {
@@ -2296,6 +2349,7 @@ function switchFromPresentationView(): SwitchFromPresentationViewActionType {
export const actions = {
acceptCall,
approveUser,
+ batchUserAction,
blockClient,
callStateChange,
cancelCall,
diff --git a/ts/state/smart/CallManager.tsx b/ts/state/smart/CallManager.tsx
index 3897486ea4..5a8bcfe47e 100644
--- a/ts/state/smart/CallManager.tsx
+++ b/ts/state/smart/CallManager.tsx
@@ -426,6 +426,7 @@ export const SmartCallManager = memo(function SmartCallManager() {
const {
approveUser,
+ batchUserAction,
denyUser,
changeCallView,
closeNeedPermissionScreen,
@@ -465,6 +466,7 @@ export const SmartCallManager = memo(function SmartCallManager() {
activeCall={activeCall}
approveUser={approveUser}
availableCameras={availableCameras}
+ batchUserAction={batchUserAction}
blockClient={blockClient}
bounceAppIconStart={bounceAppIconStart}
bounceAppIconStop={bounceAppIconStop}
diff --git a/ts/types/Toast.tsx b/ts/types/Toast.tsx
index 687f09dcca..d74f5849c7 100644
--- a/ts/types/Toast.tsx
+++ b/ts/types/Toast.tsx
@@ -3,6 +3,7 @@
export enum ToastType {
AddingUserToGroup = 'AddingUserToGroup',
+ AddedUsersToCall = 'AddedUsersToCall',
AlreadyGroupMember = 'AlreadyGroupMember',
AlreadyRequestedToJoin = 'AlreadyRequestedToJoin',
Blocked = 'Blocked',
@@ -69,6 +70,10 @@ export enum ToastType {
export type AnyToast =
| { toastType: ToastType.AddingUserToGroup; parameters: { contact: string } }
+ | {
+ toastType: ToastType.AddedUsersToCall;
+ parameters: { count: number };
+ }
| { toastType: ToastType.AlreadyGroupMember }
| { toastType: ToastType.AlreadyRequestedToJoin }
| { toastType: ToastType.Blocked }
diff --git a/ts/util/lint/exceptions.json b/ts/util/lint/exceptions.json
index 290f166e24..9bce03396f 100644
--- a/ts/util/lint/exceptions.json
+++ b/ts/util/lint/exceptions.json
@@ -3250,6 +3250,14 @@
"reasonCategory": "usageTrusted",
"updated": "2021-07-30T16:57:33.618Z"
},
+ {
+ "rule": "React-useRef",
+ "path": "ts/components/CallingPendingParticipants.tsx",
+ "line": " const expandedListRef = useRef(null);",
+ "reasonCategory": "usageTrusted",
+ "updated": "2024-06-28T01:22:22.509Z",
+ "reasonDetail": "For outside click handling"
+ },
{
"rule": "React-useRef",
"path": "ts/components/CallingPip.tsx",