import React, { useState } from 'react'; import { Modal } from '../ui/Modal'; import { useUIStore } from '../../stores/uiStore'; import { useServerStore } from '../../stores/serverStore'; import { useAuthStore } from '../../stores/authStore'; import { Avatar } from '../ui/Avatar'; import { api } from '../../api/client'; import { useNavigate } from 'react-router-dom'; import { hasPermissionBit, PermissionBits } from '../../utils/permissions'; export function ServerSettingsModal() { const activeModal = useUIStore((s) => s.activeModal); const closeModal = useUIStore((s) => s.closeModal); const currentServerId = useServerStore((s) => s.currentServerId); const servers = useServerStore((s) => s.servers); const members = useServerStore((s) => s.members); const roles = useServerStore((s) => s.roles); const updateServer = useServerStore((s) => s.updateServer); const deleteServer = useServerStore((s) => s.deleteServer); const loadServerDetail = useServerStore((s) => s.loadServerDetail); const currentUser = useAuthStore((s) => s.user); const navigate = useNavigate(); const [tab, setTab] = useState<'overview' | 'members'>('overview'); const [serverName, setServerName] = useState(''); const [error, setError] = useState(''); const [isLoading, setIsLoading] = useState(false); const [confirmDelete, setConfirmDelete] = useState(false); const [pendingRoleChanges, setPendingRoleChanges] = useState>>(new Map()); const serverPermissions = useServerStore((s) => s.serverPermissions); const isOpen = activeModal === 'serverSettings'; const server = servers.find(s => s.id === currentServerId); const isOwnerUser = server?.ownerId === currentUser?.id; const myServerPerms = currentServerId ? serverPermissions.get(currentServerId) : undefined; const canManageServer = hasPermissionBit(myServerPerms, PermissionBits.MANAGE_SERVER); const canManageRoles = hasPermissionBit(myServerPerms, PermissionBits.MANAGE_ROLES); // Assignable roles: exclude @everyone (where role.id === serverId) const assignableRoles = roles.filter(r => r.id !== currentServerId); React.useEffect(() => { if (server) { setServerName(server.name); } }, [server]); if (!server || !currentServerId) return null; const handleSave = async () => { setError(''); setIsLoading(true); try { await updateServer(currentServerId, { name: serverName.trim() }); setIsLoading(false); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to update server'); setIsLoading(false); } }; const handleDelete = async () => { if (!confirmDelete) { setConfirmDelete(true); return; } try { await deleteServer(currentServerId); closeModal(); navigate('/channels/@me'); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to delete server'); } }; const getMemberRoleIds = (member: typeof members[number]): Set => { // Check for pending (unsaved) changes first const pending = pendingRoleChanges.get(member.userId); if (pending) return pending; return new Set(member.roles?.map(r => r.id) ?? []); }; const handleRoleToggle = (userId: string, roleId: string, currentRoleIds: Set) => { const updated = new Set(currentRoleIds); if (updated.has(roleId)) { updated.delete(roleId); } else { updated.add(roleId); } setPendingRoleChanges(prev => new Map(prev).set(userId, updated)); }; const handleSaveRoles = async (userId: string) => { const roleIds = pendingRoleChanges.get(userId); if (!roleIds) return; try { await api.servers.updateMember(currentServerId, userId, { roleIds: Array.from(roleIds) }); setPendingRoleChanges(prev => { const next = new Map(prev); next.delete(userId); return next; }); await loadServerDetail(currentServerId); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to update roles'); } }; const handleCancelRoleChange = (userId: string) => { setPendingRoleChanges(prev => { const next = new Map(prev); next.delete(userId); return next; }); }; const handleKick = async (userId: string) => { try { await api.servers.removeMember(currentServerId, userId); await loadServerDetail(currentServerId); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to kick member'); } }; return (
{/* Tabs */}
{/* Content */}
{error && (
{error}
)} {tab === 'overview' && (
setServerName(e.target.value)} className="w-full px-3 py-2 bg-discord-bg-tertiary rounded text-discord-text-primary outline-none focus:ring-2 focus:ring-discord-blurple" disabled={!canManageServer} />
{canManageServer && ( <>

Danger Zone

)}
)} {tab === 'members' && (
{members.map((member) => { const displayName = member.user.displayName ?? member.user.username; const isOwner = member.userId === server.ownerId; const memberRoleIds = getMemberRoleIds(member); const hasPendingChanges = pendingRoleChanges.has(member.userId); return (
{displayName}
{isOwner && ( Owner )} {member.roles?.filter(r => r.id !== currentServerId).map(r => ( {r.name} ))} {!isOwner && (!member.roles || member.roles.filter(r => r.id !== currentServerId).length === 0) && ( No roles )}
{canManageRoles && member.userId !== currentUser?.id && !isOwner && (
)}
{/* Role checkboxes — shown for non-self, non-owner members when user can manage roles */} {canManageRoles && member.userId !== currentUser?.id && !isOwner && assignableRoles.length > 0 && (
{assignableRoles.map(role => ( ))} {hasPendingChanges && (
)}
)}
); })}
)}
); }