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", "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." "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": { "icu:CallingAdhocCallInfo__UnknownContactLabel": {
"messageformat": "{count, plural, one {# person} other {# people}}", "messageformat": "{count, plural, one {# person} other {# people}}",
"description": "Label showing number of unknown contacts in the in-call participant info popup for call links." "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 // Should match background of .module-calling-participants-list__contact:hover
outline-color: $color-gray-62; 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'), openSystemPreferencesAction: action('open-system-preferences-action'),
playRingtone: action('play-ringtone'), playRingtone: action('play-ringtone'),
removeClient: action('remove-client'), removeClient: action('remove-client'),
blockClient: action('block-client'),
renderDeviceSelection: () => <div />, renderDeviceSelection: () => <div />,
renderEmojiPicker: () => <>EmojiPicker</>, renderEmojiPicker: () => <>EmojiPicker</>,
renderReactionPicker: () => <div />, renderReactionPicker: () => <div />,

View file

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

View file

@ -68,6 +68,7 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
onCopyCallLink: action('on-copy-call-link'), onCopyCallLink: action('on-copy-call-link'),
onShareCallLinkViaSignal: action('on-share-call-link-via-signal'), onShareCallLinkViaSignal: action('on-share-call-link-via-signal'),
removeClient: overrideProps.removeClient || action('remove-client'), removeClient: overrideProps.removeClient || action('remove-client'),
blockClient: overrideProps.blockClient || action('block-client'),
showContactModal: action('show-contact-modal'), showContactModal: action('show-contact-modal'),
}); });
@ -140,3 +141,35 @@ export function Overflow(): JSX.Element {
}); });
return <CallingAdhocCallInfo {...props} />; 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 { Button } from './Button';
import { Modal } from './Modal'; import { Modal } from './Modal';
import { Theme } from '../util/theme'; import { Theme } from '../util/theme';
import { ConfirmationDialog } from './ConfirmationDialog';
const MAX_UNKNOWN_AVATARS_COUNT = 3; const MAX_UNKNOWN_AVATARS_COUNT = 3;
@ -40,7 +41,8 @@ export type PropsType = {
readonly onClose: () => void; readonly onClose: () => void;
readonly onCopyCallLink: () => void; readonly onCopyCallLink: () => void;
readonly onShareCallLinkViaSignal: () => void; readonly onShareCallLinkViaSignal: () => void;
readonly removeClient: ((payload: RemoveClientType) => void) | null; readonly removeClient: (payload: RemoveClientType) => void;
readonly blockClient: (payload: RemoveClientType) => void;
readonly showContactModal: ( readonly showContactModal: (
contactId: string, contactId: string,
conversationId?: string conversationId?: string
@ -145,6 +147,7 @@ export function CallingAdhocCallInfo({
isCallLinkAdmin, isCallLinkAdmin,
ourServiceId, ourServiceId,
participants, participants,
blockClient,
onClose, onClose,
onCopyCallLink, onCopyCallLink,
onShareCallLinkViaSignal, onShareCallLinkViaSignal,
@ -153,6 +156,11 @@ export function CallingAdhocCallInfo({
}: PropsType): JSX.Element | null { }: PropsType): JSX.Element | null {
const [isUnknownContactDialogVisible, setIsUnknownContactDialogVisible] = const [isUnknownContactDialogVisible, setIsUnknownContactDialogVisible] =
React.useState(false); React.useState(false);
const [removeClientDialogState, setRemoveClientDialogState] = React.useState<{
demuxId: number;
name: string;
} | null>(null);
const hideUnknownContactDialog = React.useCallback( const hideUnknownContactDialog = React.useCallback(
() => setIsUnknownContactDialogVisible(false), () => setIsUnknownContactDialogVisible(false),
[setIsUnknownContactDialogVisible] [setIsUnknownContactDialogVisible]
@ -256,7 +264,6 @@ export function CallingAdhocCallInfo({
)} )}
/> />
{isCallLinkAdmin && {isCallLinkAdmin &&
removeClient &&
participant.demuxId && participant.demuxId &&
!(ourServiceId && participant.serviceId === ourServiceId) ? ( !(ourServiceId && participant.serviceId === ourServiceId) ? (
<button <button
@ -273,7 +280,10 @@ export function CallingAdhocCallInfo({
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
removeClient({ demuxId: participant.demuxId }); setRemoveClientDialogState({
demuxId: participant.demuxId,
name: participant.title,
});
}} }}
type="button" type="button"
/> />
@ -285,13 +295,45 @@ export function CallingAdhocCallInfo({
isCallLinkAdmin, isCallLinkAdmin,
onClose, onClose,
ourServiceId, ourServiceId,
removeClient, setRemoveClientDialogState,
showContactModal, showContactModal,
] ]
); );
return ( 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 ? ( {isUnknownContactDialogVisible ? (
<Modal <Modal
modalName="CallingAdhocCallInfo.UnknownContactInfo" modalName="CallingAdhocCallInfo.UnknownContactInfo"

View file

@ -1592,6 +1592,15 @@ export class CallingClass {
groupCall.removeClient(demuxId); 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. // See the comment in types/Calling.ts to explain why we have to do this conversion.
private convertRingRtcConnectionState( private convertRingRtcConnectionState(
connectionState: ConnectionState connectionState: ConnectionState

View file

@ -574,6 +574,7 @@ const doGroupCallPeek = ({
const ACCEPT_CALL_PENDING = 'calling/ACCEPT_CALL_PENDING'; const ACCEPT_CALL_PENDING = 'calling/ACCEPT_CALL_PENDING';
const APPROVE_USER = 'calling/APPROVE_USER'; const APPROVE_USER = 'calling/APPROVE_USER';
const BLOCK_CLIENT = 'calling/BLOCK_CLIENT';
const CANCEL_CALL = 'calling/CANCEL_CALL'; const CANCEL_CALL = 'calling/CANCEL_CALL';
const CANCEL_INCOMING_GROUP_CALL_RING = const CANCEL_INCOMING_GROUP_CALL_RING =
'calling/CANCEL_INCOMING_GROUP_CALL_RING'; 'calling/CANCEL_INCOMING_GROUP_CALL_RING';
@ -801,6 +802,10 @@ type RemoveClientActionType = ReadonlyDeep<{
type: 'calling/REMOVE_CLIENT'; type: 'calling/REMOVE_CLIENT';
}>; }>;
type BlockClientActionType = ReadonlyDeep<{
type: 'calling/BLOCK_CLIENT';
}>;
type ReturnToActiveCallActionType = ReadonlyDeep<{ type ReturnToActiveCallActionType = ReadonlyDeep<{
type: 'calling/RETURN_TO_ACTIVE_CALL'; type: 'calling/RETURN_TO_ACTIVE_CALL';
}>; }>;
@ -1002,7 +1007,7 @@ function removeClient(
const activeCall = getActiveCall(getState().calling); const activeCall = getActiveCall(getState().calling);
if (!activeCall || !isGroupOrAdhocCallMode(activeCall.callMode)) { if (!activeCall || !isGroupOrAdhocCallMode(activeCall.callMode)) {
log.warn( 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; 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( function callStateChange(
payload: CallStateChangeType payload: CallStateChangeType
): ThunkAction< ): ThunkAction<
@ -2274,6 +2296,7 @@ function switchFromPresentationView(): SwitchFromPresentationViewActionType {
export const actions = { export const actions = {
acceptCall, acceptCall,
approveUser, approveUser,
blockClient,
callStateChange, callStateChange,
cancelCall, cancelCall,
cancelIncomingGroupCallRing, cancelIncomingGroupCallRing,

View file

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