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

View file

@ -1,6 +1,8 @@
import { autorun } from "mobx";
import { Route, Switch, useHistory, useParams } from "react-router-dom"; import { Route, Switch, useHistory, useParams } from "react-router-dom";
import { Message, SYSTEM_USER_ID, User } from "revolt.js"; import { Presence, RelationshipStatus } from "revolt-api/types/Users";
import { Users } from "revolt.js/dist/api/objects"; import { SYSTEM_USER_ID } from "revolt.js";
import { Message } from "revolt.js/dist/maps/Messages";
import { decodeTime } from "ulid"; import { decodeTime } from "ulid";
import { useContext, useEffect } from "preact/hooks"; import { useContext, useEffect } from "preact/hooks";
@ -50,41 +52,36 @@ function Notifier({ options, notifs }: Props) {
const playSound = useContext(SoundContext); const playSound = useContext(SoundContext);
async function message(msg: Message) { async function message(msg: Message) {
if (msg.author === client.user!._id) return; if (msg.author_id === client.user!._id) return;
if (msg.channel === channel_id && document.hasFocus()) return; if (msg.channel_id === channel_id && document.hasFocus()) return;
if (client.user!.status?.presence === Users.Presence.Busy) return; if (client.user!.status?.presence === Presence.Busy) return;
if (msg.author?.relationship === RelationshipStatus.Blocked) return;
const channel = client.channels.get(msg.channel); const notifState = getNotificationState(notifs, msg.channel!);
const author = client.users.get(msg.author);
if (!channel) return;
if (author?.relationship === Users.Relationship.Blocked) return;
const notifState = getNotificationState(notifs, channel);
if (!shouldNotify(notifState, msg, client.user!._id)) return; if (!shouldNotify(notifState, msg, client.user!._id)) return;
playSound("message"); playSound("message");
if (!showNotification) return; if (!showNotification) return;
let title; let title;
switch (channel.channel_type) { switch (msg.channel?.channel_type) {
case "SavedMessages": case "SavedMessages":
return; return;
case "DirectMessage": case "DirectMessage":
title = `@${author?.username}`; title = `@${msg.author?.username}`;
break; break;
case "Group": case "Group":
if (author?._id === SYSTEM_USER_ID) { if (msg.author?._id === SYSTEM_USER_ID) {
title = channel.name; title = msg.channel.name;
} else { } else {
title = `@${author?.username} - ${channel.name}`; title = `@${msg.author?.username} - ${msg.channel.name}`;
} }
break; break;
case "TextChannel": case "TextChannel":
const server = client.servers.get(channel.server); title = `@${msg.author?.username} (#${msg.channel.name}, ${msg.channel.server?.name})`;
title = `@${author?.username} (#${channel.name}, ${server?.name})`;
break; break;
default: default:
title = msg.channel; title = msg.channel?._id;
break; break;
} }
@ -103,12 +100,14 @@ function Notifier({ options, notifs }: Props) {
let body, icon; let body, icon;
if (typeof msg.content === "string") { if (typeof msg.content === "string") {
body = client.markdownToText(msg.content); body = client.markdownToText(msg.content);
icon = client.users.getAvatarURL(msg.author, { max_side: 256 }); icon = msg.author?.generateAvatarURL({ max_side: 256 });
} else { } else {
const users = client.users; const users = client.users;
switch (msg.content.type) { switch (msg.content.type) {
case "user_added": case "user_added":
case "user_remove": case "user_remove":
{
let user = users.get(msg.content.id);
body = translate( body = translate(
`app.main.channel.system.${ `app.main.channel.system.${
msg.content.type === "user_added" msg.content.type === "user_added"
@ -116,27 +115,33 @@ function Notifier({ options, notifs }: Props) {
: "removed_by" : "removed_by"
}`, }`,
{ {
user: users.get(msg.content.id)?.username, user: user?.username,
other_user: users.get(msg.content.by)?.username, other_user: users.get(msg.content.by)?.username,
}, },
); );
icon = client.users.getAvatarURL(msg.content.id, { icon = user?.generateAvatarURL({
max_side: 256, max_side: 256,
}); });
}
break; break;
case "user_joined": case "user_joined":
case "user_left": case "user_left":
case "user_kicked": case "user_kicked":
case "user_banned": case "user_banned":
{
let user = users.get(msg.content.id);
body = translate( body = translate(
`app.main.channel.system.${msg.content.type}`, `app.main.channel.system.${msg.content.type}`,
{ user: users.get(msg.content.id)?.username }, { user: user?.username },
); );
icon = client.users.getAvatarURL(msg.content.id, { icon = user?.generateAvatarURL({
max_side: 256, max_side: 256,
}); });
}
break; break;
case "channel_renamed": case "channel_renamed":
{
let user = users.get(msg.content.by);
body = translate( body = translate(
`app.main.channel.system.channel_renamed`, `app.main.channel.system.channel_renamed`,
{ {
@ -144,29 +149,33 @@ function Notifier({ options, notifs }: Props) {
name: msg.content.name, name: msg.content.name,
}, },
); );
icon = client.users.getAvatarURL(msg.content.by, { icon = user?.generateAvatarURL({
max_side: 256, max_side: 256,
}); });
}
break; break;
case "channel_description_changed": case "channel_description_changed":
case "channel_icon_changed": case "channel_icon_changed":
{
let user = users.get(msg.content.by);
body = translate( body = translate(
`app.main.channel.system.${msg.content.type}`, `app.main.channel.system.${msg.content.type}`,
{ user: users.get(msg.content.by)?.username }, { user: users.get(msg.content.by)?.username },
); );
icon = client.users.getAvatarURL(msg.content.by, { icon = user?.generateAvatarURL({
max_side: 256, max_side: 256,
}); });
}
break; break;
} }
} }
const notif = await createNotification(title, { const notif = await createNotification(title!, {
icon, icon,
image, image,
body, body,
timestamp: decodeTime(msg._id), timestamp: decodeTime(msg._id),
tag: msg.channel, tag: msg.channel?._id,
badge: "/assets/icons/android-chrome-512x512.png", badge: "/assets/icons/android-chrome-512x512.png",
silent: true, silent: true,
}); });
@ -174,7 +183,7 @@ function Notifier({ options, notifs }: Props) {
if (notif) { if (notif) {
notif.addEventListener("click", () => { notif.addEventListener("click", () => {
window.focus(); window.focus();
const id = msg.channel; const id = msg.channel_id;
if (id !== channel_id) { if (id !== channel_id) {
const channel = client.channels.get(id); const channel = client.channels.get(id);
if (channel) { if (channel) {
@ -189,15 +198,15 @@ function Notifier({ options, notifs }: Props) {
} }
}); });
notifications[msg.channel] = notif; notifications[msg.channel_id] = notif;
notif.addEventListener( notif.addEventListener(
"close", "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 (client.user?.status?.presence === Users.Presence.Busy) return;
if (property !== "relationship") return; if (property !== "relationship") return;
if (!showNotification) return; if (!showNotification) return;
@ -219,7 +228,7 @@ function Notifier({ options, notifs }: Props) {
} }
const notif = await createNotification(event, { 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", badge: "/assets/icons/android-chrome-512x512.png",
timestamp: +new Date(), timestamp: +new Date(),
}); });
@ -227,15 +236,17 @@ function Notifier({ options, notifs }: Props) {
notif?.addEventListener("click", () => { notif?.addEventListener("click", () => {
history.push(`/friends`); history.push(`/friends`);
}); });
} }*/
useEffect(() => { useEffect(() => {
// ! FIXME: need event from client about relationship
client.addListener("message", message); client.addListener("message", message);
client.users.addListener("mutation", relationship); // client.users.addListener("mutation", relationship);
return () => { return () => {
client.removeListener("message", message); client.removeListener("message", message);
client.users.removeListener("mutation", relationship); // client.users.removeListener("mutation", relationship);
}; };
}, [client, playSound, guild_id, channel_id, showNotification, notifs]); }, [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 { SingletonMessageRenderer } from "../../lib/renderer/Singleton";
import { useData } from "../../mobx/State";
import { dispatch } from "../../redux"; import { dispatch } from "../../redux";
import { connectState } from "../../redux/connector"; import { connectState } from "../../redux/connector";
import { AuthState } from "../../redux/reducers/auth"; import { AuthState } from "../../redux/reducers/auth";
@ -36,8 +35,6 @@ export interface ClientOperations {
logout: (shouldRequest?: boolean) => Promise<void>; logout: (shouldRequest?: boolean) => Promise<void>;
loggedIn: () => boolean; loggedIn: () => boolean;
ready: () => boolean; ready: () => boolean;
openDM: (user_id: string) => Promise<string>;
} }
// By the time they are used, they should all be initialized. // By the time they are used, they should all be initialized.
@ -53,7 +50,6 @@ type Props = {
}; };
function Context({ auth, children }: Props) { function Context({ auth, children }: Props) {
const history = useHistory();
const { openScreen } = useIntermediate(); const { openScreen } = useIntermediate();
const [status, setStatus] = useState(ClientStatus.INIT); const [status, setStatus] = useState(ClientStatus.INIT);
const [client, setClient] = useState<Client>( const [client, setClient] = useState<Client>(
@ -89,7 +85,6 @@ function Context({ auth, children }: Props) {
autoReconnect: false, autoReconnect: false,
apiURL: import.meta.env.VITE_API_URL, apiURL: import.meta.env.VITE_API_URL,
debug: import.meta.env.DEV, debug: import.meta.env.DEV,
db,
}); });
setClient(client); setClient(client);
@ -150,11 +145,6 @@ function Context({ auth, children }: Props) {
loggedIn: () => typeof auth.active !== "undefined", loggedIn: () => typeof auth.active !== "undefined",
ready: () => ready: () =>
operations.loggedIn() && typeof client.user !== "undefined", 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]); }, [client, auth.active]);
@ -165,10 +155,6 @@ function Context({ auth, children }: Props) {
useEffect(() => { useEffect(() => {
(async () => { (async () => {
if (client.db) {
await client.restore();
}
if (auth.active) { if (auth.active) {
dispatch({ type: "QUEUE_FAIL_ALL" }); 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. * 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"; import { useContext, useEffect } from "preact/hooks";

View file

@ -2,7 +2,7 @@
* This file monitors changes to settings and syncs them to the server. * This file monitors changes to settings and syncs them to the server.
*/ */
import isEqual from "lodash.isequal"; 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 { ClientboundNotification } from "revolt.js/dist/websocket/notifications";
import { useContext, useEffect } from "preact/hooks"; import { useContext, useEffect } from "preact/hooks";
@ -31,7 +31,7 @@ type Props = {
const lastValues: { [key in SyncKeys]?: any } = {}; const lastValues: { [key in SyncKeys]?: any } = {};
export function mapSync( export function mapSync(
packet: Sync.UserSettings, packet: UserSettings,
revision?: Record<string, number>, revision?: Record<string, number>,
) { ) {
const update: { [key in SyncKeys]?: [number, SyncData[key]] } = {}; 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 { ClientboundNotification } from "revolt.js/dist/websocket/notifications";
import { StateUpdater } from "preact/hooks"; import { StateUpdater } from "preact/hooks";
import { DataStore } from "../../mobx";
import { useData } from "../../mobx/State";
import { dispatch } from "../../redux"; import { dispatch } from "../../redux";
import { ClientOperations, ClientStatus } from "./RevoltClient"; import { ClientOperations, ClientStatus } from "./RevoltClient";
@ -78,10 +77,10 @@ export function registerEvents(
}, },
message: (message: Message) => { message: (message: Message) => {
if (message.mentions?.includes(client.user!._id)) { if (message.mention_ids?.includes(client.user!._id)) {
dispatch({ dispatch({
type: "UNREADS_MENTION", type: "UNREADS_MENTION",
channel: message.channel, channel: message.channel_id,
message: message._id, message: message._id,
}); });
} }
@ -110,13 +109,6 @@ export function registerEvents(
console.log("(o) Object mutated", target, "\nChanged:", key); 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 = () => { const online = () => {
if (operations.ready()) { if (operations.ready()) {
setStatus(ClientStatus.RECONNECTING); 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("online", online);
window.removeEventListener("offline", offline); 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

View file

@ -12,18 +12,17 @@ import {
} from "@styled-icons/boxicons-regular"; } from "@styled-icons/boxicons-regular";
import { Cog, UserVoice } from "@styled-icons/boxicons-solid"; import { Cog, UserVoice } from "@styled-icons/boxicons-solid";
import { useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom";
import { import { Attachment } from "revolt-api/types/Autumn";
Attachment, import { Presence, RelationshipStatus } from "revolt-api/types/Users";
Channels,
Message,
Servers,
Users,
} from "revolt.js/dist/api/objects";
import { import {
ChannelPermission, ChannelPermission,
ServerPermission, ServerPermission,
UserPermission, UserPermission,
} from "revolt.js/dist/api/permissions"; } from "revolt.js/dist/api/permissions";
import { Channel } from "revolt.js/dist/maps/Channels";
import { Message } from "revolt.js/dist/maps/Messages";
import { Server } from "revolt.js/dist/maps/Servers";
import { User } from "revolt.js/dist/maps/Users";
import { import {
ContextMenu, ContextMenu,
@ -34,8 +33,6 @@ import {
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { useContext } from "preact/hooks"; import { useContext } from "preact/hooks";
import { Channel, Server, User } from "../mobx";
import { useData } from "../mobx/State";
import { dispatch } from "../redux"; import { dispatch } from "../redux";
import { connectState } from "../redux/connector"; import { connectState } from "../redux/connector";
import { import {
@ -52,12 +49,6 @@ import {
StatusContext, StatusContext,
useClient, useClient,
} from "../context/revoltjs/RevoltClient"; } from "../context/revoltjs/RevoltClient";
import {
useChannelPermission,
useForceUpdate,
useServerPermission,
useUserPermission,
} from "../context/revoltjs/hooks";
import { takeError } from "../context/revoltjs/util"; import { takeError } from "../context/revoltjs/util";
import Tooltip from "../components/common/Tooltip"; import Tooltip from "../components/common/Tooltip";
@ -91,13 +82,13 @@ type Action =
| { action: "reply_message"; id: string } | { action: "reply_message"; id: string }
| { action: "quote_message"; content: string } | { action: "quote_message"; content: string }
| { action: "edit_message"; id: string } | { action: "edit_message"; id: string }
| { action: "delete_message"; target: Channels.Message } | { action: "delete_message"; target: Message }
| { action: "open_file"; attachment: Attachment } | { action: "open_file"; attachment: Attachment }
| { action: "save_file"; attachment: Attachment } | { action: "save_file"; attachment: Attachment }
| { action: "copy_file_link"; attachment: Attachment } | { action: "copy_file_link"; attachment: Attachment }
| { action: "open_link"; link: string } | { action: "open_link"; link: string }
| { action: "copy_link"; link: string } | { action: "copy_link"; link: string }
| { action: "remove_member"; channel: string; user: User } | { action: "remove_member"; channel: Channel; user: User }
| { action: "kick_member"; target: Server; user: User } | { action: "kick_member"; target: Server; user: User }
| { action: "ban_member"; target: Server; user: User } | { action: "ban_member"; target: Server; user: User }
| { action: "view_profile"; user: User } | { action: "view_profile"; user: User }
@ -107,7 +98,7 @@ type Action =
| { action: "add_friend"; user: User } | { action: "add_friend"; user: User }
| { action: "remove_friend"; user: User } | { action: "remove_friend"; user: User }
| { action: "cancel_friend"; user: User } | { action: "cancel_friend"; user: User }
| { action: "set_presence"; presence: Users.Presence } | { action: "set_presence"; presence: Presence }
| { action: "set_status" } | { action: "set_status" }
| { action: "clear_status" } | { action: "clear_status" }
| { action: "create_channel"; target: Server } | { action: "create_channel"; target: Server }
@ -196,7 +187,8 @@ function ContextMenus(props: Props) {
}); });
client.channels client.channels
.sendMessage(data.message.channel, { .get(data.message.channel)!
.sendMessage({
nonce: data.message.id, nonce: data.message.id,
content: data.message.data.content as string, content: data.message.data.content as string,
replies: data.message.data.replies, replies: data.message.data.replies,
@ -313,10 +305,7 @@ function ContextMenus(props: Props) {
case "remove_member": case "remove_member":
{ {
client.channels.removeMember( data.channel.removeMember(data.user._id);
data.channel,
data.user._id,
);
} }
break; break;
@ -326,9 +315,7 @@ function ContextMenus(props: Props) {
case "message_user": case "message_user":
{ {
const channel = await client.users.openDM( const channel = await data.user.openDM();
data.user._id,
);
if (channel) { if (channel) {
history.push(`/channel/${channel._id}`); history.push(`/channel/${channel._id}`);
} }
@ -337,7 +324,7 @@ function ContextMenus(props: Props) {
case "add_friend": case "add_friend":
{ {
await client.users.addFriend(data.user.username); await data.user.addFriend();
} }
break; break;
@ -349,7 +336,7 @@ function ContextMenus(props: Props) {
}); });
break; break;
case "unblock_user": case "unblock_user":
await client.users.unblockUser(data.user._id); await data.user.unblockUser();
break; break;
case "remove_friend": case "remove_friend":
openScreen({ openScreen({
@ -359,12 +346,12 @@ function ContextMenus(props: Props) {
}); });
break; break;
case "cancel_friend": case "cancel_friend":
await client.users.removeFriend(data.user._id); await data.user.removeFriend();
break; break;
case "set_presence": case "set_presence":
{ {
await client.users.editUser({ await client.users.edit({
status: { status: {
...client.user?.status, ...client.user?.status,
presence: data.presence, presence: data.presence,
@ -383,7 +370,7 @@ function ContextMenus(props: Props) {
case "clear_status": case "clear_status":
{ {
const { text, ...status } = client.user?.status ?? {}; const { text, ...status } = client.user?.status ?? {};
await client.users.editUser({ status }); await client.users.edit({ status });
} }
break; break;
@ -463,9 +450,6 @@ function ContextMenus(props: Props) {
unread, unread,
contextualChannel: cxid, contextualChannel: cxid,
}: ContextMenuData) => { }: ContextMenuData) => {
const store = useData();
const forceUpdate = useForceUpdate();
const elements: Children[] = []; const elements: Children[] = [];
let lastDivider = false; let lastDivider = false;
@ -495,11 +479,8 @@ function ContextMenus(props: Props) {
} }
if (server_list) { if (server_list) {
const server = store.servers.get(server_list); const server = client.servers.get(server_list)!;
const permissions = useServerPermission( const permissions = server.permission;
server_list,
forceUpdate,
);
if (server) { if (server) {
if (permissions & ServerPermission.ManageChannels) if (permissions & ServerPermission.ManageChannels)
generateAction({ generateAction({
@ -526,13 +507,13 @@ function ContextMenus(props: Props) {
pushDivider(); pushDivider();
} }
const channel = cid ? store.channels.get(cid) : undefined; const channel = cid ? client.channels.get(cid) : undefined;
const contextualChannel = cxid const contextualChannel = cxid
? store.channels.get(cxid) ? client.channels.get(cxid)
: undefined; : undefined;
const targetChannel = channel ?? contextualChannel; const targetChannel = channel ?? contextualChannel;
const user = uid ? store.users.get(uid) : undefined; const user = uid ? client.users.get(uid) : undefined;
const serverChannel = const serverChannel =
targetChannel && targetChannel &&
(targetChannel.channel_type === "TextChannel" || (targetChannel.channel_type === "TextChannel" ||
@ -540,23 +521,17 @@ function ContextMenus(props: Props) {
? targetChannel ? targetChannel
: undefined; : undefined;
const s = serverChannel ? serverChannel.server! : sid; const s = serverChannel ? serverChannel.server_id! : sid;
const server = s ? store.servers.get(s) : undefined; const server = s ? client.servers.get(s) : undefined;
const channelPermissions = targetChannel const channelPermissions = targetChannel?.permission || 0;
? useChannelPermission(targetChannel._id, forceUpdate) const serverPermissions =
: 0; (server
const serverPermissions = server ? server.permission
? useServerPermission(server._id, forceUpdate)
: serverChannel : serverChannel
? useServerPermission( ? serverChannel.server?.permission
serverChannel.server!, : 0) || 0;
forceUpdate, const userPermissions = (user ? user.permission : 0) || 0;
)
: 0;
const userPermissions = user
? useUserPermission(user._id, forceUpdate)
: 0;
if (channel && unread) { if (channel && unread) {
generateAction({ action: "mark_as_read", channel }); generateAction({ action: "mark_as_read", channel });
@ -576,29 +551,29 @@ function ContextMenus(props: Props) {
if (user) { if (user) {
let actions: Action["action"][]; let actions: Action["action"][];
switch (user.relationship) { switch (user.relationship) {
case Users.Relationship.User: case RelationshipStatus.User:
actions = []; actions = [];
break; break;
case Users.Relationship.Friend: case RelationshipStatus.Friend:
actions = ["remove_friend", "block_user"]; actions = ["remove_friend", "block_user"];
break; break;
case Users.Relationship.Incoming: case RelationshipStatus.Incoming:
actions = [ actions = [
"add_friend", "add_friend",
"cancel_friend", "cancel_friend",
"block_user", "block_user",
]; ];
break; break;
case Users.Relationship.Outgoing: case RelationshipStatus.Outgoing:
actions = ["cancel_friend", "block_user"]; actions = ["cancel_friend", "block_user"];
break; break;
case Users.Relationship.Blocked: case RelationshipStatus.Blocked:
actions = ["unblock_user"]; actions = ["unblock_user"];
break; break;
case Users.Relationship.BlockedOther: case RelationshipStatus.BlockedOther:
actions = ["block_user"]; actions = ["block_user"];
break; break;
case Users.Relationship.None: case RelationshipStatus.None:
default: default:
actions = ["add_friend", "block_user"]; actions = ["add_friend", "block_user"];
} }
@ -629,12 +604,12 @@ function ContextMenus(props: Props) {
if (contextualChannel) { if (contextualChannel) {
if (contextualChannel.channel_type === "Group" && uid) { if (contextualChannel.channel_type === "Group" && uid) {
if ( if (
contextualChannel.owner === userId && contextualChannel.owner_id === userId &&
userId !== uid userId !== uid
) { ) {
generateAction({ generateAction({
action: "remove_member", action: "remove_member",
channel: contextualChannel._id, channel: contextualChannel,
user: user!, user: user!,
}); });
} }
@ -697,7 +672,7 @@ function ContextMenus(props: Props) {
}); });
} }
if (message.author === userId) { if (message.author_id === userId) {
generateAction({ generateAction({
action: "edit_message", action: "edit_message",
id: message._id, id: message._id,
@ -705,7 +680,7 @@ function ContextMenus(props: Props) {
} }
if ( if (
message.author === userId || message.author_id === userId ||
channelPermissions & channelPermissions &
ChannelPermission.ManageMessages ChannelPermission.ManageMessages
) { ) {
@ -820,7 +795,7 @@ function ContextMenus(props: Props) {
generateAction( generateAction(
{ {
action: "open_server_channel_settings", action: "open_server_channel_settings",
server: channel.server!, server: channel.server_id!,
id: channel._id, id: channel._id,
}, },
"open_channel_settings", "open_channel_settings",
@ -885,9 +860,7 @@ function ContextMenus(props: Props) {
onClose={contextClick} onClose={contextClick}
className="Status"> className="Status">
{() => { {() => {
const store = useData(); const user = client.user!;
const user = store.users.get(client.user!._id)!;
return ( return (
<> <>
<div className="header"> <div className="header">
@ -927,7 +900,7 @@ function ContextMenus(props: Props) {
<MenuItem <MenuItem
data={{ data={{
action: "set_presence", action: "set_presence",
presence: Users.Presence.Online, presence: Presence.Online,
}} }}
disabled={!isOnline}> disabled={!isOnline}>
<div className="indicator online" /> <div className="indicator online" />
@ -936,7 +909,7 @@ function ContextMenus(props: Props) {
<MenuItem <MenuItem
data={{ data={{
action: "set_presence", action: "set_presence",
presence: Users.Presence.Idle, presence: Presence.Idle,
}} }}
disabled={!isOnline}> disabled={!isOnline}>
<div className="indicator idle" /> <div className="indicator idle" />
@ -945,7 +918,7 @@ function ContextMenus(props: Props) {
<MenuItem <MenuItem
data={{ data={{
action: "set_presence", action: "set_presence",
presence: Users.Presence.Busy, presence: Presence.Busy,
}} }}
disabled={!isOnline}> disabled={!isOnline}>
<div className="indicator busy" /> <div className="indicator busy" />
@ -954,7 +927,7 @@ function ContextMenus(props: Props) {
<MenuItem <MenuItem
data={{ data={{
action: "set_presence", action: "set_presence",
presence: Users.Presence.Invisible, presence: Presence.Invisible,
}} }}
disabled={!isOnline}> disabled={!isOnline}>
<div className="indicator invisible" /> <div className="indicator invisible" />
@ -982,7 +955,7 @@ function ContextMenus(props: Props) {
<ContextMenuWithData <ContextMenuWithData
id="NotificationOptions" id="NotificationOptions"
onClose={contextClick}> onClose={contextClick}>
{({ channel }: { channel: Channels.Channel }) => { {({ channel }: { channel: Channel }) => {
const state = props.notifications[channel._id]; const state = props.notifications[channel._id];
const actual = getNotificationState( const actual = getNotificationState(
props.notifications, props.notifications,

View file

@ -1,5 +1,6 @@
import EventEmitter3 from "eventemitter3"; import EventEmitter3 from "eventemitter3";
import { Client, Message } from "revolt.js"; import { Client } from "revolt.js";
import { Message } from "revolt.js/dist/maps/Messages";
import { useEffect, useState } from "preact/hooks"; import { useEffect, useState } from "preact/hooks";

View file

@ -1,5 +1,3 @@
import { mapMessage } from "../../../context/revoltjs/util";
import { SMOOTH_SCROLL_ON_RECEIVE } from "../Singleton"; import { SMOOTH_SCROLL_ON_RECEIVE } from "../Singleton";
import { RendererRoutines } from "../types"; import { RendererRoutines } from "../types";
@ -8,14 +6,10 @@ export const SimpleRenderer: RendererRoutines = {
if (renderer.client!.websocket.connected) { if (renderer.client!.websocket.connected) {
if (nearby) if (nearby)
renderer renderer
.client!.channels.fetchMessagesWithUsers( .client!.channels.get(id)!
id, .fetchMessagesWithUsers({ nearby, limit: 100 })
{ nearby, limit: 100 }, .then(({ messages }) => {
true, messages.sort((a, b) => a._id.localeCompare(b._id));
)
.then(({ messages: data }) => {
data.sort((a, b) => a._id.localeCompare(b._id));
const messages = data.map((x) => mapMessage(x));
renderer.setState( renderer.setState(
id, id,
{ {
@ -29,16 +23,16 @@ export const SimpleRenderer: RendererRoutines = {
}); });
else else
renderer renderer
.client!.channels.fetchMessagesWithUsers(id, {}, true) .client!.channels.get(id)!
.then(({ messages: data }) => { .fetchMessagesWithUsers({})
data.reverse(); .then(({ messages }) => {
const messages = data.map((x) => mapMessage(x)); messages.reverse();
renderer.setState( renderer.setState(
id, id,
{ {
type: "RENDER", type: "RENDER",
messages, messages,
atTop: data.length < 50, atTop: messages.length < 50,
atBottom: true, atBottom: true,
}, },
{ type: "ScrollToBottom", smooth }, { type: "ScrollToBottom", smooth },
@ -54,7 +48,7 @@ export const SimpleRenderer: RendererRoutines = {
if (renderer.state.messages.find((x) => x._id === message._id)) return; if (renderer.state.messages.find((x) => x._id === message._id)) return;
if (!renderer.state.atBottom) return; if (!renderer.state.atBottom) return;
let messages = [...renderer.state.messages, mapMessage(message)]; let messages = [...renderer.state.messages, message];
let atTop = renderer.state.atTop; let atTop = renderer.state.atTop;
if (messages.length > 150) { if (messages.length > 150) {
messages = messages.slice(messages.length - 150); messages = messages.slice(messages.length - 150);
@ -62,7 +56,7 @@ export const SimpleRenderer: RendererRoutines = {
} }
renderer.setState( renderer.setState(
message.channel, message.channel_id,
{ {
...renderer.state, ...renderer.state,
messages, messages,
@ -72,7 +66,8 @@ export const SimpleRenderer: RendererRoutines = {
); );
}, },
edit: async (renderer, id, patch) => { edit: async (renderer, id, patch) => {
const channel = renderer.channel; // ! FIXME: verify if this is needed anymore
/*const channel = renderer.channel;
if (!channel) return; if (!channel) return;
if (renderer.state.type !== "RENDER") return; if (renderer.state.type !== "RENDER") return;
@ -91,7 +86,7 @@ export const SimpleRenderer: RendererRoutines = {
}, },
{ type: "StayAtBottom" }, { type: "StayAtBottom" },
); );
} }*/
}, },
delete: async (renderer, id) => { delete: async (renderer, id) => {
const channel = renderer.channel; const channel = renderer.channel;
@ -122,14 +117,11 @@ export const SimpleRenderer: RendererRoutines = {
if (state.type !== "RENDER") return; if (state.type !== "RENDER") return;
if (state.atTop) return; if (state.atTop) return;
const { messages: data } = const { messages: data } = await renderer
await renderer.client!.channels.fetchMessagesWithUsers( .client!.channels.get(channel)!
channel, .fetchMessagesWithUsers({
{
before: state.messages[0]._id, before: state.messages[0]._id,
}, });
true,
);
if (data.length === 0) { if (data.length === 0) {
return renderer.setState(channel, { return renderer.setState(channel, {
@ -139,7 +131,7 @@ export const SimpleRenderer: RendererRoutines = {
} }
data.reverse(); data.reverse();
let messages = [...data.map((x) => mapMessage(x)), ...state.messages]; let messages = [...data, ...state.messages];
let atTop = false; let atTop = false;
if (data.length < 50) { if (data.length < 50) {
@ -166,15 +158,12 @@ export const SimpleRenderer: RendererRoutines = {
if (state.type !== "RENDER") return; if (state.type !== "RENDER") return;
if (state.atBottom) return; if (state.atBottom) return;
const { messages: data } = const { messages: data } = await renderer
await renderer.client!.channels.fetchMessagesWithUsers( .client!.channels.get(channel)!
channel, .fetchMessagesWithUsers({
{
after: state.messages[state.messages.length - 1]._id, after: state.messages[state.messages.length - 1]._id,
sort: "Oldest", sort: "Oldest",
}, });
true,
);
if (data.length === 0) { if (data.length === 0) {
return renderer.setState(channel, { return renderer.setState(channel, {
@ -183,7 +172,7 @@ export const SimpleRenderer: RendererRoutines = {
}); });
} }
let messages = [...state.messages, ...data.map((x) => mapMessage(x))]; let messages = [...state.messages, ...data];
let atBottom = false; let atBottom = false;
if (data.length < 50) { if (data.length < 50) {

View file

@ -1,6 +1,4 @@
import { Message } from "revolt.js"; import { Message } from "revolt.js/dist/maps/Messages";
import { MessageObject } from "../../context/revoltjs/util";
import { SingletonRenderer } from "./Singleton"; import { SingletonRenderer } from "./Singleton";
@ -20,7 +18,7 @@ export type RenderState =
type: "RENDER"; type: "RENDER";
atTop: boolean; atTop: boolean;
atBottom: boolean; atBottom: boolean;
messages: MessageObject[]; messages: Message[];
}; };
export interface RendererRoutines { export interface RendererRoutines {

View file

@ -29,15 +29,15 @@ export default function Open() {
useEffect(() => { useEffect(() => {
if (id === "saved") { if (id === "saved") {
for (const channel of client.channels.toArray()) { for (const channel of [...client.channels.values()]) {
if (channel?.channel_type === "SavedMessages") { if (channel?.channel_type === "SavedMessages") {
history.push(`/channel/${channel._id}`); history.push(`/channel/${channel._id}`);
return; return;
} }
} }
client.users client
.openDM(client.user?._id as string) .user!.openDM()
.then((channel) => history.push(`/channel/${channel?._id}`)) .then((channel) => history.push(`/channel/${channel?._id}`))
.catch((error) => openScreen({ id: "error", error })); .catch((error) => openScreen({ id: "error", error }));
@ -46,19 +46,20 @@ export default function Open() {
let user = client.users.get(id); let user = client.users.get(id);
if (user) { if (user) {
const channel: string | undefined = client.channels const channel: string | undefined = [
.toArray() ...client.channels.values(),
.find( ].find(
(channel) => (channel) =>
channel?.channel_type === "DirectMessage" && channel?.channel_type === "DirectMessage" &&
channel.recipients.includes(id), channel.recipient_ids!.includes(id),
)?._id; )?._id;
if (channel) { if (channel) {
history.push(`/channel/${channel}`); history.push(`/channel/${channel}`);
} else { } else {
client.users client.users
.openDM(id) .get(id)
?.openDM()
.then((channel) => history.push(`/channel/${channel?._id}`)) .then((channel) => history.push(`/channel/${channel?._id}`))
.catch((error) => openScreen({ id: "error", error })); .catch((error) => openScreen({ id: "error", error }));
} }

View file

@ -1,16 +1,16 @@
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { useParams, useHistory } from "react-router-dom"; import { useParams } from "react-router-dom";
import { Channels } from "revolt.js/dist/api/objects"; import { Channel as ChannelI } from "revolt.js/dist/maps/Channels";
import styled from "styled-components"; import styled from "styled-components";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice"; import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
import { Channel as MobXChannel } from "../../mobx";
import { useData } from "../../mobx/State";
import { dispatch, getState } from "../../redux"; import { dispatch, getState } from "../../redux";
import { useClient } from "../../context/revoltjs/RevoltClient";
import AgeGate from "../../components/common/AgeGate"; import AgeGate from "../../components/common/AgeGate";
import MessageBox from "../../components/common/messaging/MessageBox"; import MessageBox from "../../components/common/messaging/MessageBox";
import JumpToBottom from "../../components/common/messaging/bars/JumpToBottom"; import JumpToBottom from "../../components/common/messaging/bars/JumpToBottom";
@ -37,8 +37,8 @@ const ChannelContent = styled.div`
`; `;
export function Channel({ id }: { id: string }) { export function Channel({ id }: { id: string }) {
const store = useData(); const client = useClient();
const channel = store.channels.get(id); const channel = client.channels.get(id);
if (!channel) return null; if (!channel) return null;
if (channel.channel_type === "VoiceChannel") { if (channel.channel_type === "VoiceChannel") {
@ -49,7 +49,7 @@ export function Channel({ id }: { id: string }) {
} }
const MEMBERS_SIDEBAR_KEY = "sidebar_members"; const MEMBERS_SIDEBAR_KEY = "sidebar_members";
const TextChannel = observer(({ channel }: { channel: MobXChannel }) => { const TextChannel = observer(({ channel }: { channel: ChannelI }) => {
const [showMembers, setMembers] = useState( const [showMembers, setMembers] = useState(
getState().sectionToggle[MEMBERS_SIDEBAR_KEY] ?? true, getState().sectionToggle[MEMBERS_SIDEBAR_KEY] ?? true,
); );
@ -101,7 +101,7 @@ const TextChannel = observer(({ channel }: { channel: MobXChannel }) => {
); );
}); });
function VoiceChannel({ channel }: { channel: MobXChannel }) { function VoiceChannel({ channel }: { channel: ChannelI }) {
return ( return (
<> <>
<ChannelHeader channel={channel} /> <ChannelHeader channel={channel} />

View file

@ -1,18 +1,13 @@
import { At, Hash, Menu } from "@styled-icons/boxicons-regular"; import { At, Hash, Menu } from "@styled-icons/boxicons-regular";
import { Notepad, Group } from "@styled-icons/boxicons-solid"; import { Notepad, Group } from "@styled-icons/boxicons-solid";
import { observable } from "mobx";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { Channel } from "revolt.js/dist/maps/Channels";
import { User } from "revolt.js/dist/maps/Users";
import styled from "styled-components"; import styled from "styled-components";
import { useContext } from "preact/hooks";
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice"; import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
import { Channel, User } from "../../mobx";
import { useData } from "../../mobx/State";
import { useIntermediate } from "../../context/intermediate/Intermediate"; import { useIntermediate } from "../../context/intermediate/Intermediate";
import { AppContext, useClient } from "../../context/revoltjs/RevoltClient";
import { getChannelName } from "../../context/revoltjs/util"; import { getChannelName } from "../../context/revoltjs/util";
import { useStatusColour } from "../../components/common/user/UserIcon"; import { useStatusColour } from "../../components/common/user/UserIcon";
@ -71,10 +66,8 @@ const Info = styled.div`
export default observer(({ channel, toggleSidebar }: ChannelHeaderProps) => { export default observer(({ channel, toggleSidebar }: ChannelHeaderProps) => {
const { openScreen } = useIntermediate(); const { openScreen } = useIntermediate();
const client = useClient();
const state = useData();
const name = getChannelName(client, channel); const name = getChannelName(channel);
let icon, recipient: User | undefined; let icon, recipient: User | undefined;
switch (channel.channel_type) { switch (channel.channel_type) {
case "SavedMessages": case "SavedMessages":
@ -82,8 +75,7 @@ export default observer(({ channel, toggleSidebar }: ChannelHeaderProps) => {
break; break;
case "DirectMessage": case "DirectMessage":
icon = <At size={24} />; icon = <At size={24} />;
const uid = client.channels.getRecipient(channel._id); recipient = channel.recipient;
recipient = state.users.get(uid);
break; break;
case "Group": case "Group":
icon = <Group size={24} />; icon = <Group size={24} />;

View file

@ -29,7 +29,6 @@ export default function HeaderActions({
toggleSidebar, toggleSidebar,
}: ChannelHeaderProps) { }: ChannelHeaderProps) {
const { openScreen } = useIntermediate(); const { openScreen } = useIntermediate();
const client = useContext(AppContext);
const history = useHistory(); const history = useHistory();
return ( return (
@ -41,13 +40,10 @@ export default function HeaderActions({
onClick={() => onClick={() =>
openScreen({ openScreen({
id: "user_picker", id: "user_picker",
omit: channel.recipients!, omit: channel.recipient_ids!,
callback: async (users) => { callback: async (users) => {
for (const user of users) { for (const user of users) {
await client.channels.addMember( await channel.addMember(user);
channel._id,
user,
);
} }
}, },
}) })

View file

@ -3,8 +3,6 @@ import styled from "styled-components";
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { useData } from "../../../mobx/State";
import { useClient } from "../../../context/revoltjs/RevoltClient"; import { useClient } from "../../../context/revoltjs/RevoltClient";
import { getChannelName } from "../../../context/revoltjs/util"; import { getChannelName } from "../../../context/revoltjs/util";
@ -28,14 +26,13 @@ interface Props {
} }
export default observer(({ id }: Props) => { export default observer(({ id }: Props) => {
const store = useData();
const client = useClient(); const client = useClient();
const channel = store.channels.get(id); const channel = client.channels.get(id);
if (!channel) return null; if (!channel) return null;
return ( return (
<StartBase> <StartBase>
<h1>{getChannelName(client, channel, true)}</h1> <h1>{getChannelName(channel, true)}</h1>
<h4> <h4>
<Text id="app.main.channel.start.group" /> <Text id="app.main.channel.start.group" />
</h4> </h4>

View file

@ -1,3 +1,4 @@
import { Message } from "revolt.js/dist/maps/Messages";
import styled from "styled-components"; import styled from "styled-components";
import { useContext, useEffect, useState } from "preact/hooks"; import { useContext, useEffect, useState } from "preact/hooks";
@ -10,7 +11,6 @@ import {
useIntermediate, useIntermediate,
} from "../../../context/intermediate/Intermediate"; } from "../../../context/intermediate/Intermediate";
import { AppContext } from "../../../context/revoltjs/RevoltClient"; import { AppContext } from "../../../context/revoltjs/RevoltClient";
import { MessageObject } from "../../../context/revoltjs/util";
import AutoComplete, { import AutoComplete, {
useAutoComplete, useAutoComplete,
@ -44,7 +44,7 @@ const EditorBase = styled.div`
`; `;
interface Props { interface Props {
message: MessageObject; message: Message;
finish: () => void; finish: () => void;
} }
@ -52,7 +52,6 @@ export default function MessageEditor({ message, finish }: Props) {
const [content, setContent] = useState((message.content as string) ?? ""); const [content, setContent] = useState((message.content as string) ?? "");
const { focusTaken } = useContext(IntermediateContext); const { focusTaken } = useContext(IntermediateContext);
const { openScreen } = useIntermediate(); const { openScreen } = useIntermediate();
const client = useContext(AppContext);
async function save() { async function save() {
finish(); finish();
@ -60,13 +59,11 @@ export default function MessageEditor({ message, finish }: Props) {
if (content.length === 0) { if (content.length === 0) {
openScreen({ openScreen({
id: "special_prompt", id: "special_prompt",
// @ts-expect-error
type: "delete_message", type: "delete_message",
// @ts-expect-error
target: message, target: message,
}); });
} else if (content !== message.content) { } else if (content !== message.content) {
await client.channels.editMessage(message.channel, message._id, { await message.editMessage({
content, content,
}); });
} }

View file

@ -1,5 +1,7 @@
import { X } from "@styled-icons/boxicons-regular"; import { X } from "@styled-icons/boxicons-regular";
import { Users } from "revolt.js/dist/api/objects"; import { RelationshipStatus } from "revolt-api/types/Users";
import { SYSTEM_USER_ID } from "revolt.js";
import { Message as MessageObject } from "revolt.js/dist/maps/Messages";
import styled from "styled-components"; import styled from "styled-components";
import { decodeTime } from "ulid"; import { decodeTime } from "ulid";
@ -15,7 +17,6 @@ import { QueuedMessage } from "../../../redux/reducers/queue";
import RequiresOnline from "../../../context/revoltjs/RequiresOnline"; import RequiresOnline from "../../../context/revoltjs/RequiresOnline";
import { AppContext } from "../../../context/revoltjs/RevoltClient"; import { AppContext } from "../../../context/revoltjs/RevoltClient";
import { MessageObject } from "../../../context/revoltjs/util";
import Message from "../../../components/common/messaging/Message"; import Message from "../../../components/common/messaging/Message";
import { SystemMessage } from "../../../components/common/messaging/SystemMessage"; import { SystemMessage } from "../../../components/common/messaging/SystemMessage";
@ -60,7 +61,7 @@ function MessageRenderer({ id, state, queue, highlight }: Props) {
function editLast() { function editLast() {
if (state.type !== "RENDER") return; if (state.type !== "RENDER") return;
for (let i = state.messages.length - 1; i >= 0; i--) { for (let i = state.messages.length - 1; i >= 0; i--) {
if (state.messages[i].author === userId) { if (state.messages[i].author_id === userId) {
setEditing(state.messages[i]._id); setEditing(state.messages[i]._id);
internalEmit("MessageArea", "jump_to_bottom"); internalEmit("MessageArea", "jump_to_bottom");
return; return;
@ -129,10 +130,15 @@ function MessageRenderer({ id, state, queue, highlight }: Props) {
for (const message of state.messages) { for (const message of state.messages) {
if (previous) { if (previous) {
compare(message._id, message.author, previous._id, previous.author); compare(
message._id,
message.author_id,
previous._id,
previous.author_id,
);
} }
if (message.author === "00000000000000000000000000") { if (message.author_id === SYSTEM_USER_ID) {
render.push( render.push(
<SystemMessage <SystemMessage
key={message._id} key={message._id}
@ -143,10 +149,7 @@ function MessageRenderer({ id, state, queue, highlight }: Props) {
); );
} else { } else {
// ! FIXME: temp solution // ! FIXME: temp solution
if ( if (message.author?.relationship === RelationshipStatus.Blocked) {
client.users.get(message.author)?.relationship ===
Users.Relationship.Blocked
) {
blocked++; blocked++;
} else { } else {
if (blocked > 0) pushBlocked(); if (blocked > 0) pushBlocked();
@ -183,7 +186,7 @@ function MessageRenderer({ id, state, queue, highlight }: Props) {
if (nonces.includes(msg.id)) continue; if (nonces.includes(msg.id)) continue;
if (previous) { if (previous) {
compare(msg.id, userId!, previous._id, previous.author); compare(msg.id, userId!, previous._id, previous.author_id);
previous = { previous = {
_id: msg.id, _id: msg.id,
@ -191,7 +194,8 @@ function MessageRenderer({ id, state, queue, highlight }: Props) {
} as any; } as any;
} }
render.push( // ! FIXME: add queued messages back
/* render.push(
<Message <Message
message={{ message={{
...msg.data, ...msg.data,
@ -202,7 +206,7 @@ function MessageRenderer({ id, state, queue, highlight }: Props) {
head={head} head={head}
attachContext attachContext
/>, />,
); ); */
} }
} else { } else {
render.push( render.push(

View file

@ -6,8 +6,6 @@ import styled from "styled-components";
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { useContext } from "preact/hooks"; import { useContext } from "preact/hooks";
import { useData } from "../../../mobx/State";
import { import {
VoiceContext, VoiceContext,
VoiceOperationsContext, VoiceOperationsContext,
@ -77,14 +75,13 @@ export default observer(({ id }: Props) => {
const { isProducing, startProducing, stopProducing, disconnect } = const { isProducing, startProducing, stopProducing, disconnect } =
useContext(VoiceOperationsContext); useContext(VoiceOperationsContext);
const store = useData();
const client = useClient(); const client = useClient();
const self = store.users.get(client.user!._id); const self = client.users.get(client.user!._id);
//const ctx = useForceUpdate(); //const ctx = useForceUpdate();
//const self = useSelf(ctx); //const self = useSelf(ctx);
const keys = participants ? Array.from(participants.keys()) : undefined; const keys = participants ? Array.from(participants.keys()) : undefined;
const users = keys?.map((key) => store.users.get(key)); const users = keys?.map((key) => client.users.get(key));
return ( return (
<VoiceBase> <VoiceBase>

View file

@ -1,7 +1,5 @@
import { Wrench } from "@styled-icons/boxicons-solid"; import { Wrench } from "@styled-icons/boxicons-solid";
import { isObservable, isObservableProp } from "mobx";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { Channels } from "revolt.js/dist/api/objects";
import { useContext } from "preact/hooks"; import { useContext } from "preact/hooks";
@ -9,17 +7,13 @@ import PaintCounter from "../../lib/PaintCounter";
import { TextReact } from "../../lib/i18n"; import { TextReact } from "../../lib/i18n";
import { AppContext } from "../../context/revoltjs/RevoltClient"; import { AppContext } from "../../context/revoltjs/RevoltClient";
import { useUserPermission } from "../../context/revoltjs/hooks";
import UserIcon from "../../components/common/user/UserIcon";
import Header from "../../components/ui/Header"; import Header from "../../components/ui/Header";
import { useData } from "../../mobx/State";
export default function Developer() { export default function Developer() {
// const voice = useContext(VoiceContext); // const voice = useContext(VoiceContext);
const client = useContext(AppContext); const client = useContext(AppContext);
const userPermission = useUserPermission(client.user!._id); const userPermission = client.user!.permission;
return ( return (
<div> <div>
@ -40,10 +34,6 @@ export default function Developer() {
fields={{ provider: <b>GAMING!</b> }} fields={{ provider: <b>GAMING!</b> }}
/> />
</div> </div>
<ObserverTest />
<ObserverTest2 />
<ObserverTest3 />
<ObserverTest4 />
<div style={{ padding: "16px" }}> <div style={{ padding: "16px" }}>
{/*<span> {/*<span>
<b>Voice Status:</b> {VoiceStatus[voice.status]} <b>Voice Status:</b> {VoiceStatus[voice.status]}
@ -62,67 +52,3 @@ export default function Developer() {
</div> </div>
); );
} }
const ObserverTest = observer(() => {
const client = useContext(AppContext);
const store = useData();
return (
<div style={{ padding: "16px" }}>
<p>
username:{" "}
{store.users.get(client.user!._id)?.username ?? "no user!"}
<PaintCounter small />
</p>
</div>
);
});
const ObserverTest2 = observer(() => {
const client = useContext(AppContext);
const store = useData();
return (
<div style={{ padding: "16px" }}>
<p>
status:{" "}
{JSON.stringify(store.users.get(client.user!._id)?.status) ??
"none"}
<PaintCounter small />
</p>
</div>
);
});
const ObserverTest3 = observer(() => {
const client = useContext(AppContext);
const store = useData();
return (
<div style={{ padding: "16px" }}>
<p>
avatar{" "}
<UserIcon
size={64}
attachment={
store.users.get(client.user!._id)?.avatar ?? undefined
}
/>
<PaintCounter small />
</p>
</div>
);
});
const ObserverTest4 = observer(() => {
const client = useContext(AppContext);
const store = useData();
return (
<div style={{ padding: "16px" }}>
<p>
status text:{" "}
{JSON.stringify(
store.users.get(client.user!._id)?.status?.text,
) ?? "none"}
<PaintCounter small />
</p>
</div>
);
});

View file

@ -1,6 +1,7 @@
import { X, Plus } from "@styled-icons/boxicons-regular"; import { X, Plus } from "@styled-icons/boxicons-regular";
import { PhoneCall, Envelope, UserX } from "@styled-icons/boxicons-solid"; import { PhoneCall, Envelope, UserX } from "@styled-icons/boxicons-solid";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { useHistory } from "react-router-dom";
import { RelationshipStatus } from "revolt-api/types/Users"; import { RelationshipStatus } from "revolt-api/types/Users";
import { User } from "revolt.js/dist/maps/Users"; import { User } from "revolt.js/dist/maps/Users";
@ -30,9 +31,8 @@ interface Props {
} }
export const Friend = observer(({ user }: Props) => { export const Friend = observer(({ user }: Props) => {
const client = useContext(AppContext); const history = useHistory();
const { openScreen } = useIntermediate(); const { openScreen } = useIntermediate();
const { openDM } = useContext(OperationsContext);
const { connect } = useContext(VoiceOperationsContext); const { connect } = useContext(VoiceOperationsContext);
const actions: Children[] = []; const actions: Children[] = [];
@ -46,14 +46,29 @@ export const Friend = observer(({ user }: Props) => {
type="circle" type="circle"
className={classNames(styles.button, styles.success)} className={classNames(styles.button, styles.success)}
onClick={(ev) => onClick={(ev) =>
stopPropagation(ev, openDM(user._id).then(connect)) stopPropagation(
ev,
user.openDM().then((channel) => {
connect(channel._id);
history.push(`/channel/${channel._id}`);
}),
)
}> }>
<PhoneCall size={20} /> <PhoneCall size={20} />
</IconButton> </IconButton>
<IconButton <IconButton
type="circle" type="circle"
className={styles.button} className={styles.button}
onClick={(ev) => stopPropagation(ev, openDM(user._id))}> onClick={(ev) =>
stopPropagation(
ev,
user
.openDM()
.then((channel) =>
history.push(`/channel/${channel._id}`),
),
)
}>
<Envelope size={20} /> <Envelope size={20} />
</IconButton> </IconButton>
</>, </>,

View file

@ -14,7 +14,6 @@ import {
StatusContext, StatusContext,
useClient, useClient,
} from "../../../context/revoltjs/RevoltClient"; } from "../../../context/revoltjs/RevoltClient";
import { useForceUpdate } from "../../../context/revoltjs/hooks";
import Tooltip from "../../../components/common/Tooltip"; import Tooltip from "../../../components/common/Tooltip";
import UserIcon from "../../../components/common/user/UserIcon"; import UserIcon from "../../../components/common/user/UserIcon";