Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
123 commits
Select commit Hold shift + click to select a range
d85a0bb
Implement server-wide open group user bans and unbans.
ianmacd Jan 17, 2023
3f8327c
Fix failed user unban being reported as success.
ianmacd Jan 27, 2023
93c6b06
Toast a hint of the likely reason for a failed global (un)ban.
ianmacd Jan 27, 2023
db37d96
Merge branch 'clearnet' into server-wide-bans
Nov 29, 2023
82d82d8
feat: crudely add upload permission menu items
Feb 7, 2024
1c2f0e6
fix: empty global ban fail toast message
Feb 28, 2024
35818e3
Merge branch 'server-wide-bans' into dev-gravel
Apr 22, 2024
60a7a50
Merge branch 'sogs-user-upload-permission' into dev-gravel
Apr 22, 2024
f26e7bc
Merge tag 'v1.12.4' into dev-gravel
Jul 3, 2024
9cb7e77
Merge tag 'v1.12.5' into dev-gravel
Jul 23, 2024
3752204
Merge tag 'v1.13.0' into dev-gravel
Aug 12, 2024
56a33d3
Merge tag 'v1.13.2' into dev-gravel
Aug 29, 2024
3b1b805
Merge tag 'tags/v1.14.2' into dev-gravel
Nov 7, 2024
68f655d
fix: build appimage by default
Nov 7, 2024
06e2ed9
feat: add open group permissions modal
Nov 8, 2024
7e71d36
Merge branch 'manage-room-perms' into dev-gravel
Nov 8, 2024
f31b515
Merge tag 'v1.14.5' into dev-gravel
Dec 26, 2024
6cefb57
Merge tag 'v1.15.0' into dev-gravel
Mar 13, 2025
b351f51
ci: add first workflow
Mar 19, 2025
b5f1529
ci: try fix ubuntu image reference
Mar 19, 2025
58c6908
ci: try fix broad run condition
Mar 19, 2025
5bb78ed
ci: lint workflow
Mar 19, 2025
50b1f32
ci: try fix python install
Mar 20, 2025
3f48347
ci: revert docker image reference
Mar 20, 2025
bf7e43f
ci: use apt-get for stability
Mar 20, 2025
5fc758e
ci: install python using ppa
Mar 20, 2025
1fafc3d
ci: use apt-get for stability
Mar 20, 2025
be081b8
ci: fix frozen upgrade without -y
Mar 20, 2025
ed1fd34
ci: fix missing add-apt-repository
Mar 20, 2025
17fc6cb
ci: fix hang on interactive prompt
Mar 20, 2025
1abee6e
ci: fix syntax error
Mar 20, 2025
ad4d1e3
ci: fix python invocation
Mar 20, 2025
2a5b059
ci: fix missing pip
Mar 20, 2025
b4f9de7
ci: try lower runtime
Mar 20, 2025
b0aab94
ci: use prepared python image
Mar 20, 2025
fafbf32
ci: use docker node image
Mar 20, 2025
fccf335
ci: fix overwriting yarn
Mar 20, 2025
1b16e38
ci: fix wrong node version
Mar 20, 2025
7a10311
ci: fix missing git
Mar 20, 2025
4930f09
ci: fix missing cmake
Mar 20, 2025
673e6ed
ci: fix unavailable package
Mar 20, 2025
01fa5b3
ci: fix noninteractive install
Mar 20, 2025
940bb0c
ci: fix apt-get timing out
Mar 20, 2025
a40e070
ci: fix old cmake version
Mar 20, 2025
5b27e1b
ci: fix old cmake version
Mar 20, 2025
846f2e4
ci: fix non-alpine commands
Mar 20, 2025
b586e47
ci: fix non-alpine flags
Mar 20, 2025
7d95d6e
ci: fix missing git
Mar 20, 2025
320d991
ci: fix missing python
Mar 20, 2025
6891df9
ci: fix apt warning [skip ci]
Mar 20, 2025
fd1bf23
ci: fix unavailable node version
Mar 20, 2025
3ea7bf0
ci: fix missing build-essentials package
Mar 20, 2025
06ad545
ci: fix missing source command
Mar 20, 2025
9ae4781
ci: fix nvm exit code 3
Mar 20, 2025
2f52175
ci: fix unreadable nvm error
Mar 20, 2025
9ea4ea9
ci: fix fnm cannot infer shell
Mar 21, 2025
3461789
ci: fix fnm cannot infer shell
Mar 21, 2025
ff0944e
ci: fix fnm cannot infer shell
Mar 21, 2025
c07887f
ci: fix fnm not found
Mar 21, 2025
18cc541
ci: fix fnm not found
Mar 21, 2025
6124644
ci: limit build branches
Mar 21, 2025
9ae6f6c
ci: fix node version not installed
Mar 21, 2025
22b21c1
ci: fix syntax error
Mar 21, 2025
e015d59
ci: fix syntax error
Mar 21, 2025
7bbf5da
ci: build actual release AppImage
Mar 21, 2025
1249e41
ci: fix trigger release for non-tag
Mar 21, 2025
db23779
ci: add [nolinux] keyword
Mar 21, 2025
6b4723e
ci: customize artifact name [skip ci]
Mar 21, 2025
dd0ca06
ci: manage version in package.json
Mar 21, 2025
67e49da
chore: bump version to 1.15.0-0.1
Mar 21, 2025
d455d57
ci: test releasing [nolinux][testreleasing]
Mar 21, 2025
9b36571
ci: test releasing [nolinux][testreleasing]
Mar 21, 2025
6788a2c
ci: test releasing [nolinux][testreleasing]
Mar 21, 2025
593aad6
ci: test releasing [nolinux][testreleasing]
Mar 21, 2025
a121c7b
ci: fix wrong artifact name
Mar 21, 2025
c6807f7
ci: fix wrong artifact name
Mar 21, 2025
a420577
chore: bump version in package.json [skip ci]
Mar 21, 2025
0758e85
Merge tag 'v1.15.2' into dev-gravel-2
Apr 27, 2025
94dd767
fix: increase message length to accommodate PGP
Apr 27, 2025
d5e9814
chore: bump to 1.15.2-0.5
Apr 27, 2025
1a17838
fix: increase message length to accommodate PGP
Apr 27, 2025
198266c
chore: bump to 1.15.2-0.6
Apr 27, 2025
a6ef8d2
Merge tag 'v1.16.1' into dev-gravel
May 24, 2025
1353cc1
chore: bump to 1.16.1-0.1
May 24, 2025
103691e
Merge tag 'v1.16.2' into dev-gravel
Jun 8, 2025
79d9e8e
chore: bump to v1.16.2-0.1
Jun 8, 2025
820bceb
Merge tag 'v1.16.3' into dev-gravel
Jul 6, 2025
455fc72
chore: bump to v1.16.3-0.1
Jul 6, 2025
ee49596
ci: do not run on push to dev
Jul 6, 2025
f279577
Merge tag 'v1.16.5' into dev-gravel
Jul 22, 2025
4fb1ecd
chore: bump to v1.16.5-0.1
Jul 22, 2025
6afd1bc
Merge tag 'v1.16.6' into dev-gravel
Aug 14, 2025
3067c23
chore: bump to v1.16.6-0.1
Aug 14, 2025
7f52653
ci: fix updated yarn command
Aug 14, 2025
b69e042
chore: bump to v1.16.6-0.2
Aug 14, 2025
af3c7dc
Merge tag 'v1.16.8' into dev-gravel
Sep 13, 2025
6a0765a
chore: bump to v1.16.8-0.1
Sep 13, 2025
0cdb6ac
Merge tag 'v1.16.10' into dev-gravel
Sep 20, 2025
06d7685
chore: bump to v1.16.10-0.1
Sep 20, 2025
3f89e78
Merge tag 'v1.17.0' into dev-gravel
Oct 3, 2025
33c1037
chore: bump to v1.17.0-0.1
Oct 3, 2025
7bb2fad
fix: unbreak after upstream refactors
Oct 3, 2025
86ad920
chore: bump to v1.17.0-0.2
Oct 3, 2025
c718c82
fix: unbreak after upstream refactors
Oct 5, 2025
686aa2f
chore: bump to v1.17.0-0.3
Oct 5, 2025
fd2285f
fix: fix typo
Oct 5, 2025
e45f10b
chore: bump to v1.17.0-0.4
Oct 5, 2025
8cdaaeb
Merge tag 'v1.17.1' into dev-gravel
Oct 5, 2025
9a35309
chore: bump to v1.17.1-0.1
Oct 5, 2025
5eef91f
Merge tag 'v1.17.2' into dev-gravel
Nov 3, 2025
0e1013d
chore: bump to v1.17.2-0.1
Nov 3, 2025
1a061f1
Merge tag 'v1.17.4' into dev-gravel
Dec 11, 2025
2b55441
chore: bump to v1.17.4-0.1
Dec 11, 2025
0b968b9
chore: bump to v1.17.4-0.2
Dec 11, 2025
9c43929
Merge tag 'v1.17.5' into dev-gravel
Dec 25, 2025
86f1305
chore: bump to v1.17.5-0.1
Dec 25, 2025
a308d49
Merge remote-tracking branch 'upstream/dev' into dev-gravel
Bilb Feb 2, 2026
93caf27
fix: modernize global server ban/unban
Bilb Feb 3, 2026
c5feb5e
chore: manual string update
Bilb Feb 3, 2026
f40b931
fix: use dev strings for now as they are not on crowdin
Bilb Feb 10, 2026
56f41d4
fix: qa issues fixes
Bilb Feb 10, 2026
b8c1017
Merge remote-tracking branch 'upstream/dev' into dev-gravel
Bilb Feb 10, 2026
280452a
chore: lint for now
Bilb Feb 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,14 @@ import { WithMessageId } from '../../../../session/types/with';
import { DeleteItem } from '../../../menu/items/DeleteMessage/DeleteMessageMenuItem';
import { RetryItem } from '../../../menu/items/RetrySend/RetrySendMenuItem';
import { useBanUserCb } from '../../../menuAndSettingsHooks/useBanUser';
import { useUnbanUserCb } from '../../../menuAndSettingsHooks/useUnbanUser';
import { tr } from '../../../../localization/localeTools';
import { sectionActions } from '../../../../state/ducks/section';
import { useRemoveSenderFromCommunityAdmin } from '../../../menuAndSettingsHooks/useRemoveSenderFromCommunityAdmin';
import { useAddSenderAsCommunityAdmin } from '../../../menuAndSettingsHooks/useAddSenderAsCommunityAdmin';
import {
useAddUserPermissions,
useClearUserPermissions,
} from '../../../menuAndSettingsHooks/useAddUserPermissions';
import { showContextMenu } from '../../../../util/contextMenu';
import { clampNumber } from '../../../../util/maths';
import { PopoverTriggerPosition } from '../../../SessionTooltip';
Expand Down Expand Up @@ -128,8 +131,27 @@ const CommunityAdminActionItems = ({ messageId }: WithMessageId) => {
const sender = useMessageSender(messageId);
const isSenderAdmin = useMessageSenderIsAdmin(messageId);

const banUserCb = useBanUserCb(convoId, sender);
const unbanUserCb = useUnbanUserCb(convoId, sender);
const sharedBanUnbanProps = {
conversationId: convoId,
pubkey: sender,
};

const banUserCb = useBanUserCb({
banType: 'ban',
...sharedBanUnbanProps,
});
const unbanUserCb = useBanUserCb({
banType: 'unban',
...sharedBanUnbanProps,
});
const serverBanUser = useBanUserCb({
banType: 'server-ban',
...sharedBanUnbanProps,
});
const serverUnbanUser = useBanUserCb({
banType: 'server-unban',
...sharedBanUnbanProps,
});

const removeSenderFromCommunityAdminCb = useRemoveSenderFromCommunityAdmin({
conversationId: convoId,
Expand All @@ -141,6 +163,14 @@ const CommunityAdminActionItems = ({ messageId }: WithMessageId) => {
senderId: sender,
});

const addUploadPermissionCb = useAddUserPermissions(sender, convoId, ['upload']);
const clearUploadPermissionCb = useClearUserPermissions(sender, convoId, ['upload']);

// Fixed to `true` as not currently exposed by the backend/tracked in session-desktop
const isRoomUploadRestricted = true;
const canSenderUpload = true;
const canSenderNotUpload = true;

// Note: add/removeSenderFromCommunityAdminCb can be null if we are a moderator only, see below
if (!convoId || !sender || !banUserCb || !unbanUserCb) {
return null;
Expand Down Expand Up @@ -182,6 +212,29 @@ const CommunityAdminActionItems = ({ messageId }: WithMessageId) => {
{tr('adminPromoteToAdmin')}
</ItemWithDataTestId>
) : null}

{serverBanUser ? (
<ItemWithDataTestId onClick={serverBanUser}>{tr('serverBanUserDev')}</ItemWithDataTestId>
) : null}
{serverUnbanUser ? (
<ItemWithDataTestId onClick={serverUnbanUser}>
{tr('serverUnbanUserDev')}
</ItemWithDataTestId>
) : null}
{!isSenderAdmin && isRoomUploadRestricted && (
<>
{canSenderUpload && addUploadPermissionCb ? (
<ItemWithDataTestId onClick={addUploadPermissionCb}>
{tr('addUploadPermissionDev')}
</ItemWithDataTestId>
) : null}
{canSenderNotUpload && clearUploadPermissionCb ? (
<ItemWithDataTestId onClick={clearUploadPermissionCb}>
{tr('clearUploadPermissionDev')}
</ItemWithDataTestId>
) : null}
</>
)}
</>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,31 +103,32 @@ const DebugMessageInfo = ({ messageId }: { messageId: string }) => {
if (!isDevProd()) {
return null;
}
// Note: the strings here are hardcoded because we do not share them with other platforms through crowdin
return (
<>
{convoId ? <LabelWithInfo label={`Conversation ID:`} info={convoId} /> : null}
{messageHash ? <LabelWithInfo label={`Message Hash:`} info={messageHash} /> : null}
{serverId ? <LabelWithInfo label={`Server ID:`} info={`${serverId}`} /> : null}
{timestamp ? <LabelWithInfo label={`Timestamp:`} info={String(timestamp)} /> : null}
{convoId ? <LabelWithInfo label={tr('conversationIdDev')} info={convoId} /> : null}
{messageHash ? <LabelWithInfo label={tr('messageHashDev')} info={messageHash} /> : null}
{serverId ? <LabelWithInfo label={tr('serverIdDev')} info={`${serverId}`} /> : null}
{timestamp ? <LabelWithInfo label={tr('timestampDev')} info={String(timestamp)} /> : null}
{serverTimestamp ? (
<LabelWithInfo label={`Server Timestamp:`} info={String(serverTimestamp)} />
<LabelWithInfo label={tr('serverTimestampDev')} info={String(serverTimestamp)} />
) : null}
{expirationType ? (
<LabelWithInfo label={tr('expirationTypeDev')} info={expirationType} />
) : null}
{expirationType ? <LabelWithInfo label={`Expiration Type:`} info={expirationType} /> : null}
{expirationDurationMs ? (
<LabelWithInfo
label={`Expiration Duration:`}
label={tr('expirationDurationDev')}
info={formatTimeDurationMs(Math.floor(expirationDurationMs))}
/>
) : null}
{expirationTimestamp ? (
<LabelWithInfo
label={`Disappears:`}
label={tr('disappearsDev')}
info={formatTimeDistanceToNow(Math.floor(expirationTimestamp / 1000))}
/>
) : null}
{message ? (
<LabelWithInfo label={'Characters:'} info={formatNumber(message.length ?? 0)} />
<LabelWithInfo label={tr('charactersDev')} info={formatNumber(message.length ?? 0)} />
) : null}
</>
);
Expand Down
125 changes: 100 additions & 25 deletions ts/components/dialog/BanOrUnbanUserDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,27 @@ import {
import { ConvoHub } from '../../session/conversations/ConversationController';
import { PubKey } from '../../session/types';
import { ToastUtils } from '../../session/utils';
import { BanType, updateBanOrUnbanUserModal } from '../../state/ducks/modalDialog';
import {
BanType,
isBan,
isServerBanUnban,
updateBanOrUnbanUserModal,
} from '../../state/ducks/modalDialog';
import { SessionButton, SessionButtonColor, SessionButtonType } from '../basic/SessionButton';
import { SessionSpinner } from '../loading';
import {
ModalBasicHeader,
ModalActionsContainer,
SessionWrapperModal,
WrapperModalWidth,
} from '../SessionWrapperModal';
import { tr } from '../../localization/localeTools';
import { SimpleSessionInput } from '../inputs/SessionInput';
import { ModalDescription } from './shared/ModalDescriptionContainer';
import { ModalFlexContainer } from './shared/ModalFlexContainer';
import { SessionToggle } from '../basic/SessionToggle';
import { getFeatureFlag } from '../../state/ducks/types/releasedFeaturesReduxTypes';
import { Flex } from '../basic/Flex';

async function banOrUnBanUserCall(
convo: ConversationModel,
Expand All @@ -41,21 +50,47 @@ async function banOrUnBanUserCall(
try {
// this is a v2 opengroup
const roomInfos = convo.toOpenGroupV2();
const isChangeApplied =
banType === 'ban'
? await sogsV3BanUser(pubkey, roomInfos, deleteAll)
: await sogsV3UnbanUser(pubkey, roomInfos);
const isChangeApplied = isBan(banType)
? await sogsV3BanUser({
userToBan: pubkey,
roomInfos,
deleteAllMessages: deleteAll,
banType,
})
: await sogsV3UnbanUser({
userToUnban: pubkey,
roomInfos,
banType,
});

if (!isChangeApplied) {
window?.log?.warn(`failed to ${banType} user: ${isChangeApplied}`);

// eslint-disable-next-line no-unused-expressions
banType === 'ban' ? ToastUtils.pushUserBanFailure() : ToastUtils.pushUserUnbanSuccess();
switch (banType) {
case 'ban':
ToastUtils.pushUserBanFailure();
break;
case 'unban':
ToastUtils.pushUserUnbanFailure();
break;
case 'server-ban':
ToastUtils.pushGlobalUserBanFailure();
break;
case 'server-unban':
ToastUtils.pushGlobalUserUnbanFailure();
break;
default:
throw new Error('Unknown banType');
}
return false;
}
window?.log?.info(`${pubkey.key} user ${banType}ned successfully...`);
// eslint-disable-next-line no-unused-expressions
banType === 'ban' ? ToastUtils.pushUserBanSuccess() : ToastUtils.pushUserUnbanSuccess();

if (isBan(banType)) {
ToastUtils.pushUserBanSuccess();
} else {
ToastUtils.pushUserUnbanSuccess();
}
return true;
} catch (e) {
window?.log?.error(`Got error while ${banType}ning user:`, e);
Expand All @@ -70,32 +105,36 @@ export const BanOrUnBanUserDialog = (props: {
pubkey?: string;
}) => {
const { conversationId, banType, pubkey } = props;
const isBan = banType === 'ban';
const dispatch = getAppDispatch();
const convo = ConvoHub.use().get(conversationId);
const inputRef = useRef(null);

useFocusMount(inputRef, true);
const [inputBoxValue, setInputBoxValue] = useState('');
const [inProgress, setInProgress] = useState(false);
const [localBanType, setLocalBanType] = useState(banType);

const displayName = useConversationUsernameWithFallback(true, pubkey);
const isServerWide = isServerBanUnban(localBanType);
const isBanAction = isBan(localBanType);

const displayName = useConversationUsernameWithFallback(true, pubkey);
const hasPubkeyOnLoad = pubkey?.length;

const inputTextToDisplay =
!!pubkey && displayName ? `${displayName} ${PubKey.shorten(pubkey)}` : 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)
* Ban or Unban a user from a community
* @param deleteAll Delete all messages for that user in the community (only works with ban)
*/
const banOrUnBanUser = async (deleteAll: boolean) => {
const castedPubkey = pubkey?.length ? pubkey : inputBoxValue;

window?.log?.info(`asked to ${banType} user: ${castedPubkey}, banAndDeleteAll:${deleteAll}`);
window?.log?.info(
`asked to ${localBanType} user: ${castedPubkey}, banAndDeleteAll:${deleteAll}`
);
setInProgress(true);
const isBanned = await banOrUnBanUserCall(convo, castedPubkey, banType, deleteAll);
const isBanned = await banOrUnBanUserCall(convo, castedPubkey, localBanType, deleteAll);
if (isBanned) {
// clear input box
setInputBoxValue('');
Expand All @@ -107,7 +146,9 @@ export const BanOrUnBanUserDialog = (props: {
setInProgress(false);
};

const title = isBan ? tr('banUser') : tr('banUnbanUser');
const serverHost = new window.URL(convo.toOpenGroupV2().serverUrl).host;
const serverWideSuffix = isServerWide ? ` @ ${serverHost}` : '';
const title = `${isBanAction ? tr('banUser') : tr('banUnbanUser')}${serverWideSuffix}`;

/**
* Starts procedure for banning/unbanning user and all their messages using dialog
Expand All @@ -119,25 +160,41 @@ export const BanOrUnBanUserDialog = (props: {
const onClose = () => {
dispatch(updateBanOrUnbanUserModal(null));
};

useKey('Escape', onClose);

const buttonText = isBan ? tr('banUser') : tr('banUnbanUser');
let buttonText = '';

if (isServerWide) {
if (isBanAction) {
buttonText = tr('serverBanUserDev');
} else {
buttonText = tr('serverUnbanUserDev');
}
} else if (isBanAction) {
buttonText = tr('banUser');
} else {
buttonText = tr('banUnbanUser');
}

return (
<SessionWrapperModal
modalId="banOrUnbanUserModal"
$contentMinWidth={isServerWide ? WrapperModalWidth.wide : undefined}
$contentMaxWidth={isServerWide ? WrapperModalWidth.wide : undefined}
headerChildren={<ModalBasicHeader title={title} />}
onClose={onClose}
buttonChildren={
<ModalActionsContainer buttonType={SessionButtonType.Simple}>
<ModalActionsContainer
buttonType={SessionButtonType.Simple}
style={{ maxWidth: isServerWide ? '600px' : undefined }}
>
<SessionButton
buttonType={SessionButtonType.Simple}
onClick={banOrUnBanUser}
text={buttonText}
disabled={inProgress}
buttonColor={isBan && !hasPubkeyOnLoad ? SessionButtonColor.Danger : undefined}
dataTestId={isBan ? 'ban-user-confirm-button' : 'unban-user-confirm-button'}
buttonColor={isBanAction && !hasPubkeyOnLoad ? SessionButtonColor.Danger : undefined}
dataTestId={isBanAction ? 'ban-user-confirm-button' : 'unban-user-confirm-button'}
/>
{/*
* Note: we can only ban-and-delete-all when we have a pubkey on load currently.
Expand All @@ -146,12 +203,12 @@ export const BanOrUnBanUserDialog = (props: {
* When hasPubkeyOnLoad is true, the dialog was shown from a right click on the messages list,
* so we do have the blindedId already in this case.
*/}
{isBan && hasPubkeyOnLoad ? (
{isBanAction && hasPubkeyOnLoad ? (
<SessionButton
buttonType={SessionButtonType.Simple}
buttonColor={SessionButtonColor.Danger}
onClick={startBanAndDeleteAllSequence}
text={tr('banDeleteAll')}
text={isServerWide ? tr('serverBanUserAndDeleteAllDev') : tr('banDeleteAll')}
disabled={inProgress}
dataTestId="ban-user-delete-all-confirm-button"
/>
Expand All @@ -169,7 +226,7 @@ export const BanOrUnBanUserDialog = (props: {
<ModalFlexContainer>
<ModalDescription
dataTestId="modal-description"
localizerProps={{ token: isBan ? 'banUserDescription' : 'banUnbanUserDescription' }}
localizerProps={{ token: isBanAction ? 'banUserDescription' : 'banUnbanUserDescription' }}
/>

<SimpleSessionInput
Expand All @@ -183,9 +240,27 @@ export const BanOrUnBanUserDialog = (props: {
providedError={''}
// don't do anything on enter as we don't know if the user wants to ban or ban-delete-all
onEnterPressed={() => {}}
inputDataTestId={isBan ? 'ban-user-input' : 'unban-user-input'}
inputDataTestId={isBanAction ? 'ban-user-input' : 'unban-user-input'}
allowEscapeKeyPassthrough={true}
/>
{getFeatureFlag('useDevCommunityActions') ? (
<Flex
$container={true}
$justifyContent="space-between"
$alignItems="center"
$flexGap="var(--margins-sm)"
>
<p>Server-wide:</p>
<SessionToggle
active={isServerWide}
onClick={() => {
const withoutServer = localBanType.replace('server-', '') as BanType;
const withServer = `server-${withoutServer}` as BanType;
setLocalBanType(isServerWide ? withoutServer : withServer);
}}
/>
</Flex>
) : null}
<SessionSpinner $loading={inProgress} />
</ModalFlexContainer>
</SessionWrapperModal>
Expand Down
Loading