diff --git a/_locales/en/messages.json b/_locales/en/messages.json
index beeefcc030..12381a5e81 100644
--- a/_locales/en/messages.json
+++ b/_locales/en/messages.json
@@ -247,12 +247,17 @@
"blockedSettingsTitle": "Blocked Contacts",
"conversationsSettingsTitle": "Conversations",
"unbanUser": "Unban User",
+ "serverUnbanUser": "Unban User from Server",
"userUnbanned": "User unbanned successfully",
"userUnbanFailed": "Unban failed!",
+ "globalUserUnbanFailed": "Unban failed! Are you a global admin/mod?",
"banUser": "Ban User",
"banUserAndDeleteAll": "Ban and Delete All",
+ "serverBanUser": "Ban User from Server",
+ "serverBanUserAndDeleteAll": "Ban from Server and Delete All",
"userBanned": "Banned successfully",
"userBanFailed": "Ban failed!",
+ "GlobalUserBanFailed": "Ban failed! Are you a global admin/mod?",
"leaveGroup": "Leave Group",
"leaveAndRemoveForEveryone": "Leave Group and Remove for Everyone",
"leaveGroupConfirmation": "Are you sure you want to leave this group?",
diff --git a/ts/components/conversation/message/message-content/MessageContextMenu.tsx b/ts/components/conversation/message/message-content/MessageContextMenu.tsx
index 8a1c31248e..8fb3f2cf8a 100644
--- a/ts/components/conversation/message/message-content/MessageContextMenu.tsx
+++ b/ts/components/conversation/message/message-content/MessageContextMenu.tsx
@@ -210,6 +210,14 @@ export const MessageContextMenu = (props: Props) => {
MessageInteraction.unbanUser(sender, convoId);
}, [sender, convoId]);
+ const onServerBan = useCallback(() => {
+ MessageInteraction.serverBanUser(sender, convoId);
+ }, [sender, convoId]);
+
+ const onServerUnban = useCallback(() => {
+ MessageInteraction.serverUnbanUser(sender, convoId);
+ }, [sender, convoId]);
+
const onSelect = useCallback(() => {
dispatch(toggleSelectedMessageId(messageId));
}, [messageId]);
@@ -334,6 +342,10 @@ export const MessageContextMenu = (props: Props) => {
{weAreAdmin && isPublic ? (
- {window.i18n('unbanUser')}
) : null}
+ {weAreAdmin && isPublic ? - {window.i18n('serverBanUser')}
: null}
+ {weAreAdmin && isPublic ? (
+ - {window.i18n('serverUnbanUser')}
+ ) : null}
{weAreAdmin && isPublic && !isSenderAdmin ? (
- {window.i18n('addAsModerator')}
) : null}
diff --git a/ts/components/dialog/BanOrUnbanUserDialog.tsx b/ts/components/dialog/BanOrUnbanUserDialog.tsx
index 02ed38b939..a7120a1263 100644
--- a/ts/components/dialog/BanOrUnbanUserDialog.tsx
+++ b/ts/components/dialog/BanOrUnbanUserDialog.tsx
@@ -3,7 +3,11 @@ import { PubKey } from '../../session/types';
import { ToastUtils } from '../../session/utils';
import { Flex } from '../basic/Flex';
import { useDispatch, useSelector } from 'react-redux';
-import { BanType, updateBanOrUnbanUserModal } from '../../state/ducks/modalDialog';
+import {
+ BanType,
+ updateBanOrUnbanUserModal,
+ updateServerBanOrUnbanUserModal
+} from '../../state/ducks/modalDialog';
import { SpacerSM } from '../basic/Text';
import { getConversationController } from '../../session/conversations/ConversationController';
import { SessionWrapperModal } from '../SessionWrapperModal';
@@ -14,7 +18,9 @@ import { useFocusMount } from '../../hooks/useFocusMount';
import { useConversationPropsById } from '../../hooks/useParamSelector';
import {
sogsV3BanUser,
- sogsV3UnbanUser,
+ sogsV3ServerBanUser,
+ sogsV3ServerUnbanUser,
+ sogsV3UnbanUser
} from '../../session/apis/open_group_api/sogsv3/sogsV3BanUnban';
import { SessionHeaderSearchInput } from '../SessionHeaderSearchInput';
import { isDarkTheme } from '../../state/selectors/theme';
@@ -25,7 +31,8 @@ async function banOrUnBanUserCall(
convo: ConversationModel,
textValue: string,
banType: BanType,
- deleteAll: boolean
+ deleteAll: boolean,
+ isGlobal: boolean
) {
// if we don't have valid data entered by the user
const pubkey = PubKey.from(textValue);
@@ -39,13 +46,23 @@ async function banOrUnBanUserCall(
const roomInfos = convo.toOpenGroupV2();
const isChangeApplied =
banType === 'ban'
- ? await sogsV3BanUser(pubkey, roomInfos, deleteAll)
- : await sogsV3UnbanUser(pubkey, roomInfos);
+ ? isGlobal
+ ? await sogsV3ServerBanUser(pubkey, roomInfos, deleteAll)
+ : await sogsV3BanUser(pubkey, roomInfos, deleteAll)
+ : isGlobal
+ ? await sogsV3ServerUnbanUser(pubkey, roomInfos)
+ : await sogsV3UnbanUser(pubkey, roomInfos);
if (!isChangeApplied) {
window?.log?.warn(`failed to ${banType} user: ${isChangeApplied}`);
- banType === 'ban' ? ToastUtils.pushUserBanFailure() : ToastUtils.pushUserUnbanSuccess();
+ banType === 'ban'
+ ? isGlobal
+ ? ToastUtils.pushGlobalUserBanFailure()
+ : ToastUtils.pushUserBanFailure()
+ : isGlobal
+ ? ToastUtils.pushGlobalUserUnbanFailure()
+ : ToastUtils.pushUserUnbanFailure();
return false;
}
window?.log?.info(`${pubkey.key} user ${banType}ned successfully...`);
@@ -92,7 +109,7 @@ export const BanOrUnBanUserDialog = (props: {
window?.log?.info(`asked to ${banType} user: ${castedPubkey}, banAndDeleteAll:${deleteAll}`);
setInProgress(true);
- const isBanned = await banOrUnBanUserCall(convo, castedPubkey, banType, deleteAll);
+ const isBanned = await banOrUnBanUserCall(convo, castedPubkey, banType, deleteAll, false);
if (isBanned) {
// clear input box
setInputBoxValue('');
@@ -163,4 +180,112 @@ export const BanOrUnBanUserDialog = (props: {
);
-};
+}
+
+// FIXME: Refactor with BanOrUnBanUserDialog().
+export const ServerBanOrUnBanUserDialog = (props: {
+ conversationId: string;
+ banType: BanType;
+ pubkey?: string;
+}) => {
+ const { conversationId, banType, pubkey } = props;
+ const { i18n } = window;
+ const isBan = banType === 'ban';
+ const dispatch = useDispatch();
+ const darkMode = useSelector(isDarkTheme);
+ const convo = getConversationController().get(conversationId);
+ const inputRef = useRef(null);
+
+ useFocusMount(inputRef, true);
+ const wasGivenAPubkey = Boolean(pubkey?.length);
+ const [inputBoxValue, setInputBoxValue] = useState('');
+ const [inProgress, setInProgress] = useState(false);
+
+ const sourceConvoProps = useConversationPropsById(pubkey);
+
+ const inputTextToDisplay =
+ wasGivenAPubkey && sourceConvoProps
+ ? `${sourceConvoProps.displayNameInProfile} ${PubKey.shorten(sourceConvoProps.id)}`
+ : undefined;
+
+ /**
+ * Ban or Unban a user from an open group
+ * @param deleteAll Delete all messages for that user in the group (only works with ban)
+ */
+ const banOrUnBanUser = async (deleteAll: boolean = false) => {
+ const castedPubkey = pubkey?.length ? pubkey : inputBoxValue;
+
+ window?.log?.info(`asked to ${banType} user server-wide: ${castedPubkey}, banAndDeleteAll:${deleteAll}`);
+ setInProgress(true);
+ const isBanned = await banOrUnBanUserCall(convo, castedPubkey, banType, deleteAll, true);
+ if (isBanned) {
+ // clear input box
+ setInputBoxValue('');
+ if (wasGivenAPubkey) {
+ dispatch(updateServerBanOrUnbanUserModal(null));
+ }
+ }
+
+ setInProgress(false);
+ };
+
+ const serverHost = new window.URL(convo.toOpenGroupV2().serverUrl).host;
+ const title = `${isBan ? window.i18n('banUser') : window.i18n('unbanUser')} @ ${serverHost}`;
+
+ const onPubkeyBoxChanges = (e: React.ChangeEvent) => {
+ setInputBoxValue(e.target.value?.trim() || '');
+ };
+
+ /**
+ * Starts procedure for banning/unbanning user and all their messages using dialog
+ */
+ const startBanAndDeleteAllSequence = async () => {
+ await banOrUnBanUser(true);
+ };
+
+ const buttonText = isBan ? i18n('banUser') : i18n('unbanUser');
+
+ return (
+ {
+ dispatch(updateServerBanOrUnbanUserModal(null));
+ }}
+ >
+
+
+
+
+ {isBan && (
+ <>
+
+
+ >
+ )}
+
+
+
+
+ );
+}
diff --git a/ts/components/dialog/ModalContainer.tsx b/ts/components/dialog/ModalContainer.tsx
index d54bbe7e81..8d5ce620f3 100644
--- a/ts/components/dialog/ModalContainer.tsx
+++ b/ts/components/dialog/ModalContainer.tsx
@@ -14,6 +14,7 @@ import {
getReactListDialog,
getRecoveryPhraseDialog,
getRemoveModeratorsModal,
+ getServerBanOrUnbanUserModalState,
getSessionPasswordDialog,
getUpdateGroupMembersModal,
getUpdateGroupNameModal,
@@ -33,7 +34,7 @@ import { RemoveModeratorsDialog } from './ModeratorsRemoveDialog';
import { UpdateGroupMembersDialog } from './UpdateGroupMembersDialog';
import { UpdateGroupNameDialog } from './UpdateGroupNameDialog';
import { SessionNicknameDialog } from './SessionNicknameDialog';
-import { BanOrUnBanUserDialog } from './BanOrUnbanUserDialog';
+import { BanOrUnBanUserDialog, ServerBanOrUnBanUserDialog } from './BanOrUnbanUserDialog';
import { ReactListModal } from './ReactListModal';
import { ReactClearAllModal } from './ReactClearAllModal';
@@ -53,12 +54,14 @@ export const ModalContainer = () => {
const sessionPasswordModalState = useSelector(getSessionPasswordDialog);
const deleteAccountModalState = useSelector(getDeleteAccountModalState);
const banOrUnbanUserModalState = useSelector(getBanOrUnbanUserModalState);
+ const serverBanOrUnbanUserModalState = useSelector(getServerBanOrUnbanUserModalState);
const reactListModalState = useSelector(getReactListDialog);
const reactClearAllModalState = useSelector(getReactClearAllDialog);
return (
<>
{banOrUnbanUserModalState && }
+ {serverBanOrUnbanUserModalState && }
{inviteModalState && }
{addModeratorsModalState && }
{removeModeratorsModalState && }
diff --git a/ts/components/menu/ConversationHeaderMenu.tsx b/ts/components/menu/ConversationHeaderMenu.tsx
index 6ee07b4b3e..25e8730cea 100644
--- a/ts/components/menu/ConversationHeaderMenu.tsx
+++ b/ts/components/menu/ConversationHeaderMenu.tsx
@@ -18,6 +18,8 @@ import {
NotificationForConvoMenuItem,
PinConversationMenuItem,
RemoveModeratorsMenuItem,
+ ServerBanMenuItem,
+ ServerUnbanMenuItem,
ShowUserDetailsMenuItem,
UnbanMenuItem,
UpdateGroupNameMenuItem,
@@ -60,6 +62,8 @@ export const ConversationHeaderMenu = (props: PropsConversationHeaderMenu) => {
+
+
diff --git a/ts/components/menu/ConversationListItemContextMenu.tsx b/ts/components/menu/ConversationListItemContextMenu.tsx
index 7f91d0b629..5cad968085 100644
--- a/ts/components/menu/ConversationListItemContextMenu.tsx
+++ b/ts/components/menu/ConversationListItemContextMenu.tsx
@@ -17,6 +17,8 @@ import {
MarkAllReadMenuItem,
NotificationForConvoMenuItem,
PinConversationMenuItem,
+ ServerBanMenuItem,
+ ServerUnbanMenuItem,
ShowUserDetailsMenuItem,
UnbanMenuItem,
} from './Menu';
@@ -44,6 +46,8 @@ const ConversationListItemContextMenu = (props: PropsContextConversationItem) =>
+
+
diff --git a/ts/components/menu/Menu.tsx b/ts/components/menu/Menu.tsx
index 6c0fc05cc7..77c652982c 100644
--- a/ts/components/menu/Menu.tsx
+++ b/ts/components/menu/Menu.tsx
@@ -33,6 +33,8 @@ import {
showInviteContactByConvoId,
showLeaveGroupByConvoId,
showRemoveModeratorsByConvoId,
+ showServerBanUserByConvoId,
+ showServerUnbanUserByConvoId,
showUnbanUserByConvoId,
showUpdateGroupNameByConvoId,
unblockConvoById,
@@ -116,6 +118,14 @@ const showBanUser = (weAreAdmin: boolean, isPublic: boolean, isKickedFromGroup:
return !isKickedFromGroup && weAreAdmin && isPublic;
};
+const showServerUnbanUser = (weAreAdmin: boolean, isPublic: boolean) => {
+ return weAreAdmin && isPublic;
+};
+
+const showServerBanUser = (weAreAdmin: boolean, isPublic: boolean) => {
+ return weAreAdmin && isPublic;
+};
+
function showAddModerators(
weAreAdmin: boolean,
isPublic: boolean,
@@ -387,6 +397,44 @@ export const BanMenuItem = (): JSX.Element | null => {
return null;
};
+export const ServerUnbanMenuItem = (): JSX.Element | null => {
+ const convoId = useContext(ContextConversationId);
+ const isPublic = useIsPublic(convoId);
+ const weAreAdmin = useWeAreAdmin(convoId);
+
+ if (showServerUnbanUser(weAreAdmin, isPublic)) {
+ return (
+ - {
+ showServerUnbanUserByConvoId(convoId);
+ }}
+ >
+ {window.i18n('serverUnbanUser')}
+
+ );
+ }
+ return null;
+};
+
+export const ServerBanMenuItem = (): JSX.Element | null => {
+ const convoId = useContext(ContextConversationId);
+ const isPublic = useIsPublic(convoId);
+ const weAreAdmin = useWeAreAdmin(convoId);
+
+ if (showServerBanUser(weAreAdmin, isPublic)) {
+ return (
+ - {
+ showServerBanUserByConvoId(convoId);
+ }}
+ >
+ {window.i18n('serverBanUser')}
+
+ );
+ }
+ return null;
+};
+
export const CopyMenuItem = (): JSX.Element | null => {
const convoId = useContext(ContextConversationId);
const isPublic = useIsPublic(convoId);
diff --git a/ts/interactions/conversationInteractions.ts b/ts/interactions/conversationInteractions.ts
index 6b4cd28983..e09782e5ac 100644
--- a/ts/interactions/conversationInteractions.ts
+++ b/ts/interactions/conversationInteractions.ts
@@ -23,6 +23,7 @@ import {
updateGroupNameModal,
updateInviteContactModal,
updateRemoveModeratorsModal,
+ updateServerBanOrUnbanUserModal
} from '../state/ducks/modalDialog';
import { Data, hasLinkPreviewPopupBeenDisplayed, lastAvatarUploadTimestamp } from '../data/data';
import { quoteMessage, resetConversationExternal } from '../state/ducks/conversations';
@@ -275,6 +276,18 @@ export function showUnbanUserByConvoId(conversationId: string, pubkey?: string)
);
}
+export function showServerBanUserByConvoId(conversationId: string, pubkey?: string) {
+ window.inboxStore?.dispatch(
+ updateServerBanOrUnbanUserModal({ banType: 'ban', conversationId, pubkey })
+ );
+}
+
+export function showServerUnbanUserByConvoId(conversationId: string, pubkey?: string) {
+ window.inboxStore?.dispatch(
+ updateServerBanOrUnbanUserModal({ banType: 'unban', conversationId, pubkey })
+ );
+}
+
export async function markAllReadByConvoId(conversationId: string) {
const conversation = getConversationController().get(conversationId);
perfStart(`markAllReadByConvoId-${conversationId}`);
diff --git a/ts/interactions/messageInteractions.ts b/ts/interactions/messageInteractions.ts
index 76cfccec90..ecc121351c 100644
--- a/ts/interactions/messageInteractions.ts
+++ b/ts/interactions/messageInteractions.ts
@@ -12,7 +12,11 @@ import { getConversationController } from '../session/conversations';
import { PubKey } from '../session/types';
import { ToastUtils } from '../session/utils';
-import { updateBanOrUnbanUserModal, updateConfirmModal } from '../state/ducks/modalDialog';
+import {
+ updateBanOrUnbanUserModal,
+ updateConfirmModal,
+ updateServerBanOrUnbanUserModal
+} from '../state/ducks/modalDialog';
export function banUser(userToBan: string, conversationId: string) {
let pubKeyToBan: PubKey;
@@ -59,6 +63,47 @@ export function unbanUser(userToUnBan: string, conversationId: string) {
);
}
+export function serverBanUser(userToBan: string, conversationId: string) {
+ let pubKeyToBan: PubKey;
+ try {
+ pubKeyToBan = PubKey.cast(userToBan);
+ } catch (e) {
+ window?.log?.warn(e);
+ ToastUtils.pushUserBanFailure();
+ return;
+ }
+ if (!isOpenGroupV2(conversationId)) {
+ window.log.warn(`Conversation ${conversationId} is not an open group`);
+ ToastUtils.pushUserBanFailure();
+
+ return;
+ }
+
+ window.inboxStore?.dispatch(
+ updateServerBanOrUnbanUserModal({ banType: 'ban', conversationId, pubkey: pubKeyToBan.key })
+ );
+}
+
+export function serverUnbanUser(userToUnBan: string, conversationId: string) {
+ let pubKeyToUnban: PubKey;
+ try {
+ pubKeyToUnban = PubKey.cast(userToUnBan);
+ } catch (e) {
+ window?.log?.warn(e);
+ ToastUtils.pushUserBanFailure();
+ return;
+ }
+ if (!isOpenGroupV2(conversationId)) {
+ window.log.warn(`Conversation ${conversationId} is not an open group`);
+ ToastUtils.pushUserUnbanFailure();
+
+ return;
+ }
+ window.inboxStore?.dispatch(
+ updateServerBanOrUnbanUserModal({ banType: 'unban', conversationId, pubkey: pubKeyToUnban.key })
+ );
+}
+
export function copyBodyToClipboard(body?: string | null) {
window.clipboard.writeText(body);
diff --git a/ts/session/apis/open_group_api/sogsv3/sogsV3BanUnban.ts b/ts/session/apis/open_group_api/sogsv3/sogsV3BanUnban.ts
index 680d583dae..bcdb3b40ce 100644
--- a/ts/session/apis/open_group_api/sogsv3/sogsV3BanUnban.ts
+++ b/ts/session/apis/open_group_api/sogsv3/sogsV3BanUnban.ts
@@ -15,6 +15,7 @@ export const sogsV3BanUser = async (
sessionId: userToBan.key,
roomId: roomInfos.roomId,
type: 'ban',
+ isGlobal: false
},
},
];
@@ -24,6 +25,10 @@ export const sogsV3BanUser = async (
type: 'deleteAllPosts',
deleteAllPosts: { sessionId: userToBan.key, roomId: roomInfos.roomId },
});
+ sequence.push({
+ type: 'deleteAllReactions',
+ deleteAllReactions: { sessionId: userToBan.key, roomId: roomInfos.roomId },
+ });
}
const batchSendResponse = await sogsBatchSend(
@@ -51,10 +56,73 @@ export const sogsV3UnbanUser = async (
sessionId: userToBan.key,
roomId: roomInfos.roomId,
type: 'unban',
+ isGlobal: false
},
},
],
'batch'
);
return batchFirstSubIsSuccess(batchSendResponse);
+}
+
+export const sogsV3ServerBanUser = async (
+ userToBan: PubKey,
+ roomInfos: OpenGroupRequestCommonType,
+ deleteAllMessages: boolean,
+): Promise => {
+ const sequence: Array = [
+ {
+ type: 'banUnbanUser',
+ banUnbanUser: {
+ sessionId: userToBan.key,
+ roomId: roomInfos.roomId,
+ type: 'ban',
+ isGlobal: true
+ },
+ },
+ ];
+
+ if (deleteAllMessages) {
+ sequence.push({
+ type: 'deleteAllUserPosts',
+ deleteAllUserPosts: { sessionId: userToBan.key },
+ });
+ sequence.push({
+ type: 'deleteAllUserReactions',
+ deleteAllUserReactions: { sessionId: userToBan.key },
+ });
+ }
+
+ const batchSendResponse = await sogsBatchSend(
+ roomInfos.serverUrl,
+ new Set([roomInfos.roomId]),
+ new AbortController().signal,
+ sequence,
+ 'sequence'
+ );
+ return batchFirstSubIsSuccess(batchSendResponse);
};
+
+export const sogsV3ServerUnbanUser = async (
+ userToBan: PubKey,
+ roomInfos: OpenGroupRequestCommonType
+): Promise => {
+ const batchSendResponse = await sogsBatchSend(
+ roomInfos.serverUrl,
+ new Set([roomInfos.roomId]),
+ new AbortController().signal,
+ [
+ {
+ type: 'banUnbanUser',
+ banUnbanUser: {
+ sessionId: userToBan.key,
+ roomId: roomInfos.roomId,
+ type: 'unban',
+ isGlobal: true
+ },
+ },
+ ],
+ 'batch'
+ );
+ return batchFirstSubIsSuccess(batchSendResponse);
+}
diff --git a/ts/session/apis/open_group_api/sogsv3/sogsV3BatchPoll.ts b/ts/session/apis/open_group_api/sogsv3/sogsV3BatchPoll.ts
index 7461b16f4e..f413362bf0 100644
--- a/ts/session/apis/open_group_api/sogsv3/sogsV3BatchPoll.ts
+++ b/ts/session/apis/open_group_api/sogsv3/sogsV3BatchPoll.ts
@@ -179,6 +179,7 @@ export type SubRequestBanUnbanUserType = {
type: 'ban' | 'unban';
sessionId: string; // can be blinded id or not
roomId: string;
+ isGlobal: boolean;
};
};
@@ -190,6 +191,28 @@ export type SubRequestDeleteAllUserPostsType = {
};
};
+export type SubRequestDeleteAllUserServerPostsType = {
+ type: 'deleteAllUserPosts';
+ deleteAllUserPosts: {
+ sessionId: string; // can be blinded id or not
+ };
+};
+
+export type SubRequestDeleteAllUserReactionsType = {
+ type: 'deleteAllReactions';
+ deleteAllReactions: {
+ sessionId: string; // can be blinded id or not
+ roomId: string;
+ };
+};
+
+export type SubRequestDeleteAllUserServerReactionsType = {
+ type: 'deleteAllUserReactions';
+ deleteAllUserReactions: {
+ sessionId: string; // can be blinded id or not
+ };
+};
+
export type SubRequestUpdateRoomType = {
type: 'updateRoom';
updateRoom: {
@@ -218,6 +241,9 @@ export type OpenGroupBatchRow =
| SubRequestAddRemoveModeratorType
| SubRequestBanUnbanUserType
| SubRequestDeleteAllUserPostsType
+ | SubRequestDeleteAllUserReactionsType
+ | SubRequestDeleteAllUserServerPostsType
+ | SubRequestDeleteAllUserServerReactionsType
| SubRequestUpdateRoomType
| SubRequestDeleteReactionType;
@@ -225,6 +251,7 @@ export type OpenGroupBatchRow =
*
* @param options Array of subrequest options to be made.
*/
+// tslint:disable-next-line: cyclomatic-complexity
const makeBatchRequestPayload = (
options: OpenGroupBatchRow
): BatchSubRequest | Array | null => {
@@ -295,6 +322,20 @@ const makeBatchRequestPayload = (
}));
case 'banUnbanUser':
const isBan = Boolean(options.banUnbanUser.type === 'ban');
+ const isGlobal = options.banUnbanUser.isGlobal;
+ window?.log?.info(`BAN: ${options.banUnbanUser.sessionId}, global: ${options.banUnbanUser.isGlobal}`);
+ if (isGlobal) {
+ // Issue server-wide (un)ban.
+ return {
+ method: 'POST',
+ path: `/user/${options.banUnbanUser.sessionId}/${isBan ? 'ban' : 'unban'}`,
+ json: {
+ global: true,
+ // timeout: null, // for now we do not support the timeout argument
+ },
+ }
+ }
+ // Issue room-wide (un)ban.
return {
method: 'POST',
path: `/user/${options.banUnbanUser.sessionId}/${isBan ? 'ban' : 'unban'}`,
@@ -311,7 +352,22 @@ const makeBatchRequestPayload = (
method: 'DELETE',
path: `/room/${options.deleteAllPosts.roomId}/all/${options.deleteAllPosts.sessionId}`,
};
- case 'updateRoom':
+ case 'deleteAllUserPosts':
+ return {
+ method: 'DELETE',
+ path: `/rooms/all/${options.deleteAllUserPosts.sessionId}`,
+ };
+ case 'deleteAllReactions':
+ return {
+ method: 'DELETE',
+ path: `/room/${options.deleteAllReactions.roomId}/all/reactions/${options.deleteAllReactions.sessionId}`,
+ };
+ case 'deleteAllUserReactions':
+ return {
+ method: 'DELETE',
+ path: `/rooms/all/reactions/${options.deleteAllUserReactions.sessionId}`,
+ };
+ case 'updateRoom':
return {
method: 'PUT',
path: `/room/${options.updateRoom.roomId}`,
diff --git a/ts/session/utils/Toast.tsx b/ts/session/utils/Toast.tsx
index 4170dc64d2..4c3918fc83 100644
--- a/ts/session/utils/Toast.tsx
+++ b/ts/session/utils/Toast.tsx
@@ -117,6 +117,10 @@ export function pushUserBanFailure() {
pushToastError('userBanFailed', window.i18n('userBanFailed'));
}
+export function pushGlobalUserBanFailure() {
+ pushToastError('globalUserBanFailed', window.i18n('globalUserBanFailed'));
+}
+
export function pushUserUnbanSuccess() {
pushToastSuccess('userUnbanned', window.i18n('userUnbanned'));
}
@@ -125,6 +129,10 @@ export function pushUserUnbanFailure() {
pushToastError('userUnbanFailed', window.i18n('userUnbanFailed'));
}
+export function pushGlobalUserUnbanFailure() {
+ pushToastError('globalUserUnbanFailed', window.i18n('globalUserUnbanFailed'));
+}
+
export function pushMessageDeleteForbidden() {
pushToastError('messageDeletionForbidden', window.i18n('messageDeletionForbidden'));
}
diff --git a/ts/state/ducks/modalDialog.tsx b/ts/state/ducks/modalDialog.tsx
index ea8428b703..4a9213b9b5 100644
--- a/ts/state/ducks/modalDialog.tsx
+++ b/ts/state/ducks/modalDialog.tsx
@@ -10,6 +10,11 @@ export type BanOrUnbanUserModalState = {
banType: BanType;
pubkey?: string;
} | null;
+export type ServerBanOrUnbanUserModalState = {
+ conversationId: string;
+ banType: BanType;
+ pubkey?: string;
+} | null;
export type AddModeratorsModalState = InviteContactModalState;
export type RemoveModeratorsModalState = InviteContactModalState;
export type UpdateGroupMembersModalState = InviteContactModalState;
@@ -38,6 +43,7 @@ export type ModalState = {
confirmModal: ConfirmModalState;
inviteContactModal: InviteContactModalState;
banOrUnbanUserModal: BanOrUnbanUserModalState;
+ serverBanOrUnbanUserModal: ServerBanOrUnbanUserModalState;
removeModeratorsModal: RemoveModeratorsModalState;
addModeratorsModal: AddModeratorsModalState;
groupNameModal: UpdateGroupNameModalState;
@@ -60,6 +66,7 @@ export const initialModalState: ModalState = {
addModeratorsModal: null,
removeModeratorsModal: null,
banOrUnbanUserModal: null,
+ serverBanOrUnbanUserModal: null,
groupNameModal: null,
groupMembersModal: null,
userDetailsModal: null,
@@ -87,6 +94,9 @@ const ModalSlice = createSlice({
updateBanOrUnbanUserModal(state, action: PayloadAction) {
return { ...state, banOrUnbanUserModal: action.payload };
},
+ updateServerBanOrUnbanUserModal(state, action: PayloadAction) {
+ return { ...state, serverBanOrUnbanUserModal: action.payload };
+ },
updateAddModeratorsModal(state, action: PayloadAction) {
return { ...state, addModeratorsModal: action.payload };
},
@@ -149,6 +159,7 @@ export const {
sessionPassword,
updateDeleteAccountModal,
updateBanOrUnbanUserModal,
+ updateServerBanOrUnbanUserModal,
updateReactListModal,
updateReactClearAllModal,
} = actions;
diff --git a/ts/state/selectors/modal.ts b/ts/state/selectors/modal.ts
index f959bc243c..55d3e159a6 100644
--- a/ts/state/selectors/modal.ts
+++ b/ts/state/selectors/modal.ts
@@ -15,6 +15,7 @@ import {
ReactModalsState,
RecoveryPhraseModalState,
RemoveModeratorsModalState,
+ ServerBanOrUnbanUserModalState,
SessionPasswordModalState,
UpdateGroupMembersModalState,
UpdateGroupNameModalState,
@@ -50,6 +51,11 @@ export const getBanOrUnbanUserModalState = createSelector(
(state: ModalState): BanOrUnbanUserModalState => state.banOrUnbanUserModal
);
+export const getServerBanOrUnbanUserModalState = createSelector(
+ getModal,
+ (state: ModalState): ServerBanOrUnbanUserModalState => state.serverBanOrUnbanUserModal
+);
+
export const getUpdateGroupNameModal = createSelector(
getModal,
(state: ModalState): UpdateGroupNameModalState => state.groupNameModal
diff --git a/ts/types/LocalizerKeys.ts b/ts/types/LocalizerKeys.ts
index 2aa7aff76b..a94576707d 100644
--- a/ts/types/LocalizerKeys.ts
+++ b/ts/types/LocalizerKeys.ts
@@ -2,6 +2,7 @@ export type LocalizerKeys =
| 'removePassword'
| 'classicDarkThemeTitle'
| 'userUnbanFailed'
+ | 'globalUserUnbanFailed'
| 'changePassword'
| 'saved'
| 'startedACall'
@@ -39,6 +40,7 @@ export type LocalizerKeys =
| 'video'
| 'readReceiptSettingDescription'
| 'userBanFailed'
+ | 'globalUserBanFailed'
| 'autoUpdateLaterButtonLabel'
| 'maximumAttachments'
| 'deviceOnly'
@@ -147,12 +149,14 @@ export type LocalizerKeys =
| 'reactionNotification'
| 'leaveGroupConfirmation'
| 'banUserAndDeleteAll'
+ | 'serverBanUserAndDeleteAll'
| 'joinOpenGroupAfterInvitationConfirmationDesc'
| 'invalidNumberError'
| 'contextMenuNoSuggestions'
| 'callMediaPermissionsDialogTitle'
| 'recoveryPhraseRevealButtonText'
| 'banUser'
+ | 'serverBanUser'
| 'primaryColorBlue'
| 'sendMessage'
| 'recoveryPhraseRevealMessage'
@@ -491,6 +495,7 @@ export type LocalizerKeys =
| 'you'
| 'pruneSettingTitle'
| 'unbanUser'
+ | 'serverUnbanUser'
| 'notificationForConvo_mentions_only'
| 'trustThisContactDialogDescription'
| 'unknownCountry'