diff --git a/src/pages/settings/server/Roles.tsx b/src/pages/settings/server/Roles.tsx index b5e690d5..1623e598 100644 --- a/src/pages/settings/server/Roles.tsx +++ b/src/pages/settings/server/Roles.tsx @@ -1,4 +1,8 @@ -import { HelpCircle } from "@styled-icons/boxicons-solid"; +import { + HelpCircle, + ChevronUp, + ChevronDown, +} from "@styled-icons/boxicons-solid"; import isEqual from "lodash.isequal"; import { observer } from "mobx-react-lite"; import { Server } from "revolt.js"; @@ -21,12 +25,191 @@ import { import Tooltip from "../../../components/common/Tooltip"; import { PermissionList } from "../../../components/settings/roles/PermissionList"; import { RoleOrDefault } from "../../../components/settings/roles/RoleSelection"; +import { useSession } from "../../../controllers/client/ClientController"; import { modalController } from "../../../controllers/modals/ModalController"; interface Props { server: Server; } +const RoleReorderContainer = styled.div` + margin: 16px 0; +`; + +const RoleItem = styled.div` + display: flex; + align-items: center; + padding: 12px 16px; + margin: 12px 0; + background: var(--secondary-background); + border-radius: var(--border-radius); +`; + +const RoleInfo = styled.div` + flex: 1; + display: flex; + flex-direction: column; +`; + +const RoleName = styled.div` + font-weight: 600; + color: var(--foreground); +`; + +const RoleRank = styled.div` + font-size: 12px; + color: var(--secondary-foreground); +`; + +const RoleControls = styled.div` + display: flex; + gap: 4px; +`; + +const MoveButton = styled(Button)` + padding: 4px 8px; + min-width: auto; +`; + +/** + * Hook to memo-ize role information with proper ordering + * @param server Target server + * @returns Role array with default at bottom + */ +export function useRolesForReorder(server: Server) { + return useMemo(() => { + const roles = [...server.orderedRoles] as RoleOrDefault[]; + + roles.push({ + id: "default", + name: "Default", + permissions: server.default_permissions, + }); + + return roles; + }, [server.roles, server.default_permissions]); +} + +/** + * Role reordering component + */ +const RoleReorderPanel = observer(({ server }: Props) => { + const initialRoles = useRolesForReorder(server); + const [roles, setRoles] = useState(initialRoles); + const [isReordering, setIsReordering] = useState(false); + + // Update local state when server roles change + useMemo(() => { + setRoles(useRolesForReorder(server)); + }, [server.roles, server.default_permissions]); + + const moveRoleUp = (index: number) => { + if (index === 0 || roles[index].id === "default") return; + + const newRoles = [...roles]; + [newRoles[index - 1], newRoles[index]] = [ + newRoles[index], + newRoles[index - 1], + ]; + setRoles(newRoles); + }; + + const moveRoleDown = (index: number) => { + // Can't move down if it's the last non-default role or if it's default + if (index >= roles.length - 2 || roles[index].id === "default") return; + + const newRoles = [...roles]; + [newRoles[index], newRoles[index + 1]] = [ + newRoles[index + 1], + newRoles[index], + ]; + setRoles(newRoles); + }; + + const saveReorder = async () => { + setIsReordering(true); + try { + const nonDefaultRoles = roles.filter( + (role) => role.id !== "default", + ); + const roleIds = nonDefaultRoles.map((role) => role.id); + + const session = useSession()!; + const client = session.client!; + + // Make direct API request since it's not in r.js as of writing + await client.api.patch(`/servers/${server._id}/roles/ranks`, { + ranks: roleIds, + }); + + console.log("Roles reordered successfully"); + } catch (error) { + console.error("Failed to reorder roles:", error); + setRoles(initialRoles); + } finally { + setIsReordering(false); + } + }; + + const hasChanges = !isEqual( + roles.filter((r) => r.id !== "default").map((r) => r.id), + initialRoles.filter((r) => r.id !== "default").map((r) => r.id), + ); + + return ( +
-
+
-
-
+
+