diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 3f57be93..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "root": true, - "parser": "@typescript-eslint/parser", - "ignorePatterns": [ "dist", "browser" ], - "plugins": [ - "@typescript-eslint", - "simple-import-sort", - "unused-imports", - "import-alias" - ], - "rules": { - "no-restricted-syntax": [ "error", { - "selector": "AwaitExpression:not(:function *)", - "message": "Hermes does not support top-level await, and SWC cannot transform it." - } ], - "quotes": [ "error", "double", { "avoidEscape": true } ], - "jsx-quotes": [ "error", "prefer-double" ], - "no-mixed-spaces-and-tabs": "error", - "indent": [ "error", 4, { "SwitchCase": 1 } ], - "arrow-parens": [ "error", "as-needed" ], - "eol-last": [ "error", "always" ], - "func-call-spacing": [ "error", "never" ], - "no-multi-spaces": "error", - "no-trailing-spaces": "error", - "no-whitespace-before-property": "error", - "semi": [ "error", "always" ], - "semi-style": [ "error", "last" ], - "space-in-parens": [ "error", "never" ], - "block-spacing": [ "error", "always" ], - "object-curly-spacing": [ "error", "always" ], - "eqeqeq": [ "error", "always", { "null": "ignore" } ], - "spaced-comment": [ "error", "always", { "markers": [ "!" ] } ], - "yoda": "error", - "prefer-destructuring": [ "error", { "object": true, "array": false } ], - "operator-assignment": [ "error", "always" ], - "no-useless-computed-key": "error", - "no-unneeded-ternary": [ "error", { "defaultAssignment": false } ], - "no-invalid-regexp": "error", - "no-constant-condition": [ "error", { "checkLoops": false } ], - "no-duplicate-imports": "error", - "no-extra-semi": "error", - "dot-notation": "error", - "no-useless-escape": [ "error" ], - "no-fallthrough": "error", - "for-direction": "error", - "no-async-promise-executor": "error", - "no-cond-assign": "error", - "no-dupe-else-if": "error", - "no-duplicate-case": "error", - "no-irregular-whitespace": "error", - "no-loss-of-precision": "error", - "no-misleading-character-class": "error", - "no-prototype-builtins": "error", - "no-regex-spaces": "error", - "no-shadow-restricted-names": "error", - "no-unexpected-multiline": "error", - "no-unsafe-optional-chaining": "error", - "no-useless-backreference": "error", - "use-isnan": "error", - "prefer-const": "error", - "prefer-spread": "error", - "simple-import-sort/imports": "error", - "simple-import-sort/exports": "error", - "unused-imports/no-unused-imports": "error", - "import-alias/import-alias": [ - "error", - { - "relativeDepth": 0, - "aliases": [ - { "alias": "@metro", "matcher": "^src/lib/metro" }, - { "alias": "@core", "matcher": "^src/core" }, - { "alias": "@ui", "matcher": "^src/lib/ui" }, - { "alias": "@types", "matcher": "^src/lib/utils/types.ts" }, - { "alias": "@lib", "matcher": "^src/lib" } - ] - } - ] - } -} diff --git a/.vscode/settings.json b/.vscode/settings.json index 3f012b84..4c58de99 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,18 +1,18 @@ { - "editor.codeActionsOnSave": { - "source.fixAll.eslint": "explicit" - }, - "[typescript]": { - "editor.defaultFormatter": "vscode.typescript-language-features" - }, - "[typescriptreact]": { - "editor.defaultFormatter": "vscode.typescript-language-features" - }, - "[json]": { - "editor.formatOnSave": false - }, - "javascript.format.semicolons": "insert", - "typescript.format.semicolons": "insert", - "typescript.preferences.quoteStyle": "double", - "javascript.preferences.quoteStyle": "double" -} \ No newline at end of file + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + }, + "[typescript]": { + "editor.defaultFormatter": "vscode.typescript-language-features" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "vscode.typescript-language-features" + }, + "[json]": { + "editor.formatOnSave": false + }, + "javascript.format.semicolons": "insert", + "typescript.format.semicolons": "insert", + "typescript.preferences.quoteStyle": "double", + "javascript.preferences.quoteStyle": "double" +} diff --git a/biome.json b/biome.json new file mode 100644 index 00000000..e3ee0c07 --- /dev/null +++ b/biome.json @@ -0,0 +1,64 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.8.2/schema.json", + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "a11y": { + "useKeyWithClickEvents": { + "level": "off" + } + }, + "style": { + "noNonNullAssertion": { + "level": "off" + } + }, + "suspicious": { + "noAssignInExpressions": { + "level": "off" + }, + "noExplicitAny": { + "level": "warn" + } + }, + "correctness": { + "useExhaustiveDependencies": { + "level": "warn" + } + } + } + }, + "json": { + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2 + }, + "parser": { + "allowComments": true + } + }, + "javascript": { + "formatter": { + "enabled": true, + "lineEnding": "crlf", + "arrowParentheses": "always", + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 120, + "quoteProperties": "asNeeded", + "quoteStyle": "double", + "semicolons": "always", + "trailingCommas": "all" + } + }, + "files": { + "ignoreUnknown": true, + "include": ["*.js", "*.json", "*.ts", "*.tsx", "*.jsx", "*.mjs"], + "ignore": ["dist/**/*", "node_modules/**/*"] + } +} diff --git a/package.json b/package.json index 3e36b4a6..02704705 100644 --- a/package.json +++ b/package.json @@ -1,58 +1,48 @@ { - "name": "bunny", - "private": true, - "author": { - "name": "Pyoncord Team", - "url": "https://github.com/pyoncord" - }, - "scripts": { - "build": "node scripts/build.mjs", - "serve": "node scripts/serve.mjs", - "serve:adb": "pnpm run serve --adb", - "lint": "eslint ./src --ext .js,.jsx,.ts,.tsx" - }, - "license": "BSD-3-Clause", - "devDependencies": { - "@swc/core": "^1.7.0", - "@swc/helpers": "^0.5.12", - "@types/node": "^20.14.11", - "@types/yargs-parser": "^21.0.3", - "@typescript-eslint/eslint-plugin": "^7.16.1", - "@typescript-eslint/parser": "^7.16.1", - "@typescript-eslint/typescript-estree": "^7.16.1", - "chalk": "^5.3.0", - "esbuild": "^0.20.2", - "esbuild-plugin-globals": "^0.2.0", - "eslint": "^8.57.0", - "eslint-plugin-import-alias": "^1.2.0", - "eslint-plugin-react": "^7.35.0", - "eslint-plugin-simple-import-sort": "^12.1.1", - "eslint-plugin-unused-imports": "^3.2.0", - "typescript": "^5.5.3", - "yargs-parser": "^21.1.1" - }, - "dependencies": { - "@gullerya/object-observer": "^6.1.3", - "@shopify/react-native-skia": "^1.3.8", - "@tanstack/react-query": "^5.51.16", - "@types/chroma-js": "~2.4.4", - "@types/lodash": "~4.17.7", - "@types/react": "18.2.60", - "@types/react-native": "0.72.3", - "es-toolkit": "^1.13.1", - "fuzzysort": "^3.0.2", - "intl-messageformat": "^10.5.14", - "moment": "2.22.2", - "react-native-reanimated": "^3.6.2", - "spitroast": "^1.4.4", - "type-fest": "^4.22.1" - }, - "pnpm": { - "peerDependencyRules": { - "ignoreMissing": [ - "react", - "react-native" - ] - } + "name": "bunny", + "private": true, + "author": { + "name": "Pyoncord Team", + "url": "https://github.com/pyoncord" + }, + "scripts": { + "build": "node scripts/build.mjs", + "serve": "node scripts/serve.mjs", + "serve:adb": "pnpm run serve --adb", + "lint": "eslint ./src --ext .js,.jsx,.ts,.tsx" + }, + "license": "BSD-3-Clause", + "devDependencies": { + "@biomejs/biome": "^1.9.0", + "@swc/core": "^1.7.26", + "@swc/helpers": "^0.5.13", + "@types/node": "^20.16.5", + "@types/yargs-parser": "^21.0.3", + "chalk": "^5.3.0", + "esbuild": "^0.20.2", + "esbuild-plugin-globals": "^0.2.0", + "typescript": "^5.6.2", + "yargs-parser": "^21.1.1" + }, + "dependencies": { + "@gullerya/object-observer": "^6.1.3", + "@shopify/react-native-skia": "^1.3.8", + "@tanstack/react-query": "^5.51.16", + "@types/chroma-js": "~2.4.4", + "@types/lodash": "~4.17.7", + "@types/react": "18.2.60", + "@types/react-native": "0.72.3", + "es-toolkit": "^1.13.1", + "fuzzysort": "^3.0.2", + "intl-messageformat": "^10.5.14", + "moment": "2.22.2", + "react-native-reanimated": "^3.6.2", + "spitroast": "^1.4.4", + "type-fest": "^4.22.1" + }, + "pnpm": { + "peerDependencyRules": { + "ignoreMissing": ["react", "react-native"] } -} \ No newline at end of file + } +} diff --git a/scripts/adb.mjs b/scripts/adb.mjs index 6163448e..a7a093dc 100644 --- a/scripts/adb.mjs +++ b/scripts/adb.mjs @@ -1,5 +1,5 @@ // @ts-nocheck -import { execSync } from "child_process"; +import { execSync } from "node:child_process"; const packageName = process.env.DISCORD_PACKAGE_NAME ?? "com.discord"; diff --git a/scripts/build.mjs b/scripts/build.mjs index 532fd979..441ff138 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -1,12 +1,12 @@ +import { execSync } from "node:child_process"; +import crypto from "node:crypto"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; // @ts-nocheck /* eslint-disable no-restricted-syntax */ import swc from "@swc/core"; -import { execSync } from "child_process"; -import crypto from "crypto"; import { build } from "esbuild"; import globalPlugin from "esbuild-plugin-globals"; -import path from "path"; -import { fileURLToPath } from "url"; import yargs from "yargs-parser"; import { printBuildSuccess } from "./util.mjs"; @@ -14,14 +14,11 @@ import { printBuildSuccess } from "./util.mjs"; /** @type string[] */ const metroDeps = await (async () => { const ast = await swc.parseFile(path.resolve("./shims/depsModule.ts")); - return ast.body.at(-1).expression.right.properties.map(p => p.key.value); + return ast.body.at(-1).expression.right.properties.map((p) => p.key.value); })(); const args = yargs(process.argv.slice(2)); -const { - "release-branch": releaseBranch, - "build-minify": buildMinify -} = args; +const { "release-branch": releaseBranch, "build-minify": buildMinify } = args; let context = null; @@ -35,35 +32,35 @@ const config = { supported: { // Hermes does not actually support const and let, even though it syntactically // accepts it, but it's treated just like 'var' and causes issues - "const-and-let": false + "const-and-let": false, }, footer: { - js: "//# sourceURL=bunny" + js: "//# sourceURL=bunny", }, loader: { - ".png": "dataurl" + ".png": "dataurl", }, define: { - __DEV__: JSON.stringify(releaseBranch !== "main") + __DEV__: JSON.stringify(releaseBranch !== "main"), }, inject: ["./shims/asyncIteratorSymbol.js", "./shims/promiseAllSettled.js"], legalComments: "none", alias: { "!bunny-deps-shim!": "./shims/depsModule.ts", - "spitroast": "./node_modules/spitroast", - "react/jsx-runtime": "./shims/jsxRuntime" + spitroast: "./node_modules/spitroast", + "react/jsx-runtime": "./shims/jsxRuntime", }, plugins: [ globalPlugin({ ...metroDeps.reduce((obj, key) => { obj[key] = `require("!bunny-deps-shim!")[${JSON.stringify(key)}]`; return obj; - }, {}) + }, {}), }), { name: "swc", setup(build) { - build.onLoad({ filter: /\.[cm]?[jt]sx?$/ }, async args => { + build.onLoad({ filter: /\.[cm]?[jt]sx?$/ }, async (args) => { const result = await swc.transformFile(args.path, { jsc: { externalHelpers: true, @@ -71,13 +68,13 @@ const config = { constModules: { globals: { "bunny-build-info": { - version: `"${context.hash}-${releaseBranch ?? "local"}"` - } - } + version: `"${context.hash}-${releaseBranch ?? "local"}"`, + }, + }, }, react: { - runtime: "automatic" - } + runtime: "automatic", + }, }, }, // https://github.com/facebook/hermes/blob/3815fec63d1a6667ca3195160d6e12fee6a0d8d5/doc/Features.md @@ -88,7 +85,7 @@ const config = { "transform-block-scoping", "transform-classes", "transform-async-to-generator", - "transform-async-generator-functions" + "transform-async-generator-functions", ], exclude: [ "transform-parameters", @@ -98,21 +95,23 @@ const config = { "transform-nullish-coalescing-operator", "transform-object-rest-spread", "transform-optional-chaining", - "transform-logical-assignment-operators" - ] + "transform-logical-assignment-operators", + ], }, }); return { contents: result.code }; }); - } - } - ] + }, + }, + ], }; export async function buildBundle(overrideConfig = {}) { context = { - hash: releaseBranch ? execSync("git rev-parse --short HEAD").toString().trim() : crypto.randomBytes(8).toString("hex").slice(0, 7) + hash: releaseBranch + ? execSync("git rev-parse --short HEAD").toString().trim() + : crypto.randomBytes(8).toString("hex").slice(0, 7), }; const initialStartTime = performance.now(); @@ -121,7 +120,7 @@ export async function buildBundle(overrideConfig = {}) { return { config, context, - timeTook: performance.now() - initialStartTime + timeTook: performance.now() - initialStartTime, }; } @@ -132,23 +131,14 @@ const isThisFileBeingRunViaCLI = pathToThisFile.includes(pathPassedToNode); if (isThisFileBeingRunViaCLI) { const { timeTook } = await buildBundle(); - printBuildSuccess( - context.hash, - releaseBranch, - timeTook - ); + printBuildSuccess(context.hash, releaseBranch, timeTook); if (buildMinify) { const { timeTook } = await buildBundle({ minify: true, - outfile: config.outfile.replace(/\.js$/, ".min.js") + outfile: config.outfile.replace(/\.js$/, ".min.js"), }); - printBuildSuccess( - context.hash, - releaseBranch, - timeTook, - true - ); + printBuildSuccess(context.hash, releaseBranch, timeTook, true); } } diff --git a/scripts/serve.mjs b/scripts/serve.mjs index addcfd85..d321e01a 100644 --- a/scripts/serve.mjs +++ b/scripts/serve.mjs @@ -1,10 +1,10 @@ +import { readFile } from "node:fs/promises"; +import http from "node:http"; +import os from "node:os"; +import readline from "node:readline"; +import url from "node:url"; // @ts-nocheck import chalk from "chalk"; -import { readFile } from "fs/promises"; -import http from "http"; -import os from "os"; -import readline from "readline"; -import url from "url"; import yargs from "yargs-parser"; import { forceStopAppFromADB, getPackageName, isADBAvailableAndAppInstalled, restartAppFromADB } from "./adb.mjs"; @@ -21,11 +21,7 @@ export function serve(options) { try { const { config, context, timeTook } = await buildBundle(); - printBuildSuccess( - context.hash, - args.production, - timeTook - ); + printBuildSuccess(context.hash, args.production, timeTook); res.writeHead(200, { "Content-Type": "application/javascript" }); res.end(await readFile(config.outfile, "utf-8")); @@ -74,7 +70,7 @@ if (args.adb && isADBAvailableAndAppInstalled()) { process.stdin.on("keypress", (ch, key) => { if (!key) return; - if (key.name === "q" || key.ctrl && key.name === "c") { + if (key.name === "q" || (key.ctrl && key.name === "c")) { process.exit(0); } @@ -82,14 +78,14 @@ if (args.adb && isADBAvailableAndAppInstalled()) { console.info(chalk.yellow(`${chalk.bold("↻ Reloading")} ${packageName}`)); restartAppFromADB(server.port) .then(() => console.info(chalk.greenBright(`${chalk.bold("✔ Executed")} reload command`))) - .catch(e => console.error(e)); + .catch((e) => console.error(e)); } if (key.name === "s") { console.info(chalk.yellow(`${chalk.bold("⎊ Force stopping")} ${packageName}`)); forceStopAppFromADB() .then(() => console.info(chalk.greenBright(`${chalk.bold("✔ Executed")} force stop command`))) - .catch(e => console.error(e)); + .catch((e) => console.error(e)); } }); } else if (args.adb) { diff --git a/scripts/util.mjs b/scripts/util.mjs index a0148dc6..24843bd4 100644 --- a/scripts/util.mjs +++ b/scripts/util.mjs @@ -1,16 +1,15 @@ // @ts-nocheck import chalk from "chalk"; -export function printBuildSuccess( - hash, - branch, - timeTook, - minified -) { - console.info([ - chalk.bold.greenBright("✔ Built bundle" + (minified ? " (minified)" : "")), - hash && chalk.bold.blueBright(`(${hash})`), - !branch && chalk.bold.cyanBright("(local)"), - timeTook && chalk.gray(`in ${timeTook.toFixed(3)}ms`) - ].filter(Boolean).join(" ")); +export function printBuildSuccess(hash, branch, timeTook, minified) { + console.info( + [ + chalk.bold.greenBright(`✔ Built bundle${minified ? " (minified)" : ""}`), + hash && chalk.bold.blueBright(`(${hash})`), + !branch && chalk.bold.cyanBright("(local)"), + timeTook && chalk.gray(`in ${timeTook.toFixed(3)}ms`), + ] + .filter(Boolean) + .join(" "), + ); } diff --git a/shims/asyncIteratorSymbol.js b/shims/asyncIteratorSymbol.js index 28c8f81d..2e7de365 100644 --- a/shims/asyncIteratorSymbol.js +++ b/shims/asyncIteratorSymbol.js @@ -2,4 +2,4 @@ const asyncIteratorSymbol = Symbol("Symbol.asyncIterator"); // your editor may yell at you for invalid syntax here, but this is a valid JS syntax! -export { asyncIteratorSymbol as "Symbol.asyncIterator" }; \ No newline at end of file +export { asyncIteratorSymbol as "Symbol.asyncIterator" }; diff --git a/shims/depsModule.ts b/shims/depsModule.ts index 8df1d12b..83ca0fdf 100644 --- a/shims/depsModule.ts +++ b/shims/depsModule.ts @@ -1,11 +1,11 @@ import { findByPropsLazy } from "@metro/wrappers"; module.exports = { - "react": findByPropsLazy("createElement"), + react: findByPropsLazy("createElement"), "react-native": findByPropsLazy("AppRegistry"), - "util": findByPropsLazy("inspect", "isNullOrUndefined"), - "moment": findByPropsLazy("isMoment"), + util: findByPropsLazy("inspect", "isNullOrUndefined"), + moment: findByPropsLazy("isMoment"), "chroma-js": findByPropsLazy("brewer"), - "lodash": findByPropsLazy("forEachRight"), - "@shopify/react-native-skia": findByPropsLazy("useFont") + lodash: findByPropsLazy("forEachRight"), + "@shopify/react-native-skia": findByPropsLazy("useFont"), }; diff --git a/shims/promiseAllSettled.js b/shims/promiseAllSettled.js index 5edeb213..53e22076 100644 --- a/shims/promiseAllSettled.js +++ b/shims/promiseAllSettled.js @@ -1,12 +1,12 @@ // @ts-nocheck -const allSettledFulfill = value => ({ status: "fulfilled", value }); -const allSettledReject = reason => ({ status: "rejected", reason }); -const mapAllSettled = item => Promise.resolve(item).then(allSettledFulfill, allSettledReject); +const allSettledFulfill = (value) => ({ status: "fulfilled", value }); +const allSettledReject = (reason) => ({ status: "rejected", reason }); +const mapAllSettled = (item) => Promise.resolve(item).then(allSettledFulfill, allSettledReject); -const allSettled = Promise.allSettled ??= iterator => { +const allSettled = (Promise.allSettled ??= (iterator) => { return Promise.all(Array.from(iterator).map(mapAllSettled)); -}; +}); // Your editor may yell at you for this, but this is alright! It's a valid JS syntax export { allSettled as "Promise.allSettled" }; diff --git a/src/core/commands/debug.ts b/src/core/commands/debug.ts index fcbd166f..7b99ab71 100644 --- a/src/core/commands/debug.ts +++ b/src/core/commands/debug.ts @@ -1,34 +1,35 @@ import { Strings } from "@core/i18n"; -import { ApplicationCommand, ApplicationCommandOptionType } from "@lib/api/commands/types"; +import { type ApplicationCommand, ApplicationCommandOptionType } from "@lib/api/commands/types"; import { getDebugInfo } from "@lib/api/debug"; import { messageUtil } from "@metro/common"; -export default () => { - name: "debug", - description: Strings.COMMAND_DEBUG_DESC, - options: [ - { - name: "ephemeral", - type: ApplicationCommandOptionType.BOOLEAN, - description: Strings.COMMAND_DEBUG_OPT_EPHEMERALLY, - } - ], - execute([ephemeral], ctx) { - const info = getDebugInfo(); - const content = [ - "**Bunny Debug Info**", - `> Bunny: ${info.bunny.version} (${info.bunny.loader.name} ${info.bunny.loader.version})`, - `> Discord: ${info.discord.version} (${info.discord.build})`, - `> React: ${info.react.version} (RN ${info.react.nativeVersion})`, - `> Hermes: ${info.hermes.version} (bcv${info.hermes.bytecodeVersion})`, - `> System: ${info.os.name} ${info.os.version} ${info.os.sdk ? `(SDK ${info.os.sdk})` : ""}`.trimEnd(), - `> Device: ${info.device.model} (${info.device.codename})`, - ].join("\n"); +export default () => + { + name: "debug", + description: Strings.COMMAND_DEBUG_DESC, + options: [ + { + name: "ephemeral", + type: ApplicationCommandOptionType.BOOLEAN, + description: Strings.COMMAND_DEBUG_OPT_EPHEMERALLY, + }, + ], + execute([ephemeral], ctx) { + const info = getDebugInfo(); + const content = [ + "**Bunny Debug Info**", + `> Bunny: ${info.bunny.version} (${info.bunny.loader.name} ${info.bunny.loader.version})`, + `> Discord: ${info.discord.version} (${info.discord.build})`, + `> React: ${info.react.version} (RN ${info.react.nativeVersion})`, + `> Hermes: ${info.hermes.version} (bcv${info.hermes.bytecodeVersion})`, + `> System: ${info.os.name} ${info.os.version} ${info.os.sdk ? `(SDK ${info.os.sdk})` : ""}`.trimEnd(), + `> Device: ${info.device.model} (${info.device.codename})`, + ].join("\n"); - if (ephemeral?.value) { - messageUtil.sendBotMessage(ctx.channel.id, content); - } else { - messageUtil.sendMessage(ctx.channel.id, { content }); - } - } -}; + if (ephemeral?.value) { + messageUtil.sendBotMessage(ctx.channel.id, content); + } else { + messageUtil.sendMessage(ctx.channel.id, { content }); + } + }, + }; diff --git a/src/core/commands/eval.ts b/src/core/commands/eval.ts index 071019c2..a3cf755a 100644 --- a/src/core/commands/eval.ts +++ b/src/core/commands/eval.ts @@ -1,6 +1,6 @@ import { Strings } from "@core/i18n"; import BunnySettings from "@core/storage/BunnySettings"; -import { ApplicationCommand, ApplicationCommandOptionType } from "@lib/api/commands/types"; +import { type ApplicationCommand, ApplicationCommandOptionType } from "@lib/api/commands/types"; import { messageUtil } from "@metro/common"; import { findByPropsLazy } from "@metro/wrappers"; @@ -10,34 +10,35 @@ const AsyncFunction = (async () => void 0).constructor; const ZERO_WIDTH_SPACE_CHARACTER = "\u200B"; function wrapInJSCodeblock(resString: string) { - return "```js\n" + resString.replaceAll("`", "`" + ZERO_WIDTH_SPACE_CHARACTER) + "\n```"; + return `\`\`\`js\n${resString.replaceAll("`", `\`${ZERO_WIDTH_SPACE_CHARACTER}`)}\n\`\`\``; } -export default () => { - name: "eval", - description: Strings.COMMAND_EVAL_DESC, - shouldHide: () => BunnySettings.developer.evalCommandEnabled === true, - options: [ - { - name: "code", - type: ApplicationCommandOptionType.STRING, - description: Strings.COMMAND_EVAL_OPT_CODE, - required: true - }, - { - name: "async", - type: ApplicationCommandOptionType.BOOLEAN, - description: Strings.COMMAND_EVAL_OPT_ASYNC, - } - ], - async execute([code, async], ctx) { - try { - const res = util.inspect(async?.value ? await AsyncFunction(code.value)() : eval?.(code.value)); - const trimmedRes = res.length > 2000 ? res.slice(0, 2000) + "..." : res; +export default () => + { + name: "eval", + description: Strings.COMMAND_EVAL_DESC, + shouldHide: () => BunnySettings.developer.evalCommandEnabled === true, + options: [ + { + name: "code", + type: ApplicationCommandOptionType.STRING, + description: Strings.COMMAND_EVAL_OPT_CODE, + required: true, + }, + { + name: "async", + type: ApplicationCommandOptionType.BOOLEAN, + description: Strings.COMMAND_EVAL_OPT_ASYNC, + }, + ], + async execute([code, async], ctx) { + try { + const res = util.inspect(async?.value ? await AsyncFunction(code.value)() : eval?.(code.value)); + const trimmedRes = res.length > 2000 ? `${res.slice(0, 2000)}...` : res; - messageUtil.sendBotMessage(ctx.channel.id, wrapInJSCodeblock(trimmedRes)); - } catch (err: any) { - messageUtil.sendBotMessage(ctx.channel.id, wrapInJSCodeblock(err?.stack ?? err)); - } - } -}; + messageUtil.sendBotMessage(ctx.channel.id, wrapInJSCodeblock(trimmedRes)); + } catch (err: any) { + messageUtil.sendBotMessage(ctx.channel.id, wrapInJSCodeblock(err?.stack ?? err)); + } + }, + }; diff --git a/src/core/commands/plugins.ts b/src/core/commands/plugins.ts index ff56020f..9693a37a 100644 --- a/src/core/commands/plugins.ts +++ b/src/core/commands/plugins.ts @@ -1,42 +1,39 @@ import { Strings } from "@core/i18n"; import PluginManager from "@lib/addons/plugins/PluginManager"; -import { ApplicationCommand, ApplicationCommandOptionType } from "@lib/api/commands/types"; +import { type ApplicationCommand, ApplicationCommandOptionType } from "@lib/api/commands/types"; import { messageUtil } from "@metro/common"; -export default () => { - name: "plugins", - description: Strings.COMMAND_PLUGINS_DESC, - options: [ - { - name: "ephemeral", - displayName: "ephemeral", - type: ApplicationCommandOptionType.BOOLEAN, - description: Strings.COMMAND_DEBUG_OPT_EPHEMERALLY, - } - ], - execute([ephemeral], ctx) { - const plugins = PluginManager.getAllIds().map(id => PluginManager.getManifest(id)).filter(Boolean); - plugins.sort((a, b) => a.display.name.localeCompare(b.display.name)); +export default () => + { + name: "plugins", + description: Strings.COMMAND_PLUGINS_DESC, + options: [ + { + name: "ephemeral", + displayName: "ephemeral", + type: ApplicationCommandOptionType.BOOLEAN, + description: Strings.COMMAND_DEBUG_OPT_EPHEMERALLY, + }, + ], + execute([ephemeral], ctx) { + const plugins = PluginManager.getAllIds() + .map((id) => PluginManager.getManifest(id)) + .filter(Boolean); + plugins.sort((a, b) => a.display.name.localeCompare(b.display.name)); - const enabled = plugins.filter(p => PluginManager.settings[p.id].enabled).map(p => p.display.name); - const disabled = plugins.filter(p => !PluginManager.settings[p.id].enabled).map(p => p.display.name); + const enabled = plugins.filter((p) => PluginManager.settings[p.id].enabled).map((p) => p.display.name); + const disabled = plugins.filter((p) => !PluginManager.settings[p.id].enabled).map((p) => p.display.name); - const content = [ - `**Installed Plugins (${plugins.length}):**`, - ...(enabled.length > 0 ? [ - `Enabled (${enabled.length}):`, - "> " + enabled.join(", "), - ] : []), - ...(disabled.length > 0 ? [ - `Disabled (${disabled.length}):`, - "> " + disabled.join(", "), - ] : []), - ].join("\n"); + const content = [ + `**Installed Plugins (${plugins.length}):**`, + ...(enabled.length > 0 ? [`Enabled (${enabled.length}):`, `> ${enabled.join(", ")}`] : []), + ...(disabled.length > 0 ? [`Disabled (${disabled.length}):`, `> ${disabled.join(", ")}`] : []), + ].join("\n"); - if (ephemeral?.value) { - messageUtil.sendBotMessage(ctx.channel.id, content); - } else { - messageUtil.sendMessage(ctx.channel.id, { content }); - } - } -}; + if (ephemeral?.value) { + messageUtil.sendBotMessage(ctx.channel.id, content); + } else { + messageUtil.sendMessage(ctx.channel.id, { content }); + } + }, + }; diff --git a/src/core/debug/patches/patchErrorBoundary.tsx b/src/core/debug/patches/patchErrorBoundary.tsx index 81ef68f3..98fc2989 100644 --- a/src/core/debug/patches/patchErrorBoundary.tsx +++ b/src/core/debug/patches/patchErrorBoundary.tsx @@ -8,10 +8,10 @@ import { lazyDestructure } from "@lib/utils/lazy"; import { tokens } from "@metro/common"; import { Button, CompatButton, SafeAreaView } from "@metro/common/components"; import { _lazyContextSymbol } from "@metro/lazy"; -import { LazyModuleContext } from "@metro/types"; +import type { LazyModuleContext } from "@metro/types"; import { findByNameLazy, findByProps } from "@metro/wrappers"; import { Codeblock, ErrorBoundary as _ErrorBoundary } from "@ui/components"; -import { createStyles, TextStyleSheet } from "@ui/styles"; +import { TextStyleSheet, createStyles } from "@ui/styles"; import { useState } from "react"; import { Text, View } from "react-native"; @@ -71,8 +71,8 @@ const tabs: Tab[] = [ function getErrorBoundaryContext() { const ctxt: LazyModuleContext = findByNameLazy("ErrorBoundary")[_lazyContextSymbol]; - return new Promise(resolve => { - ctxt.getExports(exp => { + return new Promise((resolve) => { + ctxt.getExports((exp) => { resolve(exp.prototype); }); }); @@ -82,78 +82,92 @@ function ErrorScreen({ ret, error, rerender }: any) { const styles = useStyles(); const [activeTab, setActiveTab] = useState("message"); - const tabData = tabs.find(t => t.id === activeTab); + const tabData = tabs.find((t) => t.id === activeTab); const errorText: string = error[activeTab]; // This is in the patch and not outside of it so that we can use `this`, e.g. for setting state const buttons: Button[] = [ { text: Strings.RELOAD_DISCORD, onPress: () => RTNBundleUpdaterManager.reload() }, - ...!BunnySettings.isSafeMode() ? [{ text: Strings.RELOAD_IN_SAFE_MODE, onPress: () => toggleSafeMode({ to: true }) }] : [], + ...(!BunnySettings.isSafeMode() + ? [{ text: Strings.RELOAD_IN_SAFE_MODE, onPress: () => toggleSafeMode({ to: true }) }] + : []), { text: Strings.RETRY_RENDER, color: "red", onPress: rerender }, ]; - return <_ErrorBoundary> - - - - - {ret.props.title} - {ret.props.body} - - - - - {/* Are errors caught by ErrorBoundary guaranteed to have the component stack? */} - ({ ...t, title: t.title() }))} - activeTab={activeTab} - onTabSelected={(tab: string) => { setActiveTab(tab); }} + return ( + <_ErrorBoundary> + + + + + {ret.props.title} + {ret.props.body} + - - {/* + + + {/* Are errors caught by ErrorBoundary guaranteed to have the component stack? */} + ({ ...t, title: t.title() }))} + activeTab={activeTab} + onTabSelected={(tab: string) => { + setActiveTab(tab); + }} + /> + + + {/* TODO: I tried to get this working as intended using regex and failed. When trimWhitespace is true, each line should have it's whitespace removed but with it's spaces kept. */} - {tabData?.trimWhitespace ? errorText.split("\n").filter(i => i.length !== 0).map(i => i.trim()).join("\n") : errorText} - - - - {buttons.map(button => { - const buttonIndex = buttons.indexOf(button) !== 0 ? 8 : 0; + {tabData?.trimWhitespace + ? errorText + .split("\n") + .filter((i) => i.length !== 0) + .map((i) => i.trim()) + .join("\n") + : errorText} + + + + {buttons.map((button) => { + const buttonIndex = buttons.indexOf(button) !== 0 ? 8 : 0; - return ; - })} - - - ; + borderRadius: 16, + }} + /> + ); + })} + + + + ); } export default function patchErrorBoundary() { return after.await("render", getErrorBoundaryContext(), function (this: any, _, ret) { if (!this.state.error) return; - return this.setState({ info: null, error: null })} - />; + return ( + this.setState({ info: null, error: null })} /> + ); }); } diff --git a/src/core/debug/safeMode.ts b/src/core/debug/safeMode.ts index 70e69ae2..c03a7600 100644 --- a/src/core/debug/safeMode.ts +++ b/src/core/debug/safeMode.ts @@ -2,11 +2,8 @@ import BunnySettings from "@core/storage/BunnySettings"; import { ColorManager } from "@lib/addons/themes/colors"; import { RTNBundleUpdaterManager } from "@lib/api/native/rn-modules"; -export async function toggleSafeMode({ - to = !BunnySettings.general.safeModeEnabled, - reload = true -} = {}) { - const enabled = BunnySettings.general.safeModeEnabled = to; +export async function toggleSafeMode({ to = !BunnySettings.general.safeModeEnabled, reload = true } = {}) { + const enabled = (BunnySettings.general.safeModeEnabled = to); const currentColor = ColorManager.getCurrentManifest(); await ColorManager.writeForNative(enabled ? null : currentColor); if (reload) setTimeout(() => RTNBundleUpdaterManager.reload(), 500); diff --git a/src/core/i18n/default.json b/src/core/i18n/default.json index 83a9cccc..e133f34a 100644 --- a/src/core/i18n/default.json +++ b/src/core/i18n/default.json @@ -1,115 +1,115 @@ { - "ABOUT": "About", - "ACTIONS": "Actions", - "ARE_YOU_SURE_TO_CLEAR_DATA": "Are you sure you wish to clear the data of {name}?", - "ARE_YOU_SURE_TO_DELETE_PLUGIN": "Are you sure you wish to delete {name}? This will clear all of the plugin's data.", - "ARE_YOU_SURE_TO_DELETE_THEME": "Are you sure you wish to delete {name}?", - "ASSET_BROWSER": "Asset Browser", - "BRAND": "Brand", - "BUNNY": "Bunny", - "BUNNY_URL": "Bunny URL", - "BYTECODE": "Bytecode", - "CANCEL": "Cancel", - "CLEAR": "Clear", - "CLEAR_DATA": "Clear data", - "CLEAR_DATA_FAILED": "Failed to clear data for {name}!", - "CLEAR_DATA_SUCCESSFUL": "Cleared data for {name}.", - "CODENAME": "Codename", - "COMMAND_DEBUG_DESC": "Send Bunny debug info.", - "COMMAND_DEBUG_OPT_EPHEMERALLY": "Send debug info ephemerally.", - "COMMAND_EVAL_DESC": "Evaluate JavaScript code.", - "COMMAND_EVAL_OPT_ASYNC": "Whether to support 'await' in code. Must explicitly return for result (default: false)", - "COMMAND_EVAL_OPT_CODE": "The code to evaluate.", - "COMMAND_PLUGINS_DESC": "Send list of installed plugins.", - "COMMAND_PLUGINS_OPT_EPHEMERALLY": "Send plugins list ephemerally.", - "COMPONENT": "Component", - "CONFIRMATION_LINK_IS_A_TYPE": "This link is a **{urlType, select, plugin {Plugin} theme {Theme} other {Add-on}}**, would you like to install it?", - "CONNECT_TO_DEBUG_WEBSOCKET": "Connect to debug websocket", - "CONNECT_TO_REACT_DEVTOOLS": "Connect to React DevTools", - "CONTINUE": "Continue", - "COPIED_TO_CLIPBOARD": "Copied to clipboard", - "COPY_URL": "Copy URL", - "DEBUG": "Debug", - "DEBUGGER_URL": "Debugger URL", - "DELETE": "Delete", - "DESC_EXTRACT_FONTS_FROM_THEME": "Looks out for \"fonts\" field in your currently applied theme and install it.", - "DEVELOPER": "Developer", - "DEVELOPER_SETTINGS": "Developer Settings", - "DISABLE_THEME": "Disable Theme", - "DISABLE_UPDATES": "Disable updates", - "DISCORD_SERVER": "Discord Server", - "DONE": "Done", - "ENABLE_EVAL_COMMAND": "Enable /eval command", - "ENABLE_EVAL_COMMAND_DESC": "Evaluate JavaScript directly from command. Be cautious when using this command as it may pose a security risk. Make sure to know what you are doing.", - "ENABLE_UPDATES": "Enable updates", - "ERROR_BOUNDARY_TOOLS_LABEL": "ErrorBoundary Tools", - "EXTRACT": "Extract", - "FONT_NAME": "Font Name", - "FONTS": "Fonts", - "GENERAL": "General", - "GITHUB": "GitHub", - "HOLD_UP": "Hold Up", - "INFO": "Info", - "INSTALL": "Install", - "INSTALL_ADDON": "Install an add-on", - "INSTALL_FONT": "Install a font", - "INSTALL_PLUGIN": "Install a plugin", - "INSTALL_REACT_DEVTOOLS": "Install React DevTools", - "INSTALL_THEME": "Install Theme", - "LABEL_EXTRACT_FONTS_FROM_THEME": "Extract font from theme", - "LINKS": "Links", - "LOAD_FROM_CUSTOM_URL": "Load from custom URL", - "LOAD_FROM_CUSTOM_URL_DEC": "Load Bunny from a custom endpoint.", - "LOAD_REACT_DEVTOOLS": "Load React DevTools", - "LOADER": "Loader", - "MACHINE_ID": "Machine ID", - "MANUFACTURER": "Manufacturer", - "MESSAGE": "Message", - "MISCELLANEOUS": "Miscellaneous", - "MODAL_THEME_REFETCHED": "Theme refetched", - "MODAL_THEME_REFETCHED_DESC": "A reload is required to see the changes. Do you want to reload now?", - "MODAL_UNPROXIED_PLUGIN_DESC": "The plugin you are trying to install has not been proxied/verified by staffs. Are you sure you want to continue?", - "MODAL_UNPROXIED_PLUGIN_HEADER": "Unproxied Plugin", - "MODEL": "Model", - "OPEN_IN_BROWSER": "Open in Browser", - "OPERATING_SYSTEM": "Operating System", - "OVERFLOW_PLUGIN_SETTINGS": "Plugin settings", - "PLATFORM": "Platform", - "PLUGIN_REFETCH_FAILED": "Failed to refetch plugin!", - "PLUGIN_REFETCH_SUCCESSFUL": "Successfully refetched plugin!", - "PLUGINS": "Plugins", - "REFETCH": "Refetch", - "RELOAD": "Reload", - "RELOAD_DISCORD": "Reload Discord", - "RELOAD_IN_NORMAL_MODE": "Reload in Normal Mode", - "RELOAD_IN_NORMAL_MODE_DESC": "This will reload Discord normally", - "RELOAD_IN_SAFE_MODE": "Reload in Safe Mode", - "RELOAD_IN_SAFE_MODE_DESC": "This will reload Discord without loading addons", - "REMOVE": "Remove", - "RESTART_REQUIRED_TO_TAKE_EFFECT": "Restart is required to take effect", - "RETRY": "Retry", - "RETRY_RENDER": "Retry Render", - "SAFE_MODE": "Safe Mode", - "SAFE_MODE_NOTICE_FONTS": "You are in Safe Mode, meaning fonts have been temporarily disabled. {enabled, select, true {If a font appears to be causing the issue, you can press below to disable it persistently.} other {}}", - "SAFE_MODE_NOTICE_PLUGINS": "You are in Safe Mode, so plugins cannot be loaded. Disable any misbehaving plugins, then return to Normal Mode from the General settings page.", - "SAFE_MODE_NOTICE_THEMES": "You are in Safe Mode, meaning themes have been temporarily disabled. {enabled, select, true {If a theme appears to be causing the issue, you can press below to disable it persistently.} other {}}", - "SEARCH": "Search", - "SEPARATOR": ", ", - "SETTINGS_ACTIVATE_DISCORD_EXPERIMENTS": "Activate Discord Experiments", - "SETTINGS_ACTIVATE_DISCORD_EXPERIMENTS_DESC": "Warning: Messing with this feature may lead to account termination. We are not responsible for what you do with this feature.", - "STACK_TRACE": "Stack Trace", - "SUCCESSFULLY_INSTALLED": "Successfully installed", - "THEME_EXTRACTOR_DESC": "This pack overrides the following: {fonts}", - "THEME_REFETCH_FAILED": "Failed to refetch theme!", - "THEME_REFETCH_SUCCESSFUL": "Successfully refetched theme.", - "THEMES": "Themes", - "THEMES_RELOAD_FOR_CHANGES": "Reload the app to fully apply changes!", - "TOASTS_INSTALLED_PLUGIN": "Installed plugin", - "TOASTS_PLUGIN_UPDATE": "{update, select, true {Enabled} other {Disabled}} updates for {name}.", - "UH_OH": "Uh oh.", - "UNINSTALL": "Uninstall", - "UNINSTALL_TITLE": "Uninstall {title}", - "URL_PLACEHOLDER": "https://example.com", - "VERSION": "Version", - "VERSIONS": "Versions" -} \ No newline at end of file + "ABOUT": "About", + "ACTIONS": "Actions", + "ARE_YOU_SURE_TO_CLEAR_DATA": "Are you sure you wish to clear the data of {name}?", + "ARE_YOU_SURE_TO_DELETE_PLUGIN": "Are you sure you wish to delete {name}? This will clear all of the plugin's data.", + "ARE_YOU_SURE_TO_DELETE_THEME": "Are you sure you wish to delete {name}?", + "ASSET_BROWSER": "Asset Browser", + "BRAND": "Brand", + "BUNNY": "Bunny", + "BUNNY_URL": "Bunny URL", + "BYTECODE": "Bytecode", + "CANCEL": "Cancel", + "CLEAR": "Clear", + "CLEAR_DATA": "Clear data", + "CLEAR_DATA_FAILED": "Failed to clear data for {name}!", + "CLEAR_DATA_SUCCESSFUL": "Cleared data for {name}.", + "CODENAME": "Codename", + "COMMAND_DEBUG_DESC": "Send Bunny debug info.", + "COMMAND_DEBUG_OPT_EPHEMERALLY": "Send debug info ephemerally.", + "COMMAND_EVAL_DESC": "Evaluate JavaScript code.", + "COMMAND_EVAL_OPT_ASYNC": "Whether to support 'await' in code. Must explicitly return for result (default: false)", + "COMMAND_EVAL_OPT_CODE": "The code to evaluate.", + "COMMAND_PLUGINS_DESC": "Send list of installed plugins.", + "COMMAND_PLUGINS_OPT_EPHEMERALLY": "Send plugins list ephemerally.", + "COMPONENT": "Component", + "CONFIRMATION_LINK_IS_A_TYPE": "This link is a **{urlType, select, plugin {Plugin} theme {Theme} other {Add-on}}**, would you like to install it?", + "CONNECT_TO_DEBUG_WEBSOCKET": "Connect to debug websocket", + "CONNECT_TO_REACT_DEVTOOLS": "Connect to React DevTools", + "CONTINUE": "Continue", + "COPIED_TO_CLIPBOARD": "Copied to clipboard", + "COPY_URL": "Copy URL", + "DEBUG": "Debug", + "DEBUGGER_URL": "Debugger URL", + "DELETE": "Delete", + "DESC_EXTRACT_FONTS_FROM_THEME": "Looks out for \"fonts\" field in your currently applied theme and install it.", + "DEVELOPER": "Developer", + "DEVELOPER_SETTINGS": "Developer Settings", + "DISABLE_THEME": "Disable Theme", + "DISABLE_UPDATES": "Disable updates", + "DISCORD_SERVER": "Discord Server", + "DONE": "Done", + "ENABLE_EVAL_COMMAND": "Enable /eval command", + "ENABLE_EVAL_COMMAND_DESC": "Evaluate JavaScript directly from command. Be cautious when using this command as it may pose a security risk. Make sure to know what you are doing.", + "ENABLE_UPDATES": "Enable updates", + "ERROR_BOUNDARY_TOOLS_LABEL": "ErrorBoundary Tools", + "EXTRACT": "Extract", + "FONT_NAME": "Font Name", + "FONTS": "Fonts", + "GENERAL": "General", + "GITHUB": "GitHub", + "HOLD_UP": "Hold Up", + "INFO": "Info", + "INSTALL": "Install", + "INSTALL_ADDON": "Install an add-on", + "INSTALL_FONT": "Install a font", + "INSTALL_PLUGIN": "Install a plugin", + "INSTALL_REACT_DEVTOOLS": "Install React DevTools", + "INSTALL_THEME": "Install Theme", + "LABEL_EXTRACT_FONTS_FROM_THEME": "Extract font from theme", + "LINKS": "Links", + "LOAD_FROM_CUSTOM_URL": "Load from custom URL", + "LOAD_FROM_CUSTOM_URL_DEC": "Load Bunny from a custom endpoint.", + "LOAD_REACT_DEVTOOLS": "Load React DevTools", + "LOADER": "Loader", + "MACHINE_ID": "Machine ID", + "MANUFACTURER": "Manufacturer", + "MESSAGE": "Message", + "MISCELLANEOUS": "Miscellaneous", + "MODAL_THEME_REFETCHED": "Theme refetched", + "MODAL_THEME_REFETCHED_DESC": "A reload is required to see the changes. Do you want to reload now?", + "MODAL_UNPROXIED_PLUGIN_DESC": "The plugin you are trying to install has not been proxied/verified by staffs. Are you sure you want to continue?", + "MODAL_UNPROXIED_PLUGIN_HEADER": "Unproxied Plugin", + "MODEL": "Model", + "OPEN_IN_BROWSER": "Open in Browser", + "OPERATING_SYSTEM": "Operating System", + "OVERFLOW_PLUGIN_SETTINGS": "Plugin settings", + "PLATFORM": "Platform", + "PLUGIN_REFETCH_FAILED": "Failed to refetch plugin!", + "PLUGIN_REFETCH_SUCCESSFUL": "Successfully refetched plugin!", + "PLUGINS": "Plugins", + "REFETCH": "Refetch", + "RELOAD": "Reload", + "RELOAD_DISCORD": "Reload Discord", + "RELOAD_IN_NORMAL_MODE": "Reload in Normal Mode", + "RELOAD_IN_NORMAL_MODE_DESC": "This will reload Discord normally", + "RELOAD_IN_SAFE_MODE": "Reload in Safe Mode", + "RELOAD_IN_SAFE_MODE_DESC": "This will reload Discord without loading addons", + "REMOVE": "Remove", + "RESTART_REQUIRED_TO_TAKE_EFFECT": "Restart is required to take effect", + "RETRY": "Retry", + "RETRY_RENDER": "Retry Render", + "SAFE_MODE": "Safe Mode", + "SAFE_MODE_NOTICE_FONTS": "You are in Safe Mode, meaning fonts have been temporarily disabled. {enabled, select, true {If a font appears to be causing the issue, you can press below to disable it persistently.} other {}}", + "SAFE_MODE_NOTICE_PLUGINS": "You are in Safe Mode, so plugins cannot be loaded. Disable any misbehaving plugins, then return to Normal Mode from the General settings page.", + "SAFE_MODE_NOTICE_THEMES": "You are in Safe Mode, meaning themes have been temporarily disabled. {enabled, select, true {If a theme appears to be causing the issue, you can press below to disable it persistently.} other {}}", + "SEARCH": "Search", + "SEPARATOR": ", ", + "SETTINGS_ACTIVATE_DISCORD_EXPERIMENTS": "Activate Discord Experiments", + "SETTINGS_ACTIVATE_DISCORD_EXPERIMENTS_DESC": "Warning: Messing with this feature may lead to account termination. We are not responsible for what you do with this feature.", + "STACK_TRACE": "Stack Trace", + "SUCCESSFULLY_INSTALLED": "Successfully installed", + "THEME_EXTRACTOR_DESC": "This pack overrides the following: {fonts}", + "THEME_REFETCH_FAILED": "Failed to refetch theme!", + "THEME_REFETCH_SUCCESSFUL": "Successfully refetched theme.", + "THEMES": "Themes", + "THEMES_RELOAD_FOR_CHANGES": "Reload the app to fully apply changes!", + "TOASTS_INSTALLED_PLUGIN": "Installed plugin", + "TOASTS_PLUGIN_UPDATE": "{update, select, true {Enabled} other {Disabled}} updates for {name}.", + "UH_OH": "Uh oh.", + "UNINSTALL": "Uninstall", + "UNINSTALL_TITLE": "Uninstall {title}", + "URL_PLACEHOLDER": "https://example.com", + "VERSION": "Version", + "VERSIONS": "Versions" +} diff --git a/src/core/i18n/index.ts b/src/core/i18n/index.ts index d85d2405..0cff2575 100644 --- a/src/core/i18n/index.ts +++ b/src/core/i18n/index.ts @@ -1,7 +1,7 @@ import { logger } from "@core/logger"; import { FluxDispatcher } from "@metro/common"; import { findByNameLazy } from "@metro/wrappers"; -import { PrimitiveType } from "intl-messageformat"; +import type { PrimitiveType } from "intl-messageformat"; import langDefault from "./default.json"; @@ -15,17 +15,20 @@ let _lastSetLocale: string | null = null; const _loadedLocale = new Set(); const _loadedStrings = {} as Record; -export const Strings = new Proxy({}, { - get: (_t, prop: keyof typeof langDefault) => { - if (_currentLocale && _loadedStrings[_currentLocale]?.[prop]) { - return _loadedStrings[_currentLocale]?.[prop]; - } - return langDefault[prop]; - } -}) as Record; +export const Strings = new Proxy( + {}, + { + get: (_t, prop: keyof typeof langDefault) => { + if (_currentLocale && _loadedStrings[_currentLocale]?.[prop]) { + return _loadedStrings[_currentLocale]?.[prop]; + } + return langDefault[prop]; + }, + }, +) as Record; export function initFetchI18nStrings() { - const cb = ({ locale }: { locale: string; }) => { + const cb = ({ locale }: { locale: string }) => { const languageMap = { "es-ES": "es", "es-419": "es_419", @@ -33,10 +36,10 @@ export function initFetchI18nStrings() { "zh-CN": "zh-Hans", "pt-PT": "pt", "pt-BR": "pt_BR", - "sv-SE": "sv" + "sv-SE": "sv", } as Record; - const resolvedLocale = _lastSetLocale = languageMap[locale] ?? locale; + const resolvedLocale = (_lastSetLocale = languageMap[locale] ?? locale); if (resolvedLocale.startsWith("en-")) { _currentLocale = null; @@ -47,10 +50,10 @@ export function initFetchI18nStrings() { _loadedLocale.add(resolvedLocale); fetch(`https://raw.githubusercontent.com/pyoncord/i18n/main/resources/${resolvedLocale}/bunny.json`) - .then(r => r.json()) - .then(strings => _loadedStrings[resolvedLocale] = strings) + .then((r) => r.json()) + .then((strings) => (_loadedStrings[resolvedLocale] = strings)) .then(() => resolvedLocale === _lastSetLocale && (_currentLocale = resolvedLocale)) - .catch(e => logger.error`An error occured while fetching strings for ${resolvedLocale}: ${e}`); + .catch((e) => logger.error`An error occured while fetching strings for ${resolvedLocale}: ${e}`); } else { _currentLocale = resolvedLocale; } diff --git a/src/core/logger/debugger.ts b/src/core/logger/debugger.ts index f829852f..d79a43ff 100644 --- a/src/core/logger/debugger.ts +++ b/src/core/logger/debugger.ts @@ -34,14 +34,14 @@ export function connectToDebugger(url: string) { * @internal */ export function patchLogHook() { - const unpatch = after("nativeLoggingHook", globalThis, args => { + const unpatch = after("nativeLoggingHook", globalThis, (args) => { if (socket?.readyState === WebSocket.OPEN) { socket.send(JSON.stringify({ message: args[0], level: args[1] })); } }); return () => { - socket && socket.close(); + socket?.close(); unpatch(); }; } diff --git a/src/core/logger/index.ts b/src/core/logger/index.ts index d91ae3f9..03709cce 100644 --- a/src/core/logger/index.ts +++ b/src/core/logger/index.ts @@ -1,4 +1,3 @@ - function error(...args: any[]): void; function error(strings: TemplateStringsArray, ...args: any[]) { if (Array.isArray(strings) && "raw" in strings) { @@ -10,5 +9,5 @@ function error(strings: TemplateStringsArray, ...args: any[]) { export const logger = { ...console, - error + error, }; diff --git a/src/core/plugins/index.ts b/src/core/plugins/index.ts index ed7dc577..0e065235 100644 --- a/src/core/plugins/index.ts +++ b/src/core/plugins/index.ts @@ -1,4 +1,4 @@ -import { PluginInstanceInternal } from "@lib/addons/plugins/types"; +import type { PluginInstanceInternal } from "@lib/addons/plugins/types"; interface CorePlugin { default: PluginInstanceInternal; @@ -7,7 +7,7 @@ interface CorePlugin { // Called from @lib/plugins export const getCorePlugins = (): Record => ({ - "bunny.quickinstall": require("./quickinstall") + "bunny.quickinstall": require("./quickinstall"), }); /** diff --git a/src/core/plugins/quickinstall/forumPost.tsx b/src/core/plugins/quickinstall/forumPost.tsx index 490cde13..4f642904 100644 --- a/src/core/plugins/quickinstall/forumPost.tsx +++ b/src/core/plugins/quickinstall/forumPost.tsx @@ -6,7 +6,13 @@ import { isThemeSupported } from "@lib/api/native/loader"; import { after } from "@lib/api/patcher"; import { useProxy } from "@lib/api/storage"; import { useProxy as useNewProxy } from "@lib/api/storage"; -import { DISCORD_SERVER_ID, HTTP_REGEX_MULTI, PLUGINS_CHANNEL_ID, THEMES_CHANNEL_ID, VD_PROXY_PREFIX } from "@lib/constants"; +import { + DISCORD_SERVER_ID, + HTTP_REGEX_MULTI, + PLUGINS_CHANNEL_ID, + THEMES_CHANNEL_ID, + VD_PROXY_PREFIX, +} from "@lib/constants"; import { lazyDestructure } from "@lib/utils/lazy"; import { Button } from "@metro/common/components"; import { findByProps, findByPropsLazy } from "@metro/wrappers"; @@ -27,7 +33,7 @@ const postMap = { installOrRemove: (url: string) => { const isInstalled = !!postMap.Plugin.storage[url]; return isInstalled ? PluginManager.uninstall(url) : PluginManager.install(url); - } + }, }, Theme: { storage: themes, @@ -36,10 +42,14 @@ const postMap = { const isInstalled = postMap.Theme.storage[url]; return isInstalled ? removeTheme(url) : installTheme(url); }, - } + }, }; -function useExtractThreadContent(thread: any, _firstMessage = null, actionSheet = false): ([PostType, string]) | void { +function useExtractThreadContent( + thread: any, + _firstMessage = null, + actionSheet = false, +): [PostType, string] | undefined { if (thread.guild_id !== DISCORD_SERVER_ID) return; // Determine what type of addon this is. @@ -61,7 +71,11 @@ function useExtractThreadContent(thread: any, _firstMessage = null, actionSheet return [postType, urls[0]]; } -function useInstaller(thread: any, firstMessage = null, actionSheet = false): [true] | [false, PostType, boolean, boolean, () => Promise] { +function useInstaller( + thread: any, + firstMessage = null, + actionSheet = false, +): [true] | [false, PostType, boolean, boolean, () => Promise] { const [postType, url] = useExtractThreadContent(thread, firstMessage, actionSheet) ?? []; useNewProxy(PluginManager.settings); @@ -105,33 +119,36 @@ function useInstaller(thread: any, firstMessage = null, actionSheet = false): [t // ); // }); -const installButtonPatch = () => after("MostCommonForumPostReaction", forumReactions, ([{ thread, firstMessage }], res) => { - const [shouldReturn, _, installed, loading, installOrRemove] = useInstaller(thread, firstMessage, true); - if (shouldReturn) return; - - return <> - {res} - -