feat: Finish crappy role ranking implementation that works but is full of sin

master
Declan Chidlow 2025-08-04 22:28:24 +08:00
parent 20c8dde197
commit e7d7420d5b
1 changed files with 129 additions and 111 deletions

View File

@ -93,122 +93,129 @@ export function useRolesForReorder(server: Server) {
/** /**
* Role reordering component * Role reordering component
*/ */
const RoleReorderPanel = observer(({ server }: Props) => { const RoleReorderPanel = observer(
const initialRoles = useRolesForReorder(server); ({
const [roles, setRoles] = useState(initialRoles); server,
const [isReordering, setIsReordering] = useState(false); onRolesReordered,
}: Props & { onRolesReordered: () => void }) => {
const initialRoles = useRolesForReorder(server);
const [roles, setRoles] = useState(initialRoles);
const [isReordering, setIsReordering] = useState(false);
// Update local state when server roles change // Update local state when server roles change
useMemo(() => { useMemo(() => {
setRoles(useRolesForReorder(server)); setRoles(useRolesForReorder(server));
}, [server.roles, server.default_permissions]); }, [server.roles, server.default_permissions]);
const moveRoleUp = (index: number) => { const moveRoleUp = (index: number) => {
if (index === 0 || roles[index].id === "default") return; if (index === 0 || roles[index].id === "default") return;
const newRoles = [...roles]; const newRoles = [...roles];
[newRoles[index - 1], newRoles[index]] = [ [newRoles[index - 1], newRoles[index]] = [
newRoles[index], newRoles[index],
newRoles[index - 1], newRoles[index - 1],
]; ];
setRoles(newRoles); setRoles(newRoles);
}; };
const moveRoleDown = (index: number) => { const moveRoleDown = (index: number) => {
// Can't move down if it's the last non-default role or if it's default // 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; if (index >= roles.length - 2 || roles[index].id === "default")
return;
const newRoles = [...roles]; const newRoles = [...roles];
[newRoles[index], newRoles[index + 1]] = [ [newRoles[index], newRoles[index + 1]] = [
newRoles[index + 1], newRoles[index + 1],
newRoles[index], newRoles[index],
]; ];
setRoles(newRoles); setRoles(newRoles);
}; };
const saveReorder = async () => { const saveReorder = async () => {
setIsReordering(true); setIsReordering(true);
try { try {
const nonDefaultRoles = roles.filter( const nonDefaultRoles = roles.filter(
(role) => role.id !== "default", (role) => role.id !== "default",
); );
const roleIds = nonDefaultRoles.map((role) => role.id); const roleIds = nonDefaultRoles.map((role) => role.id);
const session = useSession()!; const session = useSession()!;
const client = session.client!; const client = session.client!;
// Make direct API request since it's not in r.js as of writing // Make direct API request since it's not in r.js as of writing
await client.api.patch(`/servers/${server._id}/roles/ranks`, { await client.api.patch(`/servers/${server._id}/roles/ranks`, {
ranks: roleIds, ranks: roleIds,
}); });
console.log("Roles reordered successfully"); console.log("Roles reordered successfully");
} catch (error) { onRolesReordered();
console.error("Failed to reorder roles:", error); } catch (error) {
setRoles(initialRoles); console.error("Failed to reorder roles:", error);
} finally { setRoles(initialRoles);
setIsReordering(false); } finally {
} setIsReordering(false);
}; }
};
const hasChanges = !isEqual( const hasChanges = !isEqual(
roles.filter((r) => r.id !== "default").map((r) => r.id), roles.filter((r) => r.id !== "default").map((r) => r.id),
initialRoles.filter((r) => r.id !== "default").map((r) => r.id), initialRoles.filter((r) => r.id !== "default").map((r) => r.id),
); );
return ( return (
<div> <div>
<SpaceBetween> <SpaceBetween>
<H1> <H1>
<Text id="app.settings.permissions.role_ranking" /> <Text id="app.settings.permissions.role_ranking" />
</H1> </H1>
<Button <Button
palette="secondary" palette="secondary"
disabled={!hasChanges || isReordering} disabled={!hasChanges || isReordering}
onClick={saveReorder}> onClick={saveReorder}>
<Text id="app.special.modals.actions.save" /> <Text id="app.special.modals.actions.save" />
</Button> </Button>
</SpaceBetween> </SpaceBetween>
<RoleReorderContainer> <RoleReorderContainer>
{roles.map((role, index) => ( {roles.map((role, index) => (
<RoleItem key={role.id}> <RoleItem key={role.id}>
<RoleInfo> <RoleInfo>
<RoleName>{role.name}</RoleName> <RoleName>{role.name}</RoleName>
<RoleRank> <RoleRank>
{role.id === "default" ? ( {role.id === "default" ? (
<Text id="app.settings.permissions.default_desc" /> <Text id="app.settings.permissions.default_desc" />
) : ( ) : (
<> <>
<Text id="app.settings.permissions.role_ranking" />{" "} <Text id="app.settings.permissions.role_ranking" />{" "}
{index} {index}
</> </>
)} )}
</RoleRank> </RoleRank>
</RoleInfo> </RoleInfo>
{role.id !== "default" && ( {role.id !== "default" && (
<RoleControls> <RoleControls>
<MoveButton <MoveButton
palette="secondary" palette="secondary"
disabled={index === 0} disabled={index === 0}
onClick={() => moveRoleUp(index)}> onClick={() => moveRoleUp(index)}>
<ChevronUp size={16} /> <ChevronUp size={16} />
</MoveButton> </MoveButton>
<MoveButton <MoveButton
palette="secondary" palette="secondary"
disabled={index >= roles.length - 2} disabled={index >= roles.length - 2}
onClick={() => moveRoleDown(index)}> onClick={() => moveRoleDown(index)}>
<ChevronDown size={16} /> <ChevronDown size={16} />
</MoveButton> </MoveButton>
</RoleControls> </RoleControls>
)} )}
</RoleItem> </RoleItem>
))} ))}
</RoleReorderContainer> </RoleReorderContainer>
</div> </div>
); );
}); },
);
/** /**
* Hook to memo-ize role information. * Hook to memo-ize role information.
@ -237,6 +244,7 @@ export function useRoles(server: Server) {
*/ */
export const Roles = observer(({ server }: Props) => { export const Roles = observer(({ server }: Props) => {
const [showReorderPanel, setShowReorderPanel] = useState(false); const [showReorderPanel, setShowReorderPanel] = useState(false);
const [rolesWereReordered, setRolesWereReordered] = useState(false);
// Consolidate all permissions that we can change right now. // Consolidate all permissions that we can change right now.
const currentRoles = useRoles(server); const currentRoles = useRoles(server);
@ -260,16 +268,26 @@ export const Roles = observer(({ server }: Props) => {
`; `;
const ReorderButton = styled(Button)` const ReorderButton = styled(Button)`
margin: 0 0 16px 0; margin-inline: auto 8px;
`; `;
const handleBackFromReorder = () => {
setShowReorderPanel(false);
if (rolesWereReordered) {
window.location.reload(); // Refresh because I don't actually care anymore.
}
};
if (showReorderPanel) { if (showReorderPanel) {
return ( return (
<div> <div>
<RoleReorderPanel server={server} /> <RoleReorderPanel
server={server}
onRolesReordered={() => setRolesWereReordered(true)}
/>
<Button <Button
palette="secondary" palette="secondary"
onClick={() => setShowReorderPanel(false)} onClick={handleBackFromReorder}
style={{ marginBottom: "16px" }}> style={{ marginBottom: "16px" }}>
<Text id="app.special.modals.actions.back" /> <Text id="app.special.modals.actions.back" />
</Button> </Button>
@ -279,11 +297,6 @@ export const Roles = observer(({ server }: Props) => {
return ( return (
<div> <div>
<ReorderButton
palette="secondary"
onClick={() => setShowReorderPanel(true)}>
<Text id="app.settings.permissions.role_ranking" />
</ReorderButton>
<PermissionsLayout <PermissionsLayout
server={server} server={server}
rank={server.member?.ranking ?? Infinity} rank={server.member?.ranking ?? Infinity}
@ -343,6 +356,11 @@ export const Roles = observer(({ server }: Props) => {
fields={{ name: currentRole.name }} fields={{ name: currentRole.name }}
/> />
</H1> </H1>
<ReorderButton
palette="secondary"
onClick={() => setShowReorderPanel(true)}>
<Text id="app.settings.permissions.role_ranking" />
</ReorderButton>
<Button <Button
palette="secondary" palette="secondary"
disabled={isEqual( disabled={isEqual(