aboutsummaryrefslogtreecommitdiff
path: root/src/utils
diff options
context:
space:
mode:
Diffstat (limited to 'src/utils')
-rw-r--r--src/utils/IpcEvents.ts22
-rw-r--r--src/utils/ipcEvents.ts3
-rw-r--r--src/utils/misc.tsx61
-rw-r--r--src/utils/patchWebpack.ts12
-rw-r--r--src/utils/quickCss.ts6
-rw-r--r--src/utils/types.ts2
-rw-r--r--src/utils/webpack.ts105
7 files changed, 97 insertions, 114 deletions
diff --git a/src/utils/IpcEvents.ts b/src/utils/IpcEvents.ts
new file mode 100644
index 0000000..70ba502
--- /dev/null
+++ b/src/utils/IpcEvents.ts
@@ -0,0 +1,22 @@
+type Enum<T extends Record<string, string>> = {
+ [k in keyof T]: T[k];
+} & { [v in keyof T as T[v]]: v; };
+
+function strEnum<T extends Record<string, string>>(obj: T): T {
+ const o = {} as T;
+ for (const key in obj) {
+ o[key] = obj[key] as any;
+ o[obj[key]] = key as any;
+ };
+ return o;
+}
+
+export default strEnum({
+ QUICK_CSS_UPDATE: "VencordQuickCssUpdate",
+ GET_QUICK_CSS: "VencordGetQuickCss",
+ GET_SETTINGS_DIR: "VencordGetSettingsDir",
+ GET_SETTINGS: "VencordGetSettings",
+ SET_SETTINGS: "VencordSetSettings",
+ OPEN_EXTERNAL: "VencordOpenExternal",
+ OPEN_PATH: "VencordOpenPath",
+} as const); \ No newline at end of file
diff --git a/src/utils/ipcEvents.ts b/src/utils/ipcEvents.ts
deleted file mode 100644
index 1920023..0000000
--- a/src/utils/ipcEvents.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export const IPC_QUICK_CSS_UPDATE = "VencordQuickCssUpdate";
-export const IPC_GET_QUICK_CSS = "VencordGetQuickCss";
-export const IPC_GET_SETTINGS_DIR = "VencordGetSettingsDir"; \ No newline at end of file
diff --git a/src/utils/misc.tsx b/src/utils/misc.tsx
new file mode 100644
index 0000000..ded052b
--- /dev/null
+++ b/src/utils/misc.tsx
@@ -0,0 +1,61 @@
+import { React } from "../webpack";
+
+/**
+ * Makes a lazy function. On first call, the value is computed.
+ * On subsequent calls, the same computed value will be returned
+ * @param factory Factory function
+ */
+export function lazy<T>(factory: () => T): () => T {
+ let cache: T;
+ return () => {
+ return cache ?? (cache = factory());
+ };
+}
+
+/**
+ * Await a promise
+ * @param factory Factory
+ * @param fallbackValue The fallback value that will be used until the promise resolved
+ * @returns A state that will either be null or the result of the promise
+ */
+export function useAwaiter<T>(factory: () => Promise<T>, fallbackValue: T | null = null): T | null {
+ const [res, setRes] = React.useState<T | null>(fallbackValue);
+
+ React.useEffect(() => {
+ factory().then(setRes);
+ }, []);
+
+ return res;
+}
+
+/**
+ * A lazy component. The factory method is called on first render. For example useful
+ * for const Component = LazyComponent(() => findByDisplayName("...").default)
+ * @param factory Function returning a Component
+ * @returns Result of factory function
+ */
+export function LazyComponent<T = any>(factory: () => React.ComponentType<T>) {
+ return (props: T) => {
+ const Component = React.useMemo(factory, []);
+ return <Component {...props} />;
+ };
+}
+
+/**
+ * Recursively merges defaults into an object and returns the same object
+ * @param obj Object
+ * @param defaults Defaults
+ * @returns obj
+ */
+export function mergeDefaults<T>(obj: T, defaults: T): T {
+ for (const key in defaults) {
+ const v = defaults[key];
+ if (typeof v === "object" && !Array.isArray(v)) {
+ obj[key] ??= {} as any;
+ mergeDefaults(obj[key], v);
+ } else {
+ obj[key] ??= v;
+ }
+ }
+ return obj;
+} \ No newline at end of file
diff --git a/src/utils/patchWebpack.ts b/src/utils/patchWebpack.ts
index 0e94694..9f4b435 100644
--- a/src/utils/patchWebpack.ts
+++ b/src/utils/patchWebpack.ts
@@ -1,6 +1,6 @@
import { WEBPACK_CHUNK } from './constants';
import Logger from "./logger";
-import { _initWebpack } from "./webpack";
+import { _initWebpack } from "../webpack";
let webpackChunk: any[];
@@ -83,9 +83,13 @@ function patchPush() {
const lastCode = code;
try {
const newCode = code.replace(replacement.match, replacement.replace);
- const newMod = (0, eval)(`// Webpack Module ${id} - Patched by ${[...patchedBy].join(", ")}\n${newCode}\n//# sourceURL=WebpackModule${id}`);
- code = newCode;
- mod = newMod;
+ if (newCode === code) {
+ logger.warn(`Patch by ${patch.plugin} had no effect: ${replacement.match}`);
+ } else {
+ const newMod = (0, eval)(`// Webpack Module ${id} - Patched by ${[...patchedBy].join(", ")}\n${newCode}\n//# sourceURL=WebpackModule${id}`);
+ code = newCode;
+ mod = newMod;
+ }
} catch (err) {
logger.error("Failed to apply patch of", patch.plugin, err);
code = lastCode;
diff --git a/src/utils/quickCss.ts b/src/utils/quickCss.ts
index 724fde8..5c9e830 100644
--- a/src/utils/quickCss.ts
+++ b/src/utils/quickCss.ts
@@ -1,6 +1,8 @@
+import IpcEvents from "./IpcEvents";
+
document.addEventListener("DOMContentLoaded", async () => {
const style = document.createElement("style");
document.head.appendChild(style);
- VencordNative.handleQuickCssUpdate((css: string) => style.innerText = css);
- style.innerText = await VencordNative.getQuickCss();
+ VencordNative.ipc.on(IpcEvents.QUICK_CSS_UPDATE, (_, css: string) => style.innerText = css);
+ style.innerText = await VencordNative.ipc.invoke(IpcEvents.GET_QUICK_CSS);
});
diff --git a/src/utils/types.ts b/src/utils/types.ts
index 282ca0e..520b506 100644
--- a/src/utils/types.ts
+++ b/src/utils/types.ts
@@ -20,6 +20,8 @@ export interface Plugin {
author: string;
start?(): void;
patches?: Patch[];
+ dependencies?: string[],
+ required?: boolean;
}
// @ts-ignore lole
diff --git a/src/utils/webpack.ts b/src/utils/webpack.ts
deleted file mode 100644
index 3f21106..0000000
--- a/src/utils/webpack.ts
+++ /dev/null
@@ -1,105 +0,0 @@
-import { startAll } from "../plugins";
-import Logger from "./logger";
-
-let webpackCache: typeof window.webpackChunkdiscord_app;
-
-export const subscriptions = new Map<FilterFn, CallbackFn>();
-export const listeners = new Set<CallbackFn>();
-
-type FilterFn = (mod: any) => boolean;
-type CallbackFn = (mod: any) => void;
-
-export let Common: {
- React: typeof import("react"),
- FluxDispatcher: any;
- UserStore: any;
-} = {} as any;
-
-export function _initWebpack(instance: typeof window.webpackChunkdiscord_app) {
- if (webpackCache !== void 0) throw "no.";
-
- webpackCache = instance.push([[Symbol()], {}, (r) => r.c]);
- instance.pop();
-
- // Abandon Hope All Ye Who Enter Here
-
- let started = false;
- waitFor("getCurrentUser", x => Common.UserStore = x);
- waitFor(["dispatch", "subscribe"], x => {
- Common.FluxDispatcher = x;
- const cb = () => {
- console.info("Connection open");
- x.unsubscribe("CONNECTION_OPEN", cb);
- startAll();
- };
- x.subscribe("CONNECTION_OPEN", cb);
- });
- waitFor("useState", x => Common.React = x);
-}
-
-export function find(filter: FilterFn, getDefault = true) {
- if (typeof filter !== "function")
- throw new Error("Invalid filter. Expected a function got", filter);
-
- for (const key in webpackCache) {
- const mod = webpackCache[key];
- if (mod?.exports && filter(mod.exports))
- return mod.exports;
- if (mod?.exports?.default && filter(mod.exports.default))
- return getDefault ? mod.exports.default : mod.exports;
- }
-
- return null;
-}
-
-export function findAll(filter: FilterFn, getDefault = true) {
- if (typeof filter !== "function") throw new Error("Invalid filter. Expected a function got", filter);
-
- const ret = [] as any[];
- for (const key in webpackCache) {
- const mod = webpackCache[key];
- if (mod?.exports && filter(mod.exports)) ret.push(mod.exports);
- if (mod?.exports?.default && filter(mod.exports.default)) ret.push(getDefault ? mod.exports.default : mod.exports);
- }
-
- return ret;
-}
-
-export const filters = {
- byProps: (props: string[]): FilterFn =>
- props.length === 1
- ? m => m[props[0]] !== void 0
- : m => props.every(p => m[p] !== void 0),
- byDisplayName: (deezNuts: string): FilterFn => m => m.default?.displayName === deezNuts
-};
-
-export function findByProps(...props: string[]) {
- return find(filters.byProps(props));
-}
-
-export function findAllByProps(...props: string[]) {
- return findAll(filters.byProps(props));
-}
-
-export function findByDisplayName(deezNuts: string) {
- return find(filters.byDisplayName(deezNuts));
-}
-
-export function waitFor(filter: string | string[] | FilterFn, callback: CallbackFn) {
- if (typeof filter === "string") filter = filters.byProps([filter]);
- else if (Array.isArray(filter)) filter = filters.byProps(filter);
- else if (typeof filter !== "function") throw new Error("filter must be a string, string[] or function, got", filter);
-
- const existing = find(filter!);
- if (existing) return void callback(existing);
-
- subscriptions.set(filter, callback);
-}
-
-export function addListener(callback: CallbackFn) {
- listeners.add(callback);
-}
-
-export function removeListener(callback: CallbackFn) {
- listeners.delete(callback);
-} \ No newline at end of file