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 (
<Base>
<img
loading="eager"
src={"https://static.revolt.chat/emoji/mutant/26a0.svg"}
draggable={false}
/>

View file

@ -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,
)}" />`;
}

View file

@ -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>

View file

@ -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 },

View file

@ -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}

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) => {
let user;
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(
(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 && (

View file

@ -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}>

View file

@ -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}

View file

@ -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 {

View file

@ -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>

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 { 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 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>
);
}

View file

@ -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) => {

View file

@ -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;
}