diff options
Diffstat (limited to 'src/utils')
-rw-r--r-- | src/utils/IpcEvents.ts | 22 | ||||
-rw-r--r-- | src/utils/ipcEvents.ts | 3 | ||||
-rw-r--r-- | src/utils/misc.tsx | 61 | ||||
-rw-r--r-- | src/utils/patchWebpack.ts | 12 | ||||
-rw-r--r-- | src/utils/quickCss.ts | 6 | ||||
-rw-r--r-- | src/utils/types.ts | 2 | ||||
-rw-r--r-- | src/utils/webpack.ts | 105 |
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 |