diff --git a/src/components/Monaco.ts b/src/components/Monaco.ts
index 16eff859..6d16c76b 100644
--- a/src/components/Monaco.ts
+++ b/src/components/Monaco.ts
@@ -25,7 +25,7 @@ import { find } from "../webpack/webpack";
const queue = new Queue();
const setCss = debounce((css: string) => {
- queue.add(() => VencordNative.ipc.invoke(IpcEvents.SET_QUICK_CSS, css));
+ queue.push(() => VencordNative.ipc.invoke(IpcEvents.SET_QUICK_CSS, css));
});
export async function launchMonacoEditor() {
diff --git a/src/ipcMain/index.ts b/src/ipcMain/index.ts
index 8a60bc6e..535c005d 100644
--- a/src/ipcMain/index.ts
+++ b/src/ipcMain/index.ts
@@ -66,14 +66,14 @@ const settingsWriteQueue = new Queue();
ipcMain.handle(IpcEvents.GET_QUICK_CSS, () => readCss());
ipcMain.handle(IpcEvents.SET_QUICK_CSS, (_, css) =>
- cssWriteQueue.add(() => writeFile(QUICKCSS_PATH, css))
+ cssWriteQueue.push(() => writeFile(QUICKCSS_PATH, css))
);
ipcMain.handle(IpcEvents.GET_SETTINGS_DIR, () => SETTINGS_DIR);
ipcMain.on(IpcEvents.GET_SETTINGS, e => e.returnValue = readSettings());
ipcMain.handle(IpcEvents.SET_SETTINGS, (_, s) => {
- settingsWriteQueue.add(() => writeFile(SETTINGS_FILE, s));
+ settingsWriteQueue.push(() => writeFile(SETTINGS_FILE, s));
});
diff --git a/src/plugins/whoReacted.tsx b/src/plugins/whoReacted.tsx
index 00799831..b1fb27d5 100644
--- a/src/plugins/whoReacted.tsx
+++ b/src/plugins/whoReacted.tsx
@@ -16,20 +16,56 @@
* along with this program. If not, see .
*/
-import { User } from "discord-types/general";
+import { ReactionEmoji, User } from "discord-types/general";
import ErrorBoundary from "../components/ErrorBoundary";
import { Devs } from "../utils/constants";
-import { LazyComponent, lazyWebpack } from "../utils/misc";
+import { LazyComponent, lazyWebpack, sleep, useForceUpdater } from "../utils/misc";
+import { Queue } from "../utils/Queue";
import definePlugin from "../utils/types";
import { filters, findByCode } from "../webpack";
-import { ChannelStore, React, Tooltip } from "../webpack/common";
+import { ChannelStore, FluxDispatcher, React, RestAPI, Tooltip } from "../webpack/common";
const UserSummaryItem = LazyComponent(() => findByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers"));
const AvatarStyles = lazyWebpack(filters.byProps("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar"));
const ReactionStore = lazyWebpack(filters.byProps("getReactions"));
+const queue = new Queue();
+
+function fetchReactions(msg: Message, emoji: ReactionEmoji) {
+ const key = emoji.name + (emoji.id ? `:${emoji.id}` : "");
+ return RestAPI.get({
+ url: `/channels/${msg.channel_id}/messages/${msg.id}/reactions/${key}`,
+ query: {
+ limit: 100
+ },
+ oldFormErrors: true
+ })
+ .then(res => FluxDispatcher.dispatch({
+ type: "MESSAGE_REACTION_ADD_USERS",
+ channelId: msg.channel_id,
+ messageId: msg.id,
+ users: res.body,
+ emoji
+ }))
+ .catch(console.error)
+ .finally(() => sleep(250));
+}
+
+function getReactionsWithQueue(msg: Message, e: ReactionEmoji) {
+ const key = `${msg.id}:${e.name}:${e.id ?? ""}`;
+ const cache = ReactionStore.__getLocalVars().reactions[key] ??= { fetched: false, users: {} };
+ if (!cache.fetched) {
+ queue.unshift(() =>
+ fetchReactions(msg, e)
+ );
+ cache.fetched = true;
+ }
+
+ return cache.users;
+}
+
function makeRenderMoreUsers(users: User[]) {
return function renderMoreUsers(_label: string, _count: number) {
return (
@@ -62,7 +98,7 @@ export default definePlugin({
}],
renderUsers(props: RootObject) {
- return (
+ return props.message.reactions.length > 10 ? null : (
@@ -70,8 +106,19 @@ export default definePlugin({
},
_renderUsers({ message, emoji }: RootObject) {
- const reactions = ReactionStore.getReactions(message.channel_id, message.id, emoji);
- const users = Object.values(reactions) as User[];
+ const forceUpdate = useForceUpdater();
+ React.useEffect(() => {
+ const cb = (e: any) => {
+ if (e.messageId === message.id)
+ forceUpdate();
+ };
+ FluxDispatcher.subscribe("MESSAGE_REACTION_ADD_USERS", cb);
+
+ return () => FluxDispatcher.unsubscribe("MESSAGE_REACTION_ADD_USERS", cb);
+ }, [message.id]);
+
+ const reactions = getReactionsWithQueue(message, emoji);
+ const users = Object.values(reactions).filter(Boolean) as User[];
return (
= Promise.resolve();
+ /**
+ * @param maxSize The maximum amount of functions that can be queued at once.
+ * If the queue is full, the oldest function will be removed.
+ */
+ constructor(public maxSize = Infinity) { }
- add(func: (lastValue: unknown) => Promisable): Promise {
- return (this.promise = this.promise.then(func));
+ queue = [] as Array<() => Promisable>;
+
+ private promise?: Promise;
+
+ private next() {
+ const func = this.queue.shift();
+ if (func)
+ this.promise = Promise.resolve()
+ .then(func)
+ .then(() => this.next());
+ else
+ this.promise = undefined;
+ }
+
+ private run() {
+ if (!this.promise)
+ this.next();
+ }
+
+ push(func: () => Promisable) {
+ if (this.size >= this.maxSize)
+ this.queue.shift();
+
+ this.queue.push(func);
+ this.run();
+ }
+
+ unshift(func: () => Promisable) {
+ if (this.size >= this.maxSize)
+ this.queue.pop();
+
+ this.queue.unshift(func);
+ this.run();
+ }
+
+ get size() {
+ return this.queue.length;
}
}
diff --git a/src/utils/misc.tsx b/src/utils/misc.tsx
index 005bcc08..44fc819a 100644
--- a/src/utils/misc.tsx
+++ b/src/utils/misc.tsx
@@ -70,6 +70,11 @@ export function useAwaiter(factory: () => Promise, fallbackValue: T | null
return [state.value, state.error, state.pending, () => setSignal(signal + 1)];
}
+export function useForceUpdater() {
+ const [, set] = React.useState(0);
+ return () => set(s => s + 1);
+}
+
/**
* A lazy component. The factory method is called on first render. For example useful
* for const Component = LazyComponent(() => findByDisplayName("...").default)
diff --git a/src/webpack/common.tsx b/src/webpack/common.tsx
index 6ebad167..2f3768eb 100644
--- a/src/webpack/common.tsx
+++ b/src/webpack/common.tsx
@@ -32,6 +32,7 @@ export const Flux = lazyWebpack(filters.byProps("connectStores"));
export let React: typeof import("react");
export const ReactDOM: typeof import("react-dom") = lazyWebpack(filters.byProps("createPortal", "render"));
+export const RestAPI = lazyWebpack(filters.byProps("getAPIBaseURL", "get"));
export const moment: typeof import("moment") = lazyWebpack(filters.byProps("parseTwoDigitYear"));
export const MessageStore = lazyWebpack(filters.byProps("getRawMessages")) as Omit & { getMessages(chanId: string): any; };