Handle role hoisting / ranking.

Closes #76 and closes #75.
This commit is contained in:
Paul 2021-08-16 17:02:24 +01:00
parent 72e9cda844
commit b4f16f0d00
3 changed files with 85 additions and 19 deletions

View file

@ -14,6 +14,8 @@ const Grid = styled.div<{ width: number; height: number }>`
max-height: min(${(props) => props.height}px, var(--attachment-max-height)); 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 ( @supports not (
aspect-ratio: ${(props) => props.width} / ${(props) => props.height} aspect-ratio: ${(props) => props.width} / ${(props) => props.height}
) { ) {

View file

@ -1,8 +1,10 @@
/* eslint-disable react-hooks/rules-of-hooks */ /* eslint-disable react-hooks/rules-of-hooks */
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { Role } from "revolt-api/types/Servers";
import { Presence } from "revolt-api/types/Users"; import { Presence } from "revolt-api/types/Users";
import { Channel } from "revolt.js/dist/maps/Channels"; 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 { User } from "revolt.js/dist/maps/Users";
import { useContext, useEffect, useMemo } from "preact/hooks"; import { useContext, useEffect, useMemo } from "preact/hooks";
@ -41,22 +43,42 @@ function useEntries(channel: Channel, keys: string[], isServer?: boolean) {
const categoryInfo: { [key: string]: string } = {}; 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) => { keys.forEach((key) => {
let u, s; let u;
if (isServer) { if (isServer) {
const { server, user } = JSON.parse(key); const { server, user } = JSON.parse(key);
if (server !== channel.server_id) return; if (server !== channel.server_id) return;
u = client.users.get(user); 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 { } else {
u = client.users.get(key); u = client.users.get(key);
} }
@ -72,15 +94,15 @@ function useEntries(channel: Channel, keys: string[], isServer?: boolean) {
} else { } else {
if (isServer) { if (isServer) {
// Sort users into hoisted roles here. // Sort users into hoisted roles here.
if (member?.roles && s?.roles) { if (member?.roles && roles) {
let success = false; let success = false;
for (const id of member.roles) { for (const role of roleList) {
if (categories[id]) { if (member.roles.includes(role)) {
categories[id].push(entry); categories[role].push(entry);
success = true; success = true;
}
break; break;
} }
}
if (success) return; if (success) return;
} }

View file

@ -38,6 +38,8 @@ export const Roles = observer(({ server }: Props) => {
const { const {
name: roleName, name: roleName,
colour: roleColour, colour: roleColour,
hoist: roleHoist,
rank: roleRank,
permissions, permissions,
} = roles[role] ?? {}; } = roles[role] ?? {};
@ -54,6 +56,8 @@ export const Roles = observer(({ server }: Props) => {
const [perm, setPerm] = useState(getPermissions(role)); const [perm, setPerm] = useState(getPermissions(role));
const [name, setName] = useState(roleName); const [name, setName] = useState(roleName);
const [hoist, setHoist] = useState(roleHoist);
const [rank, setRank] = useState(roleRank);
const [colour, setColour] = useState(roleColour); const [colour, setColour] = useState(roleColour);
useEffect( useEffect(
@ -62,12 +66,16 @@ export const Roles = observer(({ server }: Props) => {
); );
useEffect(() => setName(roleName), [role, roleName]); useEffect(() => setName(roleName), [role, roleName]);
useEffect(() => setHoist(roleHoist), [role, roleHoist]);
useEffect(() => setRank(roleRank), [role, roleRank]);
useEffect(() => setColour(roleColour), [role, roleColour]); useEffect(() => setColour(roleColour), [role, roleColour]);
const modified = const modified =
!isEqual(perm, getPermissions(role)) || !isEqual(perm, getPermissions(role)) ||
!isEqual(name, roleName) || !isEqual(name, roleName) ||
!isEqual(colour, roleColour); !isEqual(colour, roleColour) ||
!isEqual(hoist, roleHoist) ||
!isEqual(rank, roleRank);
const save = () => { const save = () => {
if (!isEqual(perm, getPermissions(role))) { if (!isEqual(perm, getPermissions(role))) {
@ -77,8 +85,13 @@ export const Roles = observer(({ server }: Props) => {
}); });
} }
if (!isEqual(name, roleName) || !isEqual(colour, roleColour)) { if (
server.editRole(role, { name, colour }); !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) => {
/> />
</p> </p>
</section> </section>
<section>
<Overline type="subtle">Role Options</Overline>
<p>
<Checkbox
checked={hoist ?? false}
onChange={(v) => setHoist(v)}
description="Display this role above others.">
Hoist Role
</Checkbox>
</p>
</section>
</> </>
)} )}
<section> <section>
@ -228,6 +252,24 @@ export const Roles = observer(({ server }: Props) => {
</Button> </Button>
)} )}
</div> </div>
{role !== "default" && (
<>
<section>
<Overline type="subtle">
Experimental Role Ranking
</Overline>
<p>
<InputBox
value={rank ?? 0}
onChange={(e) =>
setRank(parseInt(e.currentTarget.value))
}
contrast
/>
</p>
</section>
</>
)}
</div> </div>
</div> </div>
); );