From 892164f3a5cbe9e9bafa8711e9cae90bee2cfb13 Mon Sep 17 00:00:00 2001
From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com>
Date: Sat, 21 Mar 2026 18:35:38 +0100
Subject: [PATCH] refactor: use extracted PermissionsEditor in
ChannelSettingsModal
---
.../modals/ChannelSettingsModal.tsx | 724 +-----------------
1 file changed, 28 insertions(+), 696 deletions(-)
diff --git a/packages/web/src/components/modals/ChannelSettingsModal.tsx b/packages/web/src/components/modals/ChannelSettingsModal.tsx
index f810fb5d..3d5898f7 100644
--- a/packages/web/src/components/modals/ChannelSettingsModal.tsx
+++ b/packages/web/src/components/modals/ChannelSettingsModal.tsx
@@ -1,29 +1,15 @@
-import React, { useState, useEffect, useMemo, useCallback, useRef } from 'react';
+import React, { useState, useEffect, useCallback } from 'react';
import { Modal } from '../ui/Modal';
import { ConfirmDialog } from '../ui/ConfirmDialog';
import { useUIStore } from '../../stores/uiStore';
import { useSpaceStore, getApiForOrigin } from '../../stores/spaceStore';
-import { api } from '../../api/client';
import { PermissionBits, permissionsToString, stringToPermissions, hasPermissionBit } from '../../utils/permissions';
import { Toggle } from '../ui/Toggle';
-import type { Role, MemberWithUser } from '@backspace/shared';
-
-interface ChannelOverride {
- channelId: string;
- targetType: string;
- targetId: string;
- allow: string;
- deny: string;
-}
+import { PermissionsEditor } from '../ui/PermissionsEditor';
+import type { PermissionDef } from '../ui/OverrideEntry';
// ─── Permission Definitions for Channel Overrides ──────────────────────────────
-interface PermissionDef {
- key: keyof typeof PermissionBits;
- label: string;
- bit: bigint;
-}
-
const TEXT_CHANNEL_PERMISSIONS: PermissionDef[] = [
{ key: 'VIEW_CHANNEL', label: 'View Channel', bit: PermissionBits.VIEW_CHANNEL },
{ key: 'SEND_MESSAGES', label: 'Send Messages', bit: PermissionBits.SEND_MESSAGES },
@@ -44,668 +30,6 @@ const VOICE_CHANNEL_PERMISSIONS: PermissionDef[] = [
{ key: 'DISCONNECT_MEMBERS', label: 'Disconnect Members', bit: PermissionBits.DISCONNECT_MEMBERS },
];
-// ─── Tri-State Toggle ──────────────────────────────────────────────────────────
-
-type TriState = 'allow' | 'neutral' | 'deny';
-
-function TriStateToggle({
- value,
- onChange,
- disabled,
-}: {
- value: TriState;
- onChange: (v: TriState) => void;
- disabled?: boolean;
-}) {
- const btnClass = (v: TriState, active: boolean) => {
- const base = 'w-6 h-6 flex items-center justify-center rounded-full transition-colors text-xs font-bold';
- if (disabled) return `${base} cursor-not-allowed opacity-40`;
- if (!active) return `${base} cursor-pointer text-txt-muted hover:text-txt-tertiary`;
- switch (v) {
- case 'deny': return `${base} cursor-pointer bg-accent-rose/15 text-accent-rose`;
- case 'neutral': return `${base} cursor-pointer bg-white/[0.06] text-txt-tertiary`;
- case 'allow': return `${base} cursor-pointer bg-accent-primary/15 text-accent-primary`;
- }
- };
-
- return (
-
-
-
-
-
- );
-}
-
-// ─── Override Entry (expandable row) ────────────────────────────────────────────
-
-function OverrideEntry({
- label,
- color,
- permDefs,
- allow,
- deny,
- onChange,
- onRemove,
- isEveryone,
-}: {
- label: string;
- color?: string;
- permDefs: PermissionDef[];
- allow: bigint;
- deny: bigint;
- onChange: (allow: bigint, deny: bigint) => void;
- onRemove?: () => void;
- isEveryone?: boolean;
-}) {
- const [expanded, setExpanded] = useState(false);
-
- const getState = (bit: bigint): TriState => {
- if ((allow & bit) !== 0n) return 'allow';
- if ((deny & bit) !== 0n) return 'deny';
- return 'neutral';
- };
-
- const setState = (bit: bigint, state: TriState) => {
- let newAllow = allow & ~bit;
- let newDeny = deny & ~bit;
- if (state === 'allow') newAllow |= bit;
- if (state === 'deny') newDeny |= bit;
- onChange(newAllow, newDeny);
- };
-
- // Compact summary of non-neutral permissions
- const summary = permDefs.filter(p => getState(p.bit) !== 'neutral');
-
- return (
-
-
- {expanded && (
-
- {permDefs.map((perm) => (
-
- {perm.label}
- setState(perm.bit, v)}
- />
-
- ))}
-
- )}
-
- );
-}
-
-// ─── Permissions Tab ────────────────────────────────────────────────────────────
-
-function PermissionsTab({
- channelId,
- channelType,
- spaceId,
- overrides,
- onOverridesChange,
-}: {
- channelId: string;
- channelType: 'text' | 'voice';
- spaceId: string;
- overrides: ChannelOverride[];
- onOverridesChange: () => void;
-}) {
- const spaces = useSpaceStore((s) => s.spaces);
- const space = spaces.find(s => s.id === spaceId);
- const roles = useSpaceStore((s) => s.roles);
- const members = useSpaceStore((s) => s.members);
-
- // Draft state: keyed by "role:id" or "member:id"
- const [draftOverrides, setDraftOverrides] = useState