From dbaf246c27861e2f829f45a7041f77f34d39db2a Mon Sep 17 00:00:00 2001 From: Paul <paulmakles@gmail.com> Date: Sat, 24 Jul 2021 17:01:50 +0100 Subject: [PATCH] Use loading="lazy" in more places. i18n invites page. Polish the bans page. --- external/lang | 2 +- src/components/common/AgeGate.tsx | 1 + src/components/common/Emoji.tsx | 3 +- .../common/messaging/bars/FilePreview.tsx | 2 +- .../common/messaging/bars/TypingIndicator.tsx | 1 + .../common/messaging/embed/Embed.tsx | 1 + .../navigation/left/HomeSidebar.tsx | 4 +- .../navigation/right/MemberSidebar.tsx | 8 +- .../intermediate/modals/Onboarding.tsx | 2 +- .../intermediate/popovers/ImageViewer.tsx | 2 + src/context/revoltjs/hooks.ts | 3 +- src/pages/settings/panes/Appearance.tsx | 34 ++++++-- src/pages/settings/server/Bans.tsx | 84 +++++++++++++++---- src/pages/settings/server/Invites.tsx | 18 ++-- src/pages/settings/server/Panes.module.scss | 18 +++- 15 files changed, 142 insertions(+), 41 deletions(-) diff --git a/external/lang b/external/lang index b70b4f3..b40f8ce 160000 --- a/external/lang +++ b/external/lang @@ -1 +1 @@ -Subproject commit b70b4f395caf4dc4911c5ccbf7188de198875173 +Subproject commit b40f8ce53831a590c0ffdd02f8da9fd35b7a3701 diff --git a/src/components/common/AgeGate.tsx b/src/components/common/AgeGate.tsx index 0dbb8f3..324005b 100644 --- a/src/components/common/AgeGate.tsx +++ b/src/components/common/AgeGate.tsx @@ -67,6 +67,7 @@ export default function AgeGate(props: Props) { return ( <Base> <img + loading="eager" src={"https://static.revolt.chat/emoji/mutant/26a0.svg"} draggable={false} /> diff --git a/src/components/common/Emoji.tsx b/src/components/common/Emoji.tsx index f88577c..05d68f7 100644 --- a/src/components/common/Emoji.tsx +++ b/src/components/common/Emoji.tsx @@ -55,6 +55,7 @@ export default function Emoji({ return ( <img alt={emoji} + loading="lazy" className="emoji" draggable={false} src={parseEmoji(emoji)} @@ -66,7 +67,7 @@ export default function Emoji({ } export function generateEmoji(emoji: string) { - return `<img class="emoji" draggable="false" alt="${emoji}" src="${parseEmoji( + return `<img loading="lazy" class="emoji" draggable="false" alt="${emoji}" src="${parseEmoji( emoji, )}" />`; } diff --git a/src/components/common/messaging/bars/FilePreview.tsx b/src/components/common/messaging/bars/FilePreview.tsx index 1c4644a..7d18dc3 100644 --- a/src/components/common/messaging/bars/FilePreview.tsx +++ b/src/components/common/messaging/bars/FilePreview.tsx @@ -168,7 +168,7 @@ function FileEntry({ return ( <Entry className={index >= CAN_UPLOAD_AT_ONCE ? "fade" : ""}> <PreviewBox onClick={remove}> - <img class="icon" src={url} alt={file.name} /> + <img class="icon" src={url} alt={file.name} loading="eager" /> <div class="overlay"> <XCircle size={36} /> </div> diff --git a/src/components/common/messaging/bars/TypingIndicator.tsx b/src/components/common/messaging/bars/TypingIndicator.tsx index e58a85b..e319820 100644 --- a/src/components/common/messaging/bars/TypingIndicator.tsx +++ b/src/components/common/messaging/bars/TypingIndicator.tsx @@ -97,6 +97,7 @@ export function TypingIndicator({ typing }: Props) { <div className="avatars"> {users.map((user) => ( <img + loading="eager" src={client.users.getAvatarURL( user._id, { max_side: 256 }, diff --git a/src/components/common/messaging/embed/Embed.tsx b/src/components/common/messaging/embed/Embed.tsx index d28fba4..4ccfd70 100644 --- a/src/components/common/messaging/embed/Embed.tsx +++ b/src/components/common/messaging/embed/Embed.tsx @@ -91,6 +91,7 @@ export default function Embed({ embed }: Props) { <div className={styles.siteinfo}> {embed.icon_url && ( <img + loading="lazy" className={styles.favicon} src={client.proxyFile(embed.icon_url)} draggable={false} diff --git a/src/components/navigation/left/HomeSidebar.tsx b/src/components/navigation/left/HomeSidebar.tsx index d91f8d4..46497aa 100644 --- a/src/components/navigation/left/HomeSidebar.tsx +++ b/src/components/navigation/left/HomeSidebar.tsx @@ -143,7 +143,9 @@ function HomeSidebar(props: Props) { }) } /> - {channelsArr.length === 0 && <img src={placeholderSVG} />} + {channelsArr.length === 0 && ( + <img src={placeholderSVG} loading="eager" /> + )} {channelsArr.map((x) => { let user; if (x.channel_type === "DirectMessage") { diff --git a/src/components/navigation/right/MemberSidebar.tsx b/src/components/navigation/right/MemberSidebar.tsx index 0f70a03..0044b4e 100644 --- a/src/components/navigation/right/MemberSidebar.tsx +++ b/src/components/navigation/right/MemberSidebar.tsx @@ -144,7 +144,9 @@ export function GroupMemberSidebar({ } /> }> - {members.length === 0 && <img src={placeholderSVG} />} + {members.length === 0 && ( + <img src={placeholderSVG} loading="eager" /> + )} {members.map( (user) => user && ( @@ -257,7 +259,9 @@ export function ServerMemberSidebar({ {users.length} </span> }> - {users.length === 0 && <img src={placeholderSVG} />} + {users.length === 0 && ( + <img src={placeholderSVG} loading="eager" /> + )} {users.map( (user) => user && ( diff --git a/src/context/intermediate/modals/Onboarding.tsx b/src/context/intermediate/modals/Onboarding.tsx index b5db430..160f521 100644 --- a/src/context/intermediate/modals/Onboarding.tsx +++ b/src/context/intermediate/modals/Onboarding.tsx @@ -40,7 +40,7 @@ export function OnboardingModal({ onClose, callback }: Props) { <div className={styles.header}> <h1> <Text id="app.special.modals.onboarding.welcome" /> - <img src={wideSVG} /> + <img src={wideSVG} loading="eager" /> </h1> </div> <div className={styles.form}> diff --git a/src/context/intermediate/popovers/ImageViewer.tsx b/src/context/intermediate/popovers/ImageViewer.tsx index 2bd056e..b3f049e 100644 --- a/src/context/intermediate/popovers/ImageViewer.tsx +++ b/src/context/intermediate/popovers/ImageViewer.tsx @@ -37,6 +37,7 @@ export function ImageViewer({ attachment, embed, onClose }: Props) { {attachment && ( <> <img + loading="eager" src={client.generateFileURL(attachment)} width={(attachment.metadata as ImageMetadata).width} height={ @@ -49,6 +50,7 @@ export function ImageViewer({ attachment, embed, onClose }: Props) { {embed && ( <> <img + loading="eager" src={client.proxyFile(embed.url)} width={embed.width} height={embed.height} diff --git a/src/context/revoltjs/hooks.ts b/src/context/revoltjs/hooks.ts index 5eb575b..9fe16f5 100644 --- a/src/context/revoltjs/hooks.ts +++ b/src/context/revoltjs/hooks.ts @@ -3,10 +3,9 @@ import { Client, PermissionCalculator } from "revolt.js"; import { Channels, Servers, Users } from "revolt.js/dist/api/objects"; import Collection from "revolt.js/dist/maps/Collection"; -import { useCallback, useContext, useEffect, useState } from "preact/hooks"; +import { useContext, useEffect, useState } from "preact/hooks"; //#region Hooks v1 -// ! Hooks v1 will be deprecated soon. import { AppContext } from "./RevoltClient"; export interface HookContext { diff --git a/src/pages/settings/panes/Appearance.tsx b/src/pages/settings/panes/Appearance.tsx index ff1b6ce..75868ee 100644 --- a/src/pages/settings/panes/Appearance.tsx +++ b/src/pages/settings/panes/Appearance.tsx @@ -97,6 +97,7 @@ export function Component(props: Props) { <div className={styles.themes}> <div className={styles.theme}> <img + loading="eager" src={lightSVG} draggable={false} data-active={selected === "light"} @@ -104,7 +105,7 @@ export function Component(props: Props) { selected !== "light" && setTheme({ preset: "light" }) } - onContextMenu={e => e.preventDefault()} + onContextMenu={(e) => e.preventDefault()} /> <h4> <Text id="app.settings.pages.appearance.color.light" /> @@ -112,13 +113,14 @@ export function Component(props: Props) { </div> <div className={styles.theme}> <img + loading="eager" src={darkSVG} draggable={false} data-active={selected === "dark"} onClick={() => selected !== "dark" && setTheme({ preset: "dark" }) } - onContextMenu={e => e.preventDefault()} + onContextMenu={(e) => e.preventDefault()} /> <h4> <Text id="app.settings.pages.appearance.color.dark" /> @@ -202,7 +204,12 @@ export function Component(props: Props) { className={styles.button} onClick={() => setEmojiPack("mutant")} data-active={emojiPack === "mutant"}> - <img src={mutantSVG} draggable={false} onContextMenu={e => e.preventDefault()} /> + <img + loading="eager" + src={mutantSVG} + draggable={false} + onContextMenu={(e) => e.preventDefault()} + /> </div> <h4> Mutant Remix{" "} @@ -219,7 +226,12 @@ export function Component(props: Props) { className={styles.button} onClick={() => setEmojiPack("twemoji")} data-active={emojiPack === "twemoji"}> - <img src={twemojiSVG} draggable={false} onContextMenu={e => e.preventDefault()} /> + <img + loading="eager" + src={twemojiSVG} + draggable={false} + onContextMenu={(e) => e.preventDefault()} + /> </div> <h4>Twemoji</h4> </div> @@ -230,7 +242,12 @@ export function Component(props: Props) { className={styles.button} onClick={() => setEmojiPack("openmoji")} data-active={emojiPack === "openmoji"}> - <img src={openmojiSVG} draggable={false} onContextMenu={e => e.preventDefault()} /> + <img + loading="eager" + src={openmojiSVG} + draggable={false} + onContextMenu={(e) => e.preventDefault()} + /> </div> <h4>Openmoji</h4> </div> @@ -239,7 +256,12 @@ export function Component(props: Props) { className={styles.button} onClick={() => setEmojiPack("noto")} data-active={emojiPack === "noto"}> - <img src={notoSVG} draggable={false} onContextMenu={e => e.preventDefault()} /> + <img + loading="eager" + src={notoSVG} + draggable={false} + onContextMenu={(e) => e.preventDefault()} + /> </div> <h4>Noto Emoji</h4> </div> diff --git a/src/pages/settings/server/Bans.tsx b/src/pages/settings/server/Bans.tsx index af369d8..6497bba 100644 --- a/src/pages/settings/server/Bans.tsx +++ b/src/pages/settings/server/Bans.tsx @@ -1,10 +1,15 @@ -import { Servers } from "revolt.js/dist/api/objects"; +import { XCircle } from "@styled-icons/boxicons-regular"; +import { Servers, Users } from "revolt.js/dist/api/objects"; +import styles from "./Panes.module.scss"; +import { Text } from "preact-i18n"; import { useContext, useEffect, useState } from "preact/hooks"; import { AppContext } from "../../../context/revoltjs/RevoltClient"; -import Tip from "../../../components/ui/Tip"; +import UserIcon from "../../../components/common/user/UserIcon"; +import IconButton from "../../../components/ui/IconButton"; +import Preloader from "../../../components/ui/Preloader"; interface Props { server: Servers.Server; @@ -12,26 +17,71 @@ interface Props { export function Bans({ server }: Props) { const client = useContext(AppContext); - const [bans, setBans] = useState<Servers.Ban[] | undefined>(undefined); + const [deleting, setDelete] = useState<string[]>([]); + const [data, setData] = useState< + | { + users: Pick<Users.User, "_id" | "username" | "avatar">[]; + bans: Servers.Ban[]; + } + | undefined + >(undefined); useEffect(() => { - client.servers.fetchBans(server._id).then((bans) => setBans(bans)); + client.servers.fetchBans(server._id).then(setData as any); }, []); return ( - <div> - <Tip warning>This section is under construction.</Tip> - {bans?.map((x) => ( - <div> - {x._id.user}: {x.reason ?? "no reason"}{" "} - <button - onClick={() => - client.servers.unbanUser(server._id, x._id.user) - }> - unban - </button> - </div> - ))} + <div className={styles.userList}> + <div className={styles.subtitle}> + <span> + <Text id="app.settings.server_pages.bans.user" /> + </span> + <span class={styles.reason}> + <Text id="app.settings.server_pages.bans.reason" /> + </span> + <span> + <Text id="app.settings.server_pages.bans.revoke" /> + </span> + </div> + {typeof data === "undefined" && <Preloader type="ring" />} + {data?.bans.map((x) => { + let user = data.users.find((y) => y._id === x._id.user); + + return ( + <div + className={styles.ban} + data-deleting={deleting.indexOf(x._id.user) > -1}> + <span> + <UserIcon attachment={user?.avatar} size={24} /> + {user?.username} + </span> + <div className={styles.reason}> + {x.reason ?? ( + <Text id="app.settings.server_pages.bans.no_reason" /> + )} + </div> + <IconButton + onClick={async () => { + setDelete([...deleting, x._id.user]); + + await client.servers.unbanUser( + server._id, + x._id.user, + ); + + setData({ + ...data, + bans: data.bans.filter( + (y) => y._id.user !== x._id.user, + ), + }); + }} + disabled={deleting.indexOf(x._id.user) > -1}> + <XCircle size={24} /> + </IconButton> + </div> + ); + })} </div> ); } diff --git a/src/pages/settings/server/Invites.tsx b/src/pages/settings/server/Invites.tsx index fa69fbe..594c1c5 100644 --- a/src/pages/settings/server/Invites.tsx +++ b/src/pages/settings/server/Invites.tsx @@ -37,12 +37,20 @@ export function Invites({ server }: Props) { }, []); return ( - <div className={styles.invites}> + <div className={styles.userList}> <div className={styles.subtitle}> - <span>Invite Code</span> - <span>Invitor</span> - <span>Channel</span> - <span>Revoke</span> + <span> + <Text id="app.settings.server_pages.invites.code" /> + </span> + <span> + <Text id="app.settings.server_pages.invites.invitor" /> + </span> + <span> + <Text id="app.settings.server_pages.invites.channel" /> + </span> + <span> + <Text id="app.settings.server_pages.invites.revoke" /> + </span> </div> {typeof invites === "undefined" && <Preloader type="ring" />} {invites?.map((invite) => { diff --git a/src/pages/settings/server/Panes.module.scss b/src/pages/settings/server/Panes.module.scss index deb0d42..6837a83 100644 --- a/src/pages/settings/server/Panes.module.scss +++ b/src/pages/settings/server/Panes.module.scss @@ -17,21 +17,31 @@ } } -.invites { +.userList { gap: 8px; display: flex; flex-direction: column; .subtitle { + gap: 8px; display: flex; justify-content: space-between; font-size: 13px; text-transform: uppercase; color: var(--secondary-foreground); font-weight: 700; + + .reason { + text-align: center; + } } - .invite { + .reason { + flex: 2; + } + + .invite, + .ban { gap: 8px; padding: 10px; display: flex; @@ -39,8 +49,8 @@ flex-direction: row; background: var(--secondary-background); - code, - span { + span, + code { flex: 1; } -- GitLab