Merge branch 'dev' of ssh://github.com/Vendicated/Vencord into feat/usercss
This commit is contained in:
commit
f68351b31b
52 changed files with 632 additions and 417 deletions
8
.github/workflows/reportBrokenPlugins.yml
vendored
8
.github/workflows/reportBrokenPlugins.yml
vendored
|
@ -12,6 +12,12 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
if: ${{ github.event_name == 'schedule' }}
|
||||||
|
with:
|
||||||
|
ref: dev
|
||||||
|
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v2 # Install pnpm using packageManager key in package.json
|
- uses: pnpm/action-setup@v2 # Install pnpm using packageManager key in package.json
|
||||||
|
|
||||||
|
@ -29,7 +35,7 @@ jobs:
|
||||||
sudo apt-get install -y chromium-browser
|
sudo apt-get install -y chromium-browser
|
||||||
|
|
||||||
- name: Build web
|
- name: Build web
|
||||||
run: pnpm buildWeb --standalone
|
run: pnpm buildWeb --standalone --dev
|
||||||
|
|
||||||
- name: Create Report
|
- name: Create Report
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
|
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
|
@ -3,9 +3,11 @@ on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- dev
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- dev
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "vencord",
|
"name": "vencord",
|
||||||
"private": "true",
|
"private": "true",
|
||||||
"version": "1.6.3",
|
"version": "1.6.4",
|
||||||
"description": "The cutest Discord client mod",
|
"description": "The cutest Discord client mod",
|
||||||
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
|
|
|
@ -21,11 +21,11 @@ import esbuild from "esbuild";
|
||||||
import { readdir } from "fs/promises";
|
import { readdir } from "fs/promises";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
|
|
||||||
import { BUILD_TIMESTAMP, commonOpts, existsAsync, globPlugins, isStandalone, updaterDisabled, VERSION, watch } from "./common.mjs";
|
import { BUILD_TIMESTAMP, commonOpts, existsAsync, globPlugins, isDev, isStandalone, updaterDisabled, VERSION, watch } from "./common.mjs";
|
||||||
|
|
||||||
const defines = {
|
const defines = {
|
||||||
IS_STANDALONE: isStandalone,
|
IS_STANDALONE: isStandalone,
|
||||||
IS_DEV: JSON.stringify(watch),
|
IS_DEV: JSON.stringify(isDev),
|
||||||
IS_UPDATER_DISABLED: updaterDisabled,
|
IS_UPDATER_DISABLED: updaterDisabled,
|
||||||
IS_WEB: false,
|
IS_WEB: false,
|
||||||
IS_EXTENSION: false,
|
IS_EXTENSION: false,
|
||||||
|
|
|
@ -23,7 +23,7 @@ import { appendFile, mkdir, readdir, readFile, rm, writeFile } from "fs/promises
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import Zip from "zip-local";
|
import Zip from "zip-local";
|
||||||
|
|
||||||
import { BUILD_TIMESTAMP, commonOpts, globPlugins, VERSION, watch } from "./common.mjs";
|
import { BUILD_TIMESTAMP, commonOpts, globPlugins, isDev, VERSION } from "./common.mjs";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {esbuild.BuildOptions}
|
* @type {esbuild.BuildOptions}
|
||||||
|
@ -43,7 +43,7 @@ const commonOptions = {
|
||||||
IS_WEB: "true",
|
IS_WEB: "true",
|
||||||
IS_EXTENSION: "false",
|
IS_EXTENSION: "false",
|
||||||
IS_STANDALONE: "true",
|
IS_STANDALONE: "true",
|
||||||
IS_DEV: JSON.stringify(watch),
|
IS_DEV: JSON.stringify(isDev),
|
||||||
IS_DISCORD_DESKTOP: "false",
|
IS_DISCORD_DESKTOP: "false",
|
||||||
IS_VESKTOP: "false",
|
IS_VESKTOP: "false",
|
||||||
IS_UPDATER_DISABLED: "true",
|
IS_UPDATER_DISABLED: "true",
|
||||||
|
|
|
@ -33,6 +33,7 @@ export const VERSION = PackageJSON.version;
|
||||||
// https://reproducible-builds.org/docs/source-date-epoch/
|
// https://reproducible-builds.org/docs/source-date-epoch/
|
||||||
export const BUILD_TIMESTAMP = Number(process.env.SOURCE_DATE_EPOCH) || Date.now();
|
export const BUILD_TIMESTAMP = Number(process.env.SOURCE_DATE_EPOCH) || Date.now();
|
||||||
export const watch = process.argv.includes("--watch");
|
export const watch = process.argv.includes("--watch");
|
||||||
|
export const isDev = watch || process.argv.includes("--dev");
|
||||||
export const isStandalone = JSON.stringify(process.argv.includes("--standalone"));
|
export const isStandalone = JSON.stringify(process.argv.includes("--standalone"));
|
||||||
export const updaterDisabled = JSON.stringify(process.argv.includes("--disable-updater"));
|
export const updaterDisabled = JSON.stringify(process.argv.includes("--disable-updater"));
|
||||||
export const gitHash = process.env.VENCORD_HASH || execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim();
|
export const gitHash = process.env.VENCORD_HASH || execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim();
|
||||||
|
|
|
@ -34,7 +34,7 @@ for (const variable of ["DISCORD_TOKEN", "CHROMIUM_BIN"]) {
|
||||||
const CANARY = process.env.USE_CANARY === "true";
|
const CANARY = process.env.USE_CANARY === "true";
|
||||||
|
|
||||||
const browser = await pup.launch({
|
const browser = await pup.launch({
|
||||||
headless: true,
|
headless: "new",
|
||||||
executablePath: process.env.CHROMIUM_BIN
|
executablePath: process.env.CHROMIUM_BIN
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -58,14 +58,16 @@ const report = {
|
||||||
plugin: string;
|
plugin: string;
|
||||||
error: string;
|
error: string;
|
||||||
}[],
|
}[],
|
||||||
otherErrors: [] as string[]
|
otherErrors: [] as string[],
|
||||||
|
badWebpackFinds: [] as string[]
|
||||||
};
|
};
|
||||||
|
|
||||||
const IGNORED_DISCORD_ERRORS = [
|
const IGNORED_DISCORD_ERRORS = [
|
||||||
"KeybindStore: Looking for callback action",
|
"KeybindStore: Looking for callback action",
|
||||||
"Unable to process domain list delta: Client revision number is null",
|
"Unable to process domain list delta: Client revision number is null",
|
||||||
"Downloading the full bad domains file",
|
"Downloading the full bad domains file",
|
||||||
/\[GatewaySocket\].{0,110}Cannot access '/
|
/\[GatewaySocket\].{0,110}Cannot access '/,
|
||||||
|
"search for 'name' in undefined"
|
||||||
] as Array<string | RegExp>;
|
] as Array<string | RegExp>;
|
||||||
|
|
||||||
function toCodeBlock(s: string) {
|
function toCodeBlock(s: string) {
|
||||||
|
@ -74,7 +76,10 @@ function toCodeBlock(s: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function printReport() {
|
async function printReport() {
|
||||||
|
console.log();
|
||||||
|
|
||||||
console.log("# Vencord Report" + (CANARY ? " (Canary)" : ""));
|
console.log("# Vencord Report" + (CANARY ? " (Canary)" : ""));
|
||||||
|
|
||||||
console.log();
|
console.log();
|
||||||
|
|
||||||
console.log("## Bad Patches");
|
console.log("## Bad Patches");
|
||||||
|
@ -87,12 +92,19 @@ async function printReport() {
|
||||||
|
|
||||||
console.log();
|
console.log();
|
||||||
|
|
||||||
|
console.log("## Bad Webpack Finds");
|
||||||
|
report.badWebpackFinds.forEach(p => console.log("- " + p));
|
||||||
|
|
||||||
|
console.log();
|
||||||
|
|
||||||
console.log("## Bad Starts");
|
console.log("## Bad Starts");
|
||||||
report.badStarts.forEach(p => {
|
report.badStarts.forEach(p => {
|
||||||
console.log(`- ${p.plugin}`);
|
console.log(`- ${p.plugin}`);
|
||||||
console.log(` - Error: ${toCodeBlock(p.error)}`);
|
console.log(` - Error: ${toCodeBlock(p.error)}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log();
|
||||||
|
|
||||||
report.otherErrors = report.otherErrors.filter(e => !IGNORED_DISCORD_ERRORS.some(regex => e.match(regex)));
|
report.otherErrors = report.otherErrors.filter(e => !IGNORED_DISCORD_ERRORS.some(regex => e.match(regex)));
|
||||||
|
|
||||||
console.log("## Discord Errors");
|
console.log("## Discord Errors");
|
||||||
|
@ -100,8 +112,9 @@ async function printReport() {
|
||||||
console.log(`- ${toCodeBlock(e)}`);
|
console.log(`- ${toCodeBlock(e)}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log();
|
||||||
|
|
||||||
if (process.env.DISCORD_WEBHOOK) {
|
if (process.env.DISCORD_WEBHOOK) {
|
||||||
// this code was written almost entirely by Copilot xD
|
|
||||||
await fetch(process.env.DISCORD_WEBHOOK, {
|
await fetch(process.env.DISCORD_WEBHOOK, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -110,7 +123,7 @@ async function printReport() {
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
description: "Here's the latest Vencord Report!",
|
description: "Here's the latest Vencord Report!",
|
||||||
username: "Vencord Reporter" + (CANARY ? " (Canary)" : ""),
|
username: "Vencord Reporter" + (CANARY ? " (Canary)" : ""),
|
||||||
avatar_url: "https://cdn.discordapp.com/icons/1015060230222131221/f0204a918c6c9c9a43195997e97d8adf.webp",
|
avatar_url: "https://cdn.discordapp.com/icons/1015060230222131221/6101cff21e241cebb60c4a01563d0c01.webp?size=512",
|
||||||
embeds: [
|
embeds: [
|
||||||
{
|
{
|
||||||
title: "Bad Patches",
|
title: "Bad Patches",
|
||||||
|
@ -125,6 +138,11 @@ async function printReport() {
|
||||||
}).join("\n\n") || "None",
|
}).join("\n\n") || "None",
|
||||||
color: report.badPatches.length ? 0xff0000 : 0x00ff00
|
color: report.badPatches.length ? 0xff0000 : 0x00ff00
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Bad Webpack Finds",
|
||||||
|
description: report.badWebpackFinds.map(toCodeBlock).join("\n") || "None",
|
||||||
|
color: report.badWebpackFinds.length ? 0xff0000 : 0x00ff00
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "Bad Starts",
|
title: "Bad Starts",
|
||||||
description: report.badStarts.map(p => {
|
description: report.badStarts.map(p => {
|
||||||
|
@ -153,29 +171,35 @@ async function printReport() {
|
||||||
|
|
||||||
page.on("console", async e => {
|
page.on("console", async e => {
|
||||||
const level = e.type();
|
const level = e.type();
|
||||||
const args = e.args();
|
const rawArgs = e.args();
|
||||||
|
|
||||||
const firstArg = (await args[0]?.jsonValue());
|
const firstArg = await rawArgs[0]?.jsonValue();
|
||||||
if (firstArg === "PUPPETEER_TEST_DONE_SIGNAL") {
|
if (firstArg === "[PUPPETEER_TEST_DONE_SIGNAL]") {
|
||||||
await browser.close();
|
await browser.close();
|
||||||
await printReport();
|
await printReport();
|
||||||
process.exit();
|
process.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
const isVencord = (await args[0]?.jsonValue()) === "[Vencord]";
|
const isVencord = firstArg === "[Vencord]";
|
||||||
const isDebug = (await args[0]?.jsonValue()) === "[PUP_DEBUG]";
|
const isDebug = firstArg === "[PUP_DEBUG]";
|
||||||
|
const isWebpackFindFail = firstArg === "[PUP_WEBPACK_FIND_FAIL]";
|
||||||
|
|
||||||
|
if (isWebpackFindFail) {
|
||||||
|
process.exitCode = 1;
|
||||||
|
report.badWebpackFinds.push(await rawArgs[1].jsonValue() as string);
|
||||||
|
}
|
||||||
|
|
||||||
if (isVencord) {
|
if (isVencord) {
|
||||||
// make ci fail
|
const args = await Promise.all(e.args().map(a => a.jsonValue()));
|
||||||
process.exitCode = 1;
|
|
||||||
|
|
||||||
const jsonArgs = await Promise.all(args.map(a => a.jsonValue()));
|
const [, tag, message] = args as Array<string>;
|
||||||
const [, tag, message] = jsonArgs;
|
const cause = await maybeGetError(e.args()[3]);
|
||||||
const cause = await maybeGetError(args[3]);
|
|
||||||
|
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case "WebpackInterceptor:":
|
case "WebpackInterceptor:":
|
||||||
const [, plugin, type, id, regex] = (message as string).match(/Patch by (.+?) (had no effect|errored|found no module) \(Module id is (.+?)\): (.+)/)!;
|
process.exitCode = 1;
|
||||||
|
|
||||||
|
const [, plugin, type, id, regex] = message.match(/Patch by (.+?) (had no effect|errored|found no module) \(Module id is (.+?)\): (.+)/)!;
|
||||||
report.badPatches.push({
|
report.badPatches.push({
|
||||||
plugin,
|
plugin,
|
||||||
type,
|
type,
|
||||||
|
@ -183,16 +207,25 @@ page.on("console", async e => {
|
||||||
match: regex.replace(/\[A-Za-z_\$\]\[\\w\$\]\*/g, "\\i"),
|
match: regex.replace(/\[A-Za-z_\$\]\[\\w\$\]\*/g, "\\i"),
|
||||||
error: cause
|
error: cause
|
||||||
});
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case "PluginManager:":
|
case "PluginManager:":
|
||||||
const [, name] = (message as string).match(/Failed to start (.+)/)!;
|
const failedToStartMatch = message.match(/Failed to start (.+)/);
|
||||||
|
if (!failedToStartMatch) break;
|
||||||
|
|
||||||
|
process.exitCode = 1;
|
||||||
|
|
||||||
|
const [, name] = failedToStartMatch;
|
||||||
report.badStarts.push({
|
report.badStarts.push({
|
||||||
plugin: name,
|
plugin: name,
|
||||||
error: cause
|
error: cause
|
||||||
});
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (isDebug) {
|
}
|
||||||
|
|
||||||
|
if (isDebug) {
|
||||||
console.error(e.text());
|
console.error(e.text());
|
||||||
} else if (level === "error") {
|
} else if (level === "error") {
|
||||||
const text = await Promise.all(
|
const text = await Promise.all(
|
||||||
|
@ -206,8 +239,8 @@ page.on("console", async e => {
|
||||||
).then(a => a.join(" ").trim());
|
).then(a => a.join(" ").trim());
|
||||||
|
|
||||||
|
|
||||||
if (text.length && !text.startsWith("Failed to load resource: the server responded with a status of")) {
|
if (text.length && !text.startsWith("Failed to load resource: the server responded with a status of") && !text.includes("found no module Filter:")) {
|
||||||
console.error("Got unexpected error", text);
|
console.error("[Unexpected Error]", text);
|
||||||
report.otherErrors.push(text);
|
report.otherErrors.push(text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,17 +252,16 @@ page.on("pageerror", e => console.error("[Page Error]", e));
|
||||||
await page.setBypassCSP(true);
|
await page.setBypassCSP(true);
|
||||||
|
|
||||||
function runTime(token: string) {
|
function runTime(token: string) {
|
||||||
console.error("[PUP_DEBUG]", "Starting test...");
|
console.log("[PUP_DEBUG]", "Starting test...");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// spoof languages to not be suspicious
|
// Spoof languages to not be suspicious
|
||||||
Object.defineProperty(navigator, "languages", {
|
Object.defineProperty(navigator, "languages", {
|
||||||
get: function () {
|
get: function () {
|
||||||
return ["en-US", "en"];
|
return ["en-US", "en"];
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// Monkey patch Logger to not log with custom css
|
// Monkey patch Logger to not log with custom css
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
Vencord.Util.Logger.prototype._log = function (level, levelColor, args) {
|
Vencord.Util.Logger.prototype._log = function (level, levelColor, args) {
|
||||||
|
@ -237,7 +269,7 @@ function runTime(token: string) {
|
||||||
console[level]("[Vencord]", this.name + ":", ...args);
|
console[level]("[Vencord]", this.name + ":", ...args);
|
||||||
};
|
};
|
||||||
|
|
||||||
// force enable all plugins and patches
|
// Force enable all plugins and patches
|
||||||
Vencord.Plugins.patches.length = 0;
|
Vencord.Plugins.patches.length = 0;
|
||||||
Object.values(Vencord.Plugins.plugins).forEach(p => {
|
Object.values(Vencord.Plugins.plugins).forEach(p => {
|
||||||
// Needs native server to run
|
// Needs native server to run
|
||||||
|
@ -247,8 +279,14 @@ function runTime(token: string) {
|
||||||
p.patches?.forEach(patch => {
|
p.patches?.forEach(patch => {
|
||||||
patch.plugin = p.name;
|
patch.plugin = p.name;
|
||||||
delete patch.predicate;
|
delete patch.predicate;
|
||||||
|
|
||||||
if (!Array.isArray(patch.replacement))
|
if (!Array.isArray(patch.replacement))
|
||||||
patch.replacement = [patch.replacement];
|
patch.replacement = [patch.replacement];
|
||||||
|
|
||||||
|
patch.replacement.forEach(r => {
|
||||||
|
delete r.predicate;
|
||||||
|
});
|
||||||
|
|
||||||
Vencord.Plugins.patches.push(patch);
|
Vencord.Plugins.patches.push(patch);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -256,41 +294,137 @@ function runTime(token: string) {
|
||||||
Vencord.Webpack.waitFor(
|
Vencord.Webpack.waitFor(
|
||||||
"loginToken",
|
"loginToken",
|
||||||
m => {
|
m => {
|
||||||
console.error("[PUP_DEBUG]", "Logging in with token...");
|
console.log("[PUP_DEBUG]", "Logging in with token...");
|
||||||
m.loginToken(token);
|
m.loginToken(token);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// force load all chunks
|
// Force load all chunks
|
||||||
Vencord.Webpack.onceReady.then(() => setTimeout(async () => {
|
Vencord.Webpack.onceReady.then(() => setTimeout(async () => {
|
||||||
console.error("[PUP_DEBUG]", "Webpack is ready!");
|
console.log("[PUP_DEBUG]", "Webpack is ready!");
|
||||||
|
|
||||||
const { wreq } = Vencord.Webpack;
|
const { wreq } = Vencord.Webpack;
|
||||||
|
|
||||||
console.error("[PUP_DEBUG]", "Loading all chunks...");
|
console.log("[PUP_DEBUG]", "Loading all chunks...");
|
||||||
const ids = Function("return" + wreq.u.toString().match(/(?<=\()\{.+?\}/s)![0])();
|
|
||||||
for (const id in ids) {
|
let chunks = null as Record<number, string[]> | null;
|
||||||
|
const sym = Symbol("Vencord.chunksExtract");
|
||||||
|
|
||||||
|
Object.defineProperty(Object.prototype, sym, {
|
||||||
|
get() {
|
||||||
|
chunks = this;
|
||||||
|
},
|
||||||
|
set() { },
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
await (wreq as any).el(sym);
|
||||||
|
delete Object.prototype[sym];
|
||||||
|
|
||||||
|
const validChunksEntryPoints = [] as string[];
|
||||||
|
const validChunks = [] as string[];
|
||||||
|
const invalidChunks = [] as string[];
|
||||||
|
|
||||||
|
if (!chunks) throw new Error("Failed to get chunks");
|
||||||
|
|
||||||
|
chunksLoop:
|
||||||
|
for (const entryPoint in chunks) {
|
||||||
|
const chunkIds = chunks[entryPoint];
|
||||||
|
|
||||||
|
for (const id of chunkIds) {
|
||||||
|
if (!wreq.u(id)) continue;
|
||||||
|
|
||||||
const isWasm = await fetch(wreq.p + wreq.u(id))
|
const isWasm = await fetch(wreq.p + wreq.u(id))
|
||||||
.then(r => r.text())
|
.then(r => r.text())
|
||||||
.then(t => t.includes(".module.wasm") || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push"));
|
.then(t => t.includes(".module.wasm") || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push"));
|
||||||
|
|
||||||
if (!isWasm)
|
if (isWasm) {
|
||||||
await wreq.e(id as any);
|
invalidChunks.push(id);
|
||||||
|
continue chunksLoop;
|
||||||
await new Promise(r => setTimeout(r, 150));
|
|
||||||
}
|
}
|
||||||
console.error("[PUP_DEBUG]", "Finished loading chunks!");
|
|
||||||
|
validChunks.push(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
validChunksEntryPoints.push(entryPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const entryPoint of validChunksEntryPoints) {
|
||||||
|
try {
|
||||||
|
// Loads all chunks required for an entry point
|
||||||
|
await (wreq as any).el(entryPoint);
|
||||||
|
} catch (err) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
const allChunks = Function("return " + (wreq.u.toString().match(/(?<=\()\{.+?\}/s)?.[0] ?? "null"))() as Record<string | number, string[]> | null;
|
||||||
|
if (!allChunks) throw new Error("Failed to get all chunks");
|
||||||
|
const chunksLeft = Object.keys(allChunks).filter(id => {
|
||||||
|
return !(validChunks.includes(id) || invalidChunks.includes(id));
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const id of chunksLeft) {
|
||||||
|
const isWasm = await fetch(wreq.p + wreq.u(id))
|
||||||
|
.then(r => r.text())
|
||||||
|
.then(t => t.includes(".module.wasm") || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push"));
|
||||||
|
|
||||||
|
// Loads a chunk
|
||||||
|
if (!isWasm) await wreq.e(id as any);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure every chunk has finished loading
|
||||||
|
await new Promise(r => setTimeout(r, 1000));
|
||||||
|
|
||||||
|
for (const entryPoint of validChunksEntryPoints) {
|
||||||
|
try {
|
||||||
|
if (wreq.m[entryPoint]) wreq(entryPoint as any);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("[PUP_DEBUG]", "Finished loading all chunks!");
|
||||||
|
|
||||||
for (const patch of Vencord.Plugins.patches) {
|
for (const patch of Vencord.Plugins.patches) {
|
||||||
if (!patch.all) {
|
if (!patch.all) {
|
||||||
new Vencord.Util.Logger("WebpackInterceptor").warn(`Patch by ${patch.plugin} found no module (Module id is -): ${patch.find}`);
|
new Vencord.Util.Logger("WebpackInterceptor").warn(`Patch by ${patch.plugin} found no module (Module id is -): ${patch.find}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setTimeout(() => console.log("PUPPETEER_TEST_DONE_SIGNAL"), 1000);
|
|
||||||
|
for (const [searchType, args] of Vencord.Webpack.lazyWebpackSearchHistory) {
|
||||||
|
let method = searchType;
|
||||||
|
|
||||||
|
if (searchType === "findComponent") method = "find";
|
||||||
|
if (searchType === "findExportedComponent") method = "findByProps";
|
||||||
|
if (searchType === "waitFor" || searchType === "waitForComponent" || searchType === "waitForStore") {
|
||||||
|
if (typeof args[0] === "string") method = "findByProps";
|
||||||
|
else method = "find";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let result: any;
|
||||||
|
|
||||||
|
if (method === "proxyLazyWebpack" || method === "LazyComponentWebpack") {
|
||||||
|
const [factory] = args;
|
||||||
|
result = factory();
|
||||||
|
} else {
|
||||||
|
// @ts-ignore
|
||||||
|
result = Vencord.Webpack[method](...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == null || ("$$get" in result && result.$$get() == null)) throw "a rock at ben shapiro";
|
||||||
|
} catch (e) {
|
||||||
|
let logMessage = searchType;
|
||||||
|
if (method === "find" || method === "proxyLazyWebpack" || method === "LazyComponentWebpack") logMessage += `(${args[0].toString().slice(0, 147)}...)`;
|
||||||
|
else logMessage += `(${args.map(arg => `"${arg}"`).join(", ")})`;
|
||||||
|
|
||||||
|
console.log("[PUP_WEBPACK_FIND_FAIL]", logMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => console.log("[PUPPETEER_TEST_DONE_SIGNAL]"), 1000);
|
||||||
}, 1000));
|
}, 1000));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("[PUP_DEBUG]", "A fatal error occurred");
|
console.log("[PUP_DEBUG]", "A fatal error occurred:", e);
|
||||||
console.error("[PUP_DEBUG]", e);
|
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,8 @@ export { PlainSettings, Settings };
|
||||||
import "./utils/quickCss";
|
import "./utils/quickCss";
|
||||||
import "./webpack/patchWebpack";
|
import "./webpack/patchWebpack";
|
||||||
|
|
||||||
|
import { StartAt } from "@utils/types";
|
||||||
|
|
||||||
import { get as dsGet } from "./api/DataStore";
|
import { get as dsGet } from "./api/DataStore";
|
||||||
import { showNotification } from "./api/Notifications";
|
import { showNotification } from "./api/Notifications";
|
||||||
import { PlainSettings, Settings } from "./api/Settings";
|
import { PlainSettings, Settings } from "./api/Settings";
|
||||||
|
@ -79,7 +81,7 @@ async function syncSettings() {
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
await onceReady;
|
await onceReady;
|
||||||
startAllPlugins();
|
startAllPlugins(StartAt.WebpackReady);
|
||||||
|
|
||||||
syncSettings();
|
syncSettings();
|
||||||
|
|
||||||
|
@ -130,13 +132,17 @@ async function init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
startAllPlugins(StartAt.Init);
|
||||||
init();
|
init();
|
||||||
|
|
||||||
if (IS_DISCORD_DESKTOP && Settings.winNativeTitleBar && navigator.platform.toLowerCase().startsWith("win")) {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
startAllPlugins(StartAt.DOMContentLoaded);
|
||||||
|
|
||||||
|
if (IS_DISCORD_DESKTOP && Settings.winNativeTitleBar && navigator.platform.toLowerCase().startsWith("win")) {
|
||||||
document.head.append(Object.assign(document.createElement("style"), {
|
document.head.append(Object.assign(document.createElement("style"), {
|
||||||
id: "vencord-native-titlebar-style",
|
id: "vencord-native-titlebar-style",
|
||||||
textContent: "[class*=titleBar]{display: none!important}"
|
textContent: "[class*=titleBar]{display: none!important}"
|
||||||
}));
|
}));
|
||||||
}, { once: true });
|
}
|
||||||
}
|
}, { once: true });
|
||||||
|
|
||||||
|
|
|
@ -24,9 +24,8 @@ import { proxyLazy } from "@utils/lazy";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { classes, isObjectEmpty } from "@utils/misc";
|
import { classes, isObjectEmpty } from "@utils/misc";
|
||||||
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize } from "@utils/modal";
|
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize } from "@utils/modal";
|
||||||
import { LazyComponent } from "@utils/react";
|
|
||||||
import { OptionType, Plugin } from "@utils/types";
|
import { OptionType, Plugin } from "@utils/types";
|
||||||
import { findByCode, findByPropsLazy } from "@webpack";
|
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
||||||
import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "@webpack/common";
|
import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "@webpack/common";
|
||||||
import { User } from "discord-types/general";
|
import { User } from "discord-types/general";
|
||||||
import { Constructor } from "type-fest";
|
import { Constructor } from "type-fest";
|
||||||
|
@ -42,7 +41,7 @@ import {
|
||||||
} from "./components";
|
} from "./components";
|
||||||
import { openContributorModal } from "./ContributorModal";
|
import { openContributorModal } from "./ContributorModal";
|
||||||
|
|
||||||
const UserSummaryItem = LazyComponent(() => findByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers"));
|
const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
||||||
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
|
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
|
||||||
const UserRecord: Constructor<Partial<User>> = proxyLazy(() => UserStore.getCurrentUser().constructor) as any;
|
const UserRecord: Constructor<Partial<User>> = proxyLazy(() => UserStore.getCurrentUser().constructor) as any;
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { LazyComponent } from "@utils/react";
|
import { findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack";
|
||||||
import { find, findByPropsLazy, findStoreLazy } from "@webpack";
|
|
||||||
import { useStateFromStores } from "@webpack/common";
|
import { useStateFromStores } from "@webpack/common";
|
||||||
import type { CSSProperties } from "react";
|
import type { CSSProperties } from "react";
|
||||||
|
|
||||||
|
@ -26,7 +25,7 @@ import { ExpandedGuildFolderStore, settings } from ".";
|
||||||
|
|
||||||
const ChannelRTCStore = findStoreLazy("ChannelRTCStore");
|
const ChannelRTCStore = findStoreLazy("ChannelRTCStore");
|
||||||
const Animations = findByPropsLazy("a", "animated", "useTransition");
|
const Animations = findByPropsLazy("a", "animated", "useTransition");
|
||||||
const GuildsBar = LazyComponent(() => find(m => m.type?.toString().includes('("guildsnav")')));
|
const GuildsBar = findComponentByCodeLazy('("guildsnav")');
|
||||||
|
|
||||||
export default ErrorBoundary.wrap(guildsBarProps => {
|
export default ErrorBoundary.wrap(guildsBarProps => {
|
||||||
const expandedFolders = useStateFromStores([ExpandedGuildFolderStore], () => ExpandedGuildFolderStore.getExpandedFolders());
|
const expandedFolders = useStateFromStores([ExpandedGuildFolderStore], () => ExpandedGuildFolderStore.getExpandedFolders());
|
||||||
|
|
|
@ -18,9 +18,8 @@
|
||||||
|
|
||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { proxyLazy } from "@utils/lazy";
|
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByProps, findByPropsLazy, findStoreLazy } from "@webpack";
|
import { findByPropsLazy, findStoreLazy } from "@webpack";
|
||||||
import { FluxDispatcher, i18n } from "@webpack/common";
|
import { FluxDispatcher, i18n } from "@webpack/common";
|
||||||
|
|
||||||
import FolderSideBar from "./FolderSideBar";
|
import FolderSideBar from "./FolderSideBar";
|
||||||
|
@ -31,7 +30,7 @@ enum FolderIconDisplay {
|
||||||
MoreThanOneFolderExpanded
|
MoreThanOneFolderExpanded
|
||||||
}
|
}
|
||||||
|
|
||||||
const GuildsTree = proxyLazy(() => findByProps("GuildsTree").GuildsTree);
|
const { GuildsTree } = findByPropsLazy("GuildsTree");
|
||||||
const SortedGuildStore = findStoreLazy("SortedGuildStore");
|
const SortedGuildStore = findStoreLazy("SortedGuildStore");
|
||||||
export const ExpandedGuildFolderStore = findStoreLazy("ExpandedGuildFolderStore");
|
export const ExpandedGuildFolderStore = findStoreLazy("ExpandedGuildFolderStore");
|
||||||
const FolderUtils = findByPropsLazy("move", "toggleGuildFolderExpand");
|
const FolderUtils = findByPropsLazy("move", "toggleGuildFolderExpand");
|
||||||
|
|
|
@ -121,6 +121,9 @@ export const defaultRules = [
|
||||||
"t@*.twitter.com",
|
"t@*.twitter.com",
|
||||||
"s@*.twitter.com",
|
"s@*.twitter.com",
|
||||||
"ref_*@*.twitter.com",
|
"ref_*@*.twitter.com",
|
||||||
|
"t@*.x.com",
|
||||||
|
"s@*.x.com",
|
||||||
|
"ref_*@*.x.com",
|
||||||
"tt_medium",
|
"tt_medium",
|
||||||
"tt_content",
|
"tt_content",
|
||||||
"lr@yandex.*",
|
"lr@yandex.*",
|
||||||
|
|
7
src/plugins/clientTheme/README.md
Normal file
7
src/plugins/clientTheme/README.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# Classic Client Theme
|
||||||
|
|
||||||
|
Revival of the old client theme experiment (The one that came before the sucky one that we actually got)
|
||||||
|
|
||||||
|
![the ClientTheme theme colour picker](https://user-images.githubusercontent.com/37855219/230238053-e90b7098-373a-459a-bb8c-c24e82f69270.png)
|
||||||
|
|
||||||
|
https://github.com/Vendicated/Vencord/assets/45497981/6c1bcb3b-e0c7-4a02-b0b8-c4c5cd954f38
|
|
@ -11,11 +11,11 @@ import { Devs } from "@utils/constants";
|
||||||
import { getTheme, Theme } from "@utils/discord";
|
import { getTheme, Theme } from "@utils/discord";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { classes } from "@utils/misc";
|
import { classes } from "@utils/misc";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType, StartAt } from "@utils/types";
|
||||||
import { findByCodeLazy } from "@webpack";
|
import { findComponentByCodeLazy } from "@webpack";
|
||||||
import { Button, Forms } from "@webpack/common";
|
import { Button, Forms } from "@webpack/common";
|
||||||
|
|
||||||
const ColorPicker = findByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR");
|
const ColorPicker = findComponentByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR");
|
||||||
|
|
||||||
const colorPresets = [
|
const colorPresets = [
|
||||||
"#1E1514", "#172019", "#13171B", "#1C1C28", "#402D2D",
|
"#1E1514", "#172019", "#13171B", "#1C1C28", "#402D2D",
|
||||||
|
@ -24,11 +24,7 @@ const colorPresets = [
|
||||||
];
|
];
|
||||||
|
|
||||||
function onPickColor(color: number) {
|
function onPickColor(color: number) {
|
||||||
let hexColor = color.toString(16);
|
const hexColor = color.toString(16).padStart(6, "0");
|
||||||
|
|
||||||
while (hexColor.length < 6) {
|
|
||||||
hexColor = "0" + hexColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.store.color = hexColor;
|
settings.store.color = hexColor;
|
||||||
updateColorVars(hexColor);
|
updateColorVars(hexColor);
|
||||||
|
@ -59,7 +55,8 @@ function ThemeSettings() {
|
||||||
{lightnessWarning && <Forms.FormText className="client-theme-warning">Selected color is very light</Forms.FormText>}
|
{lightnessWarning && <Forms.FormText className="client-theme-warning">Selected color is very light</Forms.FormText>}
|
||||||
{lightModeWarning && <Forms.FormText className="client-theme-warning">Light mode isn't supported</Forms.FormText>}
|
{lightModeWarning && <Forms.FormText className="client-theme-warning">Light mode isn't supported</Forms.FormText>}
|
||||||
</div>
|
</div>
|
||||||
: null}
|
: null
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -85,36 +82,25 @@ const settings = definePluginSettings({
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "ClientTheme",
|
name: "ClientTheme",
|
||||||
authors: [Devs.F53],
|
authors: [Devs.F53, Devs.Nuckyz],
|
||||||
description: "Recreation of the old client theme experiment. Add a color to your Discord client theme",
|
description: "Recreation of the old client theme experiment. Add a color to your Discord client theme",
|
||||||
settings,
|
settings,
|
||||||
|
|
||||||
patches: [
|
startAt: StartAt.DOMContentLoaded,
|
||||||
{
|
start() {
|
||||||
find: "Could not find app-mount",
|
|
||||||
replacement: {
|
|
||||||
match: /(?<=Could not find app-mount"\))/,
|
|
||||||
replace: ",$self.addThemeInitializer()"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
addThemeInitializer() {
|
|
||||||
document.addEventListener("DOMContentLoaded", this.themeInitializer = () => {
|
|
||||||
updateColorVars(settings.store.color);
|
updateColorVars(settings.store.color);
|
||||||
generateColorOffsets();
|
generateColorOffsets();
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
document.removeEventListener("DOMContentLoaded", this.themeInitializer);
|
|
||||||
document.getElementById("clientThemeVars")?.remove();
|
document.getElementById("clientThemeVars")?.remove();
|
||||||
document.getElementById("clientThemeOffsets")?.remove();
|
document.getElementById("clientThemeOffsets")?.remove();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const variableRegex = /(--primary-[5-9]\d{2}-hsl):.*?(\S*)%;/g;
|
||||||
|
|
||||||
async function generateColorOffsets() {
|
async function generateColorOffsets() {
|
||||||
const variableRegex = /(--primary-[5-9]\d{2}-hsl):.*?(\S*)%;/g;
|
|
||||||
|
|
||||||
const styleLinkNodes = document.querySelectorAll('link[rel="stylesheet"]');
|
const styleLinkNodes = document.querySelectorAll('link[rel="stylesheet"]');
|
||||||
const variableLightness = {} as Record<string, number>;
|
const variableLightness = {} as Record<string, number>;
|
||||||
|
@ -213,7 +199,7 @@ function hexToHSL(hexCode: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minimized math just for lightness, lowers lag when changing colors
|
// Minimized math just for lightness, lowers lag when changing colors
|
||||||
function hexToLightness(hexCode) {
|
function hexToLightness(hexCode: string) {
|
||||||
// Hex => RGB normalized to 0-1
|
// Hex => RGB normalized to 0-1
|
||||||
const r = parseInt(hexCode.substring(0, 2), 16) / 255;
|
const r = parseInt(hexCode.substring(0, 2), 16) / 255;
|
||||||
const g = parseInt(hexCode.substring(2, 4), 16) / 255;
|
const g = parseInt(hexCode.substring(2, 4), 16) / 255;
|
||||||
|
|
|
@ -62,23 +62,27 @@ export default definePlugin({
|
||||||
}
|
}
|
||||||
|
|
||||||
let fakeRenderWin: WeakRef<Window> | undefined;
|
let fakeRenderWin: WeakRef<Window> | undefined;
|
||||||
|
const find = newFindWrapper(f => f);
|
||||||
return {
|
return {
|
||||||
|
...Vencord.Webpack.Common,
|
||||||
wp: Vencord.Webpack,
|
wp: Vencord.Webpack,
|
||||||
wpc: Webpack.wreq.c,
|
wpc: Webpack.wreq.c,
|
||||||
wreq: Webpack.wreq,
|
wreq: Webpack.wreq,
|
||||||
wpsearch: search,
|
wpsearch: search,
|
||||||
wpex: extract,
|
wpex: extract,
|
||||||
wpexs: (code: string) => Vencord.Webpack.extract(Vencord.Webpack.findModuleId(code)!),
|
wpexs: (code: string) => extract(Webpack.findModuleId(code)!),
|
||||||
find: newFindWrapper(f => f),
|
find,
|
||||||
findAll,
|
findAll,
|
||||||
findByProps: newFindWrapper(filters.byProps),
|
findByProps: newFindWrapper(filters.byProps),
|
||||||
findAllByProps: (...props: string[]) => findAll(filters.byProps(...props)),
|
findAllByProps: (...props: string[]) => findAll(filters.byProps(...props)),
|
||||||
findByCode: newFindWrapper(filters.byCode),
|
findByCode: newFindWrapper(filters.byCode),
|
||||||
findAllByCode: (code: string) => findAll(filters.byCode(code)),
|
findAllByCode: (code: string) => findAll(filters.byCode(code)),
|
||||||
|
findComponentByCode: newFindWrapper(filters.componentByCode),
|
||||||
|
findAllComponentsByCode: (...code: string[]) => findAll(filters.componentByCode(...code)),
|
||||||
|
findExportedComponent: (...props: string[]) => find(...props)[props[0]],
|
||||||
findStore: newFindWrapper(filters.byStoreName),
|
findStore: newFindWrapper(filters.byStoreName),
|
||||||
PluginsApi: Vencord.Plugins,
|
PluginsApi: Vencord.Plugins,
|
||||||
plugins: Vencord.Plugins.plugins,
|
plugins: Vencord.Plugins.plugins,
|
||||||
React,
|
|
||||||
Settings: Vencord.Settings,
|
Settings: Vencord.Settings,
|
||||||
Api: Vencord.Api,
|
Api: Vencord.Api,
|
||||||
reload: () => location.reload(),
|
reload: () => location.reload(),
|
||||||
|
@ -92,7 +96,25 @@ export default definePlugin({
|
||||||
fakeRenderWin = new WeakRef(win);
|
fakeRenderWin = new WeakRef(win);
|
||||||
win.focus();
|
win.focus();
|
||||||
|
|
||||||
ReactDOM.render(React.createElement(component, props), win.document.body);
|
const doc = win.document;
|
||||||
|
doc.body.style.margin = "1em";
|
||||||
|
|
||||||
|
if (!win.prepared) {
|
||||||
|
win.prepared = true;
|
||||||
|
|
||||||
|
[...document.querySelectorAll("style"), ...document.querySelectorAll("link[rel=stylesheet]")].forEach(s => {
|
||||||
|
const n = s.cloneNode(true) as HTMLStyleElement | HTMLLinkElement;
|
||||||
|
|
||||||
|
if (s.parentElement?.tagName === "HEAD")
|
||||||
|
doc.head.append(n);
|
||||||
|
else if (n.id?.startsWith("vencord-") || n.id?.startsWith("vcd-"))
|
||||||
|
doc.documentElement.append(n);
|
||||||
|
else
|
||||||
|
doc.body.append(n);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactDOM.render(React.createElement(component, props), doc.body.appendChild(document.createElement("div")));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
@ -22,10 +22,10 @@ import { Devs } from "@utils/constants";
|
||||||
import { isTruthy } from "@utils/guards";
|
import { isTruthy } from "@utils/guards";
|
||||||
import { useAwaiter } from "@utils/react";
|
import { useAwaiter } from "@utils/react";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByCodeLazy, findByPropsLazy } from "@webpack";
|
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
||||||
import { ApplicationAssetUtils, FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common";
|
import { ApplicationAssetUtils, FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common";
|
||||||
|
|
||||||
const ActivityComponent = findByCodeLazy("onOpenGameProfile");
|
const ActivityComponent = findComponentByCodeLazy("onOpenGameProfile");
|
||||||
const ActivityClassName = findByPropsLazy("activity", "buttonColor");
|
const ActivityClassName = findByPropsLazy("activity", "buttonColor");
|
||||||
const Colors = findByPropsLazy("profileColors");
|
const Colors = findByPropsLazy("profileColors");
|
||||||
|
|
||||||
|
|
|
@ -21,10 +21,9 @@ import { definePluginSettings, Settings } from "@api/Settings";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { ApngBlendOp, ApngDisposeOp, importApngJs } from "@utils/dependencies";
|
import { ApngBlendOp, ApngDisposeOp, importApngJs } from "@utils/dependencies";
|
||||||
import { getCurrentGuild } from "@utils/discord";
|
import { getCurrentGuild } from "@utils/discord";
|
||||||
import { proxyLazy } from "@utils/lazy";
|
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByPropsLazy, findStoreLazy } from "@webpack";
|
import { findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack";
|
||||||
import { ChannelStore, EmojiStore, FluxDispatcher, lodash, Parser, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common";
|
import { ChannelStore, EmojiStore, FluxDispatcher, lodash, Parser, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common";
|
||||||
import type { Message } from "discord-types/general";
|
import type { Message } from "discord-types/general";
|
||||||
import { applyPalette, GIFEncoder, quantize } from "gifenc";
|
import { applyPalette, GIFEncoder, quantize } from "gifenc";
|
||||||
|
@ -48,9 +47,9 @@ function searchProtoClassField(localName: string, protoClass: any) {
|
||||||
return fieldGetter?.();
|
return fieldGetter?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
const PreloadedUserSettingsActionCreators = proxyLazy(() => UserSettingsActionCreators.PreloadedUserSettingsActionCreators);
|
const PreloadedUserSettingsActionCreators = proxyLazyWebpack(() => UserSettingsActionCreators.PreloadedUserSettingsActionCreators);
|
||||||
const AppearanceSettingsActionCreators = proxyLazy(() => searchProtoClassField("appearance", PreloadedUserSettingsActionCreators.ProtoClass));
|
const AppearanceSettingsActionCreators = proxyLazyWebpack(() => searchProtoClassField("appearance", PreloadedUserSettingsActionCreators.ProtoClass));
|
||||||
const ClientThemeSettingsActionsCreators = proxyLazy(() => searchProtoClassField("clientThemeSettings", AppearanceSettingsActionCreators));
|
const ClientThemeSettingsActionsCreators = proxyLazyWebpack(() => searchProtoClassField("clientThemeSettings", AppearanceSettingsActionCreators));
|
||||||
|
|
||||||
const USE_EXTERNAL_EMOJIS = 1n << 18n;
|
const USE_EXTERNAL_EMOJIS = 1n << 18n;
|
||||||
const USE_EXTERNAL_STICKERS = 1n << 37n;
|
const USE_EXTERNAL_STICKERS = 1n << 37n;
|
||||||
|
|
23
src/plugins/fixImagesQuality/index.ts
Normal file
23
src/plugins/fixImagesQuality/index.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Devs } from "@utils/constants";
|
||||||
|
import definePlugin from "@utils/types";
|
||||||
|
|
||||||
|
export default definePlugin({
|
||||||
|
name: "FixImagesQuality",
|
||||||
|
description: "Fixes the quality of images in the chat being horrible.",
|
||||||
|
authors: [Devs.Nuckyz],
|
||||||
|
patches: [
|
||||||
|
{
|
||||||
|
find: "handleImageLoad=",
|
||||||
|
replacement: {
|
||||||
|
match: /(?<=getSrc\(\i\){.+?format:)\i/,
|
||||||
|
replace: "null"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
|
@ -23,7 +23,7 @@ import { findByPropsLazy } from "@webpack";
|
||||||
import { RestAPI, UserStore } from "@webpack/common";
|
import { RestAPI, UserStore } from "@webpack/common";
|
||||||
|
|
||||||
const FriendInvites = findByPropsLazy("createFriendInvite");
|
const FriendInvites = findByPropsLazy("createFriendInvite");
|
||||||
const uuid = findByPropsLazy("v4", "v1");
|
const { uuid4 } = findByPropsLazy("uuid4");
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "FriendInvites",
|
name: "FriendInvites",
|
||||||
|
@ -56,7 +56,7 @@ export default definePlugin({
|
||||||
|
|
||||||
let invite: any;
|
let invite: any;
|
||||||
if (uses === 1) {
|
if (uses === 1) {
|
||||||
const random = uuid.v4();
|
const random = uuid4();
|
||||||
const { body: { invite_suggestions } } = await RestAPI.post({
|
const { body: { invite_suggestions } } = await RestAPI.post({
|
||||||
url: "/friend-finder/find-friends",
|
url: "/friend-finder/find-friends",
|
||||||
body: {
|
body: {
|
||||||
|
|
|
@ -20,12 +20,12 @@ import { disableStyle, enableStyle } from "@api/Styles";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { findByCodeLazy } from "@webpack";
|
import { findComponentByCodeLazy } from "@webpack";
|
||||||
import { StatusSettingsStores } from "@webpack/common";
|
import { StatusSettingsStores } from "@webpack/common";
|
||||||
|
|
||||||
import style from "./style.css?managed";
|
import style from "./style.css?managed";
|
||||||
|
|
||||||
const Button = findByCodeLazy("Button.Sizes.NONE,disabled:");
|
const Button = findComponentByCodeLazy("Button.Sizes.NONE,disabled:");
|
||||||
|
|
||||||
function makeIcon(showCurrentGame?: boolean) {
|
function makeIcon(showCurrentGame?: boolean) {
|
||||||
return function () {
|
return function () {
|
||||||
|
|
|
@ -19,11 +19,9 @@
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { insertTextIntoChatInputBox } from "@utils/discord";
|
import { insertTextIntoChatInputBox } from "@utils/discord";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { filters, mapMangledModuleLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
|
|
||||||
const ExpressionPickerState = mapMangledModuleLazy('name:"expression-picker-last-active-view"', {
|
const { closeExpressionPicker } = findByPropsLazy("closeExpressionPicker");
|
||||||
close: filters.byCode("activeView:null", "setState")
|
|
||||||
});
|
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "GifPaste",
|
name: "GifPaste",
|
||||||
|
@ -41,7 +39,7 @@ export default definePlugin({
|
||||||
handleSelect(gif?: { url: string; }) {
|
handleSelect(gif?: { url: string; }) {
|
||||||
if (gif) {
|
if (gif) {
|
||||||
insertTextIntoChatInputBox(gif.url + " ");
|
insertTextIntoChatInputBox(gif.url + " ");
|
||||||
ExpressionPickerState.close();
|
closeExpressionPicker();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -18,10 +18,9 @@
|
||||||
|
|
||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { proxyLazy } from "@utils/lazy";
|
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByProps, findByPropsLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { ContextMenu, FluxDispatcher, Menu } from "@webpack/common";
|
import { ContextMenuApi, FluxDispatcher, Menu } from "@webpack/common";
|
||||||
import { Channel, Message } from "discord-types/general";
|
import { Channel, Message } from "discord-types/general";
|
||||||
|
|
||||||
interface Sticker {
|
interface Sticker {
|
||||||
|
@ -51,7 +50,7 @@ const settings = definePluginSettings({
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const MessageActions = findByPropsLazy("sendGreetMessage");
|
const MessageActions = findByPropsLazy("sendGreetMessage");
|
||||||
const WELCOME_STICKERS = proxyLazy(() => findByProps("WELCOME_STICKERS")?.WELCOME_STICKERS);
|
const { WELCOME_STICKERS } = findByPropsLazy("WELCOME_STICKERS");
|
||||||
|
|
||||||
function greet(channel: Channel, message: Message, stickers: string[]) {
|
function greet(channel: Channel, message: Message, stickers: string[]) {
|
||||||
const options = MessageActions.getSendMessageOptionsForReply({
|
const options = MessageActions.getSendMessageOptionsForReply({
|
||||||
|
@ -184,6 +183,6 @@ export default definePlugin({
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
if (!(props.message as any).deleted)
|
if (!(props.message as any).deleted)
|
||||||
ContextMenu.open(event, () => <GreetMenu {...props} />);
|
ContextMenuApi.openContextMenu(event, () => <GreetMenu {...props} />);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -23,7 +23,7 @@ import { makeRange } from "@components/PluginSettings/components";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { debounce } from "@utils/debounce";
|
import { debounce } from "@utils/debounce";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { ContextMenu, Menu, React, ReactDOM } from "@webpack/common";
|
import { ContextMenuApi, Menu, React, ReactDOM } from "@webpack/common";
|
||||||
import type { Root } from "react-dom/client";
|
import type { Root } from "react-dom/client";
|
||||||
|
|
||||||
import { Magnifier, MagnifierProps } from "./components/Magnifier";
|
import { Magnifier, MagnifierProps } from "./components/Magnifier";
|
||||||
|
@ -89,7 +89,7 @@ const imageContextMenuPatch: NavContextMenuPatchCallback = children => () => {
|
||||||
checked={settings.store.square}
|
checked={settings.store.square}
|
||||||
action={() => {
|
action={() => {
|
||||||
settings.store.square = !settings.store.square;
|
settings.store.square = !settings.store.square;
|
||||||
ContextMenu.close();
|
ContextMenuApi.closeContextMenu();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Menu.MenuCheckboxItem
|
<Menu.MenuCheckboxItem
|
||||||
|
@ -98,7 +98,7 @@ const imageContextMenuPatch: NavContextMenuPatchCallback = children => () => {
|
||||||
checked={settings.store.nearestNeighbour}
|
checked={settings.store.nearestNeighbour}
|
||||||
action={() => {
|
action={() => {
|
||||||
settings.store.nearestNeighbour = !settings.store.nearestNeighbour;
|
settings.store.nearestNeighbour = !settings.store.nearestNeighbour;
|
||||||
ContextMenu.close();
|
ContextMenuApi.closeContextMenu();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Menu.MenuControlItem
|
<Menu.MenuControlItem
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
import { registerCommand, unregisterCommand } from "@api/Commands";
|
import { registerCommand, unregisterCommand } from "@api/Commands";
|
||||||
import { Settings } from "@api/Settings";
|
import { Settings } from "@api/Settings";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import { Patch, Plugin } from "@utils/types";
|
import { Patch, Plugin, StartAt } from "@utils/types";
|
||||||
import { FluxDispatcher } from "@webpack/common";
|
import { FluxDispatcher } from "@webpack/common";
|
||||||
import { FluxEvents } from "@webpack/types";
|
import { FluxEvents } from "@webpack/types";
|
||||||
|
|
||||||
|
@ -85,9 +85,15 @@ for (const p of pluginsValues) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const startAllPlugins = traceFunction("startAllPlugins", function startAllPlugins() {
|
export const startAllPlugins = traceFunction("startAllPlugins", function startAllPlugins(target: StartAt) {
|
||||||
|
logger.info(`Starting plugins (stage ${target})`);
|
||||||
for (const name in Plugins)
|
for (const name in Plugins)
|
||||||
if (isPluginEnabled(name)) {
|
if (isPluginEnabled(name)) {
|
||||||
|
const p = Plugins[name];
|
||||||
|
|
||||||
|
const startAt = p.startAt ?? StartAt.WebpackReady;
|
||||||
|
if (startAt !== target) continue;
|
||||||
|
|
||||||
startPlugin(Plugins[name]);
|
startPlugin(Plugins[name]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -22,9 +22,8 @@ import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants.js";
|
import { Devs } from "@utils/constants.js";
|
||||||
import { classes } from "@utils/misc";
|
import { classes } from "@utils/misc";
|
||||||
import { Queue } from "@utils/Queue";
|
import { Queue } from "@utils/Queue";
|
||||||
import { LazyComponent } from "@utils/react";
|
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { find, findByCode, findByPropsLazy } from "@webpack";
|
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
ChannelStore,
|
ChannelStore,
|
||||||
|
@ -45,9 +44,9 @@ const messageCache = new Map<string, {
|
||||||
fetched: boolean;
|
fetched: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const Embed = LazyComponent(() => findByCode(".inlineMediaEmbed"));
|
const Embed = findComponentByCodeLazy(".inlineMediaEmbed");
|
||||||
const AutoModEmbed = LazyComponent(() => findByCode(".withFooter]:", "childrenMessageContent:"));
|
const AutoModEmbed = findComponentByCodeLazy(".withFooter]:", "childrenMessageContent:");
|
||||||
const ChannelMessage = LazyComponent(() => find(m => m.type?.toString()?.includes("renderSimpleAccessories)")));
|
const ChannelMessage = findComponentByCodeLazy("renderSimpleAccessories)");
|
||||||
|
|
||||||
const SearchResultClasses = findByPropsLazy("message", "searchResult");
|
const SearchResultClasses = findByPropsLazy("message", "searchResult");
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ import { Flex } from "@components/Flex";
|
||||||
import { InfoIcon, OwnerCrownIcon } from "@components/Icons";
|
import { InfoIcon, OwnerCrownIcon } from "@components/Icons";
|
||||||
import { getUniqueUsername } from "@utils/discord";
|
import { getUniqueUsername } from "@utils/discord";
|
||||||
import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||||
import { ContextMenu, FluxDispatcher, GuildMemberStore, Menu, PermissionsBits, Text, Tooltip, useEffect, UserStore, useState, useStateFromStores } from "@webpack/common";
|
import { ContextMenuApi, FluxDispatcher, GuildMemberStore, Menu, PermissionsBits, Text, Tooltip, useEffect, UserStore, useState, useStateFromStores } from "@webpack/common";
|
||||||
import type { Guild } from "discord-types/general";
|
import type { Guild } from "discord-types/general";
|
||||||
|
|
||||||
import { settings } from "..";
|
import { settings } from "..";
|
||||||
|
@ -111,7 +111,7 @@ function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, hea
|
||||||
className={cl("perms-list-item", { "perms-list-item-active": selectedItemIndex === index })}
|
className={cl("perms-list-item", { "perms-list-item-active": selectedItemIndex === index })}
|
||||||
onContextMenu={e => {
|
onContextMenu={e => {
|
||||||
if ((settings.store as any).unsafeViewAsRole && permission.type === PermissionType.Role)
|
if ((settings.store as any).unsafeViewAsRole && permission.type === PermissionType.Role)
|
||||||
ContextMenu.open(e, () => (
|
ContextMenuApi.openContextMenu(e, () => (
|
||||||
<RoleContextMenu
|
<RoleContextMenu
|
||||||
guild={guild}
|
guild={guild}
|
||||||
roleId={permission.id!}
|
roleId={permission.id!}
|
||||||
|
@ -194,7 +194,7 @@ function RoleContextMenu({ guild, roleId, onClose }: { guild: Guild; roleId: str
|
||||||
return (
|
return (
|
||||||
<Menu.Menu
|
<Menu.Menu
|
||||||
navId={cl("role-context-menu")}
|
navId={cl("role-context-menu")}
|
||||||
onClose={ContextMenu.close}
|
onClose={ContextMenuApi.closeContextMenu}
|
||||||
aria-label="Role Options"
|
aria-label="Role Options"
|
||||||
>
|
>
|
||||||
<Menu.MenuItem
|
<Menu.MenuItem
|
||||||
|
|
|
@ -18,9 +18,8 @@
|
||||||
|
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import ExpandableHeader from "@components/ExpandableHeader";
|
import ExpandableHeader from "@components/ExpandableHeader";
|
||||||
import { proxyLazy } from "@utils/lazy";
|
|
||||||
import { classes } from "@utils/misc";
|
import { classes } from "@utils/misc";
|
||||||
import { filters, findBulk } from "@webpack";
|
import { filters, findBulk, proxyLazyWebpack } from "@webpack";
|
||||||
import { i18n, PermissionsBits, Text, Tooltip, useMemo, UserStore } from "@webpack/common";
|
import { i18n, PermissionsBits, Text, Tooltip, useMemo, UserStore } from "@webpack/common";
|
||||||
import type { Guild, GuildMember } from "discord-types/general";
|
import type { Guild, GuildMember } from "discord-types/general";
|
||||||
|
|
||||||
|
@ -36,7 +35,7 @@ interface UserPermission {
|
||||||
|
|
||||||
type UserPermissions = Array<UserPermission>;
|
type UserPermissions = Array<UserPermission>;
|
||||||
|
|
||||||
const Classes = proxyLazy(() => {
|
const Classes = proxyLazyWebpack(() => {
|
||||||
const modules = findBulk(
|
const modules = findBulk(
|
||||||
filters.byProps("roles", "rolePill", "rolePillBorder"),
|
filters.byProps("roles", "rolePill", "rolePillBorder"),
|
||||||
filters.byProps("roleCircle", "dotBorderBase", "dotBorderColor"),
|
filters.byProps("roleCircle", "dotBorderBase", "dotBorderColor"),
|
||||||
|
|
|
@ -10,14 +10,14 @@ import { classNameFactory } from "@api/Styles";
|
||||||
import { openImageModal, openUserProfile } from "@utils/discord";
|
import { openImageModal, openUserProfile } from "@utils/discord";
|
||||||
import { classes } from "@utils/misc";
|
import { classes } from "@utils/misc";
|
||||||
import { ModalRoot, ModalSize, openModal } from "@utils/modal";
|
import { ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||||
import { LazyComponent, useAwaiter } from "@utils/react";
|
import { useAwaiter } from "@utils/react";
|
||||||
import { findByProps, findByPropsLazy } from "@webpack";
|
import { findByPropsLazy, findExportedComponentLazy } from "@webpack";
|
||||||
import { FluxDispatcher, Forms, GuildChannelStore, GuildMemberStore, moment, Parser, PresenceStore, RelationshipStore, ScrollerThin, SnowflakeUtils, TabBar, Timestamp, useEffect, UserStore, UserUtils, useState, useStateFromStores } from "@webpack/common";
|
import { FluxDispatcher, Forms, GuildChannelStore, GuildMemberStore, moment, Parser, PresenceStore, RelationshipStore, ScrollerThin, SnowflakeUtils, TabBar, Timestamp, useEffect, UserStore, UserUtils, useState, useStateFromStores } from "@webpack/common";
|
||||||
import { Guild, User } from "discord-types/general";
|
import { Guild, User } from "discord-types/general";
|
||||||
|
|
||||||
const IconUtils = findByPropsLazy("getGuildBannerURL");
|
const IconUtils = findByPropsLazy("getGuildBannerURL");
|
||||||
const IconClasses = findByPropsLazy("icon", "acronym", "childWrapper");
|
const IconClasses = findByPropsLazy("icon", "acronym", "childWrapper");
|
||||||
const FriendRow = LazyComponent(() => findByProps("FriendRow").FriendRow);
|
const FriendRow = findExportedComponentLazy("FriendRow");
|
||||||
|
|
||||||
const cl = classNameFactory("vc-gp-");
|
const cl = classNameFactory("vc-gp-");
|
||||||
|
|
||||||
|
|
|
@ -16,12 +16,11 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { LazyComponent } from "@utils/react";
|
import { findComponentByCodeLazy, findLazy } from "@webpack";
|
||||||
import { findByCode, findLazy } from "@webpack";
|
|
||||||
import { i18n, useToken } from "@webpack/common";
|
import { i18n, useToken } from "@webpack/common";
|
||||||
|
|
||||||
const ColorMap = findLazy(m => m.colors?.INTERACTIVE_MUTED?.css);
|
const ColorMap = findLazy(m => m.colors?.INTERACTIVE_MUTED?.css);
|
||||||
const VerifiedIconComponent = LazyComponent(() => findByCode(".CONNECTIONS_ROLE_OFFICIAL_ICON_TOOLTIP"));
|
const VerifiedIconComponent = findComponentByCodeLazy(".CONNECTIONS_ROLE_OFFICIAL_ICON_TOOLTIP");
|
||||||
|
|
||||||
export function VerifiedIcon() {
|
export function VerifiedIcon() {
|
||||||
const color = useToken(ColorMap.colors.INTERACTIVE_MUTED).hex();
|
const color = useToken(ColorMap.colors.INTERACTIVE_MUTED).hex();
|
||||||
|
|
|
@ -24,15 +24,14 @@ import { Flex } from "@components/Flex";
|
||||||
import { CopyIcon, LinkIcon } from "@components/Icons";
|
import { CopyIcon, LinkIcon } from "@components/Icons";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { copyWithToast } from "@utils/misc";
|
import { copyWithToast } from "@utils/misc";
|
||||||
import { LazyComponent } from "@utils/react";
|
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByCode, findByCodeLazy, findByPropsLazy, findStoreLazy } from "@webpack";
|
import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack";
|
||||||
import { Text, Tooltip, UserProfileStore } from "@webpack/common";
|
import { Text, Tooltip, UserProfileStore } from "@webpack/common";
|
||||||
import { User } from "discord-types/general";
|
import { User } from "discord-types/general";
|
||||||
|
|
||||||
import { VerifiedIcon } from "./VerifiedIcon";
|
import { VerifiedIcon } from "./VerifiedIcon";
|
||||||
|
|
||||||
const Section = LazyComponent(() => findByCode(".lastSection]:"));
|
const Section = findComponentByCodeLazy(".lastSection", "children:");
|
||||||
const ThemeStore = findStoreLazy("ThemeStore");
|
const ThemeStore = findStoreLazy("ThemeStore");
|
||||||
const platforms: { get(type: string): ConnectionPlatform; } = findByPropsLazy("isSupported", "getByUrl");
|
const platforms: { get(type: string): ConnectionPlatform; } = findByPropsLazy("isSupported", "getByUrl");
|
||||||
const getTheme: (user: User, displayProfile: any) => any = findByCodeLazy(',"--profile-gradient-primary-color"');
|
const getTheme: (user: User, displayProfile: any) => any = findByCodeLazy(',"--profile-gradient-primary-color"');
|
||||||
|
|
|
@ -18,9 +18,8 @@
|
||||||
|
|
||||||
import { Settings } from "@api/Settings";
|
import { Settings } from "@api/Settings";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { LazyComponent } from "@utils/react";
|
|
||||||
import { formatDuration } from "@utils/text";
|
import { formatDuration } from "@utils/text";
|
||||||
import { find, findByCode, findByPropsLazy } from "@webpack";
|
import { findByPropsLazy, findComponentByCodeLazy, findComponentLazy } from "@webpack";
|
||||||
import { EmojiStore, FluxDispatcher, GuildMemberStore, GuildStore, moment, Parser, PermissionsBits, PermissionStore, SnowflakeUtils, Text, Timestamp, Tooltip, useEffect, useState } from "@webpack/common";
|
import { EmojiStore, FluxDispatcher, GuildMemberStore, GuildStore, moment, Parser, PermissionsBits, PermissionStore, SnowflakeUtils, Text, Timestamp, Tooltip, useEffect, useState } from "@webpack/common";
|
||||||
import type { Channel } from "discord-types/general";
|
import type { Channel } from "discord-types/general";
|
||||||
|
|
||||||
|
@ -81,17 +80,17 @@ const enum ChannelFlags {
|
||||||
|
|
||||||
const ChatScrollClasses = findByPropsLazy("auto", "content", "scrollerBase");
|
const ChatScrollClasses = findByPropsLazy("auto", "content", "scrollerBase");
|
||||||
const ChatClasses = findByPropsLazy("chat", "content", "noChat", "chatContent");
|
const ChatClasses = findByPropsLazy("chat", "content", "noChat", "chatContent");
|
||||||
const ChannelBeginHeader = LazyComponent(() => findByCode(".Messages.ROLE_REQUIRED_SINGLE_USER_MESSAGE"));
|
const ChannelBeginHeader = findComponentByCodeLazy(".Messages.ROLE_REQUIRED_SINGLE_USER_MESSAGE");
|
||||||
const TagComponent = LazyComponent(() => find(m => {
|
const TagComponent = findComponentLazy(m => {
|
||||||
if (typeof m !== "function") return false;
|
if (typeof m !== "function") return false;
|
||||||
|
|
||||||
const code = Function.prototype.toString.call(m);
|
const code = Function.prototype.toString.call(m);
|
||||||
// Get the component which doesn't include increasedActivity
|
// Get the component which doesn't include increasedActivity
|
||||||
return code.includes(".Messages.FORUM_TAG_A11Y_FILTER_BY_TAG") && !code.includes("increasedActivityPill");
|
return code.includes(".Messages.FORUM_TAG_A11Y_FILTER_BY_TAG") && !code.includes("increasedActivityPill");
|
||||||
}));
|
});
|
||||||
|
|
||||||
const EmojiParser = findByPropsLazy("convertSurrogateToName");
|
const EmojiParser = findByPropsLazy("convertSurrogateToName");
|
||||||
const EmojiUtils = findByPropsLazy("getURL", "buildEmojiReactionColorsPlatformed");
|
const EmojiUtils = findByPropsLazy("getURL", "getEmojiColors");
|
||||||
|
|
||||||
const ChannelTypesToChannelNames = {
|
const ChannelTypesToChannelNames = {
|
||||||
[ChannelTypes.GUILD_TEXT]: "text",
|
[ChannelTypes.GUILD_TEXT]: "text",
|
||||||
|
|
|
@ -16,17 +16,27 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import { Flex } from "@components/Flex";
|
import { Flex } from "@components/Flex";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { RelationshipStore } from "@webpack/common";
|
import { RelationshipStore } from "@webpack/common";
|
||||||
import { User } from "discord-types/general";
|
import { User } from "discord-types/general";
|
||||||
import { Settings } from "Vencord";
|
|
||||||
|
const settings = definePluginSettings({
|
||||||
|
showDates: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Show dates on friend requests",
|
||||||
|
default: false,
|
||||||
|
restartNeeded: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "SortFriendRequests",
|
name: "SortFriendRequests",
|
||||||
authors: [Devs.Megu],
|
authors: [Devs.Megu],
|
||||||
description: "Sorts friend requests by date of receipt",
|
description: "Sorts friend requests by date of receipt",
|
||||||
|
settings,
|
||||||
|
|
||||||
patches: [{
|
patches: [{
|
||||||
find: "getRelationshipCounts(){",
|
find: "getRelationshipCounts(){",
|
||||||
|
@ -35,13 +45,11 @@ export default definePlugin({
|
||||||
replace: ".sortBy((row) => $self.sortList(row))"
|
replace: ".sortBy((row) => $self.sortList(row))"
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
find: "RelationshipTypes.PENDING_INCOMING?",
|
find: ".Messages.FRIEND_REQUEST_CANCEL",
|
||||||
replacement: {
|
replacement: {
|
||||||
predicate: () => Settings.plugins.SortFriendRequests.showDates,
|
predicate: () => settings.store.showDates,
|
||||||
match: /(user:(\i),.{10,50}),subText:(\i),(className:\i\.userInfo}\))/,
|
match: /subText:(\i)(?=,className:\i\.userInfo}\))(?<=user:(\i).+?)/,
|
||||||
replace: (_, pre, user, subtext, post) => `${pre},
|
replace: (_, subtext, user) => `subText:$self.makeSubtext(${subtext},${user})`
|
||||||
subText: $self.makeSubtext(${subtext}, ${user}),
|
|
||||||
${post}`
|
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
|
|
||||||
|
@ -63,14 +71,5 @@ export default definePlugin({
|
||||||
{!isNaN(since.getTime()) && <span>Received — {since.toDateString()}</span>}
|
{!isNaN(since.getTime()) && <span>Received — {since.toDateString()}</span>}
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
},
|
|
||||||
|
|
||||||
options: {
|
|
||||||
showDates: {
|
|
||||||
type: OptionType.BOOLEAN,
|
|
||||||
description: "Show dates on friend requests",
|
|
||||||
default: false,
|
|
||||||
restartNeeded: true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -24,7 +24,7 @@ import { ImageIcon, LinkIcon, OpenExternalIcon } from "@components/Icons";
|
||||||
import { debounce } from "@utils/debounce";
|
import { debounce } from "@utils/debounce";
|
||||||
import { openImageModal } from "@utils/discord";
|
import { openImageModal } from "@utils/discord";
|
||||||
import { classes, copyWithToast } from "@utils/misc";
|
import { classes, copyWithToast } from "@utils/misc";
|
||||||
import { ContextMenu, FluxDispatcher, Forms, Menu, React, useEffect, useState, useStateFromStores } from "@webpack/common";
|
import { ContextMenuApi, FluxDispatcher, Forms, Menu, React, useEffect, useState, useStateFromStores } from "@webpack/common";
|
||||||
|
|
||||||
import { SpotifyStore, Track } from "./SpotifyStore";
|
import { SpotifyStore, Track } from "./SpotifyStore";
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ function CopyContextMenu({ name, path }: { name: string; path: string; }) {
|
||||||
|
|
||||||
function makeContextMenu(name: string, path: string) {
|
function makeContextMenu(name: string, path: string) {
|
||||||
return (e: React.MouseEvent<HTMLElement, MouseEvent>) =>
|
return (e: React.MouseEvent<HTMLElement, MouseEvent>) =>
|
||||||
ContextMenu.open(e, () => <CopyContextMenu name={name} path={path} />);
|
ContextMenuApi.openContextMenu(e, () => <CopyContextMenu name={name} path={path} />);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Controls() {
|
function Controls() {
|
||||||
|
@ -277,7 +277,7 @@ function Info({ track }: { track: Track; }) {
|
||||||
alt="Album Image"
|
alt="Album Image"
|
||||||
onClick={() => setCoverExpanded(!coverExpanded)}
|
onClick={() => setCoverExpanded(!coverExpanded)}
|
||||||
onContextMenu={e => {
|
onContextMenu={e => {
|
||||||
ContextMenu.open(e, () => <AlbumContextMenu track={track} />);
|
ContextMenuApi.openContextMenu(e, () => <AlbumContextMenu track={track} />);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -17,8 +17,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Settings } from "@api/Settings";
|
import { Settings } from "@api/Settings";
|
||||||
import { proxyLazy } from "@utils/lazy";
|
import { findByProps, proxyLazyWebpack } from "@webpack";
|
||||||
import { findByPropsLazy } from "@webpack";
|
|
||||||
import { Flux, FluxDispatcher } from "@webpack/common";
|
import { Flux, FluxDispatcher } from "@webpack/common";
|
||||||
|
|
||||||
export interface Track {
|
export interface Track {
|
||||||
|
@ -66,12 +65,12 @@ interface Device {
|
||||||
type Repeat = "off" | "track" | "context";
|
type Repeat = "off" | "track" | "context";
|
||||||
|
|
||||||
// Don't wanna run before Flux and Dispatcher are ready!
|
// Don't wanna run before Flux and Dispatcher are ready!
|
||||||
export const SpotifyStore = proxyLazy(() => {
|
export const SpotifyStore = proxyLazyWebpack(() => {
|
||||||
// For some reason ts hates extends Flux.Store
|
// For some reason ts hates extends Flux.Store
|
||||||
const { Store } = Flux;
|
const { Store } = Flux;
|
||||||
|
|
||||||
const SpotifySocket = findByPropsLazy("getActiveSocketAndDevice");
|
const SpotifySocket = findByProps("getActiveSocketAndDevice");
|
||||||
const SpotifyUtils = findByPropsLazy("SpotifyAPI");
|
const SpotifyUtils = findByProps("SpotifyAPI");
|
||||||
|
|
||||||
const API_BASE = "https://api.spotify.com/v1/me/player";
|
const API_BASE = "https://api.spotify.com/v1/me/player";
|
||||||
|
|
||||||
|
|
|
@ -19,19 +19,13 @@
|
||||||
import { definePluginSettings, Settings } from "@api/Settings";
|
import { definePluginSettings, Settings } from "@api/Settings";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { LazyComponent } from "@utils/react";
|
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { find, findStoreLazy } from "@webpack";
|
import { findExportedComponentLazy, findStoreLazy } from "@webpack";
|
||||||
import { ChannelStore, GuildMemberStore, i18n, RelationshipStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common";
|
import { ChannelStore, GuildMemberStore, i18n, RelationshipStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common";
|
||||||
|
|
||||||
import { buildSeveralUsers } from "../typingTweaks";
|
import { buildSeveralUsers } from "../typingTweaks";
|
||||||
|
|
||||||
const ThreeDots = LazyComponent(() => {
|
const ThreeDots = findExportedComponentLazy("Dots", "AnimatedDots");
|
||||||
// This doesn't really need to explicitly find Dots' own module, but it's fine
|
|
||||||
const res = find(m => m.Dots && !m.Menu);
|
|
||||||
|
|
||||||
return res?.Dots;
|
|
||||||
});
|
|
||||||
|
|
||||||
const TypingStore = findStoreLazy("TypingStore");
|
const TypingStore = findStoreLazy("TypingStore");
|
||||||
const UserGuildSettingsStore = findStoreLazy("UserGuildSettingsStore");
|
const UserGuildSettingsStore = findStoreLazy("UserGuildSettingsStore");
|
||||||
|
|
|
@ -22,16 +22,12 @@ import { openNotificationLogModal } from "@api/Notifications/notificationLog";
|
||||||
import { Settings } from "@api/Settings";
|
import { Settings } from "@api/Settings";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { LazyComponent } from "@utils/react";
|
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { filters, find } from "@webpack";
|
import { findExportedComponentLazy } from "@webpack";
|
||||||
import { Menu, Popout, useState } from "@webpack/common";
|
import { Menu, Popout, useState } from "@webpack/common";
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
const HeaderBarIcon = LazyComponent(() => {
|
const HeaderBarIcon = findExportedComponentLazy("Icon", "Divider");
|
||||||
const filter = filters.byCode(".HEADER_BAR_BADGE");
|
|
||||||
return find(m => m.Icon && filter(m.Icon)).Icon;
|
|
||||||
});
|
|
||||||
|
|
||||||
function VencordPopout(onClose: () => void) {
|
function VencordPopout(onClose: () => void) {
|
||||||
const pluginEntries = [] as ReactNode[];
|
const pluginEntries = [] as ReactNode[];
|
||||||
|
|
|
@ -16,8 +16,8 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { LazyComponent, useTimer } from "@utils/react";
|
import { useTimer } from "@utils/react";
|
||||||
import { find } from "@webpack";
|
import { findComponentByCodeLazy } from "@webpack";
|
||||||
|
|
||||||
import { cl } from "./utils";
|
import { cl } from "./utils";
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ interface VoiceMessageProps {
|
||||||
src: string;
|
src: string;
|
||||||
waveform: string;
|
waveform: string;
|
||||||
}
|
}
|
||||||
const VoiceMessage = LazyComponent<VoiceMessageProps>(() => find(m => m.type?.toString().includes("waveform:")));
|
const VoiceMessage = findComponentByCodeLazy<VoiceMessageProps>("waveform:");
|
||||||
|
|
||||||
export type VoicePreviewOptions = {
|
export type VoicePreviewOptions = {
|
||||||
src?: string;
|
src?: string;
|
||||||
|
|
|
@ -20,8 +20,8 @@ import { definePluginSettings } from "@api/Settings";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { saveFile } from "@utils/web";
|
import { saveFile } from "@utils/web";
|
||||||
import { findByProps, findLazy } from "@webpack";
|
import { findByProps } from "@webpack";
|
||||||
import { Clipboard } from "@webpack/common";
|
import { Clipboard, ComponentDispatch } from "@webpack/common";
|
||||||
|
|
||||||
async function fetchImage(url: string) {
|
async function fetchImage(url: string) {
|
||||||
const res = await fetch(url);
|
const res = await fetch(url);
|
||||||
|
@ -30,7 +30,6 @@ async function fetchImage(url: string) {
|
||||||
return await res.blob();
|
return await res.blob();
|
||||||
}
|
}
|
||||||
|
|
||||||
const MiniDispatcher = findLazy(m => m.emitter?._events?.INSERT_TEXT);
|
|
||||||
|
|
||||||
const settings = definePluginSettings({
|
const settings = definePluginSettings({
|
||||||
// This needs to be all in one setting because to enable any of these, we need to make Discord use their desktop context
|
// This needs to be all in one setting because to enable any of these, we need to make Discord use their desktop context
|
||||||
|
@ -119,11 +118,12 @@ export default definePlugin({
|
||||||
// Add back image context menu
|
// Add back image context menu
|
||||||
{
|
{
|
||||||
find: 'navId:"image-context"',
|
find: 'navId:"image-context"',
|
||||||
|
all: true,
|
||||||
predicate: () => settings.store.addBack,
|
predicate: () => settings.store.addBack,
|
||||||
replacement: {
|
replacement: {
|
||||||
// return IS_DESKTOP ? React.createElement(Menu, ...)
|
// return IS_DESKTOP ? React.createElement(Menu, ...)
|
||||||
match: /return \i\.\i\?/,
|
match: /return \i\.\i(?=\?|&&)/,
|
||||||
replace: "return true?"
|
replace: "return true"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -213,7 +213,7 @@ export default definePlugin({
|
||||||
|
|
||||||
cut() {
|
cut() {
|
||||||
this.copy();
|
this.copy();
|
||||||
MiniDispatcher.dispatch("INSERT_TEXT", { rawText: "" });
|
ComponentDispatch.dispatch("INSERT_TEXT", { rawText: "" });
|
||||||
},
|
},
|
||||||
|
|
||||||
async paste() {
|
async paste() {
|
||||||
|
|
|
@ -20,14 +20,14 @@ import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { sleep } from "@utils/misc";
|
import { sleep } from "@utils/misc";
|
||||||
import { Queue } from "@utils/Queue";
|
import { Queue } from "@utils/Queue";
|
||||||
import { LazyComponent, useForceUpdater } from "@utils/react";
|
import { useForceUpdater } from "@utils/react";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { findByCode, findByPropsLazy } from "@webpack";
|
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
||||||
import { ChannelStore, FluxDispatcher, React, RestAPI, Tooltip } from "@webpack/common";
|
import { ChannelStore, FluxDispatcher, React, RestAPI, Tooltip } from "@webpack/common";
|
||||||
import { CustomEmoji } from "@webpack/types";
|
import { CustomEmoji } from "@webpack/types";
|
||||||
import { Message, ReactionEmoji, User } from "discord-types/general";
|
import { Message, ReactionEmoji, User } from "discord-types/general";
|
||||||
|
|
||||||
const UserSummaryItem = LazyComponent(() => findByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers"));
|
const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
||||||
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
|
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
|
||||||
|
|
||||||
const queue = new Queue();
|
const queue = new Queue();
|
||||||
|
|
|
@ -43,7 +43,6 @@ for (const method of [
|
||||||
"construct",
|
"construct",
|
||||||
"defineProperty",
|
"defineProperty",
|
||||||
"deleteProperty",
|
"deleteProperty",
|
||||||
"get",
|
|
||||||
"getOwnPropertyDescriptor",
|
"getOwnPropertyDescriptor",
|
||||||
"getPrototypeOf",
|
"getPrototypeOf",
|
||||||
"has",
|
"has",
|
||||||
|
@ -77,7 +76,7 @@ handler.getOwnPropertyDescriptor = (target, p) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps the result of {@see makeLazy} in a Proxy you can consume as if it wasn't lazy.
|
* Wraps the result of {@link makeLazy} in a Proxy you can consume as if it wasn't lazy.
|
||||||
* On first property access, the lazy is evaluated
|
* On first property access, the lazy is evaluated
|
||||||
* @param factory lazy factory
|
* @param factory lazy factory
|
||||||
* @param attempts how many times to try to evaluate the lazy before giving up
|
* @param attempts how many times to try to evaluate the lazy before giving up
|
||||||
|
@ -86,7 +85,11 @@ handler.getOwnPropertyDescriptor = (target, p) => {
|
||||||
* Note that the example below exists already as an api, see {@link findByPropsLazy}
|
* Note that the example below exists already as an api, see {@link findByPropsLazy}
|
||||||
* @example const mod = proxyLazy(() => findByProps("blah")); console.log(mod.blah);
|
* @example const mod = proxyLazy(() => findByProps("blah")); console.log(mod.blah);
|
||||||
*/
|
*/
|
||||||
export function proxyLazy<T>(factory: () => T, attempts = 5): T {
|
export function proxyLazy<T>(factory: () => T, attempts = 5, isChild = false): T {
|
||||||
|
let isSameTick = true;
|
||||||
|
if (!isChild)
|
||||||
|
setTimeout(() => isSameTick = false, 0);
|
||||||
|
|
||||||
let tries = 0;
|
let tries = 0;
|
||||||
const proxyDummy = Object.assign(function () { }, {
|
const proxyDummy = Object.assign(function () { }, {
|
||||||
[kCACHE]: void 0 as T | undefined,
|
[kCACHE]: void 0 as T | undefined,
|
||||||
|
@ -100,5 +103,21 @@ export function proxyLazy<T>(factory: () => T, attempts = 5): T {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return new Proxy(proxyDummy, handler) as any;
|
return new Proxy(proxyDummy, {
|
||||||
|
...handler,
|
||||||
|
get(target, p, receiver) {
|
||||||
|
// if we're still in the same tick, it means the lazy was immediately used.
|
||||||
|
// thus, we lazy proxy the get access to make things like destructuring work as expected
|
||||||
|
// meow here will also be a lazy
|
||||||
|
// `const { meow } = findByPropsLazy("meow");`
|
||||||
|
if (!isChild && isSameTick)
|
||||||
|
return proxyLazy(
|
||||||
|
() => Reflect.get(target[kGET](), p, receiver),
|
||||||
|
attempts,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
return Reflect.get(target[kGET](), p, receiver);
|
||||||
|
}
|
||||||
|
}) as any;
|
||||||
}
|
}
|
||||||
|
|
29
src/utils/lazyReact.tsx
Normal file
29
src/utils/lazyReact.tsx
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ComponentType } from "react";
|
||||||
|
|
||||||
|
import { makeLazy } from "./lazy";
|
||||||
|
|
||||||
|
const NoopComponent = () => null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A lazy component. The factory method is called on first render.
|
||||||
|
* @param factory Function returning a Component
|
||||||
|
* @param attempts How many times to try to get the component before giving up
|
||||||
|
* @returns Result of factory function
|
||||||
|
*/
|
||||||
|
export function LazyComponent<T extends object = any>(factory: () => React.ComponentType<T>, attempts = 5) {
|
||||||
|
const get = makeLazy(factory, attempts);
|
||||||
|
const LazyComponent = (props: T) => {
|
||||||
|
const Component = get() ?? NoopComponent;
|
||||||
|
return <Component {...props} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
LazyComponent.$$get = get;
|
||||||
|
|
||||||
|
return LazyComponent as ComponentType<T>;
|
||||||
|
}
|
|
@ -16,7 +16,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { filters, findByProps, findByPropsLazy, mapMangledModuleLazy } from "@webpack";
|
import { findByProps, findByPropsLazy } from "@webpack";
|
||||||
import type { ComponentType, PropsWithChildren, ReactNode, Ref } from "react";
|
import type { ComponentType, PropsWithChildren, ReactNode, Ref } from "react";
|
||||||
|
|
||||||
import { LazyComponent } from "./react";
|
import { LazyComponent } from "./react";
|
||||||
|
@ -49,13 +49,7 @@ export interface ModalOptions {
|
||||||
|
|
||||||
type RenderFunction = (props: ModalProps) => ReactNode;
|
type RenderFunction = (props: ModalProps) => ReactNode;
|
||||||
|
|
||||||
export const Modals = mapMangledModuleLazy(".closeWithCircleBackground", {
|
export const Modals = findByPropsLazy("ModalRoot", "ModalCloseButton") as {
|
||||||
ModalRoot: filters.byCode(".root"),
|
|
||||||
ModalHeader: filters.byCode(".header"),
|
|
||||||
ModalContent: filters.byCode(".content"),
|
|
||||||
ModalFooter: filters.byCode(".footerSeparator"),
|
|
||||||
ModalCloseButton: filters.byCode(".closeWithCircleBackground"),
|
|
||||||
}) as {
|
|
||||||
ModalRoot: ComponentType<PropsWithChildren<{
|
ModalRoot: ComponentType<PropsWithChildren<{
|
||||||
transitionState: ModalTransitionState;
|
transitionState: ModalTransitionState;
|
||||||
size?: ModalSize;
|
size?: ModalSize;
|
||||||
|
|
|
@ -18,9 +18,10 @@
|
||||||
|
|
||||||
import { React, useEffect, useMemo, useReducer, useState } from "@webpack/common";
|
import { React, useEffect, useMemo, useReducer, useState } from "@webpack/common";
|
||||||
|
|
||||||
import { makeLazy } from "./lazy";
|
|
||||||
import { checkIntersecting } from "./misc";
|
import { checkIntersecting } from "./misc";
|
||||||
|
|
||||||
|
export * from "./lazyReact";
|
||||||
|
|
||||||
export const NoopComponent = () => null;
|
export const NoopComponent = () => null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -77,7 +78,6 @@ interface AwaiterOpts<T> {
|
||||||
* @param fallbackValue The fallback value that will be used until the promise resolved
|
* @param fallbackValue The fallback value that will be used until the promise resolved
|
||||||
* @returns [value, error, isPending]
|
* @returns [value, error, isPending]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function useAwaiter<T>(factory: () => Promise<T>): AwaiterRes<T | null>;
|
export function useAwaiter<T>(factory: () => Promise<T>): AwaiterRes<T | null>;
|
||||||
export function useAwaiter<T>(factory: () => Promise<T>, providedOpts: AwaiterOpts<T>): AwaiterRes<T>;
|
export function useAwaiter<T>(factory: () => Promise<T>, providedOpts: AwaiterOpts<T>): AwaiterRes<T>;
|
||||||
export function useAwaiter<T>(factory: () => Promise<T>, providedOpts?: AwaiterOpts<T | null>): AwaiterRes<T | null> {
|
export function useAwaiter<T>(factory: () => Promise<T>, providedOpts?: AwaiterOpts<T | null>): AwaiterRes<T | null> {
|
||||||
|
@ -113,31 +113,16 @@ export function useAwaiter<T>(factory: () => Promise<T>, providedOpts?: AwaiterO
|
||||||
|
|
||||||
return [state.value, state.error, state.pending];
|
return [state.value, state.error, state.pending];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a function that can be used to force rerender react components
|
* Returns a function that can be used to force rerender react components
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function useForceUpdater(): () => void;
|
export function useForceUpdater(): () => void;
|
||||||
export function useForceUpdater(withDep: true): [unknown, () => void];
|
export function useForceUpdater(withDep: true): [unknown, () => void];
|
||||||
export function useForceUpdater(withDep?: true) {
|
export function useForceUpdater(withDep?: true) {
|
||||||
const r = useReducer(x => x + 1, 0);
|
const r = useReducer(x => x + 1, 0);
|
||||||
return withDep ? r : r[1];
|
return withDep ? r : r[1];
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
* @param attempts How many times to try to get the component before giving up
|
|
||||||
* @returns Result of factory function
|
|
||||||
*/
|
|
||||||
|
|
||||||
export function LazyComponent<T extends object = any>(factory: () => React.ComponentType<T>, attempts = 5) {
|
|
||||||
const get = makeLazy(factory, attempts);
|
|
||||||
return (props: T) => {
|
|
||||||
const Component = get() ?? NoopComponent;
|
|
||||||
return <Component {...props} />;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TimerOpts {
|
interface TimerOpts {
|
||||||
interval?: number;
|
interval?: number;
|
||||||
|
|
|
@ -41,6 +41,8 @@ export interface Patch {
|
||||||
all?: boolean;
|
all?: boolean;
|
||||||
/** Do not warn if this patch did no changes */
|
/** Do not warn if this patch did no changes */
|
||||||
noWarn?: boolean;
|
noWarn?: boolean;
|
||||||
|
/** Only apply this set of replacements if all of them succeed. Use this if your replacements depend on each other */
|
||||||
|
group?: boolean;
|
||||||
predicate?(): boolean;
|
predicate?(): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,6 +82,11 @@ export interface PluginDef {
|
||||||
* Whether this plugin should be enabled by default, but can be disabled
|
* Whether this plugin should be enabled by default, but can be disabled
|
||||||
*/
|
*/
|
||||||
enabledByDefault?: boolean;
|
enabledByDefault?: boolean;
|
||||||
|
/**
|
||||||
|
* When to call the start() method
|
||||||
|
* @default StartAt.WebpackReady
|
||||||
|
*/
|
||||||
|
startAt?: StartAt,
|
||||||
/**
|
/**
|
||||||
* Optionally provide settings that the user can configure in the Plugins tab of settings.
|
* Optionally provide settings that the user can configure in the Plugins tab of settings.
|
||||||
* @deprecated Use `settings` instead
|
* @deprecated Use `settings` instead
|
||||||
|
@ -117,6 +124,15 @@ export interface PluginDef {
|
||||||
tags?: string[];
|
tags?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const enum StartAt {
|
||||||
|
/** Right away, as soon as Vencord initialised */
|
||||||
|
Init = "Init",
|
||||||
|
/** On the DOMContentLoaded event, so once the document is ready */
|
||||||
|
DOMContentLoaded = "DOMContentLoaded",
|
||||||
|
/** Once Discord's core webpack modules have finished loading, so as soon as things like react and flux are available */
|
||||||
|
WebpackReady = "WebpackReady"
|
||||||
|
}
|
||||||
|
|
||||||
export const enum OptionType {
|
export const enum OptionType {
|
||||||
STRING,
|
STRING,
|
||||||
NUMBER,
|
NUMBER,
|
||||||
|
|
|
@ -19,9 +19,11 @@
|
||||||
import { LazyComponent } from "@utils/react";
|
import { LazyComponent } from "@utils/react";
|
||||||
|
|
||||||
// eslint-disable-next-line path-alias/no-relative
|
// eslint-disable-next-line path-alias/no-relative
|
||||||
import { FilterFn, filters, waitFor } from "../webpack";
|
import { FilterFn, filters, lazyWebpackSearchHistory, waitFor } from "../webpack";
|
||||||
|
|
||||||
export function waitForComponent<T extends React.ComponentType<any> = React.ComponentType<any> & Record<string, any>>(name: string, filter: FilterFn | string | string[]): T {
|
export function waitForComponent<T extends React.ComponentType<any> = React.ComponentType<any> & Record<string, any>>(name: string, filter: FilterFn | string | string[]): T {
|
||||||
|
if (IS_DEV) lazyWebpackSearchHistory.push(["waitForComponent", Array.isArray(filter) ? filter : [filter]]);
|
||||||
|
|
||||||
let myValue: T = function () {
|
let myValue: T = function () {
|
||||||
throw new Error(`Vencord could not find the ${name} Component`);
|
throw new Error(`Vencord could not find the ${name} Component`);
|
||||||
} as any;
|
} as any;
|
||||||
|
@ -30,11 +32,13 @@ export function waitForComponent<T extends React.ComponentType<any> = React.Comp
|
||||||
waitFor(filter, (v: any) => {
|
waitFor(filter, (v: any) => {
|
||||||
myValue = v;
|
myValue = v;
|
||||||
Object.assign(lazyComponent, v);
|
Object.assign(lazyComponent, v);
|
||||||
});
|
}, { isIndirect: true });
|
||||||
|
|
||||||
return lazyComponent;
|
return lazyComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function waitForStore(name: string, cb: (v: any) => void) {
|
export function waitForStore(name: string, cb: (v: any) => void) {
|
||||||
waitFor(filters.byStoreName(name), cb);
|
if (IS_DEV) lazyWebpackSearchHistory.push(["waitForStore", [name]]);
|
||||||
|
|
||||||
|
waitFor(filters.byStoreName(name), cb, { isIndirect: true });
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,16 +17,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// eslint-disable-next-line path-alias/no-relative
|
// eslint-disable-next-line path-alias/no-relative
|
||||||
import { filters, mapMangledModuleLazy, waitFor } from "../webpack";
|
import { findByPropsLazy, waitFor } from "../webpack";
|
||||||
import type * as t from "./types/menu";
|
import type * as t from "./types/menu";
|
||||||
|
|
||||||
export let Menu = {} as t.Menu;
|
export let Menu = {} as t.Menu;
|
||||||
|
|
||||||
waitFor(["MenuItem", "MenuSliderControl"], m => Menu = m);
|
waitFor(["MenuItem", "MenuSliderControl"], m => Menu = m);
|
||||||
|
|
||||||
export const ContextMenu: t.ContextMenuApi = mapMangledModuleLazy('type:"CONTEXT_MENU_OPEN"', {
|
export const ContextMenuApi: t.ContextMenuApi = findByPropsLazy("closeContextMenu", "openContextMenu");
|
||||||
open: filters.byCode("stopPropagation"),
|
|
||||||
openLazy: m => m.toString().length < 50,
|
|
||||||
close: filters.byCode("CONTEXT_MENU_CLOSE")
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
|
@ -16,11 +16,10 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { proxyLazy } from "@utils/lazy";
|
|
||||||
import type * as Stores from "discord-types/stores";
|
import type * as Stores from "discord-types/stores";
|
||||||
|
|
||||||
// eslint-disable-next-line path-alias/no-relative
|
// eslint-disable-next-line path-alias/no-relative
|
||||||
import { filters, findByProps, findByPropsLazy, mapMangledModuleLazy } from "../webpack";
|
import { findByPropsLazy } from "../webpack";
|
||||||
import { waitForStore } from "./internal";
|
import { waitForStore } from "./internal";
|
||||||
import * as t from "./types/stores";
|
import * as t from "./types/stores";
|
||||||
|
|
||||||
|
@ -63,10 +62,6 @@ export let EmojiStore: t.EmojiStore;
|
||||||
export let WindowStore: t.WindowStore;
|
export let WindowStore: t.WindowStore;
|
||||||
export let DraftStore: t.DraftStore;
|
export let DraftStore: t.DraftStore;
|
||||||
|
|
||||||
export const MaskedLinkStore = mapMangledModuleLazy('"MaskedLinkStore"', {
|
|
||||||
openUntrustedLink: filters.byCode(".apply(this,arguments)")
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* React hook that returns stateful data for one or more stores
|
* React hook that returns stateful data for one or more stores
|
||||||
* You might need a custom comparator (4th argument) if your store data is an object
|
* You might need a custom comparator (4th argument) if your store data is an object
|
||||||
|
@ -78,13 +73,15 @@ export const MaskedLinkStore = mapMangledModuleLazy('"MaskedLinkStore"', {
|
||||||
*
|
*
|
||||||
* @example const user = useStateFromStores([UserStore], () => UserStore.getCurrentUser(), null, (old, current) => old.id === current.id);
|
* @example const user = useStateFromStores([UserStore], () => UserStore.getCurrentUser(), null, (old, current) => old.id === current.id);
|
||||||
*/
|
*/
|
||||||
export const useStateFromStores: <T>(
|
export const { useStateFromStores }: {
|
||||||
|
useStateFromStores: <T>(
|
||||||
stores: t.FluxStore[],
|
stores: t.FluxStore[],
|
||||||
mapper: () => T,
|
mapper: () => T,
|
||||||
idk?: any,
|
idk?: any,
|
||||||
isEqual?: (old: T, newer: T) => boolean
|
isEqual?: (old: T, newer: T) => boolean
|
||||||
) => T
|
) => T;
|
||||||
= proxyLazy(() => findByProps("useStateFromStores").useStateFromStores);
|
}
|
||||||
|
= findByPropsLazy("useStateFromStores");
|
||||||
|
|
||||||
waitForStore("DraftStore", s => DraftStore = s);
|
waitForStore("DraftStore", s => DraftStore = s);
|
||||||
waitForStore("UserStore", s => UserStore = s);
|
waitForStore("UserStore", s => UserStore = s);
|
||||||
|
|
6
src/webpack/common/types/menu.d.ts
vendored
6
src/webpack/common/types/menu.d.ts
vendored
|
@ -75,14 +75,14 @@ export interface Menu {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ContextMenuApi {
|
export interface ContextMenuApi {
|
||||||
close(): void;
|
closeContextMenu(): void;
|
||||||
open(
|
openContextMenu(
|
||||||
event: UIEvent,
|
event: UIEvent,
|
||||||
render?: Menu["Menu"],
|
render?: Menu["Menu"],
|
||||||
options?: { enableSpellCheck?: boolean; },
|
options?: { enableSpellCheck?: boolean; },
|
||||||
renderLazy?: () => Promise<Menu["Menu"]>
|
renderLazy?: () => Promise<Menu["Menu"]>
|
||||||
): void;
|
): void;
|
||||||
openLazy(
|
openContextMenuLazy(
|
||||||
event: UIEvent,
|
event: UIEvent,
|
||||||
renderLazy?: () => Promise<Menu["Menu"]>,
|
renderLazy?: () => Promise<Menu["Menu"]>,
|
||||||
options?: { enableSpellCheck?: boolean; }
|
options?: { enableSpellCheck?: boolean; }
|
||||||
|
|
21
src/webpack/common/types/utils.d.ts
vendored
21
src/webpack/common/types/utils.d.ts
vendored
|
@ -161,3 +161,24 @@ export interface i18n {
|
||||||
|
|
||||||
Messages: Record<i18nMessages, any>;
|
Messages: Record<i18nMessages, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Clipboard {
|
||||||
|
copy(text: string): void;
|
||||||
|
SUPPORTS_COPY: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NavigationRouter {
|
||||||
|
back(): void;
|
||||||
|
forward(): void;
|
||||||
|
hasNavigated(): boolean;
|
||||||
|
getHistory(): {
|
||||||
|
action: string;
|
||||||
|
length: 50;
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
transitionTo(path: string, ...args: unknown[]): void;
|
||||||
|
transitionToGuild(guildId: string, ...args: unknown[]): void;
|
||||||
|
replaceWith(...args: unknown[]): void;
|
||||||
|
getLastRouteChangeSource(): any;
|
||||||
|
getLastRouteChangeSourceLocationStack(): any;
|
||||||
|
}
|
||||||
|
|
|
@ -16,14 +16,23 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { proxyLazy } from "@utils/lazy";
|
|
||||||
import type { Channel, User } from "discord-types/general";
|
import type { Channel, User } from "discord-types/general";
|
||||||
|
|
||||||
// eslint-disable-next-line path-alias/no-relative
|
// eslint-disable-next-line path-alias/no-relative
|
||||||
import { _resolveReady, filters, find, findByPropsLazy, findLazy, mapMangledModuleLazy, waitFor } from "../webpack";
|
import { _resolveReady, findByPropsLazy, findLazy, waitFor } from "../webpack";
|
||||||
import type * as t from "./types/utils";
|
import type * as t from "./types/utils";
|
||||||
|
|
||||||
export let FluxDispatcher: t.FluxDispatcher;
|
export let FluxDispatcher: t.FluxDispatcher;
|
||||||
|
|
||||||
|
waitFor(["dispatch", "subscribe"], m => {
|
||||||
|
FluxDispatcher = m;
|
||||||
|
const cb = () => {
|
||||||
|
m.unsubscribe("CONNECTION_OPEN", cb);
|
||||||
|
_resolveReady();
|
||||||
|
};
|
||||||
|
m.subscribe("CONNECTION_OPEN", cb);
|
||||||
|
});
|
||||||
|
|
||||||
export let ComponentDispatch;
|
export let ComponentDispatch;
|
||||||
waitFor(["ComponentDispatch", "ComponentDispatcher"], m => ComponentDispatch = m.ComponentDispatch);
|
waitFor(["ComponentDispatch", "ComponentDispatcher"], m => ComponentDispatch = m.ComponentDispatch);
|
||||||
|
|
||||||
|
@ -41,7 +50,9 @@ export let SnowflakeUtils: t.SnowflakeUtils;
|
||||||
waitFor(["fromTimestamp", "extractTimestamp"], m => SnowflakeUtils = m);
|
waitFor(["fromTimestamp", "extractTimestamp"], m => SnowflakeUtils = m);
|
||||||
|
|
||||||
export let Parser: t.Parser;
|
export let Parser: t.Parser;
|
||||||
|
waitFor("parseTopic", m => Parser = m);
|
||||||
export let Alerts: t.Alerts;
|
export let Alerts: t.Alerts;
|
||||||
|
waitFor(["show", "close"], m => Alerts = m);
|
||||||
|
|
||||||
const ToastType = {
|
const ToastType = {
|
||||||
MESSAGE: 0,
|
MESSAGE: 0,
|
||||||
|
@ -82,6 +93,13 @@ export const Toasts = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This is the same module but this is easier
|
||||||
|
waitFor("showToast", m => {
|
||||||
|
Toasts.show = m.showToast;
|
||||||
|
Toasts.pop = m.popToast;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show a simple toast. If you need more options, use Toasts.show manually
|
* Show a simple toast. If you need more options, use Toasts.show manually
|
||||||
*/
|
*/
|
||||||
|
@ -102,38 +120,12 @@ export const ApplicationAssetUtils = findByPropsLazy("fetchAssetIds", "getAssetI
|
||||||
fetchAssetIds: (applicationId: string, e: string[]) => Promise<string[]>;
|
fetchAssetIds: (applicationId: string, e: string[]) => Promise<string[]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Clipboard = mapMangledModuleLazy('document.queryCommandEnabled("copy")||document.queryCommandSupported("copy")', {
|
export const Clipboard: t.Clipboard = findByPropsLazy("SUPPORTS_COPY", "copy");
|
||||||
copy: filters.byCode(".copy("),
|
|
||||||
SUPPORTS_COPY: x => typeof x === "boolean",
|
|
||||||
});
|
|
||||||
|
|
||||||
export const NavigationRouter = mapMangledModuleLazy("transitionToGuild - ", {
|
export const NavigationRouter: t.NavigationRouter = findByPropsLazy("transitionTo", "replaceWith", "transitionToGuild");
|
||||||
transitionTo: filters.byCode("transitionTo -"),
|
|
||||||
transitionToGuild: filters.byCode("transitionToGuild -"),
|
|
||||||
goBack: filters.byCode("goBack()"),
|
|
||||||
goForward: filters.byCode("goForward()"),
|
|
||||||
});
|
|
||||||
|
|
||||||
waitFor(["dispatch", "subscribe"], m => {
|
|
||||||
FluxDispatcher = m;
|
|
||||||
const cb = () => {
|
|
||||||
m.unsubscribe("CONNECTION_OPEN", cb);
|
|
||||||
_resolveReady();
|
|
||||||
};
|
|
||||||
m.subscribe("CONNECTION_OPEN", cb);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// This is the same module but this is easier
|
|
||||||
waitFor("showToast", m => {
|
|
||||||
Toasts.show = m.showToast;
|
|
||||||
Toasts.pop = m.popToast;
|
|
||||||
});
|
|
||||||
|
|
||||||
waitFor(["show", "close"], m => Alerts = m);
|
|
||||||
waitFor("parseTopic", m => Parser = m);
|
|
||||||
|
|
||||||
export let SettingsRouter: any;
|
export let SettingsRouter: any;
|
||||||
waitFor(["open", "saveAccountChanges"], m => SettingsRouter = m);
|
waitFor(["open", "saveAccountChanges"], m => SettingsRouter = m);
|
||||||
|
|
||||||
export const PermissionsBits: t.PermissionsBits = proxyLazy(() => find(m => typeof m.Permissions?.ADMINISTRATOR === "bigint").Permissions);
|
const { Permissions } = findLazy(m => typeof m.Permissions?.ADMINISTRATOR === "bigint") as { Permissions: t.PermissionsBits; };
|
||||||
|
export { Permissions as PermissionsBits };
|
||||||
|
|
|
@ -119,12 +119,9 @@ function patchFactories(factories: Record<string | number, (module: { exports: a
|
||||||
// Additionally, `[actual newline]` is one less char than "\n", so if Discord
|
// Additionally, `[actual newline]` is one less char than "\n", so if Discord
|
||||||
// ever targets newer browsers, the minifier could potentially use this trick and
|
// ever targets newer browsers, the minifier could potentially use this trick and
|
||||||
// cause issues.
|
// cause issues.
|
||||||
let code: string = mod.toString().replaceAll("\n", "");
|
//
|
||||||
// a very small minority of modules use function() instead of arrow functions,
|
// 0, prefix is to turn it into an expression: 0,function(){} would be invalid syntax without the 0,
|
||||||
// but, unnamed toplevel functions aren't valid. However 0, function() makes it a statement
|
let code: string = "0," + mod.toString().replaceAll("\n", "");
|
||||||
if (code.startsWith("function(")) {
|
|
||||||
code = "0," + code;
|
|
||||||
}
|
|
||||||
const originalMod = mod;
|
const originalMod = mod;
|
||||||
const patchedBy = new Set();
|
const patchedBy = new Set();
|
||||||
|
|
||||||
|
@ -170,19 +167,10 @@ function patchFactories(factories: Record<string | number, (module: { exports: a
|
||||||
if (filter(exports)) {
|
if (filter(exports)) {
|
||||||
subscriptions.delete(filter);
|
subscriptions.delete(filter);
|
||||||
callback(exports, numberId);
|
callback(exports, numberId);
|
||||||
} else if (typeof exports === "object") {
|
} else if (exports.default && filter(exports.default)) {
|
||||||
if (exports.default && filter(exports.default)) {
|
|
||||||
subscriptions.delete(filter);
|
subscriptions.delete(filter);
|
||||||
callback(exports.default, numberId);
|
callback(exports.default, numberId);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const nested in exports) if (nested.length <= 3) {
|
|
||||||
if (exports[nested] && filter(exports[nested])) {
|
|
||||||
subscriptions.delete(filter);
|
|
||||||
callback(exports[nested], numberId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error("Error while firing callback for webpack chunk", err);
|
logger.error("Error while firing callback for webpack chunk", err);
|
||||||
}
|
}
|
||||||
|
@ -191,10 +179,8 @@ function patchFactories(factories: Record<string | number, (module: { exports: a
|
||||||
|
|
||||||
// for some reason throws some error on which calling .toString() leads to infinite recursion
|
// for some reason throws some error on which calling .toString() leads to infinite recursion
|
||||||
// when you force load all chunks???
|
// when you force load all chunks???
|
||||||
try {
|
|
||||||
factory.toString = () => mod.toString();
|
factory.toString = () => mod.toString();
|
||||||
factory.original = originalMod;
|
factory.original = originalMod;
|
||||||
} catch { }
|
|
||||||
|
|
||||||
for (let i = 0; i < patches.length; i++) {
|
for (let i = 0; i < patches.length; i++) {
|
||||||
const patch = patches[i];
|
const patch = patches[i];
|
||||||
|
@ -204,6 +190,9 @@ function patchFactories(factories: Record<string | number, (module: { exports: a
|
||||||
if (code.includes(patch.find)) {
|
if (code.includes(patch.find)) {
|
||||||
patchedBy.add(patch.plugin);
|
patchedBy.add(patch.plugin);
|
||||||
|
|
||||||
|
const previousMod = mod;
|
||||||
|
const previousCode = code;
|
||||||
|
|
||||||
// we change all patch.replacement to array in plugins/index
|
// we change all patch.replacement to array in plugins/index
|
||||||
for (const replacement of patch.replacement as PatchReplacement[]) {
|
for (const replacement of patch.replacement as PatchReplacement[]) {
|
||||||
if (replacement.predicate && !replacement.predicate()) continue;
|
if (replacement.predicate && !replacement.predicate()) continue;
|
||||||
|
@ -214,12 +203,21 @@ function patchFactories(factories: Record<string | number, (module: { exports: a
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const newCode = executePatch(replacement.match, replacement.replace as string);
|
const newCode = executePatch(replacement.match, replacement.replace as string);
|
||||||
if (newCode === code && !patch.noWarn) {
|
if (newCode === code) {
|
||||||
(window.explosivePlugins ??= new Set<string>()).add(patch.plugin);
|
if (!patch.noWarn) {
|
||||||
logger.warn(`Patch by ${patch.plugin} had no effect (Module id is ${id}): ${replacement.match}`);
|
logger.warn(`Patch by ${patch.plugin} had no effect (Module id is ${id}): ${replacement.match}`);
|
||||||
if (IS_DEV) {
|
if (IS_DEV) {
|
||||||
logger.debug("Function Source:\n", code);
|
logger.debug("Function Source:\n", code);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (patch.group) {
|
||||||
|
logger.warn(`Undoing patch group ${patch.find} by ${patch.plugin} because replacement ${replacement.match} had no effect`);
|
||||||
|
code = previousCode;
|
||||||
|
mod = previousMod;
|
||||||
|
patchedBy.delete(patch.plugin);
|
||||||
|
break;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
code = newCode;
|
code = newCode;
|
||||||
mod = (0, eval)(`// Webpack Module ${id} - Patched by ${[...patchedBy].join(", ")}\n${newCode}\n//# sourceURL=WebpackModule${id}`);
|
mod = (0, eval)(`// Webpack Module ${id} - Patched by ${[...patchedBy].join(", ")}\n${newCode}\n//# sourceURL=WebpackModule${id}`);
|
||||||
|
@ -259,9 +257,17 @@ function patchFactories(factories: Record<string | number, (module: { exports: a
|
||||||
const [titleFmt, ...titleElements] = Logger.makeTitle("white", "Diff");
|
const [titleFmt, ...titleElements] = Logger.makeTitle("white", "Diff");
|
||||||
logger.errorCustomFmt(titleFmt + fmt, ...titleElements, ...elements);
|
logger.errorCustomFmt(titleFmt + fmt, ...titleElements, ...elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
patchedBy.delete(patch.plugin);
|
||||||
|
if (patch.group) {
|
||||||
|
logger.warn(`Undoing patch group ${patch.find} by ${patch.plugin} because replacement ${replacement.match} errored`);
|
||||||
|
code = previousCode;
|
||||||
|
mod = previousMod;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
code = lastCode;
|
code = lastCode;
|
||||||
mod = lastMod;
|
mod = lastMod;
|
||||||
patchedBy.delete(patch.plugin);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { proxyLazy } from "@utils/lazy";
|
import { proxyLazy } from "@utils/lazy";
|
||||||
|
import { LazyComponent } from "@utils/lazyReact";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import type { WebpackInstance } from "discord-types/other";
|
import type { WebpackInstance } from "discord-types/other";
|
||||||
|
|
||||||
|
@ -51,7 +52,18 @@ export const filters = {
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
byStoreName: (name: string): FilterFn => m =>
|
byStoreName: (name: string): FilterFn => m =>
|
||||||
m.constructor?.displayName === name
|
m.constructor?.displayName === name,
|
||||||
|
|
||||||
|
componentByCode: (...code: string[]): FilterFn => {
|
||||||
|
const filter = filters.byCode(...code);
|
||||||
|
return m => {
|
||||||
|
if (filter(m)) return true;
|
||||||
|
if (!m.$$typeof) return false;
|
||||||
|
if (m.type) return filter(m.type); // memos
|
||||||
|
if (m.render) return filter(m.render); // forwardRefs
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const subscriptions = new Map<FilterFn, CallbackFn>();
|
export const subscriptions = new Map<FilterFn, CallbackFn>();
|
||||||
|
@ -67,44 +79,6 @@ export function _initWebpack(instance: typeof window.webpackChunkdiscord_app) {
|
||||||
if (!wreq) return false;
|
if (!wreq) return false;
|
||||||
|
|
||||||
cache = wreq.c;
|
cache = wreq.c;
|
||||||
|
|
||||||
for (const id in cache) {
|
|
||||||
const { exports } = cache[id];
|
|
||||||
if (!exports) continue;
|
|
||||||
|
|
||||||
const numberId = Number(id);
|
|
||||||
|
|
||||||
for (const callback of listeners) {
|
|
||||||
try {
|
|
||||||
callback(exports, numberId);
|
|
||||||
} catch (err) {
|
|
||||||
logger.error("Error in webpack listener", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [filter, callback] of subscriptions) {
|
|
||||||
try {
|
|
||||||
if (filter(exports)) {
|
|
||||||
subscriptions.delete(filter);
|
|
||||||
callback(exports, numberId);
|
|
||||||
} else if (typeof exports === "object") {
|
|
||||||
if (exports.default && filter(exports.default)) {
|
|
||||||
subscriptions.delete(filter);
|
|
||||||
callback(exports.default, numberId);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const nested in exports) if (nested.length <= 3) {
|
|
||||||
if (exports[nested] && filter(exports[nested])) {
|
|
||||||
subscriptions.delete(filter);
|
|
||||||
callback(exports[nested], numberId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
logger.error("Error while firing callback for webpack chunk", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,20 +114,10 @@ export const find = traceFunction("find", function find(filter: FilterFn, { isIn
|
||||||
return isWaitFor ? [mod.exports, Number(key)] : mod.exports;
|
return isWaitFor ? [mod.exports, Number(key)] : mod.exports;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof mod.exports !== "object") continue;
|
|
||||||
|
|
||||||
if (mod.exports.default && filter(mod.exports.default)) {
|
if (mod.exports.default && filter(mod.exports.default)) {
|
||||||
const found = mod.exports.default;
|
const found = mod.exports.default;
|
||||||
return isWaitFor ? [found, Number(key)] : found;
|
return isWaitFor ? [found, Number(key)] : found;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the length check makes search about 20% faster
|
|
||||||
for (const nestedMod in mod.exports) if (nestedMod.length <= 3) {
|
|
||||||
const nested = mod.exports[nestedMod];
|
|
||||||
if (nested && filter(nested)) {
|
|
||||||
return isWaitFor ? [nested, Number(key)] : nested;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isIndirect) {
|
if (!isIndirect) {
|
||||||
|
@ -163,13 +127,6 @@ export const find = traceFunction("find", function find(filter: FilterFn, { isIn
|
||||||
return isWaitFor ? [null, null] : null;
|
return isWaitFor ? [null, null] : null;
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* find but lazy
|
|
||||||
*/
|
|
||||||
export function findLazy(filter: FilterFn) {
|
|
||||||
return proxyLazy(() => find(filter));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function findAll(filter: FilterFn) {
|
export function findAll(filter: FilterFn) {
|
||||||
if (typeof filter !== "function")
|
if (typeof filter !== "function")
|
||||||
throw new Error("Invalid filter. Expected a function got " + typeof filter);
|
throw new Error("Invalid filter. Expected a function got " + typeof filter);
|
||||||
|
@ -181,15 +138,9 @@ export function findAll(filter: FilterFn) {
|
||||||
|
|
||||||
if (filter(mod.exports))
|
if (filter(mod.exports))
|
||||||
ret.push(mod.exports);
|
ret.push(mod.exports);
|
||||||
else if (typeof mod.exports !== "object")
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (mod.exports.default && filter(mod.exports.default))
|
if (mod.exports.default && filter(mod.exports.default))
|
||||||
ret.push(mod.exports.default);
|
ret.push(mod.exports.default);
|
||||||
else for (const nestedMod in mod.exports) if (nestedMod.length <= 3) {
|
|
||||||
const nested = mod.exports[nestedMod];
|
|
||||||
if (nested && filter(nested)) ret.push(nested);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -239,26 +190,12 @@ export const findBulk = traceFunction("findBulk", function findBulk(...filterFns
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof mod.exports !== "object")
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (mod.exports.default && filter(mod.exports.default)) {
|
if (mod.exports.default && filter(mod.exports.default)) {
|
||||||
results[j] = mod.exports.default;
|
results[j] = mod.exports.default;
|
||||||
filters[j] = undefined;
|
filters[j] = undefined;
|
||||||
if (++found === length) break outer;
|
if (++found === length) break outer;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const nestedMod in mod.exports)
|
|
||||||
if (nestedMod.length <= 3) {
|
|
||||||
const nested = mod.exports[nestedMod];
|
|
||||||
if (nested && filter(nested)) {
|
|
||||||
results[j] = nested;
|
|
||||||
filters[j] = undefined;
|
|
||||||
if (++found === length) break outer;
|
|
||||||
continue outer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,45 +237,47 @@ export const findModuleId = traceFunction("findModuleId", function findModuleId(
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const lazyWebpackSearchHistory = [] as Array<["find" | "findByProps" | "findByCode" | "findStore" | "findComponent" | "findComponentByCode" | "findExportedComponent" | "waitFor" | "waitForComponent" | "waitForStore" | "proxyLazyWebpack" | "LazyComponentWebpack", any[]]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds a mangled module by the provided code "code" (must be unique and can be anywhere in the module)
|
* This is just a wrapper around {@link proxyLazy} to make our reporter test for your webpack finds.
|
||||||
* then maps it into an easily usable module via the specified mappers
|
|
||||||
* @param code Code snippet
|
|
||||||
* @param mappers Mappers to create the non mangled exports
|
|
||||||
* @returns Unmangled exports as specified in mappers
|
|
||||||
*
|
*
|
||||||
* @example mapMangledModule("headerIdIsManaged:", {
|
* Wraps the result of {@link makeLazy} in a Proxy you can consume as if it wasn't lazy.
|
||||||
* openModal: filters.byCode("headerIdIsManaged:"),
|
* On first property access, the lazy is evaluated
|
||||||
* closeModal: filters.byCode("key==")
|
* @param factory lazy factory
|
||||||
* })
|
* @param attempts how many times to try to evaluate the lazy before giving up
|
||||||
|
* @returns Proxy
|
||||||
|
*
|
||||||
|
* Note that the example below exists already as an api, see {@link findByPropsLazy}
|
||||||
|
* @example const mod = proxyLazy(() => findByProps("blah")); console.log(mod.blah);
|
||||||
*/
|
*/
|
||||||
export const mapMangledModule = traceFunction("mapMangledModule", function mapMangledModule<S extends string>(code: string, mappers: Record<S, FilterFn>): Record<S, any> {
|
export function proxyLazyWebpack<T = any>(factory: () => any, attempts?: number) {
|
||||||
const exports = {} as Record<S, any>;
|
if (IS_DEV) lazyWebpackSearchHistory.push(["proxyLazyWebpack", [factory]]);
|
||||||
|
|
||||||
const id = findModuleId(code);
|
return proxyLazy<T>(factory, attempts);
|
||||||
if (id === null)
|
}
|
||||||
return exports;
|
|
||||||
|
|
||||||
const mod = wreq(id);
|
|
||||||
outer:
|
|
||||||
for (const key in mod) {
|
|
||||||
const member = mod[key];
|
|
||||||
for (const newName in mappers) {
|
|
||||||
// if the current mapper matches this module
|
|
||||||
if (mappers[newName](member)) {
|
|
||||||
exports[newName] = member;
|
|
||||||
continue outer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return exports;
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as {@link mapMangledModule} but lazy
|
* This is just a wrapper around {@link LazyComponent} to make our reporter test for your webpack finds.
|
||||||
|
*
|
||||||
|
* A lazy component. The factory method is called on first render.
|
||||||
|
* @param factory Function returning a Component
|
||||||
|
* @param attempts How many times to try to get the component before giving up
|
||||||
|
* @returns Result of factory function
|
||||||
*/
|
*/
|
||||||
export function mapMangledModuleLazy<S extends string>(code: string, mappers: Record<S, FilterFn>): Record<S, any> {
|
export function LazyComponentWebpack<T extends object = any>(factory: () => any, attempts?: number) {
|
||||||
return proxyLazy(() => mapMangledModule(code, mappers));
|
if (IS_DEV) lazyWebpackSearchHistory.push(["LazyComponentWebpack", [factory]]);
|
||||||
|
|
||||||
|
return LazyComponent<T>(factory, attempts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* find but lazy
|
||||||
|
*/
|
||||||
|
export function findLazy(filter: FilterFn) {
|
||||||
|
if (IS_DEV) lazyWebpackSearchHistory.push(["find", [filter]]);
|
||||||
|
|
||||||
|
return proxyLazy(() => find(filter));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -355,6 +294,8 @@ export function findByProps(...props: string[]) {
|
||||||
* findByProps but lazy
|
* findByProps but lazy
|
||||||
*/
|
*/
|
||||||
export function findByPropsLazy(...props: string[]) {
|
export function findByPropsLazy(...props: string[]) {
|
||||||
|
if (IS_DEV) lazyWebpackSearchHistory.push(["findByProps", props]);
|
||||||
|
|
||||||
return proxyLazy(() => findByProps(...props));
|
return proxyLazy(() => findByProps(...props));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,6 +313,8 @@ export function findByCode(...code: string[]) {
|
||||||
* findByCode but lazy
|
* findByCode but lazy
|
||||||
*/
|
*/
|
||||||
export function findByCodeLazy(...code: string[]) {
|
export function findByCodeLazy(...code: string[]) {
|
||||||
|
if (IS_DEV) lazyWebpackSearchHistory.push(["findByCode", code]);
|
||||||
|
|
||||||
return proxyLazy(() => findByCode(...code));
|
return proxyLazy(() => findByCode(...code));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -386,17 +329,58 @@ export function findStore(name: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* findByDisplayName but lazy
|
* findStore but lazy
|
||||||
*/
|
*/
|
||||||
export function findStoreLazy(name: string) {
|
export function findStoreLazy(name: string) {
|
||||||
|
if (IS_DEV) lazyWebpackSearchHistory.push(["findStore", [name]]);
|
||||||
|
|
||||||
return proxyLazy(() => findStore(name));
|
return proxyLazy(() => findStore(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the component which includes all the given code. Checks for plain components, memos and forwardRefs
|
||||||
|
*/
|
||||||
|
export function findComponentByCode(...code: string[]) {
|
||||||
|
const res = find(filters.componentByCode(...code), { isIndirect: true });
|
||||||
|
if (!res)
|
||||||
|
handleModuleNotFound("findComponentByCode", ...code);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the first component that matches the filter, lazily.
|
||||||
|
*/
|
||||||
|
export function findComponentLazy<T extends object = any>(filter: FilterFn) {
|
||||||
|
if (IS_DEV) lazyWebpackSearchHistory.push(["findComponent", [filter]]);
|
||||||
|
|
||||||
|
return LazyComponent<T>(() => find(filter));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the first component that includes all the given code, lazily
|
||||||
|
*/
|
||||||
|
export function findComponentByCodeLazy<T extends object = any>(...code: string[]) {
|
||||||
|
if (IS_DEV) lazyWebpackSearchHistory.push(["findComponentByCode", code]);
|
||||||
|
|
||||||
|
return LazyComponent<T>(() => findComponentByCode(...code));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the first component that is exported by the first prop name, lazily
|
||||||
|
*/
|
||||||
|
export function findExportedComponentLazy<T extends object = any>(...props: string[]) {
|
||||||
|
if (IS_DEV) lazyWebpackSearchHistory.push(["findExportedComponent", props]);
|
||||||
|
|
||||||
|
return LazyComponent<T>(() => findByProps(...props)?.[props[0]]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait for a module that matches the provided filter to be registered,
|
* Wait for a module that matches the provided filter to be registered,
|
||||||
* then call the callback with the module as the first argument
|
* then call the callback with the module as the first argument
|
||||||
*/
|
*/
|
||||||
export function waitFor(filter: string | string[] | FilterFn, callback: CallbackFn) {
|
export function waitFor(filter: string | string[] | FilterFn, callback: CallbackFn, { isIndirect = false }: { isIndirect?: boolean; } = {}) {
|
||||||
|
if (IS_DEV && !isIndirect) lazyWebpackSearchHistory.push(["waitFor", Array.isArray(filter) ? filter : [filter]]);
|
||||||
|
|
||||||
if (typeof filter === "string")
|
if (typeof filter === "string")
|
||||||
filter = filters.byProps(filter);
|
filter = filters.byProps(filter);
|
||||||
else if (Array.isArray(filter))
|
else if (Array.isArray(filter))
|
||||||
|
|
Loading…
Reference in a new issue