Make the linter happy.

This commit is contained in:
Paul
2021-08-05 14:47:00 +01:00
parent 5930415c05
commit 55e00bf93f
106 changed files with 1083 additions and 5554 deletions

View File

@@ -1,5 +1,5 @@
import { ListCheck, ListUl } from "@styled-icons/boxicons-regular";
import { Route, useHistory, useParams } from "react-router-dom";
import { Route, Switch, useHistory, useParams } from "react-router-dom";
import { Text } from "preact-i18n";
@@ -16,7 +16,9 @@ export default function ChannelSettings() {
const { channel: cid } = useParams<{ channel: string }>();
const client = useClient();
const history = useHistory();
const channel = client.channels.get(cid);
if (!channel) return null;
if (
channel.channel_type === "SavedMessages" ||
@@ -24,7 +26,6 @@ export default function ChannelSettings() {
)
return null;
const history = useHistory();
function switchPage(to?: string) {
let base_url;
switch (channel?.channel_type) {
@@ -67,18 +68,20 @@ export default function ChannelSettings() {
),
},
]}
children={[
<Route path="/server/:server/channel/:channel/settings/permissions">
<Permissions channel={channel} />
</Route>,
<Route path="/channel/:channel/settings/permissions">
<Permissions channel={channel} />
</Route>,
children={
<Switch>
<Route path="/server/:server/channel/:channel/settings/permissions">
<Permissions channel={channel} />
</Route>
<Route path="/channel/:channel/settings/permissions">
<Permissions channel={channel} />
</Route>
<Route path="/">
<Overview channel={channel} />
</Route>,
]}
<Route>
<Overview channel={channel} />
</Route>
</Switch>
}
category="channel_pages"
switchPage={switchPage}
defaultPage="overview"

View File

@@ -1,11 +1,11 @@
import { ArrowBack, X } from "@styled-icons/boxicons-regular";
import { Helmet } from "react-helmet";
import { Switch, useHistory, useParams } from "react-router-dom";
import { useHistory, useParams } from "react-router-dom";
import styles from "./Settings.module.scss";
import classNames from "classnames";
import { Text } from "preact-i18n";
import { useContext, useEffect, useState } from "preact/hooks";
import { useCallback, useContext, useEffect, useState } from "preact/hooks";
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
@@ -51,7 +51,7 @@ export function GenericSettings({
const { page } = useParams<{ page: string }>();
const [closing, setClosing] = useState(false);
function exitSettings() {
const exitSettings = useCallback(() => {
if (history.length > 1) {
setClosing(true);
@@ -61,7 +61,7 @@ export function GenericSettings({
} else {
history.push("/");
}
}
}, [history]);
useEffect(() => {
function keyDown(e: KeyboardEvent) {
@@ -72,7 +72,7 @@ export function GenericSettings({
document.body.addEventListener("keydown", keyDown);
return () => document.body.removeEventListener("keydown", keyDown);
}, []);
}, [exitSettings]);
return (
<div
@@ -158,7 +158,9 @@ export function GenericSettings({
<div className={styles.scrollbox}>
<div className={styles.contentcontainer}>
{!isTouchscreenDevice &&
!pages.find((x) => x.id === page && x.hideTitle) && (
!pages.find(
(x) => x.id === page && x.hideTitle,
) && (
<h1>
<Text
id={`app.settings.${category}.${
@@ -167,19 +169,20 @@ export function GenericSettings({
/>
</h1>
)}
<Switch>{children}</Switch>
{children}
</div>
{!isTouchscreenDevice && (
<div className={styles.action}>
<div onClick={exitSettings} className={styles.closeButton}>
<X size={28} />
<div className={styles.action}>
<div
onClick={exitSettings}
className={styles.closeButton}>
<X size={28} />
</div>
</div>
</div>
)}
</div>
</div>
)}
</div>
);
}

View File

@@ -1,7 +1,7 @@
import { ListUl, ListCheck, ListMinus } from "@styled-icons/boxicons-regular";
import { XSquare, Share, Group } from "@styled-icons/boxicons-solid";
import { observer } from "mobx-react-lite";
import { Route, useHistory, useParams } from "react-router-dom";
import { Route, Switch, useHistory, useParams } from "react-router-dom";
import { Text } from "preact-i18n";
@@ -77,34 +77,36 @@ export default observer(() => {
hideTitle: true,
},
]}
children={[
<Route path="/server/:server/settings/categories">
<Categories server={server} />
</Route>,
<Route path="/server/:server/settings/members">
<RequiresOnline>
<Members server={server} />
</RequiresOnline>
</Route>,
<Route path="/server/:server/settings/invites">
<RequiresOnline>
<Invites server={server} />
</RequiresOnline>
</Route>,
<Route path="/server/:server/settings/bans">
<RequiresOnline>
<Bans server={server} />
</RequiresOnline>
</Route>,
<Route path="/server/:server/settings/roles">
<RequiresOnline>
<Roles server={server} />
</RequiresOnline>
</Route>,
<Route path="/">
<Overview server={server} />
</Route>,
]}
children={
<Switch>
<Route path="/server/:server/settings/categories">
<Categories server={server} />
</Route>
<Route path="/server/:server/settings/members">
<RequiresOnline>
<Members server={server} />
</RequiresOnline>
</Route>
<Route path="/server/:server/settings/invites">
<RequiresOnline>
<Invites server={server} />
</RequiresOnline>
</Route>
<Route path="/server/:server/settings/bans">
<RequiresOnline>
<Bans server={server} />
</RequiresOnline>
</Route>
<Route path="/server/:server/settings/roles">
<RequiresOnline>
<Roles server={server} />
</RequiresOnline>
</Route>
<Route>
<Overview server={server} />
</Route>
</Switch>
}
category="server_pages"
switchPage={switchPage}
defaultPage="overview"

View File

@@ -15,7 +15,7 @@ import {
User,
Megaphone,
} from "@styled-icons/boxicons-solid";
import { Route, useHistory } from "react-router-dom";
import { Route, Switch, useHistory } from "react-router-dom";
import { LIBRARY_VERSION } from "revolt.js";
import styles from "./Settings.module.scss";
@@ -120,103 +120,107 @@ export default function Settings() {
title: <Text id="app.settings.pages.feedback.title" />,
},
]}
children={[
<Route path="/settings/profile">
<Profile />
</Route>,
<Route path="/settings/sessions">
<RequiresOnline>
<Sessions />
</RequiresOnline>
</Route>,
<Route path="/settings/appearance">
<Appearance />
</Route>,
<Route path="/settings/notifications">
<Notifications />
</Route>,
<Route path="/settings/language">
<Languages />
</Route>,
<Route path="/settings/sync">
<Sync />
</Route>,
<Route path="/settings/native">
<Native />
</Route>,
<Route path="/settings/experiments">
<ExperimentsPage />
</Route>,
<Route path="/settings/feedback">
<Feedback />
</Route>,
<Route path="/">
<Account />
</Route>,
]}
children={
<Switch>
<Route path="/settings/profile">
<Profile />
</Route>
<Route path="/settings/sessions">
<RequiresOnline>
<Sessions />
</RequiresOnline>
</Route>
<Route path="/settings/appearance">
<Appearance />
</Route>
<Route path="/settings/notifications">
<Notifications />
</Route>
<Route path="/settings/language">
<Languages />
</Route>
<Route path="/settings/sync">
<Sync />
</Route>
<Route path="/settings/native">
<Native />
</Route>
<Route path="/settings/experiments">
<ExperimentsPage />
</Route>
<Route path="/settings/feedback">
<Feedback />
</Route>
<Route path="/" exact>
<Account />
</Route>
</Switch>
}
defaultPage="account"
switchPage={switchPage}
category="pages"
custom={[
<a
href="https://gitlab.insrt.uk/revolt"
target="_blank"
rel="noreferrer">
<ButtonItem compact>
<Gitlab size={20} />
<Text id="app.settings.pages.source_code" />
custom={
<>
<a
href="https://gitlab.insrt.uk/revolt"
target="_blank"
rel="noreferrer">
<ButtonItem compact>
<Gitlab size={20} />
<Text id="app.settings.pages.source_code" />
</ButtonItem>
</a>
<a
href="https://insrt.uk/donate"
target="_blank"
rel="noreferrer">
<ButtonItem className={styles.donate} compact>
<Coffee size={20} />
<Text id="app.settings.pages.donate.title" />
</ButtonItem>
</a>
<LineDivider />
<ButtonItem
onClick={() => operations.logout()}
className={styles.logOut}
compact>
<LogOut size={20} />
<Text id="app.settings.pages.logOut" />
</ButtonItem>
</a>,
<a
href="https://insrt.uk/donate"
target="_blank"
rel="noreferrer">
<ButtonItem className={styles.donate} compact>
<Coffee size={20} />
<Text id="app.settings.pages.donate.title" />
</ButtonItem>
</a>,
<LineDivider />,
<ButtonItem
onClick={() => operations.logout()}
className={styles.logOut}
compact>
<LogOut size={20} />
<Text id="app.settings.pages.logOut" />
</ButtonItem>,
<div className={styles.version}>
<span className={styles.revision}>
<a
href={`${REPO_URL}/${GIT_REVISION}`}
target="_blank"
rel="noreferrer">
{GIT_REVISION.substr(0, 7)}
</a>
{` `}
<a
href={
GIT_BRANCH !== "DETACHED"
? `https://gitlab.insrt.uk/revolt/client/-/tree/${GIT_BRANCH}`
: undefined
}
target="_blank"
rel="noreferrer">
({GIT_BRANCH})
</a>
</span>
<span>
{GIT_BRANCH === "production" ? "Stable" : "Nightly"}{" "}
{APP_VERSION}
</span>
{window.isNative && (
<span>Native: {window.nativeVersion}</span>
)}
<span>
API: {client.configuration?.revolt ?? "N/A"}
</span>
<span>revolt.js: {LIBRARY_VERSION}</span>
</div>,
]}
<div className={styles.version}>
<span className={styles.revision}>
<a
href={`${REPO_URL}/${GIT_REVISION}`}
target="_blank"
rel="noreferrer">
{GIT_REVISION.substr(0, 7)}
</a>
{` `}
<a
href={
GIT_BRANCH !== "DETACHED"
? `https://gitlab.insrt.uk/revolt/client/-/tree/${GIT_BRANCH}`
: undefined
}
target="_blank"
rel="noreferrer">
({GIT_BRANCH})
</a>
</span>
<span>
{GIT_BRANCH === "production" ? "Stable" : "Nightly"}{" "}
{APP_VERSION}
</span>
{window.isNative && (
<span>Native: {window.nativeVersion}</span>
)}
<span>
API: {client.configuration?.revolt ?? "N/A"}
</span>
<span>revolt.js: {LIBRARY_VERSION}</span>
</div>
</>
}
/>
);
}

View File

@@ -1,14 +1,13 @@
import { observer } from "mobx-react-lite";
import { Channel } from "revolt.js/dist/maps/Channels";
import styled, { css } from "styled-components";
import styled from "styled-components";
import { Text } from "preact-i18n";
import { useContext, useEffect, useState } from "preact/hooks";
import { useEffect, useState } from "preact/hooks";
import TextAreaAutoSize from "../../../lib/TextAreaAutoSize";
import { FileUploader } from "../../../context/revoltjs/FileUploads";
import { AppContext } from "../../../context/revoltjs/RevoltClient";
import Button from "../../../components/ui/Button";
import InputBox from "../../../components/ui/InputBox";
@@ -31,8 +30,6 @@ const Row = styled.div`
`;
export default observer(({ channel }: Props) => {
const client = useContext(AppContext);
const [name, setName] = useState(channel.name ?? undefined);
const [description, setDescription] = useState(channel.description ?? "");
@@ -44,7 +41,7 @@ export default observer(({ channel }: Props) => {
const [changed, setChanged] = useState(false);
function save() {
const changes: any = {};
const changes: Record<string, string | undefined> = {};
if (name !== channel.name) changes.name = name;
if (description !== channel.description)
changes.description = description;

View File

@@ -64,6 +64,7 @@ export default observer(({ channel }: Props) => {
return (
<Checkbox
key={id}
checked={selected === id}
onChange={(selected) => selected && setSelected(id)}>
{role.name}

View File

@@ -6,7 +6,7 @@ import {
Trash,
} from "@styled-icons/boxicons-solid";
import { observer } from "mobx-react-lite";
import { Link, useHistory } from "react-router-dom";
import { useHistory } from "react-router-dom";
import { Profile } from "revolt-api/types/Users";
import styles from "./Panes.module.scss";
@@ -55,7 +55,7 @@ export const Account = observer(() => {
.user!.fetchProfile()
.then((profile) => setProfile(profile ?? {}));
}
}, [status]);
}, [client, email, profile, status]);
return (
<div className={styles.user}>
@@ -95,12 +95,17 @@ export const Account = observer(() => {
<div>
{(
[
["username", client.user!.username, <At size={24} />],
["email", email, <Envelope size={24} />],
["password", "•••••••••", <Key size={24} />],
[
"username",
client.user!.username,
<At key="at" size={24} />,
],
["email", email, <Envelope key="envelope" size={24} />],
["password", "•••••••••", <Key key="key" size={24} />],
] as const
).map(([field, value, icon]) => (
<CategoryButton
key={field}
icon={icon}
description={
field === "email" ? (
@@ -152,13 +157,15 @@ export const Account = observer(() => {
</h3>
<h5>
{/*<Text id="app.settings.pages.account.2fa.description" />*/}
Two-factor authentication is currently work-in-progress, see {` `}
Two-factor authentication is currently work-in-progress, see{" "}
{` `}
<a
href="https://gitlab.insrt.uk/insert/rauth/-/issues/2"
target="_blank"
rel="noreferrer">
tracking issue here
</a>.
</a>
.
</h5>
<CategoryButton
icon={<Lock size={24} color="var(--error)" />}
@@ -188,7 +195,7 @@ export const Account = observer(() => {
description={
"Delete your account, including all of your data."
}
onClick={() => {}}
hover
action="external">
<Text id="app.settings.pages.account.manage.delete" />
</CategoryButton>

View File

@@ -1,6 +1,6 @@
import { Reset, Import } from "@styled-icons/boxicons-regular";
import { Pencil } from "@styled-icons/boxicons-solid";
// @ts-ignore
// @ts-expect-error shade-blend-color does not have typings.
import pSBC from "shade-blend-color";
import styles from "./Panes.module.scss";
@@ -17,8 +17,10 @@ import { EmojiPacks, Settings } from "../../../redux/reducers/settings";
import {
DEFAULT_FONT,
DEFAULT_MONO_FONT,
Fonts,
FONTS,
FONT_KEYS,
MonospaceFonts,
MONOSPACE_FONTS,
MONOSPACE_FONT_KEYS,
Theme,
@@ -30,6 +32,7 @@ import { useIntermediate } from "../../../context/intermediate/Intermediate";
import CollapsibleSection from "../../../components/common/CollapsibleSection";
import Tooltip from "../../../components/common/Tooltip";
import Button from "../../../components/ui/Button";
import Checkbox from "../../../components/ui/Checkbox";
import ColourSwatches from "../../../components/ui/ColourSwatches";
import ComboBox from "../../../components/ui/ComboBox";
import InputBox from "../../../components/ui/InputBox";
@@ -56,12 +59,12 @@ export function Component(props: Props) {
});
}
function pushOverride(custom: Partial<Theme>) {
const pushOverride = useCallback((custom: Partial<Theme>) => {
dispatch({
type: "SETTINGS_SET_THEME_OVERRIDE",
custom,
});
}
}, []);
function setAccent(accent: string) {
setOverride({
@@ -80,12 +83,14 @@ export function Component(props: Props) {
});
}
const setOverride = useCallback(debounce(pushOverride, 200), []) as (
custom: Partial<Theme>,
) => void;
// eslint-disable-next-line react-hooks/exhaustive-deps
const setOverride = useCallback(
debounce(pushOverride as (...args: unknown[]) => void, 200),
[pushOverride],
) as (custom: Partial<Theme>) => void;
const [css, setCSS] = useState(props.settings.theme?.custom?.css ?? "");
useEffect(() => setOverride({ css }), [css]);
useEffect(() => setOverride({ css }), [setOverride, css]);
const selected = props.settings.theme?.preset ?? "dark";
return (
@@ -169,15 +174,15 @@ export function Component(props: Props) {
<ComboBox
value={theme.font ?? DEFAULT_FONT}
onChange={(e) =>
pushOverride({ font: e.currentTarget.value as any })
pushOverride({ font: e.currentTarget.value as Fonts })
}>
{FONT_KEYS.map((key) => (
<option value={key}>
<option value={key} key={key}>
{FONTS[key as keyof typeof FONTS].name}
</option>
))}
</ComboBox>
{/* TOFIX: Only show when a font with ligature support is selected, i.e.: Inter.
{/* TOFIX: Only show when a font with ligature support is selected, i.e.: Inter.*/}
<p>
<Checkbox
checked={props.settings.theme?.ligatures === true}
@@ -191,7 +196,7 @@ export function Component(props: Props) {
}>
<Text id="app.settings.pages.appearance.ligatures" />
</Checkbox>
</p>*/}
</p>
<h3>
<Text id="app.settings.pages.appearance.emoji_pack" />
@@ -405,11 +410,12 @@ export function Component(props: Props) {
value={theme.monospaceFont ?? DEFAULT_MONO_FONT}
onChange={(e) =>
pushOverride({
monospaceFont: e.currentTarget.value as any,
monospaceFont: e.currentTarget
.value as MonospaceFonts,
})
}>
{MONOSPACE_FONT_KEYS.map((key) => (
<option value={key}>
<option value={key} key={key}>
{
MONOSPACE_FONTS[
key as keyof typeof MONOSPACE_FONTS

View File

@@ -23,6 +23,7 @@ export function Component(props: Props) {
</h3>
{AVAILABLE_EXPERIMENTS.map((key) => (
<Checkbox
key={key}
checked={(props.options?.enabled ?? []).indexOf(key) > -1}
onChange={(enabled) =>
dispatch({

View File

@@ -70,7 +70,7 @@ export function Feedback() {
placeholder={
(
<Text id="app.settings.pages.feedback.other" />
) as any
) as unknown as string
}
/>
</Localizer>

View File

@@ -1,15 +1,9 @@
import { useEffect, useState } from "preact/hooks";
import { SyncOptions } from "../../../redux/reducers/sync";
import Button from "../../../components/ui/Button";
import Checkbox from "../../../components/ui/Checkbox";
interface Props {
options?: SyncOptions;
}
export function Native(props: Props) {
export function Native() {
const [config, setConfig] = useState(window.native.getConfig());
const [autoStart, setAutoStart] = useState<boolean | undefined>();
const fetchValue = () => window.native.getAutoStart().then(setAutoStart);
@@ -41,8 +35,7 @@ export function Native(props: Props) {
description="Launch Revolt when you log into your computer.">
Start with computer
</Checkbox>
<Checkbox
checked={config.discordRPC}
onChange={(discordRPC) => {
@@ -181,4 +174,4 @@ export function Native(props: Props) {
)}
</div>
);
}
}

View File

@@ -127,6 +127,7 @@ export function Component({ options }: Props) {
</h3>
{SOUNDS_ARRAY.map((key) => (
<Checkbox
key={key}
checked={!!enabledSounds[key]}
onChange={(enabled) =>
dispatch({

View File

@@ -1,8 +1,11 @@
import { Profile as ProfileI } from "revolt-api/types/Users";
import styles from "./Panes.module.scss";
import { IntlContext, Text, translate } from "preact-i18n";
import { useContext, useEffect, useState } from "preact/hooks";
import { Text } from "preact-i18n";
import { useCallback, useContext, useEffect, useState } from "preact/hooks";
import TextAreaAutoSize from "../../../lib/TextAreaAutoSize";
import { useTranslation } from "../../../lib/i18n";
import { UserProfile } from "../../../context/intermediate/popovers/UserProfile";
import { FileUploader } from "../../../context/revoltjs/FileUploads";
@@ -16,31 +19,27 @@ import AutoComplete, {
useAutoComplete,
} from "../../../components/common/AutoComplete";
import Button from "../../../components/ui/Button";
import { Profile } from "revolt-api/types/Users";
export function Profile() {
const { intl } = useContext(IntlContext);
const status = useContext(StatusContext);
const translate = useTranslation();
const client = useClient();
const [profile, setProfile] = useState<undefined | Profile>(
undefined,
);
const [profile, setProfile] = useState<undefined | ProfileI>(undefined);
// ! FIXME: temporary solution
// ! we should just announce profile changes through WS
function refreshProfile() {
const refreshProfile = useCallback(() => {
client
.user!.fetchProfile()
.then((profile) => setProfile(profile ?? {}));
}
}, [client.user, setProfile]);
useEffect(() => {
if (profile === undefined && status === ClientStatus.ONLINE) {
refreshProfile();
}
}, [status]);
}, [profile, status, refreshProfile]);
const [changed, setChanged] = useState(false);
function setContent(content?: string) {
@@ -69,7 +68,6 @@ export function Profile() {
user_id={client.user!._id}
dummy={true}
dummyProfile={profile}
onClose={() => {}}
/>
</div>
<div className={styles.row}>
@@ -85,9 +83,7 @@ export function Profile() {
behaviour="upload"
maxFileSize={4_000_000}
onUpload={(avatar) => client.users.edit({ avatar })}
remove={() =>
client.users.edit({ remove: "Avatar" })
}
remove={() => client.users.edit({ remove: "Avatar" })}
defaultPreview={client.user!.generateAvatarURL(
{ max_side: 256 },
true,
@@ -152,8 +148,6 @@ export function Profile() {
? "fetching"
: "placeholder"
}`,
"",
(intl as any).dictionary as Record<string, unknown>,
)}
onKeyUp={onKeyUp}
onKeyDown={onKeyDown}

View File

@@ -50,7 +50,7 @@ export function Sessions() {
);
setSessions(data);
});
}, []);
}, [client, setSessions, deviceId]);
if (typeof sessions === "undefined") {
return (
@@ -123,6 +123,7 @@ export function Sessions() {
const systemIcon = getSystemIcon(session);
return (
<div
key={session.id}
className={styles.entry}
data-active={session.id === deviceId}
data-deleting={

View File

@@ -26,6 +26,7 @@ export function Component(props: Props) {
] as [SyncKeys, string][]
).map(([key, title]) => (
<Checkbox
key={key}
checked={
(props.options?.disabled ?? []).indexOf(key) === -1
}

View File

@@ -5,9 +5,7 @@ import { Server } from "revolt.js/dist/maps/Servers";
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 { useEffect, useState } from "preact/hooks";
import UserIcon from "../../../components/common/user/UserIcon";
import IconButton from "../../../components/ui/IconButton";
@@ -18,15 +16,14 @@ interface Props {
}
export const Bans = observer(({ server }: Props) => {
const client = useContext(AppContext);
const [deleting, setDelete] = useState<string[]>([]);
const [data, setData] = useState<
Route<"GET", "/servers/id/bans">["response"] | undefined
>(undefined);
useEffect(() => {
server.fetchBans().then(setData as any);
}, []);
server.fetchBans().then(setData);
}, [server, setData]);
return (
<div className={styles.userList}>
@@ -43,10 +40,11 @@ export const Bans = observer(({ server }: Props) => {
</div>
{typeof data === "undefined" && <Preloader type="ring" />}
{data?.bans.map((x) => {
let user = data.users.find((y) => y._id === x._id.user);
const user = data.users.find((y) => y._id === x._id.user);
return (
<div
key={x._id.user}
className={styles.ban}
data-deleting={deleting.indexOf(x._id.user) > -1}>
<span>

View File

@@ -4,17 +4,12 @@ import { Category } from "revolt-api/types/Servers";
import { Server } from "revolt.js/dist/maps/Servers";
import { ulid } from "ulid";
import { useContext, useState } from "preact/hooks";
import { AppContext } from "../../../context/revoltjs/RevoltClient";
import { useState } from "preact/hooks";
import ChannelIcon from "../../../components/common/ChannelIcon";
import UserIcon from "../../../components/common/user/UserIcon";
import Button from "../../../components/ui/Button";
import ComboBox from "../../../components/ui/ComboBox";
import IconButton from "../../../components/ui/IconButton";
import InputBox from "../../../components/ui/InputBox";
import Preloader from "../../../components/ui/Preloader";
import Tip from "../../../components/ui/Tip";
interface Props {
@@ -23,7 +18,6 @@ interface Props {
// ! FIXME: really bad code
export const Categories = observer(({ server }: Props) => {
const client = useContext(AppContext);
const channels = server.channels.filter((x) => typeof x !== "undefined");
const [cats, setCats] = useState<Category[]>(server.categories ?? []);
@@ -95,6 +89,7 @@ export const Categories = observer(({ server }: Props) => {
{channels.map((channel) => {
return (
<div
key={channel!._id}
style={{
display: "flex",
gap: "12px",
@@ -131,7 +126,9 @@ export const Categories = observer(({ server }: Props) => {
}>
<option value="none">Uncategorised</option>
{cats.map((x) => (
<option value={x.id}>{x.title}</option>
<option key={x.id} value={x.id}>
{x.title}
</option>
))}
</ComboBox>
</div>

View File

@@ -32,7 +32,7 @@ export const Invites = observer(({ server }: Props) => {
useEffect(() => {
server.fetchInvites().then(setInvites);
}, []);
}, [server, setInvites]);
return (
<div className={styles.userList}>
@@ -57,6 +57,7 @@ export const Invites = observer(({ server }: Props) => {
return (
<div
key={invite._id}
className={styles.invite}
data-deleting={deleting.indexOf(invite._id) > -1}>
<code>{invite._id}</code>

View File

@@ -9,8 +9,6 @@ import styles from "./Panes.module.scss";
import { Text } from "preact-i18n";
import { useEffect, useState } from "preact/hooks";
import { useClient } from "../../../context/revoltjs/RevoltClient";
import UserIcon from "../../../components/common/user/UserIcon";
import Button from "../../../components/ui/Button";
import Checkbox from "../../../components/ui/Checkbox";
@@ -29,7 +27,7 @@ export const Members = observer(({ server }: Props) => {
useEffect(() => {
server.fetchMembers().then(setData);
}, []);
}, [server, setData]);
const [roles, setRoles] = useState<string[]>([]);
useEffect(() => {
@@ -38,7 +36,7 @@ export const Members = observer(({ server }: Props) => {
data!.members.find((x) => x._id.user === selected)?.roles ?? [],
);
}
}, [selected]);
}, [setRoles, selected, data]);
return (
<div className={styles.userList}>
@@ -57,9 +55,10 @@ export const Members = observer(({ server }: Props) => {
};
})
.map(({ member, user }) => (
<>
// @ts-expect-error brokey
// eslint-disable-next-line react/jsx-no-undef
<Fragment key={member._id.user}>
<div
key={member._id.user}
className={styles.member}
data-open={selected === member._id.user}
onClick={() =>
@@ -81,14 +80,15 @@ export const Members = observer(({ server }: Props) => {
</div>
{selected === member._id.user && (
<div
key={"drop_" + member._id.user}
key={`drop_${member._id.user}`}
className={styles.memberView}>
<Overline type="subtle">Roles</Overline>
{Object.keys(server.roles ?? {}).map(
(key) => {
let role = server.roles![key];
const role = server.roles![key];
return (
<Checkbox
key={key}
checked={
roles.includes(key) ??
false
@@ -134,7 +134,7 @@ export const Members = observer(({ server }: Props) => {
</Button>
</div>
)}
</>
</Fragment>
))}
</div>
);

View File

@@ -4,12 +4,11 @@ import { Server } from "revolt.js/dist/maps/Servers";
import styles from "./Panes.module.scss";
import { Text } from "preact-i18n";
import { useContext, useEffect, useState } from "preact/hooks";
import { useEffect, useState } from "preact/hooks";
import TextAreaAutoSize from "../../../lib/TextAreaAutoSize";
import { FileUploader } from "../../../context/revoltjs/FileUploads";
import { AppContext, useClient } from "../../../context/revoltjs/RevoltClient";
import { getChannelName } from "../../../context/revoltjs/util";
import Button from "../../../components/ui/Button";
@@ -21,8 +20,6 @@ interface Props {
}
export const Overview = observer(({ server }: Props) => {
const client = useClient();
const [name, setName] = useState(server.name);
const [description, setDescription] = useState(server.description ?? "");
const [systemMessages, setSystemMessages] = useState(
@@ -41,7 +38,7 @@ export const Overview = observer(({ server }: Props) => {
const [changed, setChanged] = useState(false);
function save() {
const changes: Record<string, any> = {};
const changes: Record<string, unknown> = {};
if (name !== server.name) changes.name = name;
if (description !== server.description)
changes.description = description;
@@ -122,6 +119,7 @@ export const Overview = observer(({ server }: Props) => {
].map(([i18n, key]) => (
// ! FIXME: temporary code just so we can expose the options
<p
key={key}
style={{
display: "flex",
gap: "8px",
@@ -156,7 +154,7 @@ export const Overview = observer(({ server }: Props) => {
{server.channels
.filter((x) => typeof x !== "undefined")
.map((channel) => (
<option value={channel!._id}>
<option key={channel!._id} value={channel!._id}>
{getChannelName(channel!, true)}
</option>
))}

View File

@@ -6,18 +6,15 @@ import { Server } from "revolt.js/dist/maps/Servers";
import styles from "./Panes.module.scss";
import { Text } from "preact-i18n";
import { useContext, useEffect, useState } from "preact/hooks";
import { useCallback, useEffect, useMemo, useState } from "preact/hooks";
import { useIntermediate } from "../../../context/intermediate/Intermediate";
import { AppContext } from "../../../context/revoltjs/RevoltClient";
import Button from "../../../components/ui/Button";
import Checkbox from "../../../components/ui/Checkbox";
import ColourSwatches from "../../../components/ui/ColourSwatches";
import IconButton from "../../../components/ui/IconButton";
import InputBox from "../../../components/ui/InputBox";
import Overline from "../../../components/ui/Overline";
import Tip from "../../../components/ui/Tip";
import ButtonItem from "../../../components/navigation/items/ButtonItem";
@@ -31,23 +28,29 @@ const I32ToU32 = (arr: number[]) => arr.map((x) => x >>> 0);
export const Roles = observer(({ server }: Props) => {
const [role, setRole] = useState("default");
const { openScreen } = useIntermediate();
const client = useContext(AppContext);
const roles = server.roles ?? {};
const roles = useMemo(() => server.roles ?? {}, [server]);
if (role !== "default" && typeof roles[role] === "undefined") {
useEffect(() => setRole("default"));
useEffect(() => setRole("default"), [role]);
return null;
}
function getPermissions(id: string) {
return I32ToU32(
id === "default"
? server.default_permissions
: roles[id].permissions,
);
}
const {
name: roleName,
colour: roleColour,
permissions,
} = roles[role] ?? {};
const { name: roleName, colour: roleColour } = roles[role] ?? {};
const getPermissions = useCallback(
(id: string) => {
return I32ToU32(
id === "default"
? server.default_permissions
: roles[id].permissions,
);
},
[roles, server],
);
const [perm, setPerm] = useState(getPermissions(role));
const [name, setName] = useState(roleName);
@@ -55,7 +58,7 @@ export const Roles = observer(({ server }: Props) => {
useEffect(
() => setPerm(getPermissions(role)),
[role, roles[role]?.permissions],
[getPermissions, role, permissions],
);
useEffect(() => setName(roleName), [role, roleName]);
@@ -115,6 +118,7 @@ export const Roles = observer(({ server }: Props) => {
}
return (
<ButtonItem
key={id}
active={role === id}
onClick={() => setRole(id)}
style={{ color: roles[id].colour }}>
@@ -174,6 +178,7 @@ export const Roles = observer(({ server }: Props) => {
return (
<Checkbox
key={key}
checked={(perm[0] & value) > 0}
onChange={() =>
setPerm([perm[0] ^ value, perm[1]])
@@ -199,6 +204,7 @@ export const Roles = observer(({ server }: Props) => {
return (
<Checkbox
key={key}
checked={((perm[1] >>> 0) & value) > 0}
onChange={() =>
setPerm([perm[0], perm[1] ^ value])