100 error milestone.

Remove hooks completely. :)
This commit is contained in:
Paul
2021-07-30 21:20:42 +01:00
parent 3184269ba4
commit c21453b281
22 changed files with 257 additions and 506 deletions

View File

@@ -1,17 +1,14 @@
import { Money } from "@styled-icons/boxicons-regular";
import { Envelope, Edit, UserPlus, Shield } from "@styled-icons/boxicons-solid";
import { Link, useHistory } from "react-router-dom";
import { Users } from "revolt.js/dist/api/objects";
import { Profile, RelationshipStatus } from "revolt-api/types/Users";
import { UserPermission } from "revolt.js/dist/api/permissions";
import { Route } from "revolt.js/dist/api/routes";
import { decodeTime } from "ulid";
import styles from "./UserProfile.module.scss";
import { Localizer, Text } from "preact-i18n";
import { useContext, useEffect, useLayoutEffect, useState } from "preact/hooks";
import { useData } from "../../../mobx/State";
import ChannelIcon from "../../../components/common/ChannelIcon";
import Tooltip from "../../../components/common/Tooltip";
import UserIcon from "../../../components/common/user/UserIcon";
@@ -27,14 +24,13 @@ import {
StatusContext,
useClient,
} from "../../revoltjs/RevoltClient";
import { useForceUpdate, useUserPermission } from "../../revoltjs/hooks";
import { useIntermediate } from "../Intermediate";
interface Props {
user_id: string;
dummy?: boolean;
onClose: () => void;
dummyProfile?: Users.Profile;
dummyProfile?: Profile;
}
enum Badges {
@@ -48,7 +44,7 @@ enum Badges {
export function UserProfile({ user_id, onClose, dummy, dummyProfile }: Props) {
const { openScreen, writeClipboard } = useIntermediate();
const [profile, setProfile] = useState<undefined | null | Users.Profile>(
const [profile, setProfile] = useState<undefined | null | Profile>(
undefined,
);
const [mutual, setMutual] = useState<
@@ -60,22 +56,18 @@ export function UserProfile({ user_id, onClose, dummy, dummyProfile }: Props) {
const status = useContext(StatusContext);
const [tab, setTab] = useState("profile");
const ctx = useForceUpdate();
const permissions = useUserPermission(client.user!._id, ctx);
const store = useData();
if (!store.users.has(user_id)) {
const user = client.users.get(user_id);
if (!user) {
useEffect(onClose, []);
return null;
}
const user = store.users.get(user_id)!;
const users = mutual?.users.map((id) => store.users.get(id));
const users = mutual?.users.map((id) => client.users.get(id));
const mutualGroups = [...store.channels.values()].filter(
const mutualGroups = [...client.channels.values()].filter(
(channel) =>
channel?.channel_type === "Group" &&
channel.recipients!.includes(user_id),
channel.recipient_ids!.includes(user_id),
);
useLayoutEffect(() => {
@@ -94,7 +86,7 @@ export function UserProfile({ user_id, onClose, dummy, dummyProfile }: Props) {
if (dummy) return;
if (status === ClientStatus.ONLINE && typeof mutual === "undefined") {
setMutual(null);
client.users.fetchMutual(user_id).then((data) => setMutual(data));
user.fetchMutual().then(setMutual);
}
}, [mutual, status]);
@@ -103,10 +95,9 @@ export function UserProfile({ user_id, onClose, dummy, dummyProfile }: Props) {
if (status === ClientStatus.ONLINE && typeof profile === "undefined") {
setProfile(null);
if (permissions & UserPermission.ViewProfile) {
client.users
.fetchProfile(user_id)
.then((data) => setProfile(data))
if (user.permission & UserPermission.ViewProfile) {
user.fetchProfile()
.then(setProfile)
.catch(() => {});
}
}
@@ -114,10 +105,8 @@ export function UserProfile({ user_id, onClose, dummy, dummyProfile }: Props) {
const backgroundURL =
profile &&
client.users.getBackgroundURL(profile, { width: 1000 }, true);
const badges =
(user.badges ?? 0) |
(decodeTime(user._id) < 1623751765790 ? Badges.EarlyAdopter : 0);
client.generateFileURL(profile.background, { width: 1000 }, true);
const badges = user.badges ?? 0;
return (
<Modal
@@ -150,7 +139,7 @@ export function UserProfile({ user_id, onClose, dummy, dummyProfile }: Props) {
</span>
)}
</div>
{user.relationship === Users.Relationship.Friend && (
{user.relationship === RelationshipStatus.Friend && (
<Localizer>
<Tooltip
content={
@@ -166,7 +155,7 @@ export function UserProfile({ user_id, onClose, dummy, dummyProfile }: Props) {
</Tooltip>
</Localizer>
)}
{user.relationship === Users.Relationship.User && (
{user.relationship === RelationshipStatus.User && (
<IconButton
onClick={() => {
onClose();
@@ -176,12 +165,9 @@ export function UserProfile({ user_id, onClose, dummy, dummyProfile }: Props) {
<Edit size={28} />
</IconButton>
)}
{(user.relationship === Users.Relationship.Incoming ||
user.relationship === Users.Relationship.None) && (
<IconButton
onClick={() =>
client.users.addFriend(user.username)
}>
{(user.relationship === RelationshipStatus.Incoming ||
user.relationship === RelationshipStatus.None) && (
<IconButton onClick={() => user.addFriend()}>
<UserPlus size={28} />
</IconButton>
)}
@@ -192,7 +178,7 @@ export function UserProfile({ user_id, onClose, dummy, dummyProfile }: Props) {
onClick={() => setTab("profile")}>
<Text id="app.special.popovers.user_profile.profile" />
</div>
{user.relationship !== Users.Relationship.User && (
{user.relationship !== RelationshipStatus.User && (
<>
<div
data-active={tab === "friends"}

View File

@@ -1,6 +1,8 @@
import { autorun } from "mobx";
import { Route, Switch, useHistory, useParams } from "react-router-dom";
import { Message, SYSTEM_USER_ID, User } from "revolt.js";
import { Users } from "revolt.js/dist/api/objects";
import { Presence, RelationshipStatus } from "revolt-api/types/Users";
import { SYSTEM_USER_ID } from "revolt.js";
import { Message } from "revolt.js/dist/maps/Messages";
import { decodeTime } from "ulid";
import { useContext, useEffect } from "preact/hooks";
@@ -50,41 +52,36 @@ function Notifier({ options, notifs }: Props) {
const playSound = useContext(SoundContext);
async function message(msg: Message) {
if (msg.author === client.user!._id) return;
if (msg.channel === channel_id && document.hasFocus()) return;
if (client.user!.status?.presence === Users.Presence.Busy) return;
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 channel = client.channels.get(msg.channel);
const author = client.users.get(msg.author);
if (!channel) return;
if (author?.relationship === Users.Relationship.Blocked) return;
const notifState = getNotificationState(notifs, channel);
const notifState = getNotificationState(notifs, msg.channel!);
if (!shouldNotify(notifState, msg, client.user!._id)) return;
playSound("message");
if (!showNotification) return;
let title;
switch (channel.channel_type) {
switch (msg.channel?.channel_type) {
case "SavedMessages":
return;
case "DirectMessage":
title = `@${author?.username}`;
title = `@${msg.author?.username}`;
break;
case "Group":
if (author?._id === SYSTEM_USER_ID) {
title = channel.name;
if (msg.author?._id === SYSTEM_USER_ID) {
title = msg.channel.name;
} else {
title = `@${author?.username} - ${channel.name}`;
title = `@${msg.author?.username} - ${msg.channel.name}`;
}
break;
case "TextChannel":
const server = client.servers.get(channel.server);
title = `@${author?.username} (#${channel.name}, ${server?.name})`;
title = `@${msg.author?.username} (#${msg.channel.name}, ${msg.channel.server?.name})`;
break;
default:
title = msg.channel;
title = msg.channel?._id;
break;
}
@@ -103,70 +100,82 @@ function Notifier({ options, notifs }: Props) {
let body, icon;
if (typeof msg.content === "string") {
body = client.markdownToText(msg.content);
icon = client.users.getAvatarURL(msg.author, { max_side: 256 });
icon = msg.author?.generateAvatarURL({ max_side: 256 });
} else {
const users = client.users;
switch (msg.content.type) {
case "user_added":
case "user_remove":
body = translate(
`app.main.channel.system.${
msg.content.type === "user_added"
? "added_by"
: "removed_by"
}`,
{
user: users.get(msg.content.id)?.username,
other_user: users.get(msg.content.by)?.username,
},
);
icon = client.users.getAvatarURL(msg.content.id, {
max_side: 256,
});
{
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":
body = translate(
`app.main.channel.system.${msg.content.type}`,
{ user: users.get(msg.content.id)?.username },
);
icon = client.users.getAvatarURL(msg.content.id, {
max_side: 256,
});
{
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":
body = translate(
`app.main.channel.system.channel_renamed`,
{
user: users.get(msg.content.by)?.username,
name: msg.content.name,
},
);
icon = client.users.getAvatarURL(msg.content.by, {
max_side: 256,
});
{
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":
body = translate(
`app.main.channel.system.${msg.content.type}`,
{ user: users.get(msg.content.by)?.username },
);
icon = client.users.getAvatarURL(msg.content.by, {
max_side: 256,
});
{
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, {
const notif = await createNotification(title!, {
icon,
image,
body,
timestamp: decodeTime(msg._id),
tag: msg.channel,
tag: msg.channel?._id,
badge: "/assets/icons/android-chrome-512x512.png",
silent: true,
});
@@ -174,7 +183,7 @@ function Notifier({ options, notifs }: Props) {
if (notif) {
notif.addEventListener("click", () => {
window.focus();
const id = msg.channel;
const id = msg.channel_id;
if (id !== channel_id) {
const channel = client.channels.get(id);
if (channel) {
@@ -189,15 +198,15 @@ function Notifier({ options, notifs }: Props) {
}
});
notifications[msg.channel] = notif;
notifications[msg.channel_id] = notif;
notif.addEventListener(
"close",
() => delete notifications[msg.channel],
() => delete notifications[msg.channel_id],
);
}
}
async function relationship(user: User, property: string) {
/*async function relationship(user: User, property: string) {
if (client.user?.status?.presence === Users.Presence.Busy) return;
if (property !== "relationship") return;
if (!showNotification) return;
@@ -219,7 +228,7 @@ function Notifier({ options, notifs }: Props) {
}
const notif = await createNotification(event, {
icon: client.users.getAvatarURL(user._id, { max_side: 256 }),
icon: user.generateAvatarURL({ max_side: 256 }),
badge: "/assets/icons/android-chrome-512x512.png",
timestamp: +new Date(),
});
@@ -227,15 +236,17 @@ function Notifier({ options, notifs }: Props) {
notif?.addEventListener("click", () => {
history.push(`/friends`);
});
}
}*/
useEffect(() => {
// ! FIXME: need event from client about relationship
client.addListener("message", message);
client.users.addListener("mutation", relationship);
// client.users.addListener("mutation", relationship);
return () => {
client.removeListener("message", message);
client.users.removeListener("mutation", relationship);
// client.users.removeListener("mutation", relationship);
};
}, [client, playSound, guild_id, channel_id, showNotification, notifs]);

View File

@@ -8,7 +8,6 @@ import { useContext, useEffect, useMemo, useState } from "preact/hooks";
import { SingletonMessageRenderer } from "../../lib/renderer/Singleton";
import { useData } from "../../mobx/State";
import { dispatch } from "../../redux";
import { connectState } from "../../redux/connector";
import { AuthState } from "../../redux/reducers/auth";
@@ -36,8 +35,6 @@ export interface ClientOperations {
logout: (shouldRequest?: boolean) => Promise<void>;
loggedIn: () => boolean;
ready: () => boolean;
openDM: (user_id: string) => Promise<string>;
}
// By the time they are used, they should all be initialized.
@@ -53,7 +50,6 @@ type Props = {
};
function Context({ auth, children }: Props) {
const history = useHistory();
const { openScreen } = useIntermediate();
const [status, setStatus] = useState(ClientStatus.INIT);
const [client, setClient] = useState<Client>(
@@ -89,7 +85,6 @@ function Context({ auth, children }: Props) {
autoReconnect: false,
apiURL: import.meta.env.VITE_API_URL,
debug: import.meta.env.DEV,
db,
});
setClient(client);
@@ -150,11 +145,6 @@ function Context({ auth, children }: Props) {
loggedIn: () => typeof auth.active !== "undefined",
ready: () =>
operations.loggedIn() && typeof client.user !== "undefined",
openDM: async (user_id: string) => {
const channel = await client.users.openDM(user_id);
history.push(`/channel/${channel!._id}`);
return channel!._id;
},
};
}, [client, auth.active]);
@@ -165,10 +155,6 @@ function Context({ auth, children }: Props) {
useEffect(() => {
(async () => {
if (client.db) {
await client.restore();
}
if (auth.active) {
dispatch({ type: "QUEUE_FAIL_ALL" });

View File

@@ -1,7 +1,7 @@
/**
* This file monitors the message cache to delete any queued messages that have already sent.
*/
import { Message } from "revolt.js";
import { Message } from "revolt.js/dist/maps/Messages";
import { useContext, useEffect } from "preact/hooks";

View File

@@ -2,7 +2,7 @@
* This file monitors changes to settings and syncs them to the server.
*/
import isEqual from "lodash.isequal";
import { Sync } from "revolt.js/dist/api/objects";
import { UserSettings } from "revolt-api/types/Sync";
import { ClientboundNotification } from "revolt.js/dist/websocket/notifications";
import { useContext, useEffect } from "preact/hooks";
@@ -31,7 +31,7 @@ type Props = {
const lastValues: { [key in SyncKeys]?: any } = {};
export function mapSync(
packet: Sync.UserSettings,
packet: UserSettings,
revision?: Record<string, number>,
) {
const update: { [key in SyncKeys]?: [number, SyncData[key]] } = {};

View File

@@ -1,10 +1,9 @@
import { Client, Message } from "revolt.js/dist";
import { Client } from "revolt.js/dist";
import { Message } from "revolt.js/dist/maps/Messages";
import { ClientboundNotification } from "revolt.js/dist/websocket/notifications";
import { StateUpdater } from "preact/hooks";
import { DataStore } from "../../mobx";
import { useData } from "../../mobx/State";
import { dispatch } from "../../redux";
import { ClientOperations, ClientStatus } from "./RevoltClient";
@@ -78,10 +77,10 @@ export function registerEvents(
},
message: (message: Message) => {
if (message.mentions?.includes(client.user!._id)) {
if (message.mention_ids?.includes(client.user!._id)) {
dispatch({
type: "UNREADS_MENTION",
channel: message.channel,
channel: message.channel_id,
message: message._id,
});
}
@@ -110,13 +109,6 @@ export function registerEvents(
console.log("(o) Object mutated", target, "\nChanged:", key);
}
if (import.meta.env.DEV) {
client.users.addListener("mutation", logMutation);
client.servers.addListener("mutation", logMutation);
client.channels.addListener("mutation", logMutation);
client.members.addListener("mutation", logMutation);
}
const online = () => {
if (operations.ready()) {
setStatus(ClientStatus.RECONNECTING);
@@ -144,13 +136,6 @@ export function registerEvents(
);
}
if (import.meta.env.DEV) {
client.users.removeListener("mutation", logMutation);
client.servers.removeListener("mutation", logMutation);
client.channels.removeListener("mutation", logMutation);
client.members.removeListener("mutation", logMutation);
}
window.removeEventListener("online", online);
window.removeEventListener("offline", offline);
};

View File

@@ -1,102 +0,0 @@
import { Client, PermissionCalculator } from "revolt.js";
import { useContext, useEffect, useState } from "preact/hooks";
//#region Hooks v1 (deprecated)
import { AppContext } from "./RevoltClient";
export interface HookContext {
client: Client;
forceUpdate: () => void;
}
export function useForceUpdate(context?: HookContext): HookContext {
const client = useContext(AppContext);
if (context) return context;
const H = useState(0);
let updateState: (_: number) => void;
if (Array.isArray(H)) {
const [, u] = H;
updateState = u;
} else {
console.warn("Failed to construct using useState.");
updateState = () => {};
}
return { client, forceUpdate: () => updateState(Math.random()) };
}
export function useUserPermission(id: string, context?: HookContext) {
const ctx = useForceUpdate(context);
const mutation = (target: string) => target === id && ctx.forceUpdate();
useEffect(() => {
ctx.client.users.addListener("update", mutation);
return () => ctx.client.users.removeListener("update", mutation);
}, [id]);
const calculator = new PermissionCalculator(ctx.client);
return calculator.forUser(id);
}
export function useChannelPermission(id: string, context?: HookContext) {
const ctx = useForceUpdate(context);
const channel = ctx.client.channels.get(id);
const server =
channel &&
(channel.channel_type === "TextChannel" ||
channel.channel_type === "VoiceChannel")
? channel.server
: undefined;
const mutation = (target: string) => target === id && ctx.forceUpdate();
const mutationServer = (target: string) =>
target === server && ctx.forceUpdate();
const mutationMember = (target: string) =>
target.substr(26) === ctx.client.user!._id && ctx.forceUpdate();
useEffect(() => {
ctx.client.channels.addListener("update", mutation);
if (server) {
ctx.client.servers.addListener("update", mutationServer);
ctx.client.members.addListener("update", mutationMember);
}
return () => {
ctx.client.channels.removeListener("update", mutation);
if (server) {
ctx.client.servers.removeListener("update", mutationServer);
ctx.client.members.removeListener("update", mutationMember);
}
};
}, [id]);
const calculator = new PermissionCalculator(ctx.client);
return calculator.forChannel(id);
}
export function useServerPermission(id: string, context?: HookContext) {
const ctx = useForceUpdate(context);
const mutation = (target: string) => target === id && ctx.forceUpdate();
const mutationMember = (target: string) =>
target.substr(26) === ctx.client.user!._id && ctx.forceUpdate();
useEffect(() => {
ctx.client.servers.addListener("update", mutation);
ctx.client.members.addListener("update", mutationMember);
return () => {
ctx.client.servers.removeListener("update", mutation);
ctx.client.members.removeListener("update", mutationMember);
};
}, [id]);
const calculator = new PermissionCalculator(ctx.client);
return calculator.forServer(id);
}
//#endregion