import { useEffect, useState } from "react";
import { usePermissionStore, PERMISSION_LABELS, PERMS, type ChannelOverride, hasPermission } from "../stores/permissions.ts";
import { useRoleStore } from "../stores/role.ts";
import { useMemberStore } from "../stores/member.ts";
import { useChannelStore } from "../stores/channel.ts";
import { api } from "../lib/api.ts";
interface ChannelSettingsModalProps {
serverId: string;
channelId: string;
channelName: string;
onClose: () => void;
}
type Tab = 'overview' | 'permissions';
type TargetMode = 'role' | 'user';
interface OverrideFormData {
targetType: TargetMode;
targetId: string;
allow: number;
deny: number;
}
function OverrideMatrix({ allow, deny, onChange }: { allow: number; deny: number; onChange: (allow: number, deny: number) => void }) {
const toggle = (flag: number, state: 'allow' | 'deny' | 'inherit') => {
let nextAllow = allow;
let nextDeny = deny;
if (state === 'allow') {
nextAllow |= flag;
nextDeny &= ~flag;
} else if (state === 'deny') {
nextAllow &= ~flag;
nextDeny |= flag;
} else {
nextAllow &= ~flag;
nextDeny &= ~flag;
}
onChange(nextAllow, nextDeny);
};
const state = (flag: number): 'allow' | 'deny' | 'inherit' => {
if (hasPermission(allow, flag)) return 'allow';
if (hasPermission(deny, flag)) return 'deny';
return 'inherit';
};
return (
{(Object.keys(PERMISSION_LABELS) as (keyof typeof PERMISSION_LABELS)[]).map((key) => {
const flag = PERMS[key];
const s = state(flag);
return (
{PERMISSION_LABELS[key]}
{(['inherit', 'allow', 'deny'] as const).map((opt) => (
))}
);
})}
);
}
export function ChannelSettingsModal({ serverId, channelId, channelName, onClose }: ChannelSettingsModalProps) {
const [tab, setTab] = useState('overview');
const [form, setForm] = useState({ targetType: 'role', targetId: '', allow: 0, deny: 0 });
const [saving, setSaving] = useState(false);
const [error, setError] = useState(null);
const [editName, setEditName] = useState(channelName);
const [savingName, setSavingName] = useState(false);
const updateChannel = useChannelStore((s) => s.updateChannel);
const overrides = usePermissionStore((s) => s.overridesByChannel[channelId] || []);
const fetchOverrides = usePermissionStore((s) => s.fetchOverrides);
const setOverride = usePermissionStore((s) => s.setOverride);
const deleteOverride = usePermissionStore((s) => s.deleteOverride);
const roles = useRoleStore((s) => s.roles);
const fetchRoles = useRoleStore((s) => s.fetchRoles);
const members = useMemberStore((s) => s.membersByServer[serverId] || []);
const fetchMembers = useMemberStore((s) => s.fetchMembers);
useEffect(() => {
fetchOverrides(channelId).catch(() => setError('Failed to load overrides'));
fetchRoles(serverId).catch(() => {});
fetchMembers(serverId).catch(() => {});
}, [channelId, serverId, fetchOverrides, fetchRoles, fetchMembers]);
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') onClose();
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [onClose]);
const handleSave = async () => {
if (!form.targetId) {
setError('Select a target');
return;
}
setSaving(true);
setError(null);
try {
await setOverride(channelId, form.targetType, form.targetId, form.allow, form.deny);
setForm({ targetType: 'role', targetId: '', allow: 0, deny: 0 });
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to save override');
} finally {
setSaving(false);
}
};
const handleSaveName = async () => {
const name = editName.trim();
if (!name || name === channelName) return;
setSavingName(true);
setError(null);
try {
const updated = await api.patch<{ id: string; server_id: string; name: string; type: string; category: string | null; position: number; group_id?: string | null }>(
`/servers/${serverId}/channels/${channelId}`,
{ name }
);
updateChannel(updated as any);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to rename channel');
} finally {
setSavingName(false);
}
};
const targets = form.targetType === 'role'
? roles.filter((r) => !r.is_default).map((r) => ({ id: r.id, label: r.name }))
: members.map((m) => ({ id: m.id, label: m.username }));
return (
e.stopPropagation()}>
CHANNEL SETTINGS: #{channelName}
{tab === 'overview' && (
Channel name
setEditName(e.target.value)}
onKeyDown={(e) => { if (e.key === 'Enter') handleSaveName(); }}
className="terminal-input text-sm flex-1"
/>
{error &&
ERR: {error}
}
)}
{tab === 'permissions' && (
Existing overrides
{overrides.length === 0 &&
[none]
}
{overrides.map((o: ChannelOverride) => (
{o.target_type}:{o.target_id}
allow:{o.allow_bitflags}
deny:{o.deny_bitflags}
))}
New override
setForm({ ...form, allow: a, deny: d })} />
{error && ERR: {error}
}
)}
);
}