WhoReacted: Make more reliable & don't spam api
This commit is contained in:
parent
bb398970ef
commit
b60f6cb18d
@ -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() {
|
||||
|
@ -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));
|
||||
});
|
||||
|
||||
|
||||
|
@ -16,20 +16,56 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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 : (
|
||||
<ErrorBoundary noop>
|
||||
<this._renderUsers {...props} />
|
||||
</ErrorBoundary>
|
||||
@ -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 (
|
||||
<div
|
||||
@ -79,7 +126,6 @@ export default definePlugin({
|
||||
>
|
||||
<UserSummaryItem
|
||||
users={users}
|
||||
count={users.length}
|
||||
guildId={ChannelStore.getChannel(message.channel_id)?.guild_id}
|
||||
renderIcon={false}
|
||||
max={5}
|
||||
|
@ -19,9 +19,48 @@
|
||||
import { Promisable } from "type-fest";
|
||||
|
||||
export class Queue {
|
||||
private promise: Promise<any> = 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<T>(func: (lastValue: unknown) => Promisable<T>): Promise<T> {
|
||||
return (this.promise = this.promise.then(func));
|
||||
queue = [] as Array<() => Promisable<unknown>>;
|
||||
|
||||
private promise?: Promise<any>;
|
||||
|
||||
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<T>(func: () => Promisable<T>) {
|
||||
if (this.size >= this.maxSize)
|
||||
this.queue.shift();
|
||||
|
||||
this.queue.push(func);
|
||||
this.run();
|
||||
}
|
||||
|
||||
unshift<T>(func: () => Promisable<T>) {
|
||||
if (this.size >= this.maxSize)
|
||||
this.queue.pop();
|
||||
|
||||
this.queue.unshift(func);
|
||||
this.run();
|
||||
}
|
||||
|
||||
get size() {
|
||||
return this.queue.length;
|
||||
}
|
||||
}
|
||||
|
@ -70,6 +70,11 @@ export function useAwaiter<T>(factory: () => Promise<T>, 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)
|
||||
|
@ -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<Stores.MessageStore, "getMessages"> & { getMessages(chanId: string): any; };
|
||||
|
Loading…
Reference in New Issue
Block a user