mirror of
https://github.com/Equicord/Equicord.git
synced 2025-06-17 10:27:03 -04:00
Make Settings & Settings Page
This commit is contained in:
parent
cb288e204d
commit
98cb301df5
18 changed files with 330 additions and 43 deletions
22
src/utils/IpcEvents.ts
Normal file
22
src/utils/IpcEvents.ts
Normal file
|
@ -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);
|
|
@ -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";
|
61
src/utils/misc.tsx
Normal file
61
src/utils/misc.tsx
Normal file
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -20,6 +20,8 @@ export interface Plugin {
|
|||
author: string;
|
||||
start?(): void;
|
||||
patches?: Patch[];
|
||||
dependencies?: string[],
|
||||
required?: boolean;
|
||||
}
|
||||
|
||||
// @ts-ignore lole
|
||||
|
|
|
@ -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);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue