From b4f16f0d00c3b677462541ba4a0b51728d0bad2d Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 16 Aug 2021 17:02:24 +0100 Subject: [PATCH] Handle role hoisting / ranking. Closes #76 and closes #75. --- .../common/messaging/attachments/Grid.tsx | 2 + .../navigation/right/MemberSidebar.tsx | 54 +++++++++++++------ src/pages/settings/server/Roles.tsx | 48 +++++++++++++++-- 3 files changed, 85 insertions(+), 19 deletions(-) diff --git a/src/components/common/messaging/attachments/Grid.tsx b/src/components/common/messaging/attachments/Grid.tsx index 88c99772..8d87007f 100644 --- a/src/components/common/messaging/attachments/Grid.tsx +++ b/src/components/common/messaging/attachments/Grid.tsx @@ -14,6 +14,8 @@ const Grid = styled.div<{ width: number; height: number }>` max-height: min(${(props) => props.height}px, var(--attachment-max-height)); + // This is a hack for browsers not supporting aspect-ratio. + // Stolen from https://codepen.io/una/pen/BazyaOM. @supports not ( aspect-ratio: ${(props) => props.width} / ${(props) => props.height} ) { diff --git a/src/components/navigation/right/MemberSidebar.tsx b/src/components/navigation/right/MemberSidebar.tsx index 635efba4..29af6091 100644 --- a/src/components/navigation/right/MemberSidebar.tsx +++ b/src/components/navigation/right/MemberSidebar.tsx @@ -1,8 +1,10 @@ /* eslint-disable react-hooks/rules-of-hooks */ import { observer } from "mobx-react-lite"; import { useParams } from "react-router-dom"; +import { Role } from "revolt-api/types/Servers"; import { Presence } from "revolt-api/types/Users"; import { Channel } from "revolt.js/dist/maps/Channels"; +import { Server } from "revolt.js/dist/maps/Servers"; import { User } from "revolt.js/dist/maps/Users"; import { useContext, useEffect, useMemo } from "preact/hooks"; @@ -41,22 +43,42 @@ function useEntries(channel: Channel, keys: string[], isServer?: boolean) { const categoryInfo: { [key: string]: string } = {}; + let roles: Server["roles"]; + let roleList: string[]; + if ( + channel.channel_type === "TextChannel" || + channel.channel_type === "VoiceChannel" + ) { + roles = channel.server!.roles; + if (roles) { + const list = Object.keys(roles) + .map((id) => { + return [id, roles![id], roles![id].rank ?? 0] as [ + string, + Role, + number, + ]; + }) + .filter(([, role]) => role.hoist); + + list.sort((b, a) => b[2] - a[2]); + + list.forEach(([id, role]) => { + if (categories[id]) return; + categories[id] = []; + categoryInfo[id] = role.name; + }); + + roleList = list.map((x) => x[0]); + } + } + keys.forEach((key) => { - let u, s; + let u; if (isServer) { const { server, user } = JSON.parse(key); if (server !== channel.server_id) return; u = client.users.get(user); - s = client.servers.get(server); - - if (s?.roles) { - for (const id of Object.keys(s.roles)) { - if (categories[id]) continue; - // Check if hoisted. - categories[id] = []; - categoryInfo[id] = s.roles[id].name; - } - } } else { u = client.users.get(key); } @@ -72,14 +94,14 @@ function useEntries(channel: Channel, keys: string[], isServer?: boolean) { } else { if (isServer) { // Sort users into hoisted roles here. - if (member?.roles && s?.roles) { + if (member?.roles && roles) { let success = false; - for (const id of member.roles) { - if (categories[id]) { - categories[id].push(entry); + for (const role of roleList) { + if (member.roles.includes(role)) { + categories[role].push(entry); success = true; + break; } - break; } if (success) return; diff --git a/src/pages/settings/server/Roles.tsx b/src/pages/settings/server/Roles.tsx index 91f8f2f5..8a1946f0 100644 --- a/src/pages/settings/server/Roles.tsx +++ b/src/pages/settings/server/Roles.tsx @@ -38,6 +38,8 @@ export const Roles = observer(({ server }: Props) => { const { name: roleName, colour: roleColour, + hoist: roleHoist, + rank: roleRank, permissions, } = roles[role] ?? {}; @@ -54,6 +56,8 @@ export const Roles = observer(({ server }: Props) => { const [perm, setPerm] = useState(getPermissions(role)); const [name, setName] = useState(roleName); + const [hoist, setHoist] = useState(roleHoist); + const [rank, setRank] = useState(roleRank); const [colour, setColour] = useState(roleColour); useEffect( @@ -62,12 +66,16 @@ export const Roles = observer(({ server }: Props) => { ); useEffect(() => setName(roleName), [role, roleName]); + useEffect(() => setHoist(roleHoist), [role, roleHoist]); + useEffect(() => setRank(roleRank), [role, roleRank]); useEffect(() => setColour(roleColour), [role, roleColour]); const modified = !isEqual(perm, getPermissions(role)) || !isEqual(name, roleName) || - !isEqual(colour, roleColour); + !isEqual(colour, roleColour) || + !isEqual(hoist, roleHoist) || + !isEqual(rank, roleRank); const save = () => { if (!isEqual(perm, getPermissions(role))) { @@ -77,8 +85,13 @@ export const Roles = observer(({ server }: Props) => { }); } - if (!isEqual(name, roleName) || !isEqual(colour, roleColour)) { - server.editRole(role, { name, colour }); + if ( + !isEqual(name, roleName) || + !isEqual(colour, roleColour) || + !isEqual(hoist, roleHoist) || + !isEqual(rank, roleRank) + ) { + server.editRole(role, { name, colour, hoist, rank }); } }; @@ -163,6 +176,17 @@ export const Roles = observer(({ server }: Props) => { />

+
+ Role Options +

+ setHoist(v)} + description="Display this role above others."> + Hoist Role + +

+
)}
@@ -228,6 +252,24 @@ export const Roles = observer(({ server }: Props) => { )} + {role !== "default" && ( + <> +
+ + Experimental Role Ranking + +

+ + setRank(parseInt(e.currentTarget.value)) + } + contrast + /> +

+
+ + )} );