mirror of
https://github.com/stoatchat/for-legacy-web.git
synced 2026-03-07 01:15:28 +00:00
Make the linter happy.
This commit is contained in:
@@ -5,7 +5,7 @@ import update from "dayjs/plugin/updateLocale";
|
||||
import defaultsDeep from "lodash.defaultsdeep";
|
||||
|
||||
import { IntlProvider } from "preact-i18n";
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import { useCallback, useEffect, useState } from "preact/hooks";
|
||||
|
||||
import { connectState } from "../redux/connector";
|
||||
|
||||
@@ -165,12 +165,14 @@ export interface Dictionary {
|
||||
}
|
||||
|
||||
function Locale({ children, locale }: Props) {
|
||||
const [defns, setDefinition] = useState<Dictionary>(definition as any);
|
||||
const [defns, setDefinition] = useState<Dictionary>(
|
||||
definition as Dictionary,
|
||||
);
|
||||
|
||||
// Load relevant language information, fallback to English if invalid.
|
||||
const lang = Languages[locale] ?? Languages.en;
|
||||
|
||||
function transformLanguage(source: { [key: string]: any }) {
|
||||
function transformLanguage(source: Dictionary) {
|
||||
// Fallback untranslated strings to English (UK)
|
||||
const obj = defaultsDeep(source, definition);
|
||||
|
||||
@@ -216,43 +218,46 @@ function Locale({ children, locale }: Props) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
function loadLanguage(locale: string) {
|
||||
if (locale === "en") {
|
||||
// If English, make sure to restore everything to defaults.
|
||||
// Use what we already have.
|
||||
const defn = transformLanguage(definition);
|
||||
setDefinition(defn);
|
||||
dayjs.locale("en");
|
||||
dayjs.updateLocale("en", { calendar: defn.dayjs });
|
||||
return;
|
||||
}
|
||||
|
||||
import(`../../external/lang/${lang.i18n}.json`).then(
|
||||
async (lang_file) => {
|
||||
// Transform the definitions data.
|
||||
const defn = transformLanguage(lang_file.default);
|
||||
|
||||
// Determine and load dayjs locales.
|
||||
const target = lang.dayjs ?? lang.i18n;
|
||||
const dayjs_locale = await import(
|
||||
`../../node_modules/dayjs/esm/locale/${target}.js`
|
||||
);
|
||||
|
||||
// Load dayjs locales.
|
||||
dayjs.locale(target, dayjs_locale.default);
|
||||
|
||||
if (defn.dayjs) {
|
||||
// Override dayjs calendar locales with our own.
|
||||
dayjs.updateLocale(target, { calendar: defn.dayjs });
|
||||
}
|
||||
|
||||
// Apply definition to app.
|
||||
const loadLanguage = useCallback(
|
||||
(locale: string) => {
|
||||
if (locale === "en") {
|
||||
// If English, make sure to restore everything to defaults.
|
||||
// Use what we already have.
|
||||
const defn = transformLanguage(definition as Dictionary);
|
||||
setDefinition(defn);
|
||||
},
|
||||
);
|
||||
}
|
||||
dayjs.locale("en");
|
||||
dayjs.updateLocale("en", { calendar: defn.dayjs });
|
||||
return;
|
||||
}
|
||||
|
||||
useEffect(() => loadLanguage(locale), [locale, lang]);
|
||||
import(`../../external/lang/${lang.i18n}.json`).then(
|
||||
async (lang_file) => {
|
||||
// Transform the definitions data.
|
||||
const defn = transformLanguage(lang_file.default);
|
||||
|
||||
// Determine and load dayjs locales.
|
||||
const target = lang.dayjs ?? lang.i18n;
|
||||
const dayjs_locale = await import(
|
||||
`../../node_modules/dayjs/esm/locale/${target}.js`
|
||||
);
|
||||
|
||||
// Load dayjs locales.
|
||||
dayjs.locale(target, dayjs_locale.default);
|
||||
|
||||
if (defn.dayjs) {
|
||||
// Override dayjs calendar locales with our own.
|
||||
dayjs.updateLocale(target, { calendar: defn.dayjs });
|
||||
}
|
||||
|
||||
// Apply definition to app.
|
||||
setDefinition(defn);
|
||||
},
|
||||
);
|
||||
},
|
||||
[lang.dayjs, lang.i18n],
|
||||
);
|
||||
|
||||
useEffect(() => loadLanguage(locale), [locale, lang, loadLanguage]);
|
||||
|
||||
useEffect(() => {
|
||||
// Apply RTL language format.
|
||||
|
||||
@@ -4,8 +4,6 @@ import { createGlobalStyle } from "styled-components";
|
||||
import { createContext } from "preact";
|
||||
import { useEffect } from "preact/hooks";
|
||||
|
||||
import { isTouchscreenDevice } from "../lib/isTouchscreenDevice";
|
||||
|
||||
import { connectState } from "../redux/connector";
|
||||
|
||||
import { Children } from "../types/Preact";
|
||||
@@ -311,17 +309,17 @@ function Theme({ children, options }: Props) {
|
||||
const font = theme.font ?? DEFAULT_FONT;
|
||||
root.setProperty("--font", `"${font}"`);
|
||||
FONTS[font].load();
|
||||
}, [theme.font]);
|
||||
}, [root, theme.font]);
|
||||
|
||||
useEffect(() => {
|
||||
const font = theme.monospaceFont ?? DEFAULT_MONO_FONT;
|
||||
root.setProperty("--monospace-font", `"${font}"`);
|
||||
MONOSPACE_FONTS[font].load();
|
||||
}, [theme.monospaceFont]);
|
||||
}, [root, theme.monospaceFont]);
|
||||
|
||||
useEffect(() => {
|
||||
root.setProperty("--ligatures", options?.ligatures ? "normal" : "none");
|
||||
}, [options?.ligatures]);
|
||||
}, [root, options?.ligatures]);
|
||||
|
||||
useEffect(() => {
|
||||
const resize = () =>
|
||||
@@ -330,7 +328,7 @@ function Theme({ children, options }: Props) {
|
||||
|
||||
window.addEventListener("resize", resize);
|
||||
return () => window.removeEventListener("resize", resize);
|
||||
}, []);
|
||||
}, [root]);
|
||||
|
||||
return (
|
||||
<ThemeContext.Provider value={theme}>
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
|
||||
import { createContext } from "preact";
|
||||
import { useContext, useEffect, useMemo, useRef, useState } from "preact/hooks";
|
||||
import {
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "preact/hooks";
|
||||
|
||||
import type { ProduceType, VoiceUser } from "../lib/vortex/Types";
|
||||
import type VoiceClient from "../lib/vortex/VoiceClient";
|
||||
|
||||
import { Children } from "../types/Preact";
|
||||
import { SoundContext } from "./Settings";
|
||||
import { AppContext } from "./revoltjs/RevoltClient";
|
||||
|
||||
export enum VoiceStatus {
|
||||
LOADING = 0,
|
||||
@@ -45,20 +51,22 @@ type Props = {
|
||||
};
|
||||
|
||||
export default function Voice({ children }: Props) {
|
||||
const revoltClient = useContext(AppContext);
|
||||
const [client, setClient] = useState<VoiceClient | undefined>(undefined);
|
||||
const [state, setState] = useState<VoiceState>({
|
||||
status: VoiceStatus.LOADING,
|
||||
participants: new Map(),
|
||||
});
|
||||
|
||||
function setStatus(status: VoiceStatus, roomId?: string) {
|
||||
setState({
|
||||
status,
|
||||
roomId: roomId ?? client?.roomId,
|
||||
participants: client?.participants ?? new Map(),
|
||||
});
|
||||
}
|
||||
const setStatus = useCallback(
|
||||
(status: VoiceStatus, roomId?: string) => {
|
||||
setState({
|
||||
status,
|
||||
roomId: roomId ?? client?.roomId,
|
||||
participants: client?.participants ?? new Map(),
|
||||
});
|
||||
},
|
||||
[client?.participants, client?.roomId],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
import("../lib/vortex/VoiceClient")
|
||||
@@ -76,7 +84,7 @@ export default function Voice({ children }: Props) {
|
||||
console.error("Failed to load voice library!", err);
|
||||
setStatus(VoiceStatus.UNAVAILABLE);
|
||||
});
|
||||
}, []);
|
||||
}, [setStatus]);
|
||||
|
||||
const isConnecting = useRef(false);
|
||||
const operations: VoiceOperations = useMemo(() => {
|
||||
@@ -158,7 +166,7 @@ export default function Voice({ children }: Props) {
|
||||
return client?.stopProduce(type);
|
||||
},
|
||||
};
|
||||
}, [client]);
|
||||
}, [client, setStatus]);
|
||||
|
||||
const playSound = useContext(SoundContext);
|
||||
|
||||
@@ -200,7 +208,7 @@ export default function Voice({ children }: Props) {
|
||||
client.removeListener("userStopProduce", stateUpdate);
|
||||
client.removeListener("close", stateUpdate);
|
||||
};
|
||||
}, [client, state]);
|
||||
}, [client, state, playSound, setStatus]);
|
||||
|
||||
return (
|
||||
<VoiceContext.Provider value={state}>
|
||||
|
||||
@@ -89,13 +89,16 @@ export type Screen =
|
||||
};
|
||||
|
||||
export const IntermediateContext = createContext({
|
||||
screen: { id: "none" } as Screen,
|
||||
screen: { id: "none" },
|
||||
focusTaken: false,
|
||||
});
|
||||
|
||||
export const IntermediateActionsContext = createContext({
|
||||
openScreen: (screen: Screen) => {},
|
||||
writeClipboard: (text: string) => {},
|
||||
export const IntermediateActionsContext = createContext<{
|
||||
openScreen: (screen: Screen) => void;
|
||||
writeClipboard: (text: string) => void;
|
||||
}>({
|
||||
openScreen: null!,
|
||||
writeClipboard: null!,
|
||||
});
|
||||
|
||||
interface Props {
|
||||
@@ -130,12 +133,20 @@ export default function Intermediate(props: Props) {
|
||||
const navigate = (path: string) => history.push(path);
|
||||
|
||||
const subs = [
|
||||
internalSubscribe("Intermediate", "openProfile", openProfile),
|
||||
internalSubscribe("Intermediate", "navigate", navigate),
|
||||
internalSubscribe(
|
||||
"Intermediate",
|
||||
"openProfile",
|
||||
openProfile as (...args: unknown[]) => void,
|
||||
),
|
||||
internalSubscribe(
|
||||
"Intermediate",
|
||||
"navigate",
|
||||
navigate as (...args: unknown[]) => void,
|
||||
),
|
||||
];
|
||||
|
||||
return () => subs.map((unsub) => unsub());
|
||||
}, []);
|
||||
}, [history]);
|
||||
|
||||
return (
|
||||
<IntermediateContext.Provider value={value}>
|
||||
|
||||
@@ -12,7 +12,7 @@ import { SignedOutModal } from "./modals/SignedOut";
|
||||
|
||||
export interface Props {
|
||||
screen: Screen;
|
||||
openScreen: (id: any) => void;
|
||||
openScreen: (screen: Screen) => void;
|
||||
}
|
||||
|
||||
export default function Modals({ screen, openScreen }: Props) {
|
||||
|
||||
@@ -29,7 +29,7 @@ export function OnboardingModal({ onClose, callback }: Props) {
|
||||
setLoading(true);
|
||||
callback(username, true)
|
||||
.then(() => onClose())
|
||||
.catch((err: any) => {
|
||||
.catch((err: unknown) => {
|
||||
setError(takeError(err));
|
||||
setLoading(false);
|
||||
});
|
||||
|
||||
@@ -238,7 +238,7 @@ export const SpecialPromptModal = observer((props: SpecialProps) => {
|
||||
.then((code) => setCode(code))
|
||||
.catch((err) => setError(takeError(err)))
|
||||
.finally(() => setProcessing(false));
|
||||
}, []);
|
||||
}, [props.target]);
|
||||
|
||||
return (
|
||||
<PromptModal
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import { Attachment, AttachmentMetadata } from "revolt-api/types/Autumn";
|
||||
import { EmbedImage } from "revolt-api/types/January";
|
||||
|
||||
|
||||
@@ -85,11 +85,13 @@ export function ModifyAccountModal({ onClose, field }: Props) {
|
||||
]}>
|
||||
{/* Preact / React typing incompatabilities */}
|
||||
<form
|
||||
onSubmit={
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
handleSubmit(
|
||||
onSubmit,
|
||||
) as JSX.GenericEventHandler<HTMLFormElement>
|
||||
}>
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
)(e as any);
|
||||
}}>
|
||||
{field === "email" && (
|
||||
<FormField
|
||||
type="email"
|
||||
|
||||
@@ -42,6 +42,7 @@ export function UserPicker(props: Props) {
|
||||
)
|
||||
.map((x) => (
|
||||
<UserCheckbox
|
||||
key={x._id}
|
||||
user={x}
|
||||
checked={selected.includes(x._id)}
|
||||
onChange={(v) => {
|
||||
|
||||
@@ -20,7 +20,6 @@ import Preloader from "../../../components/ui/Preloader";
|
||||
|
||||
import Markdown from "../../../components/markdown/Markdown";
|
||||
import {
|
||||
AppContext,
|
||||
ClientStatus,
|
||||
StatusContext,
|
||||
useClient,
|
||||
@@ -30,7 +29,7 @@ import { useIntermediate } from "../Intermediate";
|
||||
interface Props {
|
||||
user_id: string;
|
||||
dummy?: boolean;
|
||||
onClose: () => void;
|
||||
onClose?: () => void;
|
||||
dummyProfile?: Profile;
|
||||
}
|
||||
|
||||
@@ -60,7 +59,7 @@ export const UserProfile = observer(
|
||||
|
||||
const user = client.users.get(user_id);
|
||||
if (!user) {
|
||||
useEffect(onClose, []);
|
||||
if (onClose) useEffect(onClose, []);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -76,7 +75,7 @@ export const UserProfile = observer(
|
||||
if (!user_id) return;
|
||||
if (typeof profile !== "undefined") setProfile(undefined);
|
||||
if (typeof mutual !== "undefined") setMutual(undefined);
|
||||
}, [user_id]);
|
||||
}, [user_id, mutual, profile]);
|
||||
|
||||
if (dummy) {
|
||||
useLayoutEffect(() => {
|
||||
@@ -93,7 +92,7 @@ export const UserProfile = observer(
|
||||
setMutual(null);
|
||||
user.fetchMutual().then(setMutual);
|
||||
}
|
||||
}, [mutual, status]);
|
||||
}, [mutual, status, dummy, user]);
|
||||
|
||||
useEffect(() => {
|
||||
if (dummy) return;
|
||||
@@ -104,12 +103,10 @@ export const UserProfile = observer(
|
||||
setProfile(null);
|
||||
|
||||
if (user.permission & UserPermission.ViewProfile) {
|
||||
user.fetchProfile()
|
||||
.then(setProfile)
|
||||
.catch(() => {});
|
||||
user.fetchProfile().then(setProfile);
|
||||
}
|
||||
}
|
||||
}, [profile, status]);
|
||||
}, [profile, status, dummy, user]);
|
||||
|
||||
const backgroundURL =
|
||||
profile &&
|
||||
@@ -157,7 +154,7 @@ export const UserProfile = observer(
|
||||
}>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
onClose();
|
||||
onClose?.();
|
||||
history.push(`/open/${user_id}`);
|
||||
}}>
|
||||
<Envelope size={30} />
|
||||
@@ -168,7 +165,7 @@ export const UserProfile = observer(
|
||||
{user.relationship === RelationshipStatus.User && (
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
onClose();
|
||||
onClose?.();
|
||||
if (dummy) return;
|
||||
history.push(`/settings/profile`);
|
||||
}}>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Plus, X, XCircle } from "@styled-icons/boxicons-regular";
|
||||
import { Plus } from "@styled-icons/boxicons-regular";
|
||||
import { Pencil } from "@styled-icons/boxicons-solid";
|
||||
import Axios, { AxiosRequestConfig } from "axios";
|
||||
|
||||
@@ -147,6 +147,7 @@ export function FileUploader(props: Props) {
|
||||
}
|
||||
|
||||
if (props.behaviour === "multi" && props.append) {
|
||||
// eslint-disable-next-line
|
||||
useEffect(() => {
|
||||
// File pasting.
|
||||
function paste(e: ClipboardEvent) {
|
||||
@@ -210,7 +211,7 @@ export function FileUploader(props: Props) {
|
||||
document.removeEventListener("dragover", dragover);
|
||||
document.removeEventListener("drop", drop);
|
||||
};
|
||||
}, [props.append]);
|
||||
}, [openScreen, props, props.append]);
|
||||
}
|
||||
|
||||
if (props.style === "icon" || props.style === "banner") {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { autorun, reaction } from "mobx";
|
||||
import { Route, Switch, useHistory, useParams } from "react-router-dom";
|
||||
import { Presence, RelationshipStatus } from "revolt-api/types/Users";
|
||||
import { SYSTEM_USER_ID } from "revolt.js";
|
||||
@@ -6,7 +5,7 @@ import { Message } from "revolt.js/dist/maps/Messages";
|
||||
import { User } from "revolt.js/dist/maps/Users";
|
||||
import { decodeTime } from "ulid";
|
||||
|
||||
import { useContext, useEffect } from "preact/hooks";
|
||||
import { useCallback, useContext, useEffect } from "preact/hooks";
|
||||
|
||||
import { useTranslation } from "../../lib/i18n";
|
||||
|
||||
@@ -52,191 +51,206 @@ function Notifier({ options, notifs }: Props) {
|
||||
const history = useHistory();
|
||||
const playSound = useContext(SoundContext);
|
||||
|
||||
async function message(msg: Message) {
|
||||
if (msg.author_id === client.user!._id) return;
|
||||
if (msg.channel_id === channel_id && document.hasFocus()) return;
|
||||
if (client.user!.status?.presence === Presence.Busy) return;
|
||||
if (msg.author?.relationship === RelationshipStatus.Blocked) return;
|
||||
const message = useCallback(
|
||||
async (msg: Message) => {
|
||||
if (msg.author_id === client.user!._id) return;
|
||||
if (msg.channel_id === channel_id && document.hasFocus()) return;
|
||||
if (client.user!.status?.presence === Presence.Busy) return;
|
||||
if (msg.author?.relationship === RelationshipStatus.Blocked) return;
|
||||
|
||||
const notifState = getNotificationState(notifs, msg.channel!);
|
||||
if (!shouldNotify(notifState, msg, client.user!._id)) return;
|
||||
const notifState = getNotificationState(notifs, msg.channel!);
|
||||
if (!shouldNotify(notifState, msg, client.user!._id)) return;
|
||||
|
||||
playSound("message");
|
||||
if (!showNotification) return;
|
||||
playSound("message");
|
||||
if (!showNotification) return;
|
||||
|
||||
let title;
|
||||
switch (msg.channel?.channel_type) {
|
||||
case "SavedMessages":
|
||||
return;
|
||||
case "DirectMessage":
|
||||
title = `@${msg.author?.username}`;
|
||||
break;
|
||||
case "Group":
|
||||
if (msg.author?._id === SYSTEM_USER_ID) {
|
||||
title = msg.channel.name;
|
||||
} else {
|
||||
title = `@${msg.author?.username} - ${msg.channel.name}`;
|
||||
let title;
|
||||
switch (msg.channel?.channel_type) {
|
||||
case "SavedMessages":
|
||||
return;
|
||||
case "DirectMessage":
|
||||
title = `@${msg.author?.username}`;
|
||||
break;
|
||||
case "Group":
|
||||
if (msg.author?._id === SYSTEM_USER_ID) {
|
||||
title = msg.channel.name;
|
||||
} else {
|
||||
title = `@${msg.author?.username} - ${msg.channel.name}`;
|
||||
}
|
||||
break;
|
||||
case "TextChannel":
|
||||
title = `@${msg.author?.username} (#${msg.channel.name}, ${msg.channel.server?.name})`;
|
||||
break;
|
||||
default:
|
||||
title = msg.channel?._id;
|
||||
break;
|
||||
}
|
||||
|
||||
let image;
|
||||
if (msg.attachments) {
|
||||
const imageAttachment = msg.attachments.find(
|
||||
(x) => x.metadata.type === "Image",
|
||||
);
|
||||
if (imageAttachment) {
|
||||
image = client.generateFileURL(imageAttachment, {
|
||||
max_side: 720,
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "TextChannel":
|
||||
title = `@${msg.author?.username} (#${msg.channel.name}, ${msg.channel.server?.name})`;
|
||||
break;
|
||||
default:
|
||||
title = msg.channel?._id;
|
||||
break;
|
||||
}
|
||||
|
||||
let image;
|
||||
if (msg.attachments) {
|
||||
const imageAttachment = msg.attachments.find(
|
||||
(x) => x.metadata.type === "Image",
|
||||
);
|
||||
if (imageAttachment) {
|
||||
image = client.generateFileURL(imageAttachment, {
|
||||
max_side: 720,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let body, icon;
|
||||
if (typeof msg.content === "string") {
|
||||
body = client.markdownToText(msg.content);
|
||||
icon = msg.author?.generateAvatarURL({ max_side: 256 });
|
||||
} else {
|
||||
const users = client.users;
|
||||
switch (msg.content.type) {
|
||||
case "user_added":
|
||||
case "user_remove":
|
||||
{
|
||||
let user = users.get(msg.content.id);
|
||||
body = translate(
|
||||
`app.main.channel.system.${
|
||||
msg.content.type === "user_added"
|
||||
? "added_by"
|
||||
: "removed_by"
|
||||
}`,
|
||||
{
|
||||
user: user?.username,
|
||||
other_user: users.get(msg.content.by)?.username,
|
||||
},
|
||||
);
|
||||
icon = user?.generateAvatarURL({
|
||||
max_side: 256,
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "user_joined":
|
||||
case "user_left":
|
||||
case "user_kicked":
|
||||
case "user_banned":
|
||||
{
|
||||
let user = users.get(msg.content.id);
|
||||
body = translate(
|
||||
`app.main.channel.system.${msg.content.type}`,
|
||||
{ user: user?.username },
|
||||
);
|
||||
icon = user?.generateAvatarURL({
|
||||
max_side: 256,
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "channel_renamed":
|
||||
{
|
||||
let user = users.get(msg.content.by);
|
||||
body = translate(
|
||||
`app.main.channel.system.channel_renamed`,
|
||||
{
|
||||
user: users.get(msg.content.by)?.username,
|
||||
name: msg.content.name,
|
||||
},
|
||||
);
|
||||
icon = user?.generateAvatarURL({
|
||||
max_side: 256,
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "channel_description_changed":
|
||||
case "channel_icon_changed":
|
||||
{
|
||||
let user = users.get(msg.content.by);
|
||||
body = translate(
|
||||
`app.main.channel.system.${msg.content.type}`,
|
||||
{ user: users.get(msg.content.by)?.username },
|
||||
);
|
||||
icon = user?.generateAvatarURL({
|
||||
max_side: 256,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const notif = await createNotification(title!, {
|
||||
icon,
|
||||
image,
|
||||
body,
|
||||
timestamp: decodeTime(msg._id),
|
||||
tag: msg.channel?._id,
|
||||
badge: "/assets/icons/android-chrome-512x512.png",
|
||||
silent: true,
|
||||
});
|
||||
|
||||
if (notif) {
|
||||
notif.addEventListener("click", () => {
|
||||
window.focus();
|
||||
const id = msg.channel_id;
|
||||
if (id !== channel_id) {
|
||||
const channel = client.channels.get(id);
|
||||
if (channel) {
|
||||
if (channel.channel_type === "TextChannel") {
|
||||
history.push(
|
||||
`/server/${channel.server_id}/channel/${id}`,
|
||||
let body, icon;
|
||||
if (typeof msg.content === "string") {
|
||||
body = client.markdownToText(msg.content);
|
||||
icon = msg.author?.generateAvatarURL({ max_side: 256 });
|
||||
} else {
|
||||
const users = client.users;
|
||||
switch (msg.content.type) {
|
||||
case "user_added":
|
||||
case "user_remove":
|
||||
{
|
||||
const user = users.get(msg.content.id);
|
||||
body = translate(
|
||||
`app.main.channel.system.${
|
||||
msg.content.type === "user_added"
|
||||
? "added_by"
|
||||
: "removed_by"
|
||||
}`,
|
||||
{
|
||||
user: user?.username,
|
||||
other_user: users.get(msg.content.by)
|
||||
?.username,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
history.push(`/channel/${id}`);
|
||||
icon = user?.generateAvatarURL({
|
||||
max_side: 256,
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "user_joined":
|
||||
case "user_left":
|
||||
case "user_kicked":
|
||||
case "user_banned":
|
||||
{
|
||||
const user = users.get(msg.content.id);
|
||||
body = translate(
|
||||
`app.main.channel.system.${msg.content.type}`,
|
||||
{ user: user?.username },
|
||||
);
|
||||
icon = user?.generateAvatarURL({
|
||||
max_side: 256,
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "channel_renamed":
|
||||
{
|
||||
const user = users.get(msg.content.by);
|
||||
body = translate(
|
||||
`app.main.channel.system.channel_renamed`,
|
||||
{
|
||||
user: users.get(msg.content.by)?.username,
|
||||
name: msg.content.name,
|
||||
},
|
||||
);
|
||||
icon = user?.generateAvatarURL({
|
||||
max_side: 256,
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "channel_description_changed":
|
||||
case "channel_icon_changed":
|
||||
{
|
||||
const user = users.get(msg.content.by);
|
||||
body = translate(
|
||||
`app.main.channel.system.${msg.content.type}`,
|
||||
{ user: users.get(msg.content.by)?.username },
|
||||
);
|
||||
icon = user?.generateAvatarURL({
|
||||
max_side: 256,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const notif = await createNotification(title!, {
|
||||
icon,
|
||||
image,
|
||||
body,
|
||||
timestamp: decodeTime(msg._id),
|
||||
tag: msg.channel?._id,
|
||||
badge: "/assets/icons/android-chrome-512x512.png",
|
||||
silent: true,
|
||||
});
|
||||
|
||||
notifications[msg.channel_id] = notif;
|
||||
notif.addEventListener(
|
||||
"close",
|
||||
() => delete notifications[msg.channel_id],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function relationship(user: User) {
|
||||
if (client.user?.status?.presence === Presence.Busy) return;
|
||||
if (!showNotification) return;
|
||||
|
||||
let event;
|
||||
switch (user.relationship) {
|
||||
case RelationshipStatus.Incoming:
|
||||
event = translate("notifications.sent_request", {
|
||||
person: user.username,
|
||||
if (notif) {
|
||||
notif.addEventListener("click", () => {
|
||||
window.focus();
|
||||
const id = msg.channel_id;
|
||||
if (id !== channel_id) {
|
||||
const channel = client.channels.get(id);
|
||||
if (channel) {
|
||||
if (channel.channel_type === "TextChannel") {
|
||||
history.push(
|
||||
`/server/${channel.server_id}/channel/${id}`,
|
||||
);
|
||||
} else {
|
||||
history.push(`/channel/${id}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
case RelationshipStatus.Friend:
|
||||
event = translate("notifications.now_friends", {
|
||||
person: user.username,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
const notif = await createNotification(event, {
|
||||
icon: user.generateAvatarURL({ max_side: 256 }),
|
||||
badge: "/assets/icons/android-chrome-512x512.png",
|
||||
timestamp: +new Date(),
|
||||
});
|
||||
notifications[msg.channel_id] = notif;
|
||||
notif.addEventListener(
|
||||
"close",
|
||||
() => delete notifications[msg.channel_id],
|
||||
);
|
||||
}
|
||||
},
|
||||
[
|
||||
history,
|
||||
showNotification,
|
||||
translate,
|
||||
channel_id,
|
||||
client,
|
||||
notifs,
|
||||
playSound,
|
||||
],
|
||||
);
|
||||
|
||||
notif?.addEventListener("click", () => {
|
||||
history.push(`/friends`);
|
||||
});
|
||||
}
|
||||
const relationship = useCallback(
|
||||
async (user: User) => {
|
||||
if (client.user?.status?.presence === Presence.Busy) return;
|
||||
if (!showNotification) return;
|
||||
|
||||
let event;
|
||||
switch (user.relationship) {
|
||||
case RelationshipStatus.Incoming:
|
||||
event = translate("notifications.sent_request", {
|
||||
person: user.username,
|
||||
});
|
||||
break;
|
||||
case RelationshipStatus.Friend:
|
||||
event = translate("notifications.now_friends", {
|
||||
person: user.username,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
const notif = await createNotification(event, {
|
||||
icon: user.generateAvatarURL({ max_side: 256 }),
|
||||
badge: "/assets/icons/android-chrome-512x512.png",
|
||||
timestamp: +new Date(),
|
||||
});
|
||||
|
||||
notif?.addEventListener("click", () => {
|
||||
history.push(`/friends`);
|
||||
});
|
||||
},
|
||||
[client.user?.status?.presence, history, showNotification, translate],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
client.addListener("message", message);
|
||||
@@ -246,7 +260,16 @@ function Notifier({ options, notifs }: Props) {
|
||||
client.removeListener("message", message);
|
||||
client.removeListener("user/relationship", relationship);
|
||||
};
|
||||
}, [client, playSound, guild_id, channel_id, showNotification, notifs]);
|
||||
}, [
|
||||
client,
|
||||
playSound,
|
||||
guild_id,
|
||||
channel_id,
|
||||
showNotification,
|
||||
notifs,
|
||||
message,
|
||||
relationship,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
function visChange() {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { openDB } from "idb";
|
||||
import { useHistory } from "react-router-dom";
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import { Client } from "revolt.js";
|
||||
import { Route } from "revolt.js/dist/api/routes";
|
||||
|
||||
@@ -58,29 +57,6 @@ function Context({ auth, children }: Props) {
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
let db;
|
||||
try {
|
||||
// Match sw.ts#L23
|
||||
db = await openDB("state", 3, {
|
||||
upgrade(db) {
|
||||
for (const store of [
|
||||
"channels",
|
||||
"servers",
|
||||
"users",
|
||||
"members",
|
||||
]) {
|
||||
db.createObjectStore(store, {
|
||||
keyPath: "_id",
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(
|
||||
"Failed to open IndexedDB store, continuing without.",
|
||||
);
|
||||
}
|
||||
|
||||
const client = new Client({
|
||||
autoReconnect: false,
|
||||
apiURL: import.meta.env.VITE_API_URL,
|
||||
@@ -146,11 +122,11 @@ function Context({ auth, children }: Props) {
|
||||
ready: () =>
|
||||
operations.loggedIn() && typeof client.user !== "undefined",
|
||||
};
|
||||
}, [client, auth.active]);
|
||||
}, [client, auth.active, openScreen]);
|
||||
|
||||
useEffect(
|
||||
() => registerEvents({ operations }, setStatus, client),
|
||||
[client],
|
||||
[client, operations],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -203,6 +179,7 @@ function Context({ auth, children }: Props) {
|
||||
setStatus(ClientStatus.READY);
|
||||
}
|
||||
})();
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
if (status === ClientStatus.LOADING) {
|
||||
|
||||
@@ -37,7 +37,7 @@ function StateMonitor(props: Props) {
|
||||
|
||||
client.addListener("message", add);
|
||||
return () => client.removeListener("message", add);
|
||||
}, [props.messages]);
|
||||
}, [client, props.messages]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import isEqual from "lodash.isequal";
|
||||
import { UserSettings } from "revolt-api/types/Sync";
|
||||
import { ClientboundNotification } from "revolt.js/dist/websocket/notifications";
|
||||
|
||||
import { useContext, useEffect } from "preact/hooks";
|
||||
import { useCallback, useContext, useEffect, useMemo } from "preact/hooks";
|
||||
|
||||
import { dispatch } from "../../redux";
|
||||
import { connectState } from "../../redux/connector";
|
||||
@@ -28,7 +28,7 @@ type Props = {
|
||||
notifications: Notifications;
|
||||
};
|
||||
|
||||
const lastValues: { [key in SyncKeys]?: any } = {};
|
||||
const lastValues: { [key in SyncKeys]?: unknown } = {};
|
||||
|
||||
export function mapSync(
|
||||
packet: UserSettings,
|
||||
@@ -78,31 +78,38 @@ function SyncManager(props: Props) {
|
||||
.syncFetchUnreads()
|
||||
.then((unreads) => dispatch({ type: "UNREADS_SET", unreads }));
|
||||
}
|
||||
}, [status]);
|
||||
}, [client, props.sync?.disabled, status]);
|
||||
|
||||
function syncChange(key: SyncKeys, data: any) {
|
||||
const timestamp = +new Date();
|
||||
dispatch({
|
||||
type: "SYNC_SET_REVISION",
|
||||
key,
|
||||
timestamp,
|
||||
});
|
||||
const syncChange = useCallback(
|
||||
(key: SyncKeys, data: unknown) => {
|
||||
const timestamp = +new Date();
|
||||
dispatch({
|
||||
type: "SYNC_SET_REVISION",
|
||||
key,
|
||||
timestamp,
|
||||
});
|
||||
|
||||
client.syncSetSettings(
|
||||
{
|
||||
[key]: data,
|
||||
},
|
||||
timestamp,
|
||||
);
|
||||
}
|
||||
client.syncSetSettings(
|
||||
{
|
||||
[key]: data as string,
|
||||
},
|
||||
timestamp,
|
||||
);
|
||||
},
|
||||
[client],
|
||||
);
|
||||
|
||||
const disabled = props.sync.disabled ?? [];
|
||||
const disabled = useMemo(
|
||||
() => props.sync.disabled ?? [],
|
||||
[props.sync.disabled],
|
||||
);
|
||||
for (const [key, object] of [
|
||||
["appearance", props.settings.appearance],
|
||||
["theme", props.settings.theme],
|
||||
["locale", props.locale],
|
||||
["notifications", props.notifications],
|
||||
] as [SyncKeys, any][]) {
|
||||
] as [SyncKeys, unknown][]) {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
useEffect(() => {
|
||||
if (disabled.indexOf(key) === -1) {
|
||||
if (typeof lastValues[key] !== "undefined") {
|
||||
@@ -113,7 +120,7 @@ function SyncManager(props: Props) {
|
||||
}
|
||||
|
||||
lastValues[key] = object;
|
||||
}, [disabled, object]);
|
||||
}, [key, syncChange, disabled, object]);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@@ -131,7 +138,7 @@ function SyncManager(props: Props) {
|
||||
|
||||
client.addListener("packet", onPacket);
|
||||
return () => client.removeListener("packet", onPacket);
|
||||
}, [disabled, props.sync]);
|
||||
}, [client, disabled, props.sync]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { dispatch } from "../../redux";
|
||||
|
||||
import { ClientOperations, ClientStatus } from "./RevoltClient";
|
||||
|
||||
export var preventReconnect = false;
|
||||
export let preventReconnect = false;
|
||||
let preventUntil = 0;
|
||||
|
||||
export function setReconnectDisallowed(allowed: boolean) {
|
||||
@@ -34,6 +34,7 @@ export function registerEvents(
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let listeners: Record<string, (...args: any[]) => void> = {
|
||||
connecting: () =>
|
||||
operations.ready() && setStatus(ClientStatus.CONNECTING),
|
||||
@@ -74,7 +75,7 @@ export function registerEvents(
|
||||
if (import.meta.env.DEV) {
|
||||
listeners = new Proxy(listeners, {
|
||||
get:
|
||||
(target, listener, receiver) =>
|
||||
(target, listener) =>
|
||||
(...args: unknown[]) => {
|
||||
console.debug(`Calling ${listener.toString()} with`, args);
|
||||
Reflect.get(target, listener)(...args);
|
||||
@@ -87,10 +88,6 @@ export function registerEvents(
|
||||
client.addListener(listener, listeners[listener]);
|
||||
}
|
||||
|
||||
function logMutation(target: string, key: string) {
|
||||
console.log("(o) Object mutated", target, "\nChanged:", key);
|
||||
}
|
||||
|
||||
const online = () => {
|
||||
if (operations.ready()) {
|
||||
setStatus(ClientStatus.RECONNECTING);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Client } from "revolt.js";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
|
||||
import { Children } from "../../types/Preact";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function takeError(error: any): string {
|
||||
const type = error?.response?.data?.type;
|
||||
const id = type;
|
||||
|
||||
Reference in New Issue
Block a user