aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/components/PluginSettings/index.tsx2
-rw-r--r--src/components/Settings.tsx24
-rw-r--r--src/utils/Queue.ts23
-rw-r--r--src/utils/debounce.ts7
-rw-r--r--src/utils/misc.tsx6
-rw-r--r--src/utils/modal.tsx12
-rw-r--r--src/webpack/webpack.ts22
7 files changed, 63 insertions, 33 deletions
diff --git a/src/components/PluginSettings/index.tsx b/src/components/PluginSettings/index.tsx
index 2de9362..39fb911 100644
--- a/src/components/PluginSettings/index.tsx
+++ b/src/components/PluginSettings/index.tsx
@@ -302,7 +302,7 @@ export default ErrorBoundary.wrap(function Settings() {
<PluginCard
onMouseLeave={onMouseLeave}
onMouseEnter={onMouseEnter}
- onRestartNeeded={name => changes.add(name)}
+ onRestartNeeded={name => changes.handleChange(name)}
disabled={plugin.required || !!dependency}
plugin={plugin}
/>
diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx
index e2abff2..f8e7495 100644
--- a/src/components/Settings.tsx
+++ b/src/components/Settings.tsx
@@ -17,11 +17,10 @@
*/
import { useSettings } from "../api/settings";
-import { ChangeList } from "../utils/ChangeList";
import IpcEvents from "../utils/IpcEvents";
import { useAwaiter } from "../utils/misc";
import { downloadSettingsBackup, uploadSettingsBackup } from "../utils/settingsSync";
-import { Alerts, Button, Card, Forms, Margins, Parser, React, Switch } from "../webpack/common";
+import { Button, Card, Forms, Margins, React, Switch } from "../webpack/common";
import DonateButton from "./DonateButton";
import ErrorBoundary from "./ErrorBoundary";
import { Flex } from "./Flex";
@@ -30,27 +29,6 @@ import { handleComponentFailed } from "./handleComponentFailed";
export default ErrorBoundary.wrap(function Settings() {
const [settingsDir, , settingsDirPending] = useAwaiter(() => VencordNative.ipc.invoke<string>(IpcEvents.GET_SETTINGS_DIR), "Loading...");
const settings = useSettings();
- const changes = React.useMemo(() => new ChangeList<string>(), []);
-
- React.useEffect(() => {
- return () => void (changes.hasChanges && Alerts.show({
- title: "Restart required",
- body: (
- <>
- <p>The following plugins require a restart:</p>
- <div>{changes.map((s, i) => (
- <>
- {i > 0 && ", "}
- {Parser.parse("`" + s + "`")}
- </>
- ))}</div>
- </>
- ),
- confirmText: "Restart now",
- cancelText: "Later!",
- onConfirm: () => location.reload()
- }));
- }, []);
return (
<Forms.FormSection tag="h1" title="Vencord">
diff --git a/src/utils/Queue.ts b/src/utils/Queue.ts
index 6153d40..86eb791 100644
--- a/src/utils/Queue.ts
+++ b/src/utils/Queue.ts
@@ -18,14 +18,18 @@
import { Promisable } from "type-fest";
+/**
+ * A queue that can be used to run tasks consecutively.
+ * Highly recommended for things like fetching data from Discord
+ */
export class Queue {
/**
* @param maxSize The maximum amount of functions that can be queued at once.
- * If the queue is full, the oldest function will be removed.
+ * If the queue is full, the oldest function will be removed.
*/
constructor(public maxSize = Infinity) { }
- queue = [] as Array<() => Promisable<unknown>>;
+ private queue = [] as Array<() => Promisable<unknown>>;
private promise?: Promise<any>;
@@ -34,7 +38,7 @@ export class Queue {
if (func)
this.promise = Promise.resolve()
.then(func)
- .then(() => this.next());
+ .finally(() => this.next());
else
this.promise = undefined;
}
@@ -44,6 +48,11 @@ export class Queue {
this.next();
}
+ /**
+ * Append a task at the end of the queue. This task will be executed after all other tasks
+ * If the queue exceeds the specified maxSize, the first task in queue will be removed.
+ * @param func Task
+ */
push<T>(func: () => Promisable<T>) {
if (this.size >= this.maxSize)
this.queue.shift();
@@ -52,6 +61,11 @@ export class Queue {
this.run();
}
+ /**
+ * Prepend a task at the beginning of the queue. This task will be executed next
+ * If the queue exceeds the specified maxSize, the last task in queue will be removed.
+ * @param func Task
+ */
unshift<T>(func: () => Promisable<T>) {
if (this.size >= this.maxSize)
this.queue.pop();
@@ -60,6 +74,9 @@ export class Queue {
this.run();
}
+ /**
+ * The amount of tasks in the queue
+ */
get size() {
return this.queue.length;
}
diff --git a/src/utils/debounce.ts b/src/utils/debounce.ts
index d9e19de..6e5bba6 100644
--- a/src/utils/debounce.ts
+++ b/src/utils/debounce.ts
@@ -16,6 +16,13 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+/**
+ * Returns a new function that will call the wrapped function
+ * after the specified delay. If the function is called again
+ * within the delay, the timer will be reset.
+ * @param func The function to wrap
+ * @param delay The delay in milliseconds
+ */
export function debounce<T extends Function>(func: T, delay = 300): T {
let timeout: NodeJS.Timeout;
return function (...args: any[]) {
diff --git a/src/utils/misc.tsx b/src/utils/misc.tsx
index 44fc819..7389cc2 100644
--- a/src/utils/misc.tsx
+++ b/src/utils/misc.tsx
@@ -70,6 +70,9 @@ export function useAwaiter<T>(factory: () => Promise<T>, fallbackValue: T | null
return [state.value, state.error, state.pending, () => setSignal(signal + 1)];
}
+/**
+ * Returns a function that can be used to force rerender react components
+ */
export function useForceUpdater() {
const [, set] = React.useState(0);
return () => set(s => s + 1);
@@ -144,6 +147,9 @@ export function classes(...classes: string[]) {
return classes.join(" ");
}
+/**
+ * Returns a promise that resolves after the specified amount of time
+ */
export function sleep(ms: number): Promise<void> {
return new Promise(r => setTimeout(r, ms));
}
diff --git a/src/utils/modal.tsx b/src/utils/modal.tsx
index 2affbd7..886e325 100644
--- a/src/utils/modal.tsx
+++ b/src/utils/modal.tsx
@@ -76,14 +76,26 @@ const ModalAPI = mapMangledModuleLazy("onCloseRequest:null!=", {
openModalLazy: m => m?.length === 1 && filters.byCode(".apply(this,arguments)")(m),
});
+/**
+ * Wait for the render promise to resolve, then open a modal with it.
+ * This is equivalent to render().then(openModal)
+ * You should use the Modal components exported by this file
+ */
export function openModalLazy(render: () => Promise<RenderFunction>, options?: ModalOptions & { contextKey?: string; }): Promise<string> {
return ModalAPI.openModalLazy(render, options);
}
+/**
+ * Open a Modal with the given render function.
+ * You should use the Modal components exported by this file
+ */
export function openModal(render: RenderFunction, options?: ModalOptions, contextKey?: string): string {
return ModalAPI.openModal(render, options, contextKey);
}
+/**
+ * Close a modal by its key
+ */
export function closeModal(modalKey: string, contextKey?: string): void {
return ModalAPI.closeModal(modalKey, contextKey);
}
diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts
index 1231d9e..bd06b03 100644
--- a/src/webpack/webpack.ts
+++ b/src/webpack/webpack.ts
@@ -42,8 +42,6 @@ export const filters = {
? m => m[props[0]] !== void 0
: m => props.every(p => m[p] !== void 0),
- byDisplayName: (deezNuts: string): FilterFn => m => m.default?.displayName === deezNuts,
-
byCode: (...code: string[]): FilterFn => m => {
if (typeof m !== "function") return false;
const s = Function.prototype.toString.call(m);
@@ -75,6 +73,9 @@ if (IS_DEV && !IS_WEB) {
}, 0);
}
+/**
+ * Find the first module that matches the filter
+ */
export const find = traceFunction("find", function find(filter: FilterFn, getDefault = true, isWaitFor = false) {
if (typeof filter !== "function")
throw new Error("Invalid filter. Expected a function got " + typeof filter);
@@ -283,22 +284,31 @@ export function mapMangledModuleLazy<S extends string>(code: string, mappers: Re
return proxyLazy(() => mapMangledModule(code, mappers));
}
+/**
+ * Find the first module that has the specified properties
+ */
export function findByProps(...props: string[]) {
return find(filters.byProps(...props));
}
+/**
+ * Find all modules that have the specified properties
+ */
export function findAllByProps(...props: string[]) {
return findAll(filters.byProps(...props));
}
+/**
+ * Find a function by its code
+ */
export function findByCode(...code: string[]) {
return find(filters.byCode(...code));
}
-export function findByDisplayName(deezNuts: string) {
- return find(filters.byDisplayName(deezNuts));
-}
-
+/**
+ * Wait for a module that matches the provided filter to be registered,
+ * then call the callback with the module as the first argument
+ */
export function waitFor(filter: string | string[] | FilterFn, callback: CallbackFn) {
if (typeof filter === "string")
filter = filters.byProps(filter);