merge: remote-tracking branch 'origin/role-reordering'
commit
8ba7a769b3
|
|
@ -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,198 @@ 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,
|
||||
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
|
||||
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");
|
||||
onRolesReordered();
|
||||
} 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 (
|
||||
<div>
|
||||
<SpaceBetween>
|
||||
<H1>
|
||||
<Text id="app.settings.permissions.role_ranking" />
|
||||
</H1>
|
||||
<Button
|
||||
palette="secondary"
|
||||
disabled={!hasChanges || isReordering}
|
||||
onClick={saveReorder}>
|
||||
<Text id="app.special.modals.actions.save" />
|
||||
</Button>
|
||||
</SpaceBetween>
|
||||
|
||||
<RoleReorderContainer>
|
||||
{roles.map((role, index) => (
|
||||
<RoleItem key={role.id}>
|
||||
<RoleInfo>
|
||||
<RoleName>{role.name}</RoleName>
|
||||
<RoleRank>
|
||||
{role.id === "default" ? (
|
||||
<Text id="app.settings.permissions.default_desc" />
|
||||
) : (
|
||||
<>
|
||||
<Text id="app.settings.permissions.role_ranking" />{" "}
|
||||
{index}
|
||||
</>
|
||||
)}
|
||||
</RoleRank>
|
||||
</RoleInfo>
|
||||
|
||||
{role.id !== "default" && (
|
||||
<RoleControls>
|
||||
<MoveButton
|
||||
palette="secondary"
|
||||
disabled={index === 0}
|
||||
onClick={() => moveRoleUp(index)}>
|
||||
<ChevronUp size={16} />
|
||||
</MoveButton>
|
||||
<MoveButton
|
||||
palette="secondary"
|
||||
disabled={index >= roles.length - 2}
|
||||
onClick={() => moveRoleDown(index)}>
|
||||
<ChevronDown size={16} />
|
||||
</MoveButton>
|
||||
</RoleControls>
|
||||
)}
|
||||
</RoleItem>
|
||||
))}
|
||||
</RoleReorderContainer>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Hook to memo-ize role information.
|
||||
* @param server Target server
|
||||
|
|
@ -50,9 +240,12 @@ export function useRoles(server: Server) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Roles settings menu
|
||||
* Updated Roles settings menu with reordering panel
|
||||
*/
|
||||
export const Roles = observer(({ server }: Props) => {
|
||||
const [showReorderPanel, setShowReorderPanel] = useState(false);
|
||||
const [rolesWereReordered, setRolesWereReordered] = useState(false);
|
||||
|
||||
// Consolidate all permissions that we can change right now.
|
||||
const currentRoles = useRoles(server);
|
||||
|
||||
|
|
@ -74,7 +267,36 @@ export const Roles = observer(({ server }: Props) => {
|
|||
margin: 16px 0;
|
||||
`;
|
||||
|
||||
const ReorderButton = styled(Button)`
|
||||
margin-inline: auto 8px;
|
||||
`;
|
||||
|
||||
const handleBackFromReorder = () => {
|
||||
setShowReorderPanel(false);
|
||||
if (rolesWereReordered) {
|
||||
window.location.reload(); // Refresh because I don't actually care anymore.
|
||||
}
|
||||
};
|
||||
|
||||
if (showReorderPanel) {
|
||||
return (
|
||||
<div>
|
||||
<RoleReorderPanel
|
||||
server={server}
|
||||
onRolesReordered={() => setRolesWereReordered(true)}
|
||||
/>
|
||||
<Button
|
||||
palette="secondary"
|
||||
onClick={handleBackFromReorder}
|
||||
style={{ marginBottom: "16px" }}>
|
||||
<Text id="app.special.modals.actions.back" />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<PermissionsLayout
|
||||
server={server}
|
||||
rank={server.member?.ranking ?? Infinity}
|
||||
|
|
@ -92,15 +314,12 @@ export const Roles = observer(({ server }: Props) => {
|
|||
|
||||
if (!currentRole) return null;
|
||||
|
||||
// Keep track of whatever role we're editing right now.
|
||||
const [value, setValue] = useState<Partial<RoleOrDefault>>({});
|
||||
const [value, setValue] = useState<Partial<RoleOrDefault>>(
|
||||
{},
|
||||
);
|
||||
|
||||
const currentRoleValue = { ...currentRole, ...value };
|
||||
|
||||
// Calculate permissions we have access to on this server.
|
||||
const current = server.permission;
|
||||
|
||||
// Upload new role information to server.
|
||||
function save() {
|
||||
const { permissions: permsCurrent, ...current } =
|
||||
currentRole;
|
||||
|
|
@ -124,7 +343,6 @@ export const Roles = observer(({ server }: Props) => {
|
|||
}
|
||||
}
|
||||
|
||||
// Delete the role from this server.
|
||||
function deleteRole() {
|
||||
server.deleteRole(selected);
|
||||
}
|
||||
|
|
@ -138,6 +356,11 @@ export const Roles = observer(({ server }: Props) => {
|
|||
fields={{ name: currentRole.name }}
|
||||
/>
|
||||
</H1>
|
||||
<ReorderButton
|
||||
palette="secondary"
|
||||
onClick={() => setShowReorderPanel(true)}>
|
||||
<Text id="app.settings.permissions.role_ranking" />
|
||||
</ReorderButton>
|
||||
<Button
|
||||
palette="secondary"
|
||||
disabled={isEqual(
|
||||
|
|
@ -161,7 +384,8 @@ export const Roles = observer(({ server }: Props) => {
|
|||
onChange={(e) =>
|
||||
setValue({
|
||||
...value,
|
||||
name: e.currentTarget.value,
|
||||
name: e.currentTarget
|
||||
.value,
|
||||
})
|
||||
}
|
||||
palette="secondary"
|
||||
|
|
@ -203,7 +427,10 @@ export const Roles = observer(({ server }: Props) => {
|
|||
"gray"
|
||||
}
|
||||
onChange={(colour) =>
|
||||
setValue({ ...value, colour })
|
||||
setValue({
|
||||
...value,
|
||||
colour,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
|
|
@ -215,10 +442,14 @@ export const Roles = observer(({ server }: Props) => {
|
|||
<p>
|
||||
<Checkbox
|
||||
value={
|
||||
currentRoleValue.hoist ?? false
|
||||
currentRoleValue.hoist ??
|
||||
false
|
||||
}
|
||||
onChange={(hoist) =>
|
||||
setValue({ ...value, hoist })
|
||||
setValue({
|
||||
...value,
|
||||
hoist,
|
||||
})
|
||||
}
|
||||
title={
|
||||
<Text id="app.settings.permissions.hoist_role" />
|
||||
|
|
@ -229,26 +460,6 @@ export const Roles = observer(({ server }: Props) => {
|
|||
/>
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<Category>
|
||||
<Text id="app.settings.permissions.role_ranking" />
|
||||
</Category>
|
||||
<p>
|
||||
<InputBox
|
||||
type="number"
|
||||
value={currentRoleValue.rank ?? 0}
|
||||
onChange={(e) =>
|
||||
setValue({
|
||||
...value,
|
||||
rank: parseInt(
|
||||
e.currentTarget.value,
|
||||
),
|
||||
})
|
||||
}
|
||||
palette="secondary"
|
||||
/>
|
||||
</p>
|
||||
</section>
|
||||
</>
|
||||
)}
|
||||
<h1>
|
||||
|
|
@ -282,5 +493,6 @@ export const Roles = observer(({ server }: Props) => {
|
|||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue