From 71020e6e8c45734d3670ed6db5f1a1da91b6020d Mon Sep 17 00:00:00 2001 From: Paul <paulmakles@gmail.com> Date: Sat, 24 Jul 2021 18:46:33 +0100 Subject: [PATCH] Bring back ability to edit roles on members. Improve ban list design. --- package.json | 2 +- src/pages/settings/server/Bans.tsx | 7 +- src/pages/settings/server/Invites.tsx | 6 +- src/pages/settings/server/Members.tsx | 131 ++++++++++++++++++-- src/pages/settings/server/Panes.module.scss | 31 +++-- yarn.lock | 8 +- 6 files changed, 148 insertions(+), 37 deletions(-) diff --git a/package.json b/package.json index aa6a303..c2d8fb9 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "react-router-dom": "^5.2.0", "react-scroll": "^1.8.2", "redux": "^4.1.0", - "revolt.js": "4.3.3-alpha.16", + "revolt.js": "4.3.3-alpha.17", "rimraf": "^3.0.2", "sass": "^1.35.1", "shade-blend-color": "^1.0.0", diff --git a/src/pages/settings/server/Bans.tsx b/src/pages/settings/server/Bans.tsx index 6497bba..b196a20 100644 --- a/src/pages/settings/server/Bans.tsx +++ b/src/pages/settings/server/Bans.tsx @@ -1,5 +1,6 @@ import { XCircle } from "@styled-icons/boxicons-regular"; import { Servers, Users } from "revolt.js/dist/api/objects"; +import { Route } from "revolt.js/dist/api/routes"; import styles from "./Panes.module.scss"; import { Text } from "preact-i18n"; @@ -19,11 +20,7 @@ export function Bans({ server }: Props) { const client = useContext(AppContext); const [deleting, setDelete] = useState<string[]>([]); const [data, setData] = useState< - | { - users: Pick<Users.User, "_id" | "username" | "avatar">[]; - bans: Servers.Ban[]; - } - | undefined + Route<"GET", "/servers/id/bans">["response"] | undefined >(undefined); useEffect(() => { diff --git a/src/pages/settings/server/Invites.tsx b/src/pages/settings/server/Invites.tsx index 594c1c5..ce41170 100644 --- a/src/pages/settings/server/Invites.tsx +++ b/src/pages/settings/server/Invites.tsx @@ -64,12 +64,14 @@ export function Invites({ server }: Props) { <code>{invite._id}</code> <span> <UserIcon target={creator} size={24} />{" "} - {creator?.username ?? "unknown"} + {creator?.username ?? ( + <Text id="app.main.channel.unknown_user" /> + )} </span> <span> {channel && creator ? getChannelName(ctx.client, channel, true) - : "#unknown"} + : "#??"} </span> <IconButton onClick={async () => { diff --git a/src/pages/settings/server/Members.tsx b/src/pages/settings/server/Members.tsx index 9bfeb9d..4f83a68 100644 --- a/src/pages/settings/server/Members.tsx +++ b/src/pages/settings/server/Members.tsx @@ -1,21 +1,30 @@ +import { ChevronDown } from "@styled-icons/boxicons-regular"; +import { isEqual } from "lodash"; import { Servers } from "revolt.js/dist/api/objects"; import styles from "./Panes.module.scss"; +import { Text } from "preact-i18n"; import { useEffect, useState } from "preact/hooks"; import { useForceUpdate, useUsers } from "../../../context/revoltjs/hooks"; +import UserIcon from "../../../components/common/user/UserIcon"; +import Button from "../../../components/ui/Button"; +import Checkbox from "../../../components/ui/Checkbox"; +import IconButton from "../../../components/ui/IconButton"; +import Overline from "../../../components/ui/Overline"; + interface Props { server: Servers.Server; } -// ! FIXME: bad code :) export function Members({ server }: Props) { const [members, setMembers] = useState<Servers.Member[] | undefined>( undefined, ); const ctx = useForceUpdate(); + const [selected, setSelected] = useState<undefined | string>(); const users = useUsers(members?.map((x) => x._id.user) ?? [], ctx); useEffect(() => { @@ -24,21 +33,125 @@ export function Members({ server }: Props) { .then((members) => setMembers(members)); }, []); + const [roles, setRoles] = useState<string[]>([]); + useEffect(() => { + if (selected) { + setRoles( + members!.find((x) => x._id.user === selected)?.roles ?? [], + ); + } + }, [selected]); + return ( - <div className={styles.members}> + <div className={styles.userList}> <div className={styles.subtitle}> {members?.length ?? 0} Members </div> {members && members.length > 0 && - users?.map( - (x) => - x && ( - <div className={styles.member}> - <div>@{x.username}</div> + members + .map((x) => { + return { + member: x, + user: users.find((y) => y?._id === x._id.user), + }; + }) + .map(({ member, user }) => ( + <> + <div + key={member._id.user} + className={styles.member} + data-open={selected === member._id.user} + onClick={() => + setSelected( + selected === member._id.user + ? undefined + : member._id.user, + ) + }> + <span> + <UserIcon target={user} size={24} />{" "} + {user?.username ?? ( + <Text id="app.main.channel.unknown_user" /> + )} + </span> + <IconButton className={styles.chevron}> + <ChevronDown size={24} /> + </IconButton> </div> - ), - )} + {selected === member._id.user && ( + <div + key={"drop_" + member._id.user} + className={styles.memberView}> + <Overline type="subtle">Roles</Overline> + {Object.keys(server.roles ?? {}).map( + (key) => { + let role = server.roles![key]; + return ( + <Checkbox + checked={ + roles.includes(key) ?? + false + } + onChange={(v) => { + if (v) { + setRoles([ + ...roles, + key, + ]); + } else { + setRoles( + roles.filter( + (x) => + x !== + key, + ), + ); + } + }}> + <span + style={{ + color: role.colour, + }}> + {role.name} + </span> + </Checkbox> + ); + }, + )} + <Button + compact + disabled={isEqual( + member.roles ?? [], + roles, + )} + onClick={async () => { + await ctx.client.servers.members.editMember( + server._id, + member._id.user, + { + roles, + }, + ); + + setMembers( + members.map((x) => + x._id.user === + member._id.user + ? { + ...x, + roles, + } + : x, + ), + ); + }}> + <Text id="app.special.modals.actions.save" /> + </Button> + </div> + )} + </> + ))} </div> ); } diff --git a/src/pages/settings/server/Panes.module.scss b/src/pages/settings/server/Panes.module.scss index 6837a83..117f8fa 100644 --- a/src/pages/settings/server/Panes.module.scss +++ b/src/pages/settings/server/Panes.module.scss @@ -41,7 +41,8 @@ } .invite, - .ban { + .ban, + .member { gap: 8px; padding: 10px; display: flex; @@ -69,25 +70,23 @@ opacity: 0.5; } } -} -.members { - .subtitle { - display: flex; - justify-content: space-between; - font-size: 13px; - text-transform: uppercase; - color: var(--secondary-foreground); - font-weight: 700; + .member { + cursor: pointer; + + .chevron { + transition: 0.2s ease all; + } + + &:not([data-open="true"]) .chevron { + transform: rotateZ(90deg); + } } - .member { - gap: 8px; + .memberView { padding: 10px; - display: flex; - align-items: center; - flex-direction: row; - background: var(--secondary-background); + margin: 0 10px; + background: var(--background); } } diff --git a/yarn.lock b/yarn.lock index 5e18fae..db7d5ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3563,10 +3563,10 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -revolt.js@4.3.3-alpha.16: - version "4.3.3-alpha.16" - resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-4.3.3-alpha.16.tgz#ed595d34cdefc1d8756694787abda01e28d373e8" - integrity sha512-UejqRKYoO98Uj2eki6dZMbEbX8msYz/JgY3EYxhbe1qMnBvLD8JxjcemHTLBf2Iytom8fFQ1EV0ee4Z89Jkcjw== +revolt.js@4.3.3-alpha.17: + version "4.3.3-alpha.17" + resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-4.3.3-alpha.17.tgz#0745d251c695840b87e98098bcc4d67c7cc15de5" + integrity sha512-MjxVnkkeX5md5NxZNRS9fl06jsjcDciAxKnbZ2rkBYJofQ94tvr1CYBWvFhS/u/tAR80HAPIEjJVC9HKJDK9Fg== dependencies: "@insertish/mutable" "1.1.0" axios "^0.19.2" -- GitLab