Use loading="lazy" in more places.

i18n invites page.
Polish the bans page.
This commit is contained in:
Paul 2021-07-24 17:01:50 +01:00
parent ca975aae7b
commit dbaf246c27
15 changed files with 142 additions and 41 deletions

2
external/lang vendored

@ -1 +1 @@
Subproject commit b70b4f395caf4dc4911c5ccbf7188de198875173 Subproject commit b40f8ce53831a590c0ffdd02f8da9fd35b7a3701

View file

@ -67,6 +67,7 @@ export default function AgeGate(props: Props) {
return ( return (
<Base> <Base>
<img <img
loading="eager"
src={"https://static.revolt.chat/emoji/mutant/26a0.svg"} src={"https://static.revolt.chat/emoji/mutant/26a0.svg"}
draggable={false} draggable={false}
/> />

View file

@ -55,6 +55,7 @@ export default function Emoji({
return ( return (
<img <img
alt={emoji} alt={emoji}
loading="lazy"
className="emoji" className="emoji"
draggable={false} draggable={false}
src={parseEmoji(emoji)} src={parseEmoji(emoji)}
@ -66,7 +67,7 @@ export default function Emoji({
} }
export function generateEmoji(emoji: string) { 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, emoji,
)}" />`; )}" />`;
} }

View file

@ -168,7 +168,7 @@ function FileEntry({
return ( return (
<Entry className={index >= CAN_UPLOAD_AT_ONCE ? "fade" : ""}> <Entry className={index >= CAN_UPLOAD_AT_ONCE ? "fade" : ""}>
<PreviewBox onClick={remove}> <PreviewBox onClick={remove}>
<img class="icon" src={url} alt={file.name} /> <img class="icon" src={url} alt={file.name} loading="eager" />
<div class="overlay"> <div class="overlay">
<XCircle size={36} /> <XCircle size={36} />
</div> </div>

View file

@ -97,6 +97,7 @@ export function TypingIndicator({ typing }: Props) {
<div className="avatars"> <div className="avatars">
{users.map((user) => ( {users.map((user) => (
<img <img
loading="eager"
src={client.users.getAvatarURL( src={client.users.getAvatarURL(
user._id, user._id,
{ max_side: 256 }, { max_side: 256 },

View file

@ -91,6 +91,7 @@ export default function Embed({ embed }: Props) {
<div className={styles.siteinfo}> <div className={styles.siteinfo}>
{embed.icon_url && ( {embed.icon_url && (
<img <img
loading="lazy"
className={styles.favicon} className={styles.favicon}
src={client.proxyFile(embed.icon_url)} src={client.proxyFile(embed.icon_url)}
draggable={false} draggable={false}

View file

@ -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) => { {channelsArr.map((x) => {
let user; let user;
if (x.channel_type === "DirectMessage") { if (x.channel_type === "DirectMessage") {

View file

@ -144,7 +144,9 @@ export function GroupMemberSidebar({
} }
/> />
}> }>
{members.length === 0 && <img src={placeholderSVG} />} {members.length === 0 && (
<img src={placeholderSVG} loading="eager" />
)}
{members.map( {members.map(
(user) => (user) =>
user && ( user && (
@ -257,7 +259,9 @@ export function ServerMemberSidebar({
{users.length} {users.length}
</span> </span>
}> }>
{users.length === 0 && <img src={placeholderSVG} />} {users.length === 0 && (
<img src={placeholderSVG} loading="eager" />
)}
{users.map( {users.map(
(user) => (user) =>
user && ( user && (

View file

@ -40,7 +40,7 @@ export function OnboardingModal({ onClose, callback }: Props) {
<div className={styles.header}> <div className={styles.header}>
<h1> <h1>
<Text id="app.special.modals.onboarding.welcome" /> <Text id="app.special.modals.onboarding.welcome" />
<img src={wideSVG} /> <img src={wideSVG} loading="eager" />
</h1> </h1>
</div> </div>
<div className={styles.form}> <div className={styles.form}>

View file

@ -37,6 +37,7 @@ export function ImageViewer({ attachment, embed, onClose }: Props) {
{attachment && ( {attachment && (
<> <>
<img <img
loading="eager"
src={client.generateFileURL(attachment)} src={client.generateFileURL(attachment)}
width={(attachment.metadata as ImageMetadata).width} width={(attachment.metadata as ImageMetadata).width}
height={ height={
@ -49,6 +50,7 @@ export function ImageViewer({ attachment, embed, onClose }: Props) {
{embed && ( {embed && (
<> <>
<img <img
loading="eager"
src={client.proxyFile(embed.url)} src={client.proxyFile(embed.url)}
width={embed.width} width={embed.width}
height={embed.height} height={embed.height}

View file

@ -3,10 +3,9 @@ import { Client, PermissionCalculator } from "revolt.js";
import { Channels, Servers, Users } from "revolt.js/dist/api/objects"; import { Channels, Servers, Users } from "revolt.js/dist/api/objects";
import Collection from "revolt.js/dist/maps/Collection"; 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 //#region Hooks v1
// ! Hooks v1 will be deprecated soon.
import { AppContext } from "./RevoltClient"; import { AppContext } from "./RevoltClient";
export interface HookContext { export interface HookContext {

View file

@ -97,6 +97,7 @@ export function Component(props: Props) {
<div className={styles.themes}> <div className={styles.themes}>
<div className={styles.theme}> <div className={styles.theme}>
<img <img
loading="eager"
src={lightSVG} src={lightSVG}
draggable={false} draggable={false}
data-active={selected === "light"} data-active={selected === "light"}
@ -104,7 +105,7 @@ export function Component(props: Props) {
selected !== "light" && selected !== "light" &&
setTheme({ preset: "light" }) setTheme({ preset: "light" })
} }
onContextMenu={e => e.preventDefault()} onContextMenu={(e) => e.preventDefault()}
/> />
<h4> <h4>
<Text id="app.settings.pages.appearance.color.light" /> <Text id="app.settings.pages.appearance.color.light" />
@ -112,13 +113,14 @@ export function Component(props: Props) {
</div> </div>
<div className={styles.theme}> <div className={styles.theme}>
<img <img
loading="eager"
src={darkSVG} src={darkSVG}
draggable={false} draggable={false}
data-active={selected === "dark"} data-active={selected === "dark"}
onClick={() => onClick={() =>
selected !== "dark" && setTheme({ preset: "dark" }) selected !== "dark" && setTheme({ preset: "dark" })
} }
onContextMenu={e => e.preventDefault()} onContextMenu={(e) => e.preventDefault()}
/> />
<h4> <h4>
<Text id="app.settings.pages.appearance.color.dark" /> <Text id="app.settings.pages.appearance.color.dark" />
@ -202,7 +204,12 @@ export function Component(props: Props) {
className={styles.button} className={styles.button}
onClick={() => setEmojiPack("mutant")} onClick={() => setEmojiPack("mutant")}
data-active={emojiPack === "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> </div>
<h4> <h4>
Mutant Remix{" "} Mutant Remix{" "}
@ -219,7 +226,12 @@ export function Component(props: Props) {
className={styles.button} className={styles.button}
onClick={() => setEmojiPack("twemoji")} onClick={() => setEmojiPack("twemoji")}
data-active={emojiPack === "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> </div>
<h4>Twemoji</h4> <h4>Twemoji</h4>
</div> </div>
@ -230,7 +242,12 @@ export function Component(props: Props) {
className={styles.button} className={styles.button}
onClick={() => setEmojiPack("openmoji")} onClick={() => setEmojiPack("openmoji")}
data-active={emojiPack === "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> </div>
<h4>Openmoji</h4> <h4>Openmoji</h4>
</div> </div>
@ -239,7 +256,12 @@ export function Component(props: Props) {
className={styles.button} className={styles.button}
onClick={() => setEmojiPack("noto")} onClick={() => setEmojiPack("noto")}
data-active={emojiPack === "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> </div>
<h4>Noto Emoji</h4> <h4>Noto Emoji</h4>
</div> </div>

View file

@ -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 { useContext, useEffect, useState } from "preact/hooks";
import { AppContext } from "../../../context/revoltjs/RevoltClient"; 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 { interface Props {
server: Servers.Server; server: Servers.Server;
@ -12,26 +17,71 @@ interface Props {
export function Bans({ server }: Props) { export function Bans({ server }: Props) {
const client = useContext(AppContext); 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(() => { useEffect(() => {
client.servers.fetchBans(server._id).then((bans) => setBans(bans)); client.servers.fetchBans(server._id).then(setData as any);
}, []); }, []);
return ( return (
<div> <div className={styles.userList}>
<Tip warning>This section is under construction.</Tip> <div className={styles.subtitle}>
{bans?.map((x) => ( <span>
<div> <Text id="app.settings.server_pages.bans.user" />
{x._id.user}: {x.reason ?? "no reason"}{" "} </span>
<button <span class={styles.reason}>
onClick={() => <Text id="app.settings.server_pages.bans.reason" />
client.servers.unbanUser(server._id, x._id.user) </span>
}> <span>
unban <Text id="app.settings.server_pages.bans.revoke" />
</button> </span>
</div> </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> </div>
); );
} }

View file

@ -37,12 +37,20 @@ export function Invites({ server }: Props) {
}, []); }, []);
return ( return (
<div className={styles.invites}> <div className={styles.userList}>
<div className={styles.subtitle}> <div className={styles.subtitle}>
<span>Invite Code</span> <span>
<span>Invitor</span> <Text id="app.settings.server_pages.invites.code" />
<span>Channel</span> </span>
<span>Revoke</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> </div>
{typeof invites === "undefined" && <Preloader type="ring" />} {typeof invites === "undefined" && <Preloader type="ring" />}
{invites?.map((invite) => { {invites?.map((invite) => {

View file

@ -17,21 +17,31 @@
} }
} }
.invites { .userList {
gap: 8px; gap: 8px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.subtitle { .subtitle {
gap: 8px;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
font-size: 13px; font-size: 13px;
text-transform: uppercase; text-transform: uppercase;
color: var(--secondary-foreground); color: var(--secondary-foreground);
font-weight: 700; font-weight: 700;
.reason {
text-align: center;
}
} }
.invite { .reason {
flex: 2;
}
.invite,
.ban {
gap: 8px; gap: 8px;
padding: 10px; padding: 10px;
display: flex; display: flex;
@ -39,8 +49,8 @@
flex-direction: row; flex-direction: row;
background: var(--secondary-background); background: var(--secondary-background);
code, span,
span { code {
flex: 1; flex: 1;
} }