Block users from joining call links

This commit is contained in:
ayumi-signal 2024-06-28 17:13:20 -07:00 committed by GitHub
parent 71ae6ea93a
commit 4ae563cc95
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 137 additions and 5 deletions

View file

@ -3694,6 +3694,18 @@
"messageformat": "Remove this person from the call",
"description": "Button in the in-call info popup for call link calls showing all participants. The action is to remove the participant from the call."
},
"icu:CallingAdhocCallInfo__RemoveClientDialogBody": {
"messageformat": "Remove {name} from the call?",
"description": "Info text in confirmation dialog when removing or blocking someone from an ongoing call for a call link."
},
"icu:CallingAdhocCallInfo__RemoveClientDialogButton--remove": {
"messageformat": "Remove",
"description": "Button in confirmation dialog when removing or blocking someone from an ongoing call for a call link."
},
"icu:CallingAdhocCallInfo__RemoveClientDialogButton--block": {
"messageformat": "Block from call",
"description": "Button in confirmation dialog when removing or blocking someone from an ongoing call for a call link."
},
"icu:CallingAdhocCallInfo__UnknownContactLabel": {
"messageformat": "{count, plural, one {# person} other {# people}}",
"description": "Label showing number of unknown contacts in the in-call participant info popup for call links."

View file

@ -137,3 +137,7 @@
// Should match background of .module-calling-participants-list__contact:hover
outline-color: $color-gray-62;
}
.CallingAdhocCallInfo__RemoveClientDialog {
width: 440px;
}

View file

@ -100,6 +100,7 @@ const createProps = (storyProps: Partial<PropsType> = {}): PropsType => ({
openSystemPreferencesAction: action('open-system-preferences-action'),
playRingtone: action('play-ringtone'),
removeClient: action('remove-client'),
blockClient: action('block-client'),
renderDeviceSelection: () => <div />,
renderEmojiPicker: () => <>EmojiPicker</>,
renderReactionPicker: () => <div />,

View file

@ -117,6 +117,7 @@ export type PropsType = {
openSystemPreferencesAction: () => unknown;
playRingtone: () => unknown;
removeClient: (payload: RemoveClientType) => void;
blockClient: (payload: RemoveClientType) => void;
sendGroupCallRaiseHand: (payload: SendGroupCallRaiseHandType) => void;
sendGroupCallReaction: (payload: SendGroupCallReactionType) => void;
setGroupCallVideoRequest: (_: SetGroupCallVideoRequestType) => void;
@ -163,6 +164,7 @@ function ActiveCallManager({
activeCall,
approveUser,
availableCameras,
blockClient,
callLink,
cancelCall,
changeCallView,
@ -397,6 +399,7 @@ function ActiveCallManager({
onCopyCallLink={onCopyCallLink}
onShareCallLinkViaSignal={handleShareCallLinkViaSignal}
removeClient={removeClient}
blockClient={blockClient}
showContactModal={showContactModal}
/>
) : (
@ -494,6 +497,7 @@ function ActiveCallManager({
onCopyCallLink={onCopyCallLink}
onShareCallLinkViaSignal={handleShareCallLinkViaSignal}
removeClient={removeClient}
blockClient={blockClient}
showContactModal={showContactModal}
/>
) : (
@ -515,6 +519,7 @@ export function CallManager({
activeCall,
approveUser,
availableCameras,
blockClient,
bounceAppIconStart,
bounceAppIconStop,
callLink,
@ -613,6 +618,7 @@ export function CallManager({
activeCall={activeCall}
availableCameras={availableCameras}
approveUser={approveUser}
blockClient={blockClient}
callLink={callLink}
cancelCall={cancelCall}
changeCallView={changeCallView}

View file

@ -68,6 +68,7 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
onCopyCallLink: action('on-copy-call-link'),
onShareCallLinkViaSignal: action('on-share-call-link-via-signal'),
removeClient: overrideProps.removeClient || action('remove-client'),
blockClient: overrideProps.blockClient || action('block-client'),
showContactModal: action('show-contact-modal'),
});
@ -140,3 +141,35 @@ export function Overflow(): JSX.Element {
});
return <CallingAdhocCallInfo {...props} />;
}
export function AsAdmin(): JSX.Element {
const props = createProps({
participants: [
createParticipant({
title: 'Son Goku',
}),
createParticipant({
hasRemoteAudio: true,
hasRemoteVideo: true,
presenting: true,
name: 'Rage Trunks',
title: 'Rage Trunks',
}),
createParticipant({
hasRemoteAudio: true,
title: 'Prince Vegeta',
}),
createParticipant({
hasRemoteAudio: true,
hasRemoteVideo: true,
name: 'Goku',
title: 'Goku',
}),
createParticipant({
title: 'Someone With A Really Long Name',
}),
],
isCallLinkAdmin: true,
});
return <CallingAdhocCallInfo {...props} />;
}

View file

@ -20,6 +20,7 @@ import { AVATAR_COLOR_COUNT, AvatarColors } from '../types/Colors';
import { Button } from './Button';
import { Modal } from './Modal';
import { Theme } from '../util/theme';
import { ConfirmationDialog } from './ConfirmationDialog';
const MAX_UNKNOWN_AVATARS_COUNT = 3;
@ -40,7 +41,8 @@ export type PropsType = {
readonly onClose: () => void;
readonly onCopyCallLink: () => void;
readonly onShareCallLinkViaSignal: () => void;
readonly removeClient: ((payload: RemoveClientType) => void) | null;
readonly removeClient: (payload: RemoveClientType) => void;
readonly blockClient: (payload: RemoveClientType) => void;
readonly showContactModal: (
contactId: string,
conversationId?: string
@ -145,6 +147,7 @@ export function CallingAdhocCallInfo({
isCallLinkAdmin,
ourServiceId,
participants,
blockClient,
onClose,
onCopyCallLink,
onShareCallLinkViaSignal,
@ -153,6 +156,11 @@ export function CallingAdhocCallInfo({
}: PropsType): JSX.Element | null {
const [isUnknownContactDialogVisible, setIsUnknownContactDialogVisible] =
React.useState(false);
const [removeClientDialogState, setRemoveClientDialogState] = React.useState<{
demuxId: number;
name: string;
} | null>(null);
const hideUnknownContactDialog = React.useCallback(
() => setIsUnknownContactDialogVisible(false),
[setIsUnknownContactDialogVisible]
@ -256,7 +264,6 @@ export function CallingAdhocCallInfo({
)}
/>
{isCallLinkAdmin &&
removeClient &&
participant.demuxId &&
!(ourServiceId && participant.serviceId === ourServiceId) ? (
<button
@ -273,7 +280,10 @@ export function CallingAdhocCallInfo({
event.stopPropagation();
event.preventDefault();
removeClient({ demuxId: participant.demuxId });
setRemoveClientDialogState({
demuxId: participant.demuxId,
name: participant.title,
});
}}
type="button"
/>
@ -285,13 +295,45 @@ export function CallingAdhocCallInfo({
isCallLinkAdmin,
onClose,
ourServiceId,
removeClient,
setRemoveClientDialogState,
showContactModal,
]
);
return (
<>
{removeClientDialogState != null ? (
<ConfirmationDialog
dialogName="CallingAdhocCallInfo.removeClientDialog"
moduleClassName="CallingAdhocCallInfo__RemoveClientDialog"
actions={[
{
action: () =>
blockClient({ demuxId: removeClientDialogState.demuxId }),
style: 'negative',
text: i18n(
'icu:CallingAdhocCallInfo__RemoveClientDialogButton--block'
),
},
{
action: () =>
removeClient({ demuxId: removeClientDialogState.demuxId }),
style: 'negative',
text: i18n(
'icu:CallingAdhocCallInfo__RemoveClientDialogButton--remove'
),
},
]}
cancelText={i18n('icu:cancel')}
i18n={i18n}
theme={Theme.Dark}
onClose={() => setRemoveClientDialogState(null)}
>
{i18n('icu:CallingAdhocCallInfo__RemoveClientDialogBody', {
name: removeClientDialogState.name,
})}
</ConfirmationDialog>
) : null}
{isUnknownContactDialogVisible ? (
<Modal
modalName="CallingAdhocCallInfo.UnknownContactInfo"

View file

@ -1592,6 +1592,15 @@ export class CallingClass {
groupCall.removeClient(demuxId);
}
public blockClient(conversationId: string, demuxId: number): void {
const groupCall = this.getGroupCall(conversationId);
if (!groupCall) {
throw new Error('Could not find matching call');
}
groupCall.blockClient(demuxId);
}
// See the comment in types/Calling.ts to explain why we have to do this conversion.
private convertRingRtcConnectionState(
connectionState: ConnectionState

View file

@ -574,6 +574,7 @@ const doGroupCallPeek = ({
const ACCEPT_CALL_PENDING = 'calling/ACCEPT_CALL_PENDING';
const APPROVE_USER = 'calling/APPROVE_USER';
const BLOCK_CLIENT = 'calling/BLOCK_CLIENT';
const CANCEL_CALL = 'calling/CANCEL_CALL';
const CANCEL_INCOMING_GROUP_CALL_RING =
'calling/CANCEL_INCOMING_GROUP_CALL_RING';
@ -801,6 +802,10 @@ type RemoveClientActionType = ReadonlyDeep<{
type: 'calling/REMOVE_CLIENT';
}>;
type BlockClientActionType = ReadonlyDeep<{
type: 'calling/BLOCK_CLIENT';
}>;
type ReturnToActiveCallActionType = ReadonlyDeep<{
type: 'calling/RETURN_TO_ACTIVE_CALL';
}>;
@ -1002,7 +1007,7 @@ function removeClient(
const activeCall = getActiveCall(getState().calling);
if (!activeCall || !isGroupOrAdhocCallMode(activeCall.callMode)) {
log.warn(
'approveUser: Trying to approve pending user without active group or adhoc call'
'removeClient: Trying to remove client without active group or adhoc call'
);
return;
}
@ -1012,6 +1017,23 @@ function removeClient(
};
}
function blockClient(
payload: RemoveClientType
): ThunkAction<void, RootStateType, unknown, BlockClientActionType> {
return (dispatch, getState) => {
const activeCall = getActiveCall(getState().calling);
if (!activeCall || !isGroupOrAdhocCallMode(activeCall.callMode)) {
log.warn(
'blockClient: Trying to block client without active group or adhoc call'
);
return;
}
calling.blockClient(activeCall.conversationId, payload.demuxId);
dispatch({ type: BLOCK_CLIENT });
};
}
function callStateChange(
payload: CallStateChangeType
): ThunkAction<
@ -2274,6 +2296,7 @@ function switchFromPresentationView(): SwitchFromPresentationViewActionType {
export const actions = {
acceptCall,
approveUser,
blockClient,
callStateChange,
cancelCall,
cancelIncomingGroupCallRing,

View file

@ -437,6 +437,7 @@ export const SmartCallManager = memo(function SmartCallManager() {
declineCall,
openSystemPreferencesAction,
removeClient,
blockClient,
sendGroupCallRaiseHand,
sendGroupCallReaction,
setGroupCallVideoRequest,
@ -464,6 +465,7 @@ export const SmartCallManager = memo(function SmartCallManager() {
activeCall={activeCall}
approveUser={approveUser}
availableCameras={availableCameras}
blockClient={blockClient}
bounceAppIconStart={bounceAppIconStart}
bounceAppIconStop={bounceAppIconStop}
callLink={callLink}