ESLint hops in
This commit is contained in:
parent
77b78071d7
commit
3692cc41c8
79 changed files with 2926 additions and 472 deletions
77
.eslintrc.json
Normal file
77
.eslintrc.json
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
{
|
||||||
|
"root": true,
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"ignorePatterns": [ "dist", "browser" ],
|
||||||
|
"plugins": [
|
||||||
|
"@typescript-eslint",
|
||||||
|
"simple-import-sort",
|
||||||
|
"path-alias",
|
||||||
|
"unused-imports"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"import/resolver": {
|
||||||
|
"alias": {
|
||||||
|
"map": [
|
||||||
|
[ "@lib/*", "./src/lib/*" ],
|
||||||
|
[ "@core/*", "./src/core/*" ],
|
||||||
|
[ "@metro/*", "src/lib/metro/*"],
|
||||||
|
[ "@ui/*", "src/lib/ui/*"],
|
||||||
|
[ "@types", "src/lib/utils/types.ts"]
|
||||||
|
],
|
||||||
|
"extensions": [".ts", ".js", ".jsx", ".tsx", ".json"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"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",
|
||||||
|
"path-alias/no-relative": "error",
|
||||||
|
"unused-imports/no-unused-imports": "error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"printWidth": 240,
|
|
||||||
"tabWidth": 4,
|
|
||||||
"singleQuote": false,
|
|
||||||
"jsxSingleQuote": false,
|
|
||||||
"bracketSpacing": true,
|
|
||||||
"useTabs": false
|
|
||||||
}
|
|
||||||
16
.vscode/settings.json
vendored
Normal file
16
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll.eslint": "explicit"
|
||||||
|
},
|
||||||
|
"[typescript]": {
|
||||||
|
"editor.defaultFormatter": "vscode.typescript-language-features"
|
||||||
|
},
|
||||||
|
"[typescriptreact]": {
|
||||||
|
"editor.defaultFormatter": "vscode.typescript-language-features"
|
||||||
|
},
|
||||||
|
"javascript.format.semicolons": "insert",
|
||||||
|
"typescript.format.semicolons": "insert",
|
||||||
|
"typescript.preferences.quoteStyle": "double",
|
||||||
|
"javascript.preferences.quoteStyle": "double"
|
||||||
|
}
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
github: [maisymoe, wingio]
|
|
||||||
21
build.mjs
21
build.mjs
|
|
@ -2,8 +2,8 @@
|
||||||
import swc from "@swc/core";
|
import swc from "@swc/core";
|
||||||
import { execSync } from "child_process";
|
import { execSync } from "child_process";
|
||||||
import esbuild from "esbuild";
|
import esbuild from "esbuild";
|
||||||
import { copyFile, copyFileSync } from "fs";
|
import { copyFileSync } from "fs";
|
||||||
import { readFile, writeFile } from "fs/promises";
|
import { readFile } from "fs/promises";
|
||||||
import { createServer } from "http";
|
import { createServer } from "http";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { argv } from "process";
|
import { argv } from "process";
|
||||||
|
|
@ -36,8 +36,11 @@ try {
|
||||||
loader: { ".png": "dataurl" },
|
loader: { ".png": "dataurl" },
|
||||||
legalComments: "none",
|
legalComments: "none",
|
||||||
alias: {
|
alias: {
|
||||||
"@*": "src*",
|
"@lib/*": "src/lib/*",
|
||||||
"@types": "src/utils/types.ts"
|
"@core/*": "src/core/*",
|
||||||
|
"@metro/*": "src/lib/metro/*",
|
||||||
|
"@ui/*": "src/lib/ui/*",
|
||||||
|
"@types": "src/utils/types.ts",
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
{
|
{
|
||||||
|
|
@ -45,12 +48,12 @@ try {
|
||||||
setup: async build => {
|
setup: async build => {
|
||||||
build.onStart(() => {
|
build.onStart(() => {
|
||||||
console.clear();
|
console.clear();
|
||||||
console.log(`Building with commit hash "${commitHash}", isRelease="${isRelease}"`)
|
console.log(`Building with commit hash "${commitHash}", isRelease="${isRelease}"`);
|
||||||
});
|
});
|
||||||
|
|
||||||
build.onEnd(result => {
|
build.onEnd(result => {
|
||||||
const { outfile } = build.initialOptions;
|
const { outfile } = build.initialOptions;
|
||||||
copyFileSync(outfile, path.resolve(outfile, "..", "pyondetta.js"))
|
copyFileSync(outfile, path.resolve(outfile, "..", "pyondetta.js"));
|
||||||
console.log(`Built with ${result.errors?.length} errors!`);
|
console.log(`Built with ${result.errors?.length} errors!`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -59,12 +62,12 @@ try {
|
||||||
name: "swc",
|
name: "swc",
|
||||||
setup(build) {
|
setup(build) {
|
||||||
let timeString = Number(new Date).toString(36);
|
let timeString = Number(new Date).toString(36);
|
||||||
|
|
||||||
build.onStart(() => {
|
build.onStart(() => {
|
||||||
timeString = Number(new Date).toString(36);
|
timeString = Number(new Date).toString(36);
|
||||||
console.log(`swc plugin: time-string="${timeString}"`);
|
console.log(`swc plugin: time-string="${timeString}"`);
|
||||||
})
|
});
|
||||||
|
|
||||||
build.onLoad({ filter: /\.[jt]sx?$/ }, async args => {
|
build.onLoad({ filter: /\.[jt]sx?$/ }, async args => {
|
||||||
const result = await swc.transformFile(args.path, {
|
const result = await swc.transformFile(args.path, {
|
||||||
jsc: {
|
jsc: {
|
||||||
|
|
|
||||||
14
package.json
14
package.json
|
|
@ -6,7 +6,8 @@
|
||||||
"dev": "node build.mjs",
|
"dev": "node build.mjs",
|
||||||
"build": "node build.mjs --release",
|
"build": "node build.mjs --release",
|
||||||
"serve": "node build.mjs --serve",
|
"serve": "node build.mjs --serve",
|
||||||
"watch": "node build.mjs --watch"
|
"watch": "node build.mjs --watch",
|
||||||
|
"lint": "eslint ./src --ext .js,.jsx,.ts,.tsx"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"discord",
|
"discord",
|
||||||
|
|
@ -26,13 +27,22 @@
|
||||||
"@types/node": "^20.11.21",
|
"@types/node": "^20.11.21",
|
||||||
"@types/react": "18.2.60",
|
"@types/react": "18.2.60",
|
||||||
"@types/react-native": "0.72.3",
|
"@types/react-native": "0.72.3",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^7.1.1",
|
||||||
|
"@typescript-eslint/parser": "^7.1.1",
|
||||||
|
"@typescript-eslint/typescript-estree": "^7.1.1",
|
||||||
"esbuild": "^0.17.19",
|
"esbuild": "^0.17.19",
|
||||||
|
"eslint": "^8.57.0",
|
||||||
|
"eslint-import-resolver-alias": "^1.1.2",
|
||||||
|
"eslint-plugin-path-alias": "^1.0.0",
|
||||||
|
"eslint-plugin-react": "^7.34.0",
|
||||||
|
"eslint-plugin-unused-imports": "^3.1.0",
|
||||||
"moment": "2.22.2",
|
"moment": "2.22.2",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
"typescript-transform-paths": "^3.4.7"
|
"typescript-transform-paths": "^3.4.7"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@swc/helpers": "0.5.0",
|
"@swc/helpers": "0.5.0",
|
||||||
|
"eslint-plugin-simple-import-sort": "^12.0.0",
|
||||||
"fuzzysort": "^2.0.4",
|
"fuzzysort": "^2.0.4",
|
||||||
"intl-messageformat": "^10.5.11",
|
"intl-messageformat": "^10.5.11",
|
||||||
"spitroast": "^1.4.3"
|
"spitroast": "^1.4.3"
|
||||||
|
|
@ -45,4 +55,4 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
2355
pnpm-lock.yaml
generated
2355
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,7 +1,7 @@
|
||||||
import { Strings } from "@/core/i18n";
|
import { Strings } from "@core/i18n";
|
||||||
|
import { ApplicationCommand, ApplicationCommandOptionType } from "@lib/api/commands/types";
|
||||||
import { getDebugInfo } from "@lib/debug";
|
import { getDebugInfo } from "@lib/debug";
|
||||||
import { messageUtil } from "@metro/common";
|
import { messageUtil } from "@metro/common";
|
||||||
import { ApplicationCommand, ApplicationCommandOptionType } from "@/lib/api/commands/types";
|
|
||||||
|
|
||||||
export default () => <ApplicationCommand>{
|
export default () => <ApplicationCommand>{
|
||||||
name: "debug",
|
name: "debug",
|
||||||
|
|
@ -31,4 +31,4 @@ export default () => <ApplicationCommand>{
|
||||||
messageUtil.sendMessage(ctx.channel.id, { content });
|
messageUtil.sendMessage(ctx.channel.id, { content });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { findByProps } from "@metro/filters";
|
import { Strings } from "@core/i18n";
|
||||||
|
import { ApplicationCommand, ApplicationCommandOptionType } from "@lib/api/commands/types";
|
||||||
import { messageUtil } from "@metro/common";
|
import { messageUtil } from "@metro/common";
|
||||||
import { Strings } from "@/core/i18n";
|
import { findByProps } from "@metro/filters";
|
||||||
import { ApplicationCommand, ApplicationCommandOptionType } from "@/lib/api/commands/types";
|
|
||||||
|
|
||||||
const util = findByProps("inspect");
|
const util = findByProps("inspect");
|
||||||
const AsyncFunction = (async () => void 0).constructor;
|
const AsyncFunction = (async () => void 0).constructor;
|
||||||
|
|
@ -38,4 +38,4 @@ export default () => <ApplicationCommand>{
|
||||||
messageUtil.sendBotMessage(ctx.channel.id, wrapInJSCodeblock(err?.stack ?? err));
|
messageUtil.sendBotMessage(ctx.channel.id, wrapInJSCodeblock(err?.stack ?? err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { messageUtil } from "@metro/common";
|
import { Strings } from "@core/i18n";
|
||||||
|
import { ApplicationCommand, ApplicationCommandOptionType } from "@lib/api/commands/types";
|
||||||
import { plugins as pluginStorage } from "@lib/managers/plugins";
|
import { plugins as pluginStorage } from "@lib/managers/plugins";
|
||||||
import { Strings } from "@/core/i18n";
|
import { messageUtil } from "@metro/common";
|
||||||
import { ApplicationCommand, ApplicationCommandOptionType } from "@/lib/api/commands/types";
|
|
||||||
|
|
||||||
export default () => <ApplicationCommand>{
|
export default () => <ApplicationCommand>{
|
||||||
name: "plugins",
|
name: "plugins",
|
||||||
|
|
@ -38,4 +38,4 @@ export default () => <ApplicationCommand>{
|
||||||
messageUtil.sendMessage(ctx.channel.id, { content });
|
messageUtil.sendMessage(ctx.channel.id, { content });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,10 @@
|
||||||
* - @pylixonly
|
* - @pylixonly
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { FluxDispatcher, moment } from "@metro/common";
|
|
||||||
import logger from "@lib/utils/logger";
|
import logger from "@lib/utils/logger";
|
||||||
|
import { FluxDispatcher, moment } from "@metro/common";
|
||||||
|
|
||||||
function onDispatch({ locale }: { locale: string }) {
|
function onDispatch({ locale }: { locale: string; }) {
|
||||||
// Timestamps
|
// Timestamps
|
||||||
try {
|
try {
|
||||||
moment.locale(locale.toLowerCase());
|
moment.locale(locale.toLowerCase());
|
||||||
|
|
@ -22,5 +22,5 @@ function onDispatch({ locale }: { locale: string }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
FluxDispatcher.subscribe("I18N_LOAD_SUCCESS", onDispatch)
|
FluxDispatcher.subscribe("I18N_LOAD_SUCCESS", onDispatch);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { findByName } from "@/lib/metro/filters";
|
import { findByName } from "@lib/metro/filters";
|
||||||
|
|
||||||
import langDefault from "./default.json";
|
import langDefault from "./default.json";
|
||||||
|
|
||||||
const IntlMessageFormat = findByName("MessageFormat") as typeof import("intl-messageformat").default;
|
const IntlMessageFormat = findByName("MessageFormat") as typeof import("intl-messageformat").default;
|
||||||
|
|
@ -11,7 +12,10 @@ export const Strings = new Proxy({}, {
|
||||||
}
|
}
|
||||||
}) as Record<I18nKey, string>;
|
}) as Record<I18nKey, string>;
|
||||||
|
|
||||||
export function formatString<T = void>(key: I18nKey, val: Record<string, T>) {
|
type FRet<T> = T extends string ? string : string | T | (string | T)[];
|
||||||
|
|
||||||
|
export function formatString<T = void>(key: I18nKey, val: Record<string, T>): FRet<T> {
|
||||||
const str = Strings[key];
|
const str = Strings[key];
|
||||||
|
// @ts-ignore
|
||||||
return new IntlMessageFormat(str).format(val);
|
return new IntlMessageFormat(str).format(val);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,4 +7,4 @@ export function initCorePlugins() {
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => unloads.forEach(m => typeof m === "function" && m());
|
return () => unloads.forEach(m => typeof m === "function" && m());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
import { findByName, findByProps } from "@metro/filters";
|
import { formatString, Strings } from "@core/i18n";
|
||||||
import { DISCORD_SERVER_ID, PLUGINS_CHANNEL_ID, THEMES_CHANNEL_ID, HTTP_REGEX_MULTI, PROXY_PREFIX } from "@lib/utils/constants";
|
import { getAssetIDByName } from "@lib/api/assets";
|
||||||
|
import { isThemeSupported } from "@lib/api/native/loader";
|
||||||
import { after } from "@lib/api/patcher";
|
import { after } from "@lib/api/patcher";
|
||||||
|
import { useProxy } from "@lib/api/storage";
|
||||||
import { installPlugin, plugins, removePlugin } from "@lib/managers/plugins";
|
import { installPlugin, plugins, removePlugin } from "@lib/managers/plugins";
|
||||||
import { installTheme, removeTheme, themes } from "@lib/managers/themes";
|
import { installTheme, removeTheme, themes } from "@lib/managers/themes";
|
||||||
import { findInReactTree } from "@lib/utils";
|
import { findInReactTree } from "@lib/utils";
|
||||||
import { getAssetIDByName } from "@/lib/api/assets";
|
import { DISCORD_SERVER_ID, HTTP_REGEX_MULTI, PLUGINS_CHANNEL_ID, PROXY_PREFIX, THEMES_CHANNEL_ID } from "@lib/utils/constants";
|
||||||
|
import { findByName, findByProps } from "@metro/filters";
|
||||||
import { showToast } from "@ui/toasts";
|
import { showToast } from "@ui/toasts";
|
||||||
import { useProxy } from "@/lib/api/storage";
|
|
||||||
import { isThemeSupported } from "@/lib/api/native/loader";
|
|
||||||
import { Strings, formatString } from "@/core/i18n";
|
|
||||||
|
|
||||||
type PostType = "Plugin" | "Theme";
|
type PostType = "Plugin" | "Theme";
|
||||||
|
|
||||||
|
|
@ -35,7 +35,7 @@ const postMap = {
|
||||||
return isInstalled ? removeTheme(url) : installTheme(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]) | void {
|
||||||
if (thread.guild_id !== DISCORD_SERVER_ID) return;
|
if (thread.guild_id !== DISCORD_SERVER_ID) return;
|
||||||
|
|
@ -50,7 +50,7 @@ function useExtractThreadContent(thread: any, _firstMessage = null, actionSheet
|
||||||
|
|
||||||
const { firstMessage } = actionSheet ? useFirstForumPostMessage(thread) : { firstMessage: _firstMessage };
|
const { firstMessage } = actionSheet ? useFirstForumPostMessage(thread) : { firstMessage: _firstMessage };
|
||||||
|
|
||||||
let urls = firstMessage?.content?.match(HTTP_REGEX_MULTI)?.filter(postMap[postType].urlsFilter);
|
const urls = firstMessage?.content?.match(HTTP_REGEX_MULTI)?.filter(postMap[postType].urlsFilter);
|
||||||
if (!urls || !urls[0]) return;
|
if (!urls || !urls[0]) return;
|
||||||
|
|
||||||
// Sync with lib/managers/plugins
|
// Sync with lib/managers/plugins
|
||||||
|
|
@ -82,14 +82,14 @@ function useInstaller(thread: any, firstMessage = null, actionSheet = false): [t
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return [false, postType, isInstalled, isInstalling, installOrRemove]
|
return [false, postType, isInstalled, isInstalling, installOrRemove];
|
||||||
}
|
}
|
||||||
|
|
||||||
const actionSheetPatch = () => after("default", ForumPostLongPressActionSheet, ([{ thread }], res) => {
|
const actionSheetPatch = () => after("default", ForumPostLongPressActionSheet, ([{ thread }], res) => {
|
||||||
const [shouldReturn, postType, installed, loading, installOrRemove] = useInstaller(thread);
|
const [shouldReturn, postType, installed, loading, installOrRemove] = useInstaller(thread);
|
||||||
if (shouldReturn) return;
|
if (shouldReturn) return;
|
||||||
|
|
||||||
const actions = findInReactTree(res, (t) => t?.[0]?.key);
|
const actions = findInReactTree(res, t => t?.[0]?.key);
|
||||||
const ActionsSection = actions[0].type;
|
const ActionsSection = actions[0].type;
|
||||||
|
|
||||||
actions.unshift(<ActionsSection key="install">
|
actions.unshift(<ActionsSection key="install">
|
||||||
|
|
@ -118,14 +118,14 @@ const installButtonPatch = () => after("MostCommonForumPostReaction", forumReact
|
||||||
icon={getAssetIDByName(installed ? "ic_message_delete" : "DownloadIcon")}
|
icon={getAssetIDByName(installed ? "ic_message_delete" : "DownloadIcon")}
|
||||||
style={{ marginLeft: 8 }}
|
style={{ marginLeft: 8 }}
|
||||||
/>
|
/>
|
||||||
</>
|
</>;
|
||||||
});
|
});
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const patches = [
|
const patches = [
|
||||||
actionSheetPatch(),
|
actionSheetPatch(),
|
||||||
installButtonPatch()
|
installButtonPatch()
|
||||||
]
|
];
|
||||||
|
|
||||||
return () => patches.map(p => p());
|
return () => patches.map(p => p());
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -8,4 +8,4 @@ export default function onLoad() {
|
||||||
patches.push(patchUrl());
|
patches.push(patchUrl());
|
||||||
|
|
||||||
return () => patches.forEach(p => p());
|
return () => patches.forEach(p => p());
|
||||||
};
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
import { findByProps, find } from "@metro/filters";
|
import { formatString, Strings } from "@core/i18n";
|
||||||
import { ReactNative as RN, channels, url } from "@metro/common";
|
import { getAssetIDByName } from "@lib/api/assets";
|
||||||
import { PROXY_PREFIX, THEMES_CHANNEL_ID } from "@lib/utils/constants";
|
import { isThemeSupported } from "@lib/api/native/loader";
|
||||||
import { after, instead } from "@lib/api/patcher";
|
import { after, instead } from "@lib/api/patcher";
|
||||||
import { installPlugin } from "@lib/managers/plugins";
|
import { installPlugin } from "@lib/managers/plugins";
|
||||||
import { installTheme } from "@lib/managers/themes";
|
import { installTheme } from "@lib/managers/themes";
|
||||||
|
import { PROXY_PREFIX, THEMES_CHANNEL_ID } from "@lib/utils/constants";
|
||||||
|
import { channels, url } from "@metro/common";
|
||||||
|
import { find, findByProps } from "@metro/filters";
|
||||||
import { showConfirmationAlert } from "@ui/alerts";
|
import { showConfirmationAlert } from "@ui/alerts";
|
||||||
import { getAssetIDByName } from "@/lib/api/assets";
|
|
||||||
import { showToast } from "@ui/toasts";
|
import { showToast } from "@ui/toasts";
|
||||||
import { isThemeSupported } from "@/lib/api/native/loader";
|
|
||||||
import { Strings, formatString } from "@/core/i18n";
|
|
||||||
|
|
||||||
const showSimpleActionSheet = find((m) => m?.showSimpleActionSheet && !Object.getOwnPropertyDescriptor(m, "showSimpleActionSheet")?.get);
|
const showSimpleActionSheet = find(m => m?.showSimpleActionSheet && !Object.getOwnPropertyDescriptor(m, "showSimpleActionSheet")?.get);
|
||||||
const handleClick = findByProps("handleClick");
|
const handleClick = findByProps("handleClick");
|
||||||
const { openURL } = url;
|
const { openURL } = url;
|
||||||
const { getChannelId } = channels;
|
const { getChannelId } = channels;
|
||||||
|
|
@ -38,7 +38,7 @@ export default () => {
|
||||||
const patches = new Array<Function>();
|
const patches = new Array<Function>();
|
||||||
|
|
||||||
patches.push(
|
patches.push(
|
||||||
after("showSimpleActionSheet", showSimpleActionSheet, (args) => {
|
after("showSimpleActionSheet", showSimpleActionSheet, args => {
|
||||||
if (args[0].key !== "LongPressUrl") return;
|
if (args[0].key !== "LongPressUrl") return;
|
||||||
const {
|
const {
|
||||||
header: { title: url },
|
header: { title: url },
|
||||||
|
|
@ -77,5 +77,5 @@ export default () => {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
return () => patches.forEach((p) => p());
|
return () => patches.forEach(p => p());
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,4 @@ const mapAllSettled = <T>(item: T) => Promise.resolve(item).then(allSettledFulfi
|
||||||
export const allSettled = <T extends unknown[]>(iterator: T) => Promise.all(Array.from(iterator).map(mapAllSettled));
|
export const allSettled = <T extends unknown[]>(iterator: T) => Promise.all(Array.from(iterator).map(mapAllSettled));
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
Promise.allSettled ??= allSettled;
|
Promise.allSettled ??= allSettled;
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,36 @@
|
||||||
import { NavigationNative, ReactNative as RN, clipboard } from "@metro/common";
|
import { formatString, Strings } from "@core/i18n";
|
||||||
import { useProxy } from "@/lib/api/storage";
|
import { CardWrapper } from "@core/ui/components/Card";
|
||||||
import { HelpMessage, ErrorBoundary, Search } from "@ui/components";
|
import { getAssetIDByName } from "@lib/api/assets";
|
||||||
import { CardWrapper } from "@/core/ui/components/Card";
|
import { useProxy } from "@lib/api/storage";
|
||||||
|
import { findByProps } from "@lib/metro/filters";
|
||||||
import { settings } from "@lib/settings";
|
import { settings } from "@lib/settings";
|
||||||
import { findByProps } from "@/lib/metro/filters";
|
import { HTTP_REGEX_MULTI } from "@lib/utils/constants";
|
||||||
import { HTTP_REGEX_MULTI } from "@/lib/utils/constants";
|
import { clipboard, ReactNative as RN } from "@metro/common";
|
||||||
import { showInputAlert } from "@ui/alerts";
|
import { showInputAlert } from "@ui/alerts";
|
||||||
import { getAssetIDByName } from "@/lib/api/assets";
|
import { ErrorBoundary, HelpMessage, Search } from "@ui/components";
|
||||||
import { Strings, formatString } from "@/core/i18n";
|
|
||||||
import fuzzysort from "fuzzysort";
|
import fuzzysort from "fuzzysort";
|
||||||
|
|
||||||
interface AddonPageProps<T> {
|
interface AddonPageProps<T> {
|
||||||
title: string;
|
title: string;
|
||||||
fetchFunction: (url: string) => Promise<void>;
|
fetchFunction: (url: string) => Promise<void>;
|
||||||
items: Record<string, T & { id: string }>;
|
items: Record<string, T & { id: string; }>;
|
||||||
safeModeMessage: string;
|
safeModeMessage: string;
|
||||||
safeModeExtras?: JSX.Element | JSX.Element[];
|
safeModeExtras?: JSX.Element | JSX.Element[];
|
||||||
card: React.ComponentType<CardWrapper<T>>;
|
card: React.ComponentType<CardWrapper<T>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getItemsByQuery<T extends { id?: string }>(items: T[], query: string): T[] {
|
function getItemsByQuery<T extends { id?: string; }>(items: T[], query: string): T[] {
|
||||||
if (!query) return items;
|
if (!query) return items;
|
||||||
|
|
||||||
return fuzzysort.go(query, items, { keys: [
|
return fuzzysort.go(query, items, {
|
||||||
"id",
|
keys: [
|
||||||
"manifest.name",
|
"id",
|
||||||
"manifest.description",
|
"manifest.name",
|
||||||
"manifest.authors.0.name",
|
"manifest.description",
|
||||||
"manifest.authors.1.name"
|
"manifest.authors.0.name",
|
||||||
]}).map(r => r.obj);
|
"manifest.authors.1.name"
|
||||||
|
]
|
||||||
|
}).map(r => r.obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
const reanimated = findByProps("useSharedValue");
|
const reanimated = findByProps("useSharedValue");
|
||||||
|
|
@ -68,13 +70,13 @@ export default function AddonPage<T>({ title, fetchFunction, items, safeModeMess
|
||||||
data={getItemsByQuery(Object.values(items), search)}
|
data={getItemsByQuery(Object.values(items), search)}
|
||||||
renderItem={({ item, index }) => <CardComponent item={item} index={index} />}
|
renderItem={({ item, index }) => <CardComponent item={item} index={index} />}
|
||||||
/>
|
/>
|
||||||
<FloatingActionButton
|
<FloatingActionButton
|
||||||
text={formatString("INSTALL_TITLE", { title })}
|
text={formatString("INSTALL_TITLE", { title })}
|
||||||
icon={getAssetIDByName("DownloadIcon")}
|
icon={getAssetIDByName("DownloadIcon")}
|
||||||
state={{ collapseText }}
|
state={{ collapseText }}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
// from ./InstallButton.tsx
|
// from ./InstallButton.tsx
|
||||||
clipboard.getString().then((content) =>
|
clipboard.getString().then(content =>
|
||||||
showInputAlert({
|
showInputAlert({
|
||||||
title: formatString("INSTALL_TITLE", { title }),
|
title: formatString("INSTALL_TITLE", { title }),
|
||||||
initialValue: content.match(HTTP_REGEX_MULTI)?.[0] ?? "",
|
initialValue: content.match(HTTP_REGEX_MULTI)?.[0] ?? "",
|
||||||
|
|
@ -83,9 +85,9 @@ export default function AddonPage<T>({ title, fetchFunction, items, safeModeMess
|
||||||
confirmText: Strings.INSTALL,
|
confirmText: Strings.INSTALL,
|
||||||
cancelText: Strings.CANCEL,
|
cancelText: Strings.CANCEL,
|
||||||
})
|
})
|
||||||
)
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { ReactNative as RN, clipboard } from "@metro/common";
|
import { Asset } from "@lib/api/assets";
|
||||||
import { showToast } from "@ui/toasts";
|
import { clipboard, ReactNative as RN } from "@metro/common";
|
||||||
import { Forms } from "@ui/components";
|
import { Forms } from "@ui/components";
|
||||||
import { Asset } from "@/lib/api/assets";
|
import { showToast } from "@ui/toasts";
|
||||||
|
|
||||||
interface AssetDisplayProps { asset: Asset }
|
interface AssetDisplayProps { asset: Asset; }
|
||||||
|
|
||||||
const { FormRow } = Forms;
|
const { FormRow } = Forms;
|
||||||
|
|
||||||
|
|
@ -17,5 +17,5 @@ export default function AssetDisplay({ asset }: AssetDisplayProps) {
|
||||||
showToast.showCopyToClipboard();
|
showToast.showCopyToClipboard();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import ContextMenu from "@ui/components/ContextMenu";
|
import { getAssetIDByName } from "@lib/api/assets";
|
||||||
import { ReactNative as RN, stylesheet } from "@metro/common";
|
import { ReactNative as RN, stylesheet } from "@metro/common";
|
||||||
import { findByProps } from "@metro/filters";
|
import { findByProps } from "@metro/filters";
|
||||||
import { getAssetIDByName } from "@/lib/api/assets";
|
|
||||||
import { semanticColors } from "@ui/color";
|
import { semanticColors } from "@ui/color";
|
||||||
|
import ContextMenu from "@ui/components/ContextMenu";
|
||||||
|
|
||||||
const { TableRow, TableRowIcon, TableSwitchRow, TableCheckboxRow, TableRowGroup } = findByProps("TableRow");
|
const { TableRow, TableRowIcon, TableSwitchRow, TableCheckboxRow, TableRowGroup } = findByProps("TableRow");
|
||||||
|
|
||||||
|
|
@ -33,7 +33,7 @@ const styles = stylesheet.createThemedStyleSheet({
|
||||||
marginLeft: 5,
|
marginLeft: 5,
|
||||||
tintColor: semanticColors?.INTERACTIVE_NORMAL,
|
tintColor: semanticColors?.INTERACTIVE_NORMAL,
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
interface Action {
|
interface Action {
|
||||||
icon: string;
|
icon: string;
|
||||||
|
|
@ -77,43 +77,43 @@ export default function Card(props: CardProps) {
|
||||||
return (
|
return (
|
||||||
<RN.View style={[styles.card, { marginTop: props.index !== 0 ? 10 : 0 }]}>
|
<RN.View style={[styles.card, { marginTop: props.index !== 0 ? 10 : 0 }]}>
|
||||||
<TableRowGroup>
|
<TableRowGroup>
|
||||||
{props.toggleType && (props.toggleType === "switch" ?
|
{props.toggleType && (props.toggleType === "switch" ?
|
||||||
<TableSwitchRow
|
<TableSwitchRow
|
||||||
{...headerProps}
|
{...headerProps}
|
||||||
value={props.toggleValue}
|
value={props.toggleValue}
|
||||||
onValueChange={props.onToggleChange}
|
onValueChange={props.onToggleChange}
|
||||||
/> : props.toggleType === "radio" ?
|
/> : props.toggleType === "radio" ?
|
||||||
<TableCheckboxRow
|
<TableCheckboxRow
|
||||||
{...headerProps}
|
{...headerProps}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
pressableState = !pressableState;
|
pressableState = !pressableState;
|
||||||
props.onToggleChange?.(pressableState)
|
props.onToggleChange?.(pressableState);
|
||||||
}}
|
}}
|
||||||
checked={props.toggleValue}
|
checked={props.toggleValue}
|
||||||
/> : undefined) || <TableRow {...headerProps} />}
|
/> : undefined) || <TableRow {...headerProps} />}
|
||||||
<TableRow
|
<TableRow
|
||||||
label={props.descriptionLabel}
|
label={props.descriptionLabel}
|
||||||
trailing={
|
trailing={
|
||||||
<RN.View style={styles.actions}>
|
<RN.View style={styles.actions}>
|
||||||
{props.overflowActions &&
|
{props.overflowActions &&
|
||||||
<ContextMenu
|
<ContextMenu
|
||||||
triggerOnLongPress={false}
|
triggerOnLongPress={false}
|
||||||
items={props.overflowActions.map(i => ({
|
items={props.overflowActions.map(i => ({
|
||||||
label: i.label,
|
label: i.label,
|
||||||
iconSource: getAssetIDByName(i.icon),
|
iconSource: getAssetIDByName(i.icon),
|
||||||
action: i.onPress
|
action: i.onPress
|
||||||
}))}
|
}))}
|
||||||
align="auto"
|
align="auto"
|
||||||
title={props.overflowTitle!!}
|
title={props.overflowTitle!!}
|
||||||
children={(props) => <RN.TouchableOpacity {...props}>
|
children={props => <RN.TouchableOpacity {...props}>
|
||||||
<RN.Image style={styles.icon} source={getAssetIDByName("ic_more_24px")} />
|
<RN.Image style={styles.icon} source={getAssetIDByName("ic_more_24px")} />
|
||||||
</RN.TouchableOpacity>}
|
</RN.TouchableOpacity>}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</RN.View>
|
</RN.View>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</TableRowGroup>
|
</TableRowGroup>
|
||||||
</RN.View>
|
</RN.View>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { ReactNative as RN, stylesheet, clipboard } from "@metro/common";
|
import { Strings } from "@core/i18n";
|
||||||
|
import { getAssetIDByName } from "@lib/api/assets";
|
||||||
import { HTTP_REGEX_MULTI } from "@lib/utils/constants";
|
import { HTTP_REGEX_MULTI } from "@lib/utils/constants";
|
||||||
|
import { clipboard, ReactNative as RN, stylesheet } from "@metro/common";
|
||||||
import { showInputAlert } from "@ui/alerts";
|
import { showInputAlert } from "@ui/alerts";
|
||||||
import { getAssetIDByName } from "@/lib/api/assets";
|
|
||||||
import { semanticColors } from "@ui/color";
|
import { semanticColors } from "@ui/color";
|
||||||
import { Strings } from "@/core/i18n";
|
|
||||||
|
|
||||||
const styles = stylesheet.createThemedStyleSheet({
|
const styles = stylesheet.createThemedStyleSheet({
|
||||||
icon: {
|
icon: {
|
||||||
|
|
@ -20,7 +20,7 @@ interface InstallButtonProps {
|
||||||
export default function InstallButton({ alertTitle, installFunction: fetchFunction }: InstallButtonProps) {
|
export default function InstallButton({ alertTitle, installFunction: fetchFunction }: InstallButtonProps) {
|
||||||
return (
|
return (
|
||||||
<RN.TouchableOpacity onPress={() =>
|
<RN.TouchableOpacity onPress={() =>
|
||||||
clipboard.getString().then((content) =>
|
clipboard.getString().then(content =>
|
||||||
showInputAlert({
|
showInputAlert({
|
||||||
title: alertTitle,
|
title: alertTitle,
|
||||||
initialValue: content.match(HTTP_REGEX_MULTI)?.[0] ?? "",
|
initialValue: content.match(HTTP_REGEX_MULTI)?.[0] ?? "",
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import { NavigationNative, clipboard } from "@metro/common";
|
import { formatString, Strings } from "@core/i18n";
|
||||||
import { removePlugin, startPlugin, stopPlugin, getSettings, fetchPlugin, BunnyPlugin } from "@lib/managers/plugins";
|
import Card, { CardWrapper } from "@core/ui/components/Card";
|
||||||
|
import { getAssetIDByName } from "@lib/api/assets";
|
||||||
import { MMKVManager } from "@lib/api/native/modules";
|
import { MMKVManager } from "@lib/api/native/modules";
|
||||||
import { getAssetIDByName } from "@/lib/api/assets";
|
import { useProxy } from "@lib/api/storage";
|
||||||
import { showToast } from "@ui/toasts";
|
import { BunnyPlugin, fetchPlugin, getSettings, removePlugin, startPlugin, stopPlugin } from "@lib/managers/plugins";
|
||||||
|
import { ButtonColors } from "@lib/utils/types";
|
||||||
|
import { clipboard, NavigationNative } from "@metro/common";
|
||||||
import { showConfirmationAlert } from "@ui/alerts";
|
import { showConfirmationAlert } from "@ui/alerts";
|
||||||
import Card, { CardWrapper } from "@/core/ui/components/Card";
|
import { showToast } from "@ui/toasts";
|
||||||
import { useProxy } from "@/lib/api/storage";
|
|
||||||
import { ButtonColors } from "@/lib/utils/types";
|
|
||||||
import { Strings, formatString } from "@/core/i18n";
|
|
||||||
|
|
||||||
async function stopThenStart(plugin: BunnyPlugin, callback: Function) {
|
async function stopThenStart(plugin: BunnyPlugin, callback: Function) {
|
||||||
if (plugin.enabled) stopPlugin(plugin.id, false);
|
if (plugin.enabled) stopPlugin(plugin.id, false);
|
||||||
|
|
@ -60,7 +60,7 @@ export default function PluginCard({ item: plugin, index }: CardWrapper<BunnyPlu
|
||||||
showToast("Successfully refetched plugin.", getAssetIDByName("toast_image_saved"));
|
showToast("Successfully refetched plugin.", getAssetIDByName("toast_image_saved"));
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
showToast("Failed to refetch plugin!", getAssetIDByName("Small"));
|
showToast("Failed to refetch plugin!", getAssetIDByName("Small"));
|
||||||
})
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -72,7 +72,7 @@ export default function PluginCard({ item: plugin, index }: CardWrapper<BunnyPlu
|
||||||
showToast.showCopyToClipboard();
|
showToast.showCopyToClipboard();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ic_download_24px",
|
icon: "ic_download_24px",
|
||||||
label: plugin.update ? Strings.DISABLE_UPDATES : Strings.ENABLE_UPDATES,
|
label: plugin.update ? Strings.DISABLE_UPDATES : Strings.ENABLE_UPDATES,
|
||||||
onPress: () => {
|
onPress: () => {
|
||||||
|
|
@ -108,7 +108,7 @@ export default function PluginCard({ item: plugin, index }: CardWrapper<BunnyPlu
|
||||||
isDestructive: true,
|
isDestructive: true,
|
||||||
onPress: () => showConfirmationAlert({
|
onPress: () => showConfirmationAlert({
|
||||||
title: Strings.HOLD_UP,
|
title: Strings.HOLD_UP,
|
||||||
content: formatString("ARE_YOU_SURE_TO_DELETE_PLUGIN", {name: plugin.manifest.name}),
|
content: formatString("ARE_YOU_SURE_TO_DELETE_PLUGIN", { name: plugin.manifest.name }),
|
||||||
confirmText: Strings.DELETE,
|
confirmText: Strings.DELETE,
|
||||||
cancelText: Strings.CANCEL,
|
cancelText: Strings.CANCEL,
|
||||||
confirmColor: ButtonColors.RED,
|
confirmColor: ButtonColors.RED,
|
||||||
|
|
@ -124,5 +124,5 @@ export default function PluginCard({ item: plugin, index }: CardWrapper<BunnyPlu
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { NavigationNative } from "@metro/common";
|
import { Strings } from "@core/i18n";
|
||||||
import { useProxy } from "@/lib/api/storage";
|
import { getScreens } from "@core/ui/settings/data";
|
||||||
import { getAssetIDByName } from "@/lib/api/assets";
|
import { getAssetIDByName } from "@lib/api/assets";
|
||||||
import { getScreens } from "@/core/ui/settings/data";
|
import { useProxy } from "@lib/api/storage";
|
||||||
import { ErrorBoundary, Forms } from "@ui/components";
|
|
||||||
import { settings } from "@lib/settings";
|
import { settings } from "@lib/settings";
|
||||||
import { Strings } from "@/core/i18n";
|
import { NavigationNative } from "@metro/common";
|
||||||
|
import { ErrorBoundary, Forms } from "@ui/components";
|
||||||
|
|
||||||
const { FormRow, FormSection, FormDivider } = Forms;
|
const { FormRow, FormSection, FormDivider } = Forms;
|
||||||
|
|
||||||
|
|
@ -12,7 +12,7 @@ export default function SettingsSection() {
|
||||||
const navigation = NavigationNative.useNavigation();
|
const navigation = NavigationNative.useNavigation();
|
||||||
useProxy(settings);
|
useProxy(settings);
|
||||||
|
|
||||||
const screens = getScreens()
|
const screens = getScreens();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
|
|
@ -30,5 +30,5 @@ export default function SettingsSection() {
|
||||||
))}
|
))}
|
||||||
</FormSection>
|
</FormSection>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
import { clipboard } from "@metro/common";
|
import { formatString, Strings } from "@core/i18n";
|
||||||
import { Theme, applyTheme, fetchTheme, removeTheme, selectTheme, themes } from "@lib/managers/themes";
|
import Card, { CardWrapper } from "@core/ui/components/Card";
|
||||||
import { useProxy } from "@/lib/api/storage";
|
import { getAssetIDByName } from "@lib/api/assets";
|
||||||
import { BundleUpdaterManager } from "@lib/api/native/modules";
|
import { BundleUpdaterManager } from "@lib/api/native/modules";
|
||||||
import { getAssetIDByName } from "@/lib/api/assets";
|
import { useProxy } from "@lib/api/storage";
|
||||||
|
import { applyTheme, fetchTheme, removeTheme, selectTheme, Theme, themes } from "@lib/managers/themes";
|
||||||
|
import { settings } from "@lib/settings";
|
||||||
|
import { ButtonColors } from "@lib/utils/types";
|
||||||
|
import { clipboard } from "@metro/common";
|
||||||
import { showConfirmationAlert } from "@ui/alerts";
|
import { showConfirmationAlert } from "@ui/alerts";
|
||||||
import { showToast } from "@ui/toasts";
|
import { showToast } from "@ui/toasts";
|
||||||
import { settings } from "@lib/settings";
|
|
||||||
import Card, { CardWrapper } from "@/core/ui/components/Card";
|
|
||||||
import { ButtonColors } from "@/lib/utils/types";
|
|
||||||
import { Strings, formatString } from "@/core/i18n";
|
|
||||||
|
|
||||||
async function selectAndApply(value: boolean, id: string) {
|
async function selectAndApply(value: boolean, id: string) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -18,8 +18,8 @@ async function selectAndApply(value: boolean, id: string) {
|
||||||
// TODO: Implement native side reload-less & check if it's applied by 100%
|
// TODO: Implement native side reload-less & check if it's applied by 100%
|
||||||
showToast(Strings.THEMES_RELOAD_FOR_CHANGES, getAssetIDByName("yellow-alert"));
|
showToast(Strings.THEMES_RELOAD_FOR_CHANGES, getAssetIDByName("yellow-alert"));
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error("Error while selectAndApply,", e)
|
console.error("Error while selectAndApply,", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ThemeCard({ item: theme, index }: CardWrapper<Theme>) {
|
export default function ThemeCard({ item: theme, index }: CardWrapper<Theme>) {
|
||||||
|
|
@ -30,7 +30,7 @@ export default function ThemeCard({ item: theme, index }: CardWrapper<Theme>) {
|
||||||
// This is needed because of React™
|
// This is needed because of React™
|
||||||
if (removed) return null;
|
if (removed) return null;
|
||||||
|
|
||||||
const authors = theme.data.authors;
|
const { authors } = theme.data;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
|
|
@ -57,7 +57,7 @@ export default function ThemeCard({ item: theme, index }: CardWrapper<Theme>) {
|
||||||
cancelText: Strings.CANCEL,
|
cancelText: Strings.CANCEL,
|
||||||
confirmColor: ButtonColors.RED,
|
confirmColor: ButtonColors.RED,
|
||||||
onConfirm: () => BundleUpdaterManager.reload(),
|
onConfirm: () => BundleUpdaterManager.reload(),
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
showToast(Strings.THEME_REFETCH_SUCCESSFUL, getAssetIDByName("toast_image_saved"));
|
showToast(Strings.THEME_REFETCH_SUCCESSFUL, getAssetIDByName("toast_image_saved"));
|
||||||
}
|
}
|
||||||
|
|
@ -85,7 +85,7 @@ export default function ThemeCard({ item: theme, index }: CardWrapper<Theme>) {
|
||||||
cancelText: Strings.CANCEL,
|
cancelText: Strings.CANCEL,
|
||||||
confirmColor: ButtonColors.RED,
|
confirmColor: ButtonColors.RED,
|
||||||
onConfirm: () => {
|
onConfirm: () => {
|
||||||
removeTheme(theme.id).then((wasSelected) => {
|
removeTheme(theme.id).then(wasSelected => {
|
||||||
setRemoved(true);
|
setRemoved(true);
|
||||||
if (wasSelected) selectAndApply(false, theme.id);
|
if (wasSelected) selectAndApply(false, theme.id);
|
||||||
}).catch((e: Error) => {
|
}).catch((e: Error) => {
|
||||||
|
|
@ -96,5 +96,5 @@ export default function ThemeCard({ item: theme, index }: CardWrapper<Theme>) {
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
|
import { getAssetIDByName } from "@lib/api/assets";
|
||||||
|
import { findByProps } from "@lib/metro/filters";
|
||||||
import { clipboard } from "@metro/common";
|
import { clipboard } from "@metro/common";
|
||||||
import { getAssetIDByName } from "@/lib/api/assets";
|
|
||||||
import { showToast } from "@ui/toasts";
|
|
||||||
import { Forms } from "@ui/components";
|
import { Forms } from "@ui/components";
|
||||||
import { findByProps } from "@/lib/metro/filters";
|
import { showToast } from "@ui/toasts";
|
||||||
|
|
||||||
interface VersionProps {
|
interface VersionProps {
|
||||||
label: string;
|
label: string;
|
||||||
|
|
@ -15,7 +15,7 @@ const { TableRow } = findByProps("TableRow");
|
||||||
const { FormText } = Forms;
|
const { FormText } = Forms;
|
||||||
|
|
||||||
export default function Version({ label, version, icon, padding }: VersionProps) {
|
export default function Version({ label, version, icon, padding }: VersionProps) {
|
||||||
return (
|
return (
|
||||||
<TableRow
|
<TableRow
|
||||||
style={padding && { paddingHorizontal: 15, paddingVertical: -15 }}
|
style={padding && { paddingHorizontal: 15, paddingVertical: -15 }}
|
||||||
label={label}
|
label={label}
|
||||||
|
|
@ -26,5 +26,5 @@ export default function Version({ label, version, icon, padding }: VersionProps)
|
||||||
showToast.showCopyToClipboard();
|
showToast.showCopyToClipboard();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,23 @@
|
||||||
import { ReactNative as RN, NavigationNative, stylesheet, lodash } from "@metro/common";
|
import { Strings } from "@core/i18n";
|
||||||
|
import InstallButton from "@core/ui/components/InstallButton";
|
||||||
|
import Developer from "@core/ui/settings/pages/Developer";
|
||||||
|
import General from "@core/ui/settings/pages/General";
|
||||||
|
import Plugins from "@core/ui/settings/pages/Plugins";
|
||||||
|
import Themes from "@core/ui/settings/pages/Themes";
|
||||||
|
import { getAssetIDByName } from "@lib/api/assets";
|
||||||
|
import { isThemeSupported } from "@lib/api/native/loader";
|
||||||
|
import { useProxy } from "@lib/api/storage";
|
||||||
import { installPlugin } from "@lib/managers/plugins";
|
import { installPlugin } from "@lib/managers/plugins";
|
||||||
import { installTheme } from "@lib/managers/themes";
|
import { installTheme } from "@lib/managers/themes";
|
||||||
|
import { findByProps } from "@lib/metro/filters";
|
||||||
|
import { settings } from "@lib/settings";
|
||||||
|
import { without } from "@lib/utils";
|
||||||
|
import { PROXY_PREFIX } from "@lib/utils/constants";
|
||||||
|
import { NavigationNative, ReactNative as RN, stylesheet } from "@metro/common";
|
||||||
import { showConfirmationAlert } from "@ui/alerts";
|
import { showConfirmationAlert } from "@ui/alerts";
|
||||||
import { semanticColors } from "@ui/color";
|
import { semanticColors } from "@ui/color";
|
||||||
import { showToast } from "@ui/toasts";
|
|
||||||
import { without } from "@lib/utils";
|
|
||||||
import { getAssetIDByName } from "@/lib/api/assets";
|
|
||||||
import { settings } from "@lib/settings";
|
|
||||||
import ErrorBoundary from "@ui/components/ErrorBoundary";
|
import ErrorBoundary from "@ui/components/ErrorBoundary";
|
||||||
import InstallButton from "@/core/ui/components/InstallButton";
|
import { showToast } from "@ui/toasts";
|
||||||
import General from "@/core/ui/settings/pages/General";
|
|
||||||
import Plugins from "@/core/ui/settings/pages/Plugins";
|
|
||||||
import Themes from "@/core/ui/settings/pages/Themes";
|
|
||||||
import Developer from "@/core/ui/settings/pages/Developer";
|
|
||||||
import { PROXY_PREFIX } from "@/lib/utils/constants";
|
|
||||||
import { findByProps } from "@/lib/metro/filters";
|
|
||||||
import { isThemeSupported } from "@/lib/api/native/loader";
|
|
||||||
import { Strings } from "@/core/i18n";
|
|
||||||
import { useProxy } from "@/lib/api/storage";
|
|
||||||
|
|
||||||
const { useSafeAreaInsets } = findByProps("useSafeAreaInsets");
|
const { useSafeAreaInsets } = findByProps("useSafeAreaInsets");
|
||||||
|
|
||||||
|
|
@ -31,10 +31,10 @@ interface Screen {
|
||||||
render: React.ComponentType<any>;
|
render: React.ComponentType<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = stylesheet.createStyles({
|
const useStyles = stylesheet.createStyles({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
backgroundColor: semanticColors.BACKGROUND_MOBILE_PRIMARY
|
backgroundColor: semanticColors.BACKGROUND_MOBILE_PRIMARY
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -58,7 +58,7 @@ export const getScreens = (): Screen[] => [
|
||||||
headerRight: () => (
|
headerRight: () => (
|
||||||
<InstallButton
|
<InstallButton
|
||||||
alertTitle={Strings.INSTALL_PLUGIN}
|
alertTitle={Strings.INSTALL_PLUGIN}
|
||||||
installFunction={async (input) => {
|
installFunction={async input => {
|
||||||
if (!input.startsWith(PROXY_PREFIX) && !settings.developerSettings)
|
if (!input.startsWith(PROXY_PREFIX) && !settings.developerSettings)
|
||||||
setImmediate(() => showConfirmationAlert({
|
setImmediate(() => showConfirmationAlert({
|
||||||
title: Strings.MODAL_UNPROXIED_PLUGIN_HEADER,
|
title: Strings.MODAL_UNPROXIED_PLUGIN_HEADER,
|
||||||
|
|
@ -99,16 +99,16 @@ export const getScreens = (): Screen[] => [
|
||||||
key: "VendettaCustomPage",
|
key: "VendettaCustomPage",
|
||||||
title: "Vendetta Page",
|
title: "Vendetta Page",
|
||||||
shouldRender: () => false,
|
shouldRender: () => false,
|
||||||
render: ({ render: PageView, noErrorBoundary, ...options }: { render: React.ComponentType; noErrorBoundary: boolean } & Record<string, object>) => {
|
render: ({ render: PageView, noErrorBoundary, ...options }: { render: React.ComponentType; noErrorBoundary: boolean; } & Record<string, object>) => {
|
||||||
const navigation = NavigationNative.useNavigation();
|
const navigation = NavigationNative.useNavigation();
|
||||||
|
|
||||||
navigation.addListener("focus", () => navigation.setOptions(without(options, "render", "noErrorBoundary")));
|
navigation.addListener("focus", () => navigation.setOptions(without(options, "render", "noErrorBoundary")));
|
||||||
return noErrorBoundary ? <PageView /> : <ErrorBoundary><PageView /></ErrorBoundary>
|
return noErrorBoundary ? <PageView /> : <ErrorBoundary><PageView /></ErrorBoundary>;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const getPanelsScreens = () => keyMap(getScreens(), (s) => ({
|
export const getPanelsScreens = () => keyMap(getScreens(), s => ({
|
||||||
title: s.title,
|
title: s.title,
|
||||||
render: s.render,
|
render: s.render,
|
||||||
...s.options,
|
...s.options,
|
||||||
|
|
@ -128,12 +128,12 @@ export const getYouData = () => {
|
||||||
}),
|
}),
|
||||||
titleConfig: keyMap(screens, "title"),
|
titleConfig: keyMap(screens, "title"),
|
||||||
relationships: keyMap(screens, null),
|
relationships: keyMap(screens, null),
|
||||||
rendererConfigs: keyMap(screens, (s) => {
|
rendererConfigs: keyMap(screens, s => {
|
||||||
const WrappedComponent = React.memo(({ navigation, route }: any) => {
|
const WrappedComponent = React.memo(({ navigation, route }: any) => {
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
const { bottom: paddingBottom } = useSafeAreaInsets();
|
const { bottom: paddingBottom } = useSafeAreaInsets();
|
||||||
navigation.addListener("focus", () => navigation.setOptions(s.options));
|
navigation.addListener("focus", () => navigation.setOptions(s.options));
|
||||||
return <RN.View style={[{ paddingBottom }, styles.container]}><s.render {...route.params} /></RN.View>
|
return <RN.View style={[{ paddingBottom }, styles.container]}><s.render {...route.params} /></RN.View>;
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -145,7 +145,7 @@ export const getYouData = () => {
|
||||||
route: s.key,
|
route: s.key,
|
||||||
getComponent: () => WrappedComponent,
|
getComponent: () => WrappedComponent,
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
// TODO: make settings patches part of the api
|
// TODO: make settings patches part of the api
|
||||||
import patchPanels from "@/core/ui/settings/patches/panels";
|
import patchPanels from "@core/ui/settings/patches/panels";
|
||||||
import patchYou from "@/core/ui/settings/patches/you";
|
import patchYou from "@core/ui/settings/patches/you";
|
||||||
|
|
||||||
export default function initSettings() {
|
export default function initSettings() {
|
||||||
const patches = [
|
const patches = [
|
||||||
patchPanels(),
|
patchPanels(),
|
||||||
patchYou(),
|
patchYou(),
|
||||||
]
|
];
|
||||||
|
|
||||||
return () => patches.forEach(p => p?.());
|
return () => patches.forEach(p => p?.());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
|
import AssetDisplay from "@core/ui/components/AssetDisplay";
|
||||||
|
import { all } from "@lib/api/assets";
|
||||||
import { ReactNative as RN } from "@metro/common";
|
import { ReactNative as RN } from "@metro/common";
|
||||||
import { all } from "@/lib/api/assets";
|
import { ErrorBoundary, Forms, Search } from "@ui/components";
|
||||||
import { Forms, Search, ErrorBoundary } from "@ui/components";
|
|
||||||
import AssetDisplay from "@/core/ui/components/AssetDisplay";
|
|
||||||
|
|
||||||
const { FormDivider } = Forms;
|
const { FormDivider } = Forms;
|
||||||
|
|
||||||
|
|
@ -24,5 +24,5 @@ export default function AssetBrowser() {
|
||||||
/>
|
/>
|
||||||
</RN.View>
|
</RN.View>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
import { ReactNative as RN, NavigationNative, stylesheet } from "@metro/common";
|
import { Strings } from "@core/i18n";
|
||||||
import { findByProps } from "@metro/filters";
|
import AssetBrowser from "@core/ui/settings/pages/AssetBrowser";
|
||||||
|
import { getAssetIDByName } from "@lib/api/assets";
|
||||||
|
import { getReactDevToolsProp, getReactDevToolsVersion, isLoaderConfigSupported, isReactDevToolsPreloaded } from "@lib/api/native/loader";
|
||||||
|
import { useProxy } from "@lib/api/storage";
|
||||||
import { connectToDebugger } from "@lib/debug";
|
import { connectToDebugger } from "@lib/debug";
|
||||||
import { useProxy } from "@/lib/api/storage";
|
import { loaderConfig, settings } from "@lib/settings";
|
||||||
import { getAssetIDByName } from "@/lib/api/assets";
|
import { NavigationNative, ReactNative as RN, stylesheet } from "@metro/common";
|
||||||
import { ErrorBoundary, Forms } from "@ui/components";
|
import { findByProps } from "@metro/filters";
|
||||||
import { settings, loaderConfig } from "@lib/settings";
|
|
||||||
import AssetBrowser from "@/core/ui/settings/pages/AssetBrowser";
|
|
||||||
import { semanticColors } from "@ui/color";
|
import { semanticColors } from "@ui/color";
|
||||||
import { getReactDevToolsProp, getReactDevToolsVersion, isLoaderConfigSupported, isReactDevToolsPreloaded } from "@/lib/api/native/loader";
|
import { ErrorBoundary, Forms } from "@ui/components";
|
||||||
import { Strings } from "@/core/i18n";
|
|
||||||
|
|
||||||
const { Stack, TableRow, TableSwitchRow, TableRowGroup, TextInput } = findByProps("TableRow");
|
const { Stack, TableRow, TableSwitchRow, TableRowGroup, TextInput } = findByProps("TableRow");
|
||||||
const { FormText } = Forms;
|
const { FormText } = Forms;
|
||||||
|
|
@ -99,7 +99,7 @@ export default function Developer() {
|
||||||
render: AssetBrowser,
|
render: AssetBrowser,
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<TableRow
|
<TableRow
|
||||||
arrow
|
arrow
|
||||||
label="ErrorBoundary Tools"
|
label="ErrorBoundary Tools"
|
||||||
icon={<TableRow.Icon source={getAssetIDByName("ic_warning_24px")} />}
|
icon={<TableRow.Icon source={getAssetIDByName("ic_warning_24px")} />}
|
||||||
|
|
@ -111,7 +111,7 @@ export default function Developer() {
|
||||||
onClose: () => hideActionSheet(),
|
onClose: () => hideActionSheet(),
|
||||||
},
|
},
|
||||||
options: [
|
options: [
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
// Of course, to trigger an error, we need to do something incorrectly. The below will do!
|
// Of course, to trigger an error, we need to do something incorrectly. The below will do!
|
||||||
{ label: Strings.BUNNY, onPress: () => navigation.push("VendettaCustomPage", { render: () => <undefined /> }) },
|
{ label: Strings.BUNNY, onPress: () => navigation.push("VendettaCustomPage", { render: () => <undefined /> }) },
|
||||||
{ label: "Discord", isDestructive: true, onPress: () => navigation.push("VendettaCustomPage", { noErrorBoundary: true }) },
|
{ label: "Discord", isDestructive: true, onPress: () => navigation.push("VendettaCustomPage", { noErrorBoundary: true }) },
|
||||||
|
|
@ -122,5 +122,5 @@ export default function Developer() {
|
||||||
</Stack>
|
</Stack>
|
||||||
</RN.ScrollView>
|
</RN.ScrollView>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
import { ReactNative as RN, stylesheet, url } from "@metro/common";
|
import { Strings } from "@core/i18n";
|
||||||
import { DISCORD_SERVER, GITHUB } from "@lib/utils/constants";
|
import Version from "@core/ui/components/Version";
|
||||||
|
import { getAssetIDByName } from "@lib/api/assets";
|
||||||
|
import { useProxy } from "@lib/api/storage";
|
||||||
import { getDebugInfo, toggleSafeMode } from "@lib/debug";
|
import { getDebugInfo, toggleSafeMode } from "@lib/debug";
|
||||||
import { useProxy } from "@/lib/api/storage";
|
|
||||||
import { getAssetIDByName } from "@/lib/api/assets";
|
|
||||||
import { Summary } from "@ui/components";
|
|
||||||
import { settings } from "@lib/settings";
|
|
||||||
import Version from "@/core/ui/components/Version";
|
|
||||||
import { findByProps } from "@lib/metro/filters";
|
import { findByProps } from "@lib/metro/filters";
|
||||||
|
import { settings } from "@lib/settings";
|
||||||
|
import { DISCORD_SERVER, GITHUB } from "@lib/utils/constants";
|
||||||
|
import { ReactNative as RN, stylesheet, url } from "@metro/common";
|
||||||
import { semanticColors } from "@ui/color";
|
import { semanticColors } from "@ui/color";
|
||||||
import { Strings } from "@/core/i18n";
|
import { Summary } from "@ui/components";
|
||||||
|
|
||||||
const { Stack, TableRow, TableSwitchRow, TableRowGroup } = findByProps("TableRow");
|
const { Stack, TableRow, TableSwitchRow, TableRowGroup } = findByProps("TableRow");
|
||||||
const debugInfo = getDebugInfo();
|
const debugInfo = getDebugInfo();
|
||||||
|
|
@ -18,7 +18,7 @@ const styles = stylesheet.createThemedStyleSheet({
|
||||||
flex: 1,
|
flex: 1,
|
||||||
backgroundColor: semanticColors.BACKGROUND_MOBILE_SECONDARY,
|
backgroundColor: semanticColors.BACKGROUND_MOBILE_SECONDARY,
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
export default function General() {
|
export default function General() {
|
||||||
useProxy(settings);
|
useProxy(settings);
|
||||||
|
|
@ -137,5 +137,5 @@ export default function General() {
|
||||||
</TableRowGroup>
|
</TableRowGroup>
|
||||||
</Stack>
|
</Stack>
|
||||||
</RN.ScrollView>
|
</RN.ScrollView>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import { useProxy } from "@/lib/api/storage";
|
import { Strings } from "@core/i18n";
|
||||||
|
import AddonPage from "@core/ui/components/AddonPage";
|
||||||
|
import PluginCard from "@core/ui/components/PluginCard";
|
||||||
|
import { useProxy } from "@lib/api/storage";
|
||||||
import { BunnyPlugin, installPlugin, plugins } from "@lib/managers/plugins";
|
import { BunnyPlugin, installPlugin, plugins } from "@lib/managers/plugins";
|
||||||
import { settings } from "@lib/settings";
|
import { settings } from "@lib/settings";
|
||||||
import AddonPage from "@/core/ui/components/AddonPage";
|
|
||||||
import PluginCard from "@/core/ui/components/PluginCard";
|
|
||||||
import { Strings } from "@/core/i18n";
|
|
||||||
|
|
||||||
export default function Plugins() {
|
export default function Plugins() {
|
||||||
useProxy(settings)
|
useProxy(settings);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AddonPage<BunnyPlugin>
|
<AddonPage<BunnyPlugin>
|
||||||
|
|
@ -16,5 +16,5 @@ export default function Plugins() {
|
||||||
safeModeMessage={Strings.SAFE_MODE_NOTICE_PLUGINS}
|
safeModeMessage={Strings.SAFE_MODE_NOTICE_PLUGINS}
|
||||||
card={PluginCard}
|
card={PluginCard}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { useProxy } from "@/lib/api/storage";
|
import { Strings } from "@core/i18n";
|
||||||
import { Theme, installTheme, themes } from "@lib/managers/themes";
|
import AddonPage from "@core/ui/components/AddonPage";
|
||||||
import { Button } from "@ui/components";
|
import ThemeCard from "@core/ui/components/ThemeCard";
|
||||||
|
import { useProxy } from "@lib/api/storage";
|
||||||
|
import { installTheme, Theme, themes } from "@lib/managers/themes";
|
||||||
import { settings } from "@lib/settings";
|
import { settings } from "@lib/settings";
|
||||||
import AddonPage from "@/core/ui/components/AddonPage";
|
import { ButtonColors } from "@lib/utils/types";
|
||||||
import ThemeCard from "@/core/ui/components/ThemeCard";
|
import { Button } from "@ui/components";
|
||||||
import { ButtonColors } from "@/lib/utils/types";
|
|
||||||
import { Strings } from "@/core/i18n";
|
|
||||||
|
|
||||||
export default function Themes() {
|
export default function Themes() {
|
||||||
useProxy(settings);
|
useProxy(settings);
|
||||||
|
|
@ -27,5 +27,5 @@ export default function Themes() {
|
||||||
/> : undefined}
|
/> : undefined}
|
||||||
card={ThemeCard}
|
card={ThemeCard}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { i18n } from "@metro/common";
|
import SettingsSection from "@core/ui/components/SettingsSection";
|
||||||
import { findByName } from "@metro/filters";
|
import { getPanelsScreens } from "@core/ui/settings/data";
|
||||||
import { after } from "@lib/api/patcher";
|
import { after } from "@lib/api/patcher";
|
||||||
import { findInReactTree } from "@lib/utils";
|
import { findInReactTree } from "@lib/utils";
|
||||||
import { getPanelsScreens } from "@/core/ui/settings/data";
|
import { i18n } from "@metro/common";
|
||||||
import SettingsSection from "@/core/ui/components/SettingsSection";
|
import { findByName } from "@metro/filters";
|
||||||
|
|
||||||
const screensModule = findByName("getScreens", false);
|
const screensModule = findByName("getScreens", false);
|
||||||
const settingsModule = findByName("UserSettingsOverviewWrapper", false);
|
const settingsModule = findByName("UserSettingsOverviewWrapper", false);
|
||||||
|
|
@ -27,9 +27,9 @@ export default function patchPanels() {
|
||||||
|
|
||||||
// TODO: Rewrite this whole patch, the index hasn't been properly found for months now
|
// TODO: Rewrite this whole patch, the index hasn't been properly found for months now
|
||||||
patches.push(after("render", Overview.type.prototype, (_, { props: { children } }) => {
|
patches.push(after("render", Overview.type.prototype, (_, { props: { children } }) => {
|
||||||
const titles = [i18n.Messages["BILLING_SETTINGS"], i18n.Messages["PREMIUM_SETTINGS"]];
|
const titles = [i18n.Messages.BILLING_SETTINGS, i18n.Messages.PREMIUM_SETTINGS];
|
||||||
//! Fix for Android 174201 and iOS 42188
|
//! Fix for Android 174201 and iOS 42188
|
||||||
children = findInReactTree(children, i => i.children?.[1].type?.name === "FormSection").children;
|
({ children } = findInReactTree(children, i => i.children?.[1].type?.name === "FormSection"));
|
||||||
const index = children.findIndex((c: any) => titles.includes(c?.props.label));
|
const index = children.findIndex((c: any) => titles.includes(c?.props.label));
|
||||||
children.splice(index === -1 ? 4 : index, 0, <SettingsSection />);
|
children.splice(index === -1 ? 4 : index, 0, <SettingsSection />);
|
||||||
}));
|
}));
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { findByProps } from "@metro/filters";
|
import { Strings } from "@core/i18n";
|
||||||
|
import { getScreens, getYouData } from "@core/ui/settings/data";
|
||||||
import { after, before } from "@lib/api/patcher";
|
import { after, before } from "@lib/api/patcher";
|
||||||
import { getScreens, getYouData } from "@/core/ui/settings/data";
|
|
||||||
import { i18n } from "@lib/metro/common";
|
import { i18n } from "@lib/metro/common";
|
||||||
import { Strings } from "@/core/i18n";
|
import { findByProps } from "@metro/filters";
|
||||||
|
|
||||||
export default function patchYou() {
|
export default function patchYou() {
|
||||||
const patches = new Array<Function>;
|
const patches = new Array<Function>;
|
||||||
|
|
@ -53,7 +53,7 @@ function oldYouPatch(patches: Function[]) {
|
||||||
|
|
||||||
const oldRelationships = miscModule.SETTING_RELATIONSHIPS;
|
const oldRelationships = miscModule.SETTING_RELATIONSHIPS;
|
||||||
const oldRendererConfigs = miscModule.SETTING_RENDERER_CONFIGS;
|
const oldRendererConfigs = miscModule.SETTING_RENDERER_CONFIGS;
|
||||||
|
|
||||||
miscModule.SETTING_RELATIONSHIPS = { ...oldRelationships, ...data.relationships };
|
miscModule.SETTING_RELATIONSHIPS = { ...oldRelationships, ...data.relationships };
|
||||||
miscModule.SETTING_RENDERER_CONFIGS = { ...oldRendererConfigs, ...data.rendererConfigs };
|
miscModule.SETTING_RENDERER_CONFIGS = { ...oldRendererConfigs, ...data.rendererConfigs };
|
||||||
|
|
||||||
|
|
@ -78,7 +78,7 @@ function newYouPatch(patches: Function[]) {
|
||||||
patches.push(before("type", settingsListComponents.SearchableSettingsList, ([{ sections }]) => manipulateSections(sections, data.getLayout())));
|
patches.push(before("type", settingsListComponents.SearchableSettingsList, ([{ sections }]) => manipulateSections(sections, data.getLayout())));
|
||||||
|
|
||||||
patches.push(after("getSettingListSearchResultItems", gettersModule, (_, ret) => {
|
patches.push(after("getSettingListSearchResultItems", gettersModule, (_, ret) => {
|
||||||
ret.forEach((s: any) => screens.some(b => b.key === s.setting) && (s.breadcrumbs = [Strings.BUNNY]))
|
ret.forEach((s: any) => screens.some(b => b.key === s.setting) && (s.breadcrumbs = [Strings.BUNNY]));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const oldRendererConfig = settingConstantsModule.SETTING_RENDERER_CONFIG;
|
const oldRendererConfig = settingConstantsModule.SETTING_RENDERER_CONFIG;
|
||||||
|
|
@ -102,5 +102,5 @@ function manipulateSections(sections: any[], layout: any) {
|
||||||
|
|
||||||
// Upload Logs button be gone
|
// Upload Logs button be gone
|
||||||
const supportCategory = sections.find((i: any) => isLabel(i, i18n.Messages.SUPPORT));
|
const supportCategory = sections.find((i: any) => isLabel(i, i18n.Messages.SUPPORT));
|
||||||
if (supportCategory) supportCategory.settings = supportCategory.settings.filter((s: string) => s !== "UPLOAD_DEBUG_LOGS")
|
if (supportCategory) supportCategory.settings = supportCategory.settings.filter((s: string) => s !== "UPLOAD_DEBUG_LOGS");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
import patcher from "@lib/api/patcher";
|
import * as assets from "@lib/api/assets";
|
||||||
|
import * as commands from "@lib/api/commands";
|
||||||
import { getVendettaLoaderIdentity } from "@lib/api/native/loader";
|
import { getVendettaLoaderIdentity } from "@lib/api/native/loader";
|
||||||
import { settings, loaderConfig } from "@lib/settings";
|
import patcher from "@lib/api/patcher";
|
||||||
|
import * as storage from "@lib/api/storage";
|
||||||
import * as debug from "@lib/debug";
|
import * as debug from "@lib/debug";
|
||||||
import * as plugins from "@lib/managers/plugins";
|
import * as plugins from "@lib/managers/plugins";
|
||||||
import * as themes from "@lib/managers/themes";
|
import * as themes from "@lib/managers/themes";
|
||||||
import * as commands from "@/lib/api/commands";
|
import { loaderConfig, settings } from "@lib/settings";
|
||||||
import * as storage from "@/lib/api/storage";
|
import * as utils from "@lib/utils";
|
||||||
import * as metro from "@metro/filters";
|
|
||||||
import * as common from "@metro/common";
|
import * as common from "@metro/common";
|
||||||
|
import * as metro from "@metro/filters";
|
||||||
|
import * as alerts from "@ui/alerts";
|
||||||
|
import * as color from "@ui/color";
|
||||||
import * as components from "@ui/components";
|
import * as components from "@ui/components";
|
||||||
import * as toasts from "@ui/toasts";
|
import * as toasts from "@ui/toasts";
|
||||||
import * as alerts from "@ui/alerts";
|
|
||||||
import * as assets from "@/lib/api/assets";
|
|
||||||
import * as color from "@ui/color";
|
|
||||||
import * as utils from "@lib/utils";
|
|
||||||
|
|
||||||
export const createVendettaObject = (unloads: any[]): any => ({
|
export const createVendettaObject = (unloads: any[]): any => ({
|
||||||
patcher: {
|
patcher: {
|
||||||
|
|
@ -21,7 +21,7 @@ export const createVendettaObject = (unloads: any[]): any => ({
|
||||||
after: patcher.after,
|
after: patcher.after,
|
||||||
instead: patcher.instead
|
instead: patcher.instead
|
||||||
},
|
},
|
||||||
metro: {
|
metro: {
|
||||||
find: (filter: (m: any) => boolean) => metro.find(filter),
|
find: (filter: (m: any) => boolean) => metro.find(filter),
|
||||||
findAll: (filter: (m: any) => boolean) => metro.findAll(filter),
|
findAll: (filter: (m: any) => boolean) => metro.findAll(filter),
|
||||||
findByProps: (...props: any) => metro.findByProps(...props),
|
findByProps: (...props: any) => metro.findByProps(...props),
|
||||||
|
|
@ -60,9 +60,9 @@ export const createVendettaObject = (unloads: any[]): any => ({
|
||||||
constants: {
|
constants: {
|
||||||
DISCORD_SERVER: "https://discord.gg/n9QQ4XhhJP",
|
DISCORD_SERVER: "https://discord.gg/n9QQ4XhhJP",
|
||||||
GITHUB: "https://github.com/vendetta-mod",
|
GITHUB: "https://github.com/vendetta-mod",
|
||||||
PROXY_PREFIX: "https://vd-plugins.github.io/proxy",
|
PROXY_PREFIX: "https://vd-plugins.github.io/proxy",
|
||||||
HTTP_REGEX: /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)$/,
|
HTTP_REGEX: /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)$/,
|
||||||
HTTP_REGEX_MULTI: /https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g,
|
HTTP_REGEX_MULTI: /https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_+.~#?&//=]*)/g,
|
||||||
DISCORD_SERVER_ID: "1015931589865246730",
|
DISCORD_SERVER_ID: "1015931589865246730",
|
||||||
PLUGINS_CHANNEL_ID: "1091880384561684561",
|
PLUGINS_CHANNEL_ID: "1091880384561684561",
|
||||||
THEMES_CHANNEL_ID: "1091880434939482202",
|
THEMES_CHANNEL_ID: "1091880434939482202",
|
||||||
|
|
|
||||||
18
src/entry.ts
18
src/entry.ts
|
|
@ -1,5 +1,5 @@
|
||||||
import { instead } from "spitroast";
|
|
||||||
import { version } from "bunny-build";
|
import { version } from "bunny-build";
|
||||||
|
import { instead } from "spitroast";
|
||||||
|
|
||||||
// @ts-ignore - shut up fr
|
// @ts-ignore - shut up fr
|
||||||
globalThis.window = globalThis;
|
globalThis.window = globalThis;
|
||||||
|
|
@ -8,11 +8,11 @@ const init = (async () => {
|
||||||
try {
|
try {
|
||||||
// This logs in the native logging implementation, e.g. logcat
|
// This logs in the native logging implementation, e.g. logcat
|
||||||
console.log("Hello from Bunny! Pyon!");
|
console.log("Hello from Bunny! Pyon!");
|
||||||
|
|
||||||
// Make 'freeze' and 'seal' do nothing
|
// Make 'freeze' and 'seal' do nothing
|
||||||
Object.freeze = Object.seal = Object;
|
Object.freeze = Object.seal = Object;
|
||||||
|
|
||||||
await import(".").then((m) => m.default());
|
await import(".").then(m => m.default());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const { ClientInfoManager } = require("@lib/api/native/modules");
|
const { ClientInfoManager } = require("@lib/api/native/modules");
|
||||||
const stack = e instanceof Error ? e.stack : undefined;
|
const stack = e instanceof Error ? e.stack : undefined;
|
||||||
|
|
@ -50,14 +50,14 @@ if (typeof globalThis.__r !== "undefined") {
|
||||||
unpatch();
|
unpatch();
|
||||||
originalRequire(0);
|
originalRequire(0);
|
||||||
|
|
||||||
callQueue.forEach(arg =>
|
callQueue.forEach(arg =>
|
||||||
batchedBridge.getCallableModule(arg[0])
|
batchedBridge.getCallableModule(arg[0])
|
||||||
&& batchedBridge.__callFunction(...arg));
|
&& batchedBridge.__callFunction(...arg));
|
||||||
};
|
};
|
||||||
|
|
||||||
startDiscord();
|
startDiscord();
|
||||||
}
|
}
|
||||||
|
|
||||||
var _requireFunc: any; // We can't set properties to 'this' during __r set for some reason
|
var _requireFunc: any; // We can't set properties to 'this' during __r set for some reason
|
||||||
|
|
||||||
Object.defineProperties(globalThis, {
|
Object.defineProperties(globalThis, {
|
||||||
|
|
@ -71,7 +71,7 @@ if (typeof globalThis.__r !== "undefined") {
|
||||||
onceIndexRequired(v);
|
onceIndexRequired(v);
|
||||||
_requireFunc = v;
|
_requireFunc = v;
|
||||||
} else return v(a);
|
} else return v(a);
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
__d: {
|
__d: {
|
||||||
|
|
@ -83,7 +83,7 @@ if (typeof globalThis.__r !== "undefined") {
|
||||||
}
|
}
|
||||||
return this.value;
|
return this.value;
|
||||||
},
|
},
|
||||||
set(v) { this.value = v }
|
set(v) { this.value = v; }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
16
src/index.ts
16
src/index.ts
|
|
@ -1,15 +1,15 @@
|
||||||
|
import initFixes from "@core/fixes";
|
||||||
|
import { initCorePlugins } from "@core/plugins";
|
||||||
|
import initSettings from "@core/ui/settings";
|
||||||
|
import { patchAssets } from "@lib/api/assets";
|
||||||
|
import { patchCommands } from "@lib/api/commands";
|
||||||
|
import { isThemeSupported } from "@lib/api/native/loader";
|
||||||
import { patchLogHook } from "@lib/debug";
|
import { patchLogHook } from "@lib/debug";
|
||||||
import { patchCommands } from "@/lib/api/commands";
|
|
||||||
import { initPlugins } from "@lib/managers/plugins";
|
import { initPlugins } from "@lib/managers/plugins";
|
||||||
import { initThemes, patchChatBackground } from "@lib/managers/themes";
|
import { initThemes, patchChatBackground } from "@lib/managers/themes";
|
||||||
import { patchAssets } from "@/lib/api/assets";
|
|
||||||
import initSafeMode from "@ui/safeMode";
|
|
||||||
import initSettings from "@/core/ui/settings";
|
|
||||||
import initFixes from "@/core/fixes";
|
|
||||||
import logger from "@lib/utils/logger";
|
import logger from "@lib/utils/logger";
|
||||||
import initWindowObject from "@lib/windowObject";
|
import initWindowObject from "@lib/windowObject";
|
||||||
import { initCorePlugins } from "./core/plugins";
|
import initSafeMode from "@ui/safeMode";
|
||||||
import { isThemeSupported } from "./lib/api/native/loader";
|
|
||||||
|
|
||||||
export default async () => {
|
export default async () => {
|
||||||
// Themes
|
// Themes
|
||||||
|
|
@ -41,4 +41,4 @@ export default async () => {
|
||||||
|
|
||||||
// We good :)
|
// We good :)
|
||||||
logger.log("Bunny is ready!");
|
logger.log("Bunny is ready!");
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { assets } from "@metro/common";
|
|
||||||
import { after } from "@lib/api/patcher";
|
import { after } from "@lib/api/patcher";
|
||||||
|
import { assets } from "@metro/common";
|
||||||
|
|
||||||
export const all: Record<string, Asset> = {};
|
export const all: Record<string, Asset> = {};
|
||||||
|
|
||||||
|
|
@ -19,7 +19,7 @@ export function patchAssets() {
|
||||||
if (!asset) break;
|
if (!asset) break;
|
||||||
if (all[asset.name]) continue;
|
if (all[asset.name]) continue;
|
||||||
all[asset.name] = { ...asset, id: id };
|
all[asset.name] = { ...asset, id: id };
|
||||||
};
|
}
|
||||||
|
|
||||||
return unpatch;
|
return unpatch;
|
||||||
}
|
}
|
||||||
|
|
@ -27,4 +27,4 @@ export function patchAssets() {
|
||||||
export const find = (filter: (a: any) => void): Asset | null | undefined => Object.values(all).find(filter);
|
export const find = (filter: (a: any) => void): Asset | null | undefined => Object.values(all).find(filter);
|
||||||
export const getAssetByName = (name: string): Asset => all[name];
|
export const getAssetByName = (name: string): Asset => all[name];
|
||||||
export const getAssetByID = (id: number): Asset => assets.getAssetByID(id);
|
export const getAssetByID = (id: number): Asset => assets.getAssetByID(id);
|
||||||
export const getAssetIDByName = (name: string) => all[name]?.id;
|
export const getAssetIDByName = (name: string) => all[name]?.id;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { commands as commandsModule } from "@metro/common";
|
|
||||||
import { after } from "@lib/api/patcher";
|
import { after } from "@lib/api/patcher";
|
||||||
import { ApplicationCommand, ApplicationCommandType, ApplicationCommandInputType } from "./types";
|
import { commands as commandsModule } from "@metro/common";
|
||||||
|
|
||||||
|
import { ApplicationCommand, ApplicationCommandInputType, ApplicationCommandType } from "./types";
|
||||||
|
|
||||||
let commands: ApplicationCommand[] = [];
|
let commands: ApplicationCommand[] = [];
|
||||||
|
|
||||||
|
|
@ -11,9 +12,9 @@ export function patchCommands() {
|
||||||
|
|
||||||
// Register core commands
|
// Register core commands
|
||||||
[
|
[
|
||||||
// require("@/core/commands/eval"),
|
// require("@core/commands/eval"),
|
||||||
require("@/core/commands/debug"),
|
require("@core/commands/debug"),
|
||||||
require("@/core/commands/plugins")
|
require("@core/commands/plugins")
|
||||||
].forEach(r => registerCommand(r.default()));
|
].forEach(r => registerCommand(r.default()));
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
|
|
||||||
|
|
@ -65,4 +65,4 @@ export interface CommandContext {
|
||||||
export interface CommandResult {
|
export interface CommandResult {
|
||||||
content: string;
|
content: string;
|
||||||
tts?: boolean;
|
tts?: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
|
export * as assets from "./assets";
|
||||||
export * as commands from "./commands";
|
export * as commands from "./commands";
|
||||||
export * as native from "./native";
|
export * as native from "./native";
|
||||||
|
export * as patcher from "./patcher";
|
||||||
export * as storage from "./storage";
|
export * as storage from "./storage";
|
||||||
export * as assets from "./assets";
|
|
||||||
export * as patcher from "./patcher";
|
|
||||||
|
|
@ -70,4 +70,4 @@ export async function downloadFile(url: string, path: string, prefix = "pyoncord
|
||||||
// const data = Buffer.from(arrayBuffer).toString("base64");
|
// const data = Buffer.from(arrayBuffer).toString("base64");
|
||||||
|
|
||||||
// return void await FileManager.writeFile("documents", `${prefix}${path}`, data, "base64");
|
// return void await FileManager.writeFile("documents", `${prefix}${path}`, data, "base64");
|
||||||
// }
|
// }
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
export * as fs from "./fs";
|
export * as fs from "./fs";
|
||||||
export * as modules from "./modules";
|
|
||||||
export * as loader from "./loader";
|
export * as loader from "./loader";
|
||||||
|
export * as modules from "./modules";
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Theme } from "../../managers/themes";
|
import { Theme } from "@lib/managers/themes";
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const pyonLoaderIdentity = globalThis.__PYON_LOADER__;
|
const pyonLoaderIdentity = globalThis.__PYON_LOADER__;
|
||||||
|
|
@ -15,8 +15,8 @@ export interface VendettaLoaderIdentity {
|
||||||
},
|
},
|
||||||
themes?: {
|
themes?: {
|
||||||
prop: string;
|
prop: string;
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isVendettaLoader() {
|
export function isVendettaLoader() {
|
||||||
|
|
@ -33,13 +33,13 @@ function polyfillVendettaLoaderIdentity() {
|
||||||
const loader = {
|
const loader = {
|
||||||
name: pyonLoaderIdentity.loaderName,
|
name: pyonLoaderIdentity.loaderName,
|
||||||
features: {} as Record<string, any>
|
features: {} as Record<string, any>
|
||||||
}
|
};
|
||||||
|
|
||||||
if (isLoaderConfigSupported()) loader.features.loaderConfig = true;
|
if (isLoaderConfigSupported()) loader.features.loaderConfig = true;
|
||||||
if (isSysColorsSupported()) {
|
if (isSysColorsSupported()) {
|
||||||
loader.features.syscolors = {
|
loader.features.syscolors = {
|
||||||
prop: "__vendetta_syscolors"
|
prop: "__vendetta_syscolors"
|
||||||
}
|
};
|
||||||
|
|
||||||
Object.defineProperty(globalThis, "__vendetta_syscolors", {
|
Object.defineProperty(globalThis, "__vendetta_syscolors", {
|
||||||
get: () => getSysColors(),
|
get: () => getSysColors(),
|
||||||
|
|
@ -49,7 +49,7 @@ function polyfillVendettaLoaderIdentity() {
|
||||||
if (isThemeSupported()) {
|
if (isThemeSupported()) {
|
||||||
loader.features.themes = {
|
loader.features.themes = {
|
||||||
prop: "__vendetta_theme"
|
prop: "__vendetta_theme"
|
||||||
}
|
};
|
||||||
|
|
||||||
Object.defineProperty(globalThis, "__vendetta_theme", {
|
Object.defineProperty(globalThis, "__vendetta_theme", {
|
||||||
get: () => getStoredTheme(),
|
get: () => getStoredTheme(),
|
||||||
|
|
@ -126,7 +126,7 @@ export function getStoredTheme(): Theme | null {
|
||||||
|
|
||||||
export function getThemeFilePath() {
|
export function getThemeFilePath() {
|
||||||
if (isPyonLoader()) {
|
if (isPyonLoader()) {
|
||||||
return "pyoncord/current-theme.json"
|
return "pyoncord/current-theme.json";
|
||||||
} else if (isVendettaLoader()) {
|
} else if (isVendettaLoader()) {
|
||||||
return "vendetta_theme.json";
|
return "vendetta_theme.json";
|
||||||
}
|
}
|
||||||
|
|
@ -157,7 +157,7 @@ export function getReactDevToolsVersion() {
|
||||||
if (!isReactDevToolsPreloaded()) return null;
|
if (!isReactDevToolsPreloaded()) return null;
|
||||||
|
|
||||||
if (isVendettaLoader()) {
|
if (isVendettaLoader()) {
|
||||||
return vendettaLoaderIdentity!!.features.devtools!!.version
|
return vendettaLoaderIdentity!!.features.devtools!!.version;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -80,5 +80,5 @@ export const FileManager = (nmp.DCDFileManager ?? nmp.RTNFileManager) as FileMan
|
||||||
export const ClientInfoManager = nmp.InfoDictionaryManager ?? nmp.RTNClientInfoManager;
|
export const ClientInfoManager = nmp.InfoDictionaryManager ?? nmp.RTNClientInfoManager;
|
||||||
//! 173.14 renamed this to RTNDeviceManager.
|
//! 173.14 renamed this to RTNDeviceManager.
|
||||||
export const DeviceManager = nmp.DCDDeviceManager ?? nmp.RTNDeviceManager;
|
export const DeviceManager = nmp.DCDDeviceManager ?? nmp.RTNDeviceManager;
|
||||||
export const BundleUpdaterManager = nmp.BundleUpdaterManager;
|
export const { BundleUpdaterManager } = nmp;
|
||||||
export const ThemeManager = nmp.RTNThemeManager ?? nmp.DCDTheme;
|
export const ThemeManager = nmp.RTNThemeManager ?? nmp.DCDTheme;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import * as _spitroast from "spitroast";
|
import * as _spitroast from "spitroast";
|
||||||
|
|
||||||
export * from "spitroast";
|
export * from "spitroast";
|
||||||
export default { ..._spitroast };
|
export default { ..._spitroast };
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
|
import { FileManager, MMKVManager } from "@lib/api/native/modules";
|
||||||
import { ReactNative as RN } from "@metro/common";
|
import { ReactNative as RN } from "@metro/common";
|
||||||
import { FileManager, MMKVManager } from "../native/modules";
|
|
||||||
|
|
||||||
export interface StorageBackend {
|
export interface StorageBackend {
|
||||||
get: () => unknown | Promise<unknown>;
|
get: () => unknown | Promise<unknown>;
|
||||||
set: (data: unknown) => void | Promise<void>;
|
set: (data: unknown) => void | Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ILLEGAL_CHARS_REGEX = /[<>:"\/\\|?*]/g;
|
const ILLEGAL_CHARS_REGEX = /[<>:"/\\|?*]/g;
|
||||||
|
|
||||||
const filePathFixer = (file: string): string => RN.Platform.select({
|
const filePathFixer = (file: string): string => RN.Platform.select({
|
||||||
default: file,
|
default: file,
|
||||||
|
|
@ -16,11 +16,11 @@ const filePathFixer = (file: string): string => RN.Platform.select({
|
||||||
const getMMKVPath = (name: string): string => {
|
const getMMKVPath = (name: string): string => {
|
||||||
if (ILLEGAL_CHARS_REGEX.test(name)) {
|
if (ILLEGAL_CHARS_REGEX.test(name)) {
|
||||||
// Replace forbidden characters with hyphens
|
// Replace forbidden characters with hyphens
|
||||||
name = name.replace(ILLEGAL_CHARS_REGEX, '-').replace(/-+/g, '-');
|
name = name.replace(ILLEGAL_CHARS_REGEX, "-").replace(/-+/g, "-");
|
||||||
}
|
}
|
||||||
|
|
||||||
return `vd_mmkv/${name}`;
|
return `vd_mmkv/${name}`;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const purgeStorage = async (store: string) => {
|
export const purgeStorage = async (store: string) => {
|
||||||
if (await MMKVManager.getItem(store)) {
|
if (await MMKVManager.getItem(store)) {
|
||||||
|
|
@ -31,7 +31,7 @@ export const purgeStorage = async (store: string) => {
|
||||||
if (await FileManager.fileExists(`${FileManager.getConstants().DocumentsDirPath}/${mmkvPath}`)) {
|
if (await FileManager.fileExists(`${FileManager.getConstants().DocumentsDirPath}/${mmkvPath}`)) {
|
||||||
await FileManager.removeFile?.("documents", mmkvPath);
|
await FileManager.removeFile?.("documents", mmkvPath);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export const createMMKVBackend = (store: string, defaultData = {}) => {
|
export const createMMKVBackend = (store: string, defaultData = {}) => {
|
||||||
const mmkvPath = getMMKVPath(store);
|
const mmkvPath = getMMKVPath(store);
|
||||||
|
|
@ -47,7 +47,7 @@ export const createMMKVBackend = (store: string, defaultData = {}) => {
|
||||||
if (oldData === "!!LARGE_VALUE!!") {
|
if (oldData === "!!LARGE_VALUE!!") {
|
||||||
const cachePath = `${FileManager.getConstants().CacheDirPath}/mmkv/${store}`;
|
const cachePath = `${FileManager.getConstants().CacheDirPath}/mmkv/${store}`;
|
||||||
if (await FileManager.fileExists(cachePath)) {
|
if (await FileManager.fileExists(cachePath)) {
|
||||||
oldData = await FileManager.readFile(cachePath, "utf8")
|
oldData = await FileManager.readFile(cachePath, "utf8");
|
||||||
} else {
|
} else {
|
||||||
console.log(`${store}: Experienced data loss :(`);
|
console.log(`${store}: Experienced data loss :(`);
|
||||||
oldData = defaultStr;
|
oldData = defaultStr;
|
||||||
|
|
@ -67,7 +67,7 @@ export const createMMKVBackend = (store: string, defaultData = {}) => {
|
||||||
console.log(`Successfully migrated ${store} store from MMKV storage to fs`);
|
console.log(`Successfully migrated ${store} store from MMKV storage to fs`);
|
||||||
}
|
}
|
||||||
})());
|
})());
|
||||||
}
|
};
|
||||||
|
|
||||||
export const createFileBackend = (file: string, defaultData = {}, migratePromise?: Promise<void>): StorageBackend => {
|
export const createFileBackend = (file: string, defaultData = {}, migratePromise?: Promise<void>): StorageBackend => {
|
||||||
return {
|
return {
|
||||||
|
|
@ -86,7 +86,7 @@ export const createFileBackend = (file: string, defaultData = {}, migratePromise
|
||||||
await FileManager.writeFile("documents", filePathFixer(file), JSON.stringify(defaultData), "utf8");
|
await FileManager.writeFile("documents", filePathFixer(file), JSON.stringify(defaultData), "utf8");
|
||||||
return JSON.parse(await FileManager.readFile(path, "utf8"));
|
return JSON.parse(await FileManager.readFile(path, "utf8"));
|
||||||
},
|
},
|
||||||
set: async (data) => {
|
set: async data => {
|
||||||
await migratePromise;
|
await migratePromise;
|
||||||
await FileManager.writeFile("documents", filePathFixer(file), JSON.stringify(data), "utf8");
|
await FileManager.writeFile("documents", filePathFixer(file), JSON.stringify(data), "utf8");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { createEmitter, Emitter } from "@lib/utils/emitter";
|
|
||||||
import { StorageBackend } from "@lib/api/storage/backends";
|
import { StorageBackend } from "@lib/api/storage/backends";
|
||||||
|
import { createEmitter, Emitter } from "@lib/utils/emitter";
|
||||||
|
|
||||||
const emitterSymbol = Symbol.for("vendetta.storage.emitter");
|
const emitterSymbol = Symbol.for("vendetta.storage.emitter");
|
||||||
const syncAwaitSymbol = Symbol.for("vendetta.storage.accessor");
|
const syncAwaitSymbol = Symbol.for("vendetta.storage.accessor");
|
||||||
|
|
@ -59,7 +59,7 @@ export function createProxy(target: any = {}): { proxy: any; emitter: Emitter }
|
||||||
export function useProxy<T>(storage: T): T {
|
export function useProxy<T>(storage: T): T {
|
||||||
const emitter = (storage as any)[emitterSymbol] as Emitter;
|
const emitter = (storage as any)[emitterSymbol] as Emitter;
|
||||||
|
|
||||||
const [, forceUpdate] = React.useReducer((n) => ~n, 0);
|
const [, forceUpdate] = React.useReducer(n => ~n, 0);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const listener = () => forceUpdate();
|
const listener = () => forceUpdate();
|
||||||
|
|
@ -93,16 +93,16 @@ export function wrapSync<T extends Promise<any>>(store: T): Awaited<T> {
|
||||||
const awaitQueue: (() => void)[] = [];
|
const awaitQueue: (() => void)[] = [];
|
||||||
const awaitInit = (cb: () => void) => (awaited ? cb() : awaitQueue.push(cb));
|
const awaitInit = (cb: () => void) => (awaited ? cb() : awaitQueue.push(cb));
|
||||||
|
|
||||||
store.then((v) => {
|
store.then(v => {
|
||||||
awaited = v;
|
awaited = v;
|
||||||
awaitQueue.forEach((cb) => cb());
|
awaitQueue.forEach(cb => cb());
|
||||||
});
|
});
|
||||||
|
|
||||||
return new Proxy({} as Awaited<T>, {
|
return new Proxy({} as Awaited<T>, {
|
||||||
...Object.fromEntries(
|
...Object.fromEntries(
|
||||||
Object.getOwnPropertyNames(Reflect)
|
Object.getOwnPropertyNames(Reflect)
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
.map((k) => [k, (t: T, ...a: any[]) => Reflect[k](awaited ?? t, ...a)])
|
.map(k => [k, (t: T, ...a: any[]) => Reflect[k](awaited ?? t, ...a)])
|
||||||
),
|
),
|
||||||
get(target, prop, recv) {
|
get(target, prop, recv) {
|
||||||
if (prop === syncAwaitSymbol) return awaitInit;
|
if (prop === syncAwaitSymbol) return awaitInit;
|
||||||
|
|
@ -111,6 +111,6 @@ export function wrapSync<T extends Promise<any>>(store: T): Awaited<T> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const awaitSyncWrapper = (store: any) => new Promise<void>((res) => store[syncAwaitSymbol](res));
|
export const awaitSyncWrapper = (store: any) => new Promise<void>(res => store[syncAwaitSymbol](res));
|
||||||
|
|
||||||
export * from "@lib/api/storage/backends";
|
export * from "@lib/api/storage/backends";
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
import { ReactNative as RN } from "@metro/common";
|
import { getAssetIDByName } from "@lib/api/assets";
|
||||||
|
import { getLoaderName, isThemeSupported } from "@lib/api/native/loader";
|
||||||
|
import { BundleUpdaterManager, ClientInfoManager, DeviceManager } from "@lib/api/native/modules";
|
||||||
import { after } from "@lib/api/patcher";
|
import { after } from "@lib/api/patcher";
|
||||||
import { getThemeFromLoader, selectTheme } from "@lib/managers/themes";
|
import { getThemeFromLoader, selectTheme } from "@lib/managers/themes";
|
||||||
import { ClientInfoManager, DeviceManager, BundleUpdaterManager } from "@lib/api/native/modules";
|
|
||||||
import { getAssetIDByName } from "@/lib/api/assets";
|
|
||||||
import { showToast } from "@ui/toasts";
|
|
||||||
import { settings } from "@lib/settings";
|
import { settings } from "@lib/settings";
|
||||||
import logger from "@lib/utils/logger";
|
import logger from "@lib/utils/logger";
|
||||||
import { getLoaderName, isThemeSupported } from "./api/native/loader";
|
import { ReactNative as RN } from "@metro/common";
|
||||||
import type { PlatformConstants } from "react-native";
|
import { showToast } from "@ui/toasts";
|
||||||
import { version } from "bunny-build";
|
import { version } from "bunny-build";
|
||||||
|
import type { PlatformConstants } from "react-native";
|
||||||
export let socket: WebSocket;
|
export let socket: WebSocket;
|
||||||
|
|
||||||
export interface RNConstants extends PlatformConstants {
|
export interface RNConstants extends PlatformConstants {
|
||||||
|
|
@ -30,7 +30,7 @@ export interface RNConstants extends PlatformConstants {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function toggleSafeMode() {
|
export async function toggleSafeMode() {
|
||||||
settings.safeMode = { ...settings.safeMode, enabled: !settings.safeMode?.enabled }
|
settings.safeMode = { ...settings.safeMode, enabled: !settings.safeMode?.enabled };
|
||||||
if (isThemeSupported()) {
|
if (isThemeSupported()) {
|
||||||
if (getThemeFromLoader()?.id) settings.safeMode!.currentThemeId = getThemeFromLoader()!.id;
|
if (getThemeFromLoader()?.id) settings.safeMode!.currentThemeId = getThemeFromLoader()!.id;
|
||||||
if (settings.safeMode?.enabled) {
|
if (settings.safeMode?.enabled) {
|
||||||
|
|
@ -68,7 +68,7 @@ export function connectToDebugger(url: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function patchLogHook() {
|
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] }));
|
if (socket?.readyState === WebSocket.OPEN) socket.send(JSON.stringify({ message: args[0], level: args[1] }));
|
||||||
logger.log(args[0]);
|
logger.log(args[0]);
|
||||||
});
|
});
|
||||||
|
|
@ -76,7 +76,7 @@ export function patchLogHook() {
|
||||||
return () => {
|
return () => {
|
||||||
socket && socket.close();
|
socket && socket.close();
|
||||||
unpatch();
|
unpatch();
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const versionHash = version;
|
export const versionHash = version;
|
||||||
|
|
@ -111,7 +111,7 @@ export function getDebugInfo() {
|
||||||
},
|
},
|
||||||
hermes: {
|
hermes: {
|
||||||
version: hermesVer,
|
version: hermesVer,
|
||||||
buildType: hermesProps["Build"],
|
buildType: hermesProps.Build,
|
||||||
bytecodeVersion: hermesProps["Bytecode Version"],
|
bytecodeVersion: hermesProps["Bytecode Version"],
|
||||||
},
|
},
|
||||||
...RN.Platform.select(
|
...RN.Platform.select(
|
||||||
|
|
@ -151,5 +151,5 @@ export function getDebugInfo() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)!
|
)!
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { safeFetch } from "@lib/utils";
|
import { allSettled } from "@core/polyfills";
|
||||||
import { awaitSyncWrapper, createMMKVBackend, createStorage, purgeStorage, wrapSync } from "@/lib/api/storage";
|
import { awaitSyncWrapper, createMMKVBackend, createStorage, purgeStorage, wrapSync } from "@lib/api/storage";
|
||||||
import logger, { logModule } from "@lib/utils/logger";
|
|
||||||
import { settings } from "@lib/settings";
|
import { settings } from "@lib/settings";
|
||||||
import { Author } from "../utils/types";
|
import { safeFetch } from "@lib/utils";
|
||||||
import { allSettled } from "@/core/polyfills";
|
import logger, { logModule } from "@lib/utils/logger";
|
||||||
|
import { Author } from "@lib/utils/types";
|
||||||
|
|
||||||
type EvaledPlugin = {
|
type EvaledPlugin = {
|
||||||
onLoad?(): void;
|
onLoad?(): void;
|
||||||
|
|
@ -53,7 +53,7 @@ export async function fetchPlugin(id: string) {
|
||||||
try {
|
try {
|
||||||
// by polymanifest spec, plugins should always specify their main file, but just in case
|
// by polymanifest spec, plugins should always specify their main file, but just in case
|
||||||
pluginJs = await (await safeFetch(id + (pluginManifest.main || "index.js"), { cache: "no-store" })).text();
|
pluginJs = await (await safeFetch(id + (pluginManifest.main || "index.js"), { cache: "no-store" })).text();
|
||||||
} catch {} // Empty catch, checked below
|
} catch { } // Empty catch, checked below
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pluginJs && !existingPlugin) throw new Error(`Failed to fetch JS for ${id}`);
|
if (!pluginJs && !existingPlugin) throw new Error(`Failed to fetch JS for ${id}`);
|
||||||
|
|
@ -88,7 +88,7 @@ export async function evalPlugin(plugin: BunnyPlugin) {
|
||||||
const pluginString = `vendetta=>{return ${plugin.js}}\n//# sourceURL=${plugin.id}`;
|
const pluginString = `vendetta=>{return ${plugin.js}}\n//# sourceURL=${plugin.id}`;
|
||||||
|
|
||||||
const raw = (0, eval)(pluginString)(vendettaForPlugins);
|
const raw = (0, eval)(pluginString)(vendettaForPlugins);
|
||||||
const ret = typeof raw == "function" ? raw() : raw;
|
const ret = typeof raw === "function" ? raw() : raw;
|
||||||
return ret?.default ?? ret ?? {};
|
return ret?.default ?? ret ?? {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -152,10 +152,10 @@ export async function initPlugins() {
|
||||||
|
|
||||||
if (!settings.safeMode?.enabled) {
|
if (!settings.safeMode?.enabled) {
|
||||||
// Loop over any plugin that is enabled, update it if allowed, then start it.
|
// Loop over any plugin that is enabled, update it if allowed, then start it.
|
||||||
await allSettled(allIds.filter(pl => plugins[pl].enabled).map(async (pl) => (plugins[pl].update && await fetchPlugin(pl).catch((e: Error) => logger.error(e.message)), await startPlugin(pl))));
|
await allSettled(allIds.filter(pl => plugins[pl].enabled).map(async pl => (plugins[pl].update && await fetchPlugin(pl).catch((e: Error) => logger.error(e.message)), await startPlugin(pl))));
|
||||||
// Wait for the above to finish, then update all disabled plugins that are allowed to.
|
// Wait for the above to finish, then update all disabled plugins that are allowed to.
|
||||||
allIds.filter(pl => !plugins[pl].enabled && plugins[pl].update).forEach(pl => fetchPlugin(pl));
|
allIds.filter(pl => !plugins[pl].enabled && plugins[pl].update).forEach(pl => fetchPlugin(pl));
|
||||||
};
|
}
|
||||||
|
|
||||||
return stopAllPlugins;
|
return stopAllPlugins;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
// TODO: rewrite most of this
|
// TODO: rewrite most of this
|
||||||
import { ReactNative as RN, chroma } from "@metro/common";
|
import { getStoredTheme, getThemeFilePath } from "@lib/api/native/loader";
|
||||||
import { findInReactTree, safeFetch } from "@lib/utils";
|
|
||||||
import { find, findByName, findByProps, findByStoreName } from "@metro/filters";
|
|
||||||
import { after, before, instead } from "@lib/api/patcher";
|
|
||||||
import { createFileBackend, createMMKVBackend, createStorage, wrapSync, awaitSyncWrapper } from "@/lib/api/storage";
|
|
||||||
import logger from "@lib/utils/logger";
|
|
||||||
import { ThemeManager } from "@lib/api/native/modules";
|
import { ThemeManager } from "@lib/api/native/modules";
|
||||||
import { getStoredTheme, getThemeFilePath } from "../api/native/loader";
|
import { after, before, instead } from "@lib/api/patcher";
|
||||||
import { Author } from "../utils/types";
|
import { awaitSyncWrapper, createFileBackend, createMMKVBackend, createStorage, wrapSync } from "@lib/api/storage";
|
||||||
|
import { findInReactTree, safeFetch } from "@lib/utils";
|
||||||
|
import logger from "@lib/utils/logger";
|
||||||
|
import { Author } from "@lib/utils/types";
|
||||||
|
import { chroma, ReactNative as RN } from "@metro/common";
|
||||||
|
import { find, findByName, findByProps, findByStoreName } from "@metro/filters";
|
||||||
|
|
||||||
export interface ThemeData {
|
export interface ThemeData {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
@ -24,7 +24,7 @@ export interface ThemeData {
|
||||||
* `CHAT_BACKGROUND` of semanticColors alpha value will be ignored when this is specified
|
* `CHAT_BACKGROUND` of semanticColors alpha value will be ignored when this is specified
|
||||||
*/
|
*/
|
||||||
alpha?: number;
|
alpha?: number;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Theme {
|
export interface Theme {
|
||||||
|
|
@ -54,7 +54,7 @@ const semanticAlternativeMap: Record<string, string> = {
|
||||||
"BG_SURFACE_OVERLAY": "BACKGROUND_FLOATING",
|
"BG_SURFACE_OVERLAY": "BACKGROUND_FLOATING",
|
||||||
"BG_SURFACE_OVERLAY_TMP": "BACKGROUND_FLOATING",
|
"BG_SURFACE_OVERLAY_TMP": "BACKGROUND_FLOATING",
|
||||||
"BG_SURFACE_RAISED": "BACKGROUND_MOBILE_PRIMARY"
|
"BG_SURFACE_RAISED": "BACKGROUND_MOBILE_PRIMARY"
|
||||||
}
|
};
|
||||||
|
|
||||||
async function writeTheme(theme: Theme | {}) {
|
async function writeTheme(theme: Theme | {}) {
|
||||||
if (typeof theme !== "object") throw new Error("Theme must be an object");
|
if (typeof theme !== "object") throw new Error("Theme must be an object");
|
||||||
|
|
@ -78,7 +78,7 @@ export function patchChatBackground() {
|
||||||
children: ret,
|
children: ret,
|
||||||
})),
|
})),
|
||||||
after("render", MessagesWrapper.prototype, (_, ret) => {
|
after("render", MessagesWrapper.prototype, (_, ret) => {
|
||||||
const Messages = findInReactTree(ret, (x) => "HACK_fixModalInteraction" in x?.props && x?.props?.style);
|
const Messages = findInReactTree(ret, x => x && "HACK_fixModalInteraction" in x.props && x?.props?.style);
|
||||||
if (Messages)
|
if (Messages)
|
||||||
Messages.props.style = Object.assign(
|
Messages.props.style = Object.assign(
|
||||||
RN.StyleSheet.flatten(Messages.props.style ?? {}),
|
RN.StyleSheet.flatten(Messages.props.style ?? {}),
|
||||||
|
|
@ -100,7 +100,7 @@ function normalizeToHex(colorString: string): string {
|
||||||
const color = Number(RN.processColor(colorString));
|
const color = Number(RN.processColor(colorString));
|
||||||
|
|
||||||
return chroma.rgb(
|
return chroma.rgb(
|
||||||
color >> 16 & 0xff, // red
|
color >> 16 & 0xff, // red
|
||||||
color >> 8 & 0xff, // green
|
color >> 8 & 0xff, // green
|
||||||
color & 0xff, // blue
|
color & 0xff, // blue
|
||||||
color >> 24 & 0xff // alpha
|
color >> 24 & 0xff // alpha
|
||||||
|
|
@ -110,7 +110,7 @@ function normalizeToHex(colorString: string): string {
|
||||||
// Process data for some compatiblity with native side
|
// Process data for some compatiblity with native side
|
||||||
function processData(data: ThemeData) {
|
function processData(data: ThemeData) {
|
||||||
if (data.semanticColors) {
|
if (data.semanticColors) {
|
||||||
const semanticColors = data.semanticColors;
|
const { semanticColors } = data;
|
||||||
|
|
||||||
for (const key in semanticColors) {
|
for (const key in semanticColors) {
|
||||||
for (const index in semanticColors[key]) {
|
for (const index in semanticColors[key]) {
|
||||||
|
|
@ -120,7 +120,7 @@ function processData(data: ThemeData) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.rawColors) {
|
if (data.rawColors) {
|
||||||
const rawColors = data.rawColors;
|
const { rawColors } = data;
|
||||||
|
|
||||||
for (const key in rawColors) {
|
for (const key in rawColors) {
|
||||||
data.rawColors[key] = normalizeToHex(rawColors[key]);
|
data.rawColors[key] = normalizeToHex(rawColors[key]);
|
||||||
|
|
@ -227,7 +227,7 @@ function patchColor() {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
get: () => {
|
get: () => {
|
||||||
return enabled ? currentTheme?.data?.rawColors?.[k] ?? origRawColor[k] : origRawColor[k]
|
return enabled ? currentTheme?.data?.rawColors?.[k] ?? origRawColor[k] : origRawColor[k];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -258,7 +258,7 @@ function patchColor() {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Prevent setting to real Discord settings
|
// Prevent setting to real Discord settings
|
||||||
before("set", mmkvStorage, (args) => {
|
before("set", mmkvStorage, args => {
|
||||||
if (!args[1]) return;
|
if (!args[1]) return;
|
||||||
|
|
||||||
const key = args[0];
|
const key = args[0];
|
||||||
|
|
@ -267,11 +267,11 @@ function patchColor() {
|
||||||
const interceptors: Record<string, () => void> = ({
|
const interceptors: Record<string, () => void> = ({
|
||||||
SelectivelySyncedUserSettingsStore: () => {
|
SelectivelySyncedUserSettingsStore: () => {
|
||||||
if (value._state?.appearance?.settings?.theme) {
|
if (value._state?.appearance?.settings?.theme) {
|
||||||
const { theme } = value._state?.appearance?.settings;
|
const { theme } = value._state?.appearance?.settings ?? {};
|
||||||
if (isDiscordTheme(theme)) {
|
if (isDiscordTheme(theme)) {
|
||||||
vdThemeFallback = theme;
|
vdThemeFallback = theme;
|
||||||
} else {
|
} else {
|
||||||
value._state.appearance.settings.theme = vdThemeFallback;
|
value._state.appearance.settings.theme = vdThemeFallback;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -282,8 +282,8 @@ function patchColor() {
|
||||||
selectTheme("default");
|
selectTheme("default");
|
||||||
vdThemeFallback = theme;
|
vdThemeFallback = theme;
|
||||||
} else {
|
} else {
|
||||||
value._state.theme = vdThemeFallback;
|
value._state.theme = vdThemeFallback;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -299,9 +299,9 @@ function patchColor() {
|
||||||
if (args[1] !== vdKey) return orig(...args);
|
if (args[1] !== vdKey) return orig(...args);
|
||||||
|
|
||||||
const [name, colorDef] = extractInfo(vdThemeFallback, args[1]);
|
const [name, colorDef] = extractInfo(vdThemeFallback, args[1]);
|
||||||
|
|
||||||
const themeIndex = vdThemeFallback === "midnight" ? 2 : vdThemeFallback === "light" ? 1 : 0;
|
const themeIndex = vdThemeFallback === "midnight" ? 2 : vdThemeFallback === "light" ? 1 : 0;
|
||||||
|
|
||||||
//! As of 192.7, Tabs v2 uses BG_ semantic colors instead of BACKGROUND_ ones
|
//! As of 192.7, Tabs v2 uses BG_ semantic colors instead of BACKGROUND_ ones
|
||||||
const alternativeName = semanticAlternativeMap[name] ?? name;
|
const alternativeName = semanticAlternativeMap[name] ?? name;
|
||||||
|
|
||||||
|
|
@ -345,7 +345,7 @@ export function applyTheme(appliedTheme: Theme | null, fallbackTheme?: string, u
|
||||||
|
|
||||||
Object.keys(color.Shadow).forEach(k => color.Shadow[k][vdKey] = color.Shadow[k][fallbackTheme!!]);
|
Object.keys(color.Shadow).forEach(k => color.Shadow[k][vdKey] = color.Shadow[k][fallbackTheme!!]);
|
||||||
Object.keys(color.SemanticColor).forEach(k => {
|
Object.keys(color.SemanticColor).forEach(k => {
|
||||||
color.SemanticColor[k][vdKey] = {
|
color.SemanticColor[k][vdKey] = {
|
||||||
...color.SemanticColor[k][fallbackTheme!!],
|
...color.SemanticColor[k][fallbackTheme!!],
|
||||||
override: appliedTheme?.data?.semanticColors?.[k]?.[0]
|
override: appliedTheme?.data?.semanticColors?.[k]?.[0]
|
||||||
};
|
};
|
||||||
|
|
@ -374,4 +374,4 @@ function extractInfo(themeName: string, colorObj: any): [name: string, colorDef:
|
||||||
const colorDef = color.SemanticColor[propName];
|
const colorDef = color.SemanticColor[propName];
|
||||||
|
|
||||||
return [propName, colorDef[themeName]];
|
return [propName, colorDef[themeName]];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,11 @@ function createThemedStyleSheet<T extends StyleSheet.NamedStyles<T>>(sheet: T) {
|
||||||
for (const key in sheet) {
|
for (const key in sheet) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
sheet[key] = new Proxy(ReactNative.StyleSheet.flatten(sheet[key]), {
|
sheet[key] = new Proxy(ReactNative.StyleSheet.flatten(sheet[key]), {
|
||||||
get(target, prop, receiver) {
|
get(target, prop, receiver) {
|
||||||
const res = Reflect.get(target, prop, receiver);
|
const res = Reflect.get(target, prop, receiver);
|
||||||
return colorResolver.isSemanticColor(res)
|
return colorResolver.isSemanticColor(res)
|
||||||
? colorResolver.resolveSemanticColor(ThemeStore.theme, res)
|
? colorResolver.resolveSemanticColor(ThemeStore.theme, res)
|
||||||
: res
|
: res;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ for (const key in window.modules) {
|
||||||
const id = key as MetroModulesIndex;
|
const id = key as MetroModulesIndex;
|
||||||
const module = window.modules[id]?.publicModule?.exports;
|
const module = window.modules[id]?.publicModule?.exports;
|
||||||
|
|
||||||
if (!module || module === window || module["proxygone"] === null) {
|
if (!module || module === window || module.proxygone === null) {
|
||||||
blacklist(id);
|
blacklist(id);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -44,9 +44,9 @@ const onModuleCheck = (exports: any) => {
|
||||||
} catch {
|
} catch {
|
||||||
return args[0];
|
return args[0];
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
// Function to filter through modules
|
// Function to filter through modules
|
||||||
const filterModules = (modules: MetroModules, single = false) => (filter: (m: any) => boolean) => {
|
const filterModules = (modules: MetroModules, single = false) => (filter: (m: any) => boolean) => {
|
||||||
|
|
@ -81,13 +81,13 @@ const filterModules = (modules: MetroModules, single = false) => (filter: (m: an
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!single) return found;
|
if (!single) return found;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const modules = window.modules;
|
export const { modules } = window;
|
||||||
export const find = filterModules(modules, true);
|
export const find = filterModules(modules, true);
|
||||||
export const findAll = filterModules(modules);
|
export const findAll = filterModules(modules);
|
||||||
|
|
||||||
const propsFilter = (props: (string | symbol)[]) => (m: any) => props.every((p) => m[p] !== undefined);
|
const propsFilter = (props: (string | symbol)[]) => (m: any) => props.every(p => m[p] !== undefined);
|
||||||
const nameFilter = (name: string, defaultExp: boolean) => (defaultExp ? (m: any) => m?.name === name : (m: any) => m?.default?.name === name);
|
const nameFilter = (name: string, defaultExp: boolean) => (defaultExp ? (m: any) => m?.name === name : (m: any) => m?.default?.name === name);
|
||||||
const dNameFilter = (displayName: string, defaultExp: boolean) => (defaultExp ? (m: any) => m?.displayName === displayName : (m: any) => m?.default?.displayName === displayName);
|
const dNameFilter = (displayName: string, defaultExp: boolean) => (defaultExp ? (m: any) => m?.displayName === displayName : (m: any) => m?.default?.displayName === displayName);
|
||||||
const tNameFilter = (typeName: string, defaultExp: boolean) => (defaultExp ? (m: any) => m?.type?.name === typeName : (m: any) => m?.default?.type?.name === typeName);
|
const tNameFilter = (typeName: string, defaultExp: boolean) => (defaultExp ? (m: any) => m?.type?.name === typeName : (m: any) => m?.default?.type?.name === typeName);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
|
export * as common from "./common";
|
||||||
export * from "./filters";
|
export * from "./filters";
|
||||||
export * as filters from "./filters";
|
export * as filters from "./filters";
|
||||||
export * as common from "./common";
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { createFileBackend, createMMKVBackend, createStorage, wrapSync } from "@/lib/api/storage";
|
import { createFileBackend, createMMKVBackend, createStorage, wrapSync } from "@lib/api/storage";
|
||||||
|
|
||||||
import { getLoaderConfigPath } from "./api/native/loader";
|
import { getLoaderConfigPath } from "./api/native/loader";
|
||||||
|
|
||||||
export interface Settings {
|
export interface Settings {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
|
import { ButtonColors } from "@lib/utils/types";
|
||||||
import { findByProps } from "@metro/filters";
|
import { findByProps } from "@metro/filters";
|
||||||
import InputAlert, { InputAlertProps } from "@ui/components/InputAlert";
|
import InputAlert, { InputAlertProps } from "@ui/components/InputAlert";
|
||||||
import { ButtonColors } from "@/lib/utils/types";
|
|
||||||
|
|
||||||
const Alerts = findByProps("openLazy", "close");
|
const Alerts = findByProps("openLazy", "close");
|
||||||
|
|
||||||
interface InternalConfirmationAlertOptions extends Omit<ConfirmationAlertOptions, "content"> {
|
interface InternalConfirmationAlertOptions extends Omit<ConfirmationAlertOptions, "content"> {
|
||||||
content?: ConfirmationAlertOptions["content"];
|
content?: ConfirmationAlertOptions["content"];
|
||||||
body?: ConfirmationAlertOptions["content"];
|
body?: ConfirmationAlertOptions["content"];
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface ConfirmationAlertOptions {
|
export interface ConfirmationAlertOptions {
|
||||||
title?: string;
|
title?: string;
|
||||||
|
|
@ -31,7 +31,7 @@ export function showConfirmationAlert(options: ConfirmationAlertOptions) {
|
||||||
internalOptions.isDismissable ??= true;
|
internalOptions.isDismissable ??= true;
|
||||||
|
|
||||||
return Alerts.show(internalOptions);
|
return Alerts.show(internalOptions);
|
||||||
};
|
}
|
||||||
|
|
||||||
export const showCustomAlert = (component: React.ComponentType<any>, props: any) => Alerts.openLazy({
|
export const showCustomAlert = (component: React.ComponentType<any>, props: any) => Alerts.openLazy({
|
||||||
importer: async () => () => React.createElement(component, props),
|
importer: async () => () => React.createElement(component, props),
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
import { constants } from "@metro/common";
|
|
||||||
import { color } from "@lib/managers/themes";
|
import { color } from "@lib/managers/themes";
|
||||||
|
import { constants } from "@metro/common";
|
||||||
|
|
||||||
//! This module is only found on 165.0+, under the assumption that iOS 165.0 is the same as Android 165.0.
|
//! This module is only found on 165.0+, under the assumption that iOS 165.0 is the same as Android 165.0.
|
||||||
//* In 167.1, most if not all traces of the old color modules were removed.
|
//* In 167.1, most if not all traces of the old color modules were removed.
|
||||||
//* In 168.6, Discord restructured EVERYTHING again. SemanticColor on this module no longer works when passed to a stylesheet. We must now use what you see below.
|
//* In 168.6, Discord restructured EVERYTHING again. SemanticColor on this module no longer works when passed to a stylesheet. We must now use what you see below.
|
||||||
//* In 173.10, Discord restructured a lot of the app. These changes included making the color module impossible to early-find.
|
//* In 173.10, Discord restructured a lot of the app. These changes included making the color module impossible to early-find.
|
||||||
//? To stop duplication, it is now exported in our theming code.
|
// ? To stop duplication, it is now exported in our theming code.
|
||||||
//? These comments are preserved for historical purposes.
|
// ? These comments are preserved for historical purposes.
|
||||||
// const colorModule = findByProps("colors", "meta");
|
// const colorModule = findByProps("colors", "meta");
|
||||||
|
|
||||||
//? SemanticColor and default.colors are effectively ThemeColorMap
|
// ? SemanticColor and default.colors are effectively ThemeColorMap
|
||||||
export const semanticColors = (color?.default?.colors ?? constants?.ThemeColorMap);
|
export const semanticColors = (color?.default?.colors ?? constants?.ThemeColorMap);
|
||||||
|
|
||||||
//? RawColor and default.unsafe_rawColors are effectively Colors
|
// ? RawColor and default.unsafe_rawColors are effectively Colors
|
||||||
//* Note that constants.Colors does still appear to exist on newer versions despite Discord not internally using it - what the fuck?
|
//* Note that constants.Colors does still appear to exist on newer versions despite Discord not internally using it - what the fuck?
|
||||||
export const rawColors = (color?.default?.unsafe_rawColors ?? constants?.Colors);
|
export const rawColors = (color?.default?.unsafe_rawColors ?? constants?.Colors);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { ReactNative as RN, stylesheet, constants } from "@metro/common";
|
import { constants,ReactNative as RN, stylesheet } from "@metro/common";
|
||||||
import { semanticColors } from "@ui/color";
|
import { semanticColors } from "@ui/color";
|
||||||
|
|
||||||
export interface CodeblockProps {
|
export interface CodeblockProps {
|
||||||
|
|
@ -22,14 +22,14 @@ const styles = stylesheet.createThemedStyleSheet({
|
||||||
});
|
});
|
||||||
|
|
||||||
// iOS doesn't support the selectable property on RN.Text...
|
// iOS doesn't support the selectable property on RN.Text...
|
||||||
const InputBasedCodeblock = ({ style, children }: CodeblockProps) => <RN.TextInput editable={false} multiline style={[styles.codeBlock, style && style]} value={children} />
|
const InputBasedCodeblock = ({ style, children }: CodeblockProps) => <RN.TextInput editable={false} multiline style={[styles.codeBlock, style && style]} value={children} />;
|
||||||
const TextBasedCodeblock = ({ selectable, style, children }: CodeblockProps) => <RN.Text selectable={selectable} style={[styles.codeBlock, style && style]}>{children}</RN.Text>
|
const TextBasedCodeblock = ({ selectable, style, children }: CodeblockProps) => <RN.Text selectable={selectable} style={[styles.codeBlock, style && style]}>{children}</RN.Text>;
|
||||||
|
|
||||||
export default function Codeblock({ selectable, style, children }: CodeblockProps) {
|
export default function Codeblock({ selectable, style, children }: CodeblockProps) {
|
||||||
if (!selectable) return <TextBasedCodeblock style={style} children={children} />;
|
if (!selectable) return <TextBasedCodeblock style={style} children={children} />;
|
||||||
|
|
||||||
return RN.Platform.select({
|
return RN.Platform.select({
|
||||||
ios: <InputBasedCodeblock style={style} children={children} />,
|
ios: <InputBasedCodeblock style={style} children={children} />,
|
||||||
default: <TextBasedCodeblock style={style} children={children} selectable />,
|
default: <TextBasedCodeblock style={style} children={children} selectable />,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { findByProps } from "@lib/metro/filters";
|
|
||||||
import { instead } from "@lib/api/patcher";
|
import { instead } from "@lib/api/patcher";
|
||||||
import { ReactNative } from "@/lib/metro/common";
|
import { ReactNative } from "@lib/metro/common";
|
||||||
|
import { findByProps } from "@lib/metro/filters";
|
||||||
|
|
||||||
const { ContextMenu: _ContextMenu } = findByProps("ContextMenu");
|
const { ContextMenu: _ContextMenu } = findByProps("ContextMenu");
|
||||||
const { AccessibilityInfo } = ReactNative;
|
const { AccessibilityInfo } = ReactNative;
|
||||||
|
|
@ -10,7 +10,7 @@ type ContextMenuItem = {
|
||||||
iconSource: import("react-native").ImageSourcePropType,
|
iconSource: import("react-native").ImageSourcePropType,
|
||||||
action: () => unknown,
|
action: () => unknown,
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
};
|
||||||
|
|
||||||
type ContextMenuProps = {
|
type ContextMenuProps = {
|
||||||
triggerOnLongPress: boolean;
|
triggerOnLongPress: boolean;
|
||||||
|
|
@ -19,7 +19,7 @@ type ContextMenuProps = {
|
||||||
title: string;
|
title: string;
|
||||||
children: React.FC;
|
children: React.FC;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
};
|
||||||
|
|
||||||
export default function ContextMenu(props: ContextMenuProps) {
|
export default function ContextMenu(props: ContextMenuProps) {
|
||||||
const ref = React.useRef(null);
|
const ref = React.useRef(null);
|
||||||
|
|
@ -41,12 +41,12 @@ export default function ContextMenu(props: ContextMenuProps) {
|
||||||
unpatch(); // and skip this call
|
unpatch(); // and skip this call
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return <ReactNative.View ref={ref}>
|
return <ReactNative.View ref={ref}>
|
||||||
<_ContextMenu
|
<_ContextMenu
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
</ReactNative.View>
|
</ReactNative.View>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { Strings } from "@/core/i18n";
|
import { Strings } from "@core/i18n";
|
||||||
import { React, ReactNative as RN, stylesheet } from "@metro/common";
|
import { React, ReactNative as RN, stylesheet } from "@metro/common";
|
||||||
import { Forms, Button, Codeblock } from "@ui/components";
|
import { Button, Codeblock, Forms } from "@ui/components";
|
||||||
|
|
||||||
interface ErrorBoundaryState {
|
interface ErrorBoundaryState {
|
||||||
hasErr: boolean;
|
hasErr: boolean;
|
||||||
|
|
@ -47,6 +47,6 @@ export default class ErrorBoundary extends React.Component<ErrorBoundaryProps, E
|
||||||
text={Strings.RETRY}
|
text={Strings.RETRY}
|
||||||
/>
|
/>
|
||||||
</RN.ScrollView>
|
</RN.ScrollView>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { ButtonColors } from "@/lib/utils/types";
|
import { ButtonColors } from "@lib/utils/types";
|
||||||
import { findByProps } from "@metro/filters";
|
import { findByProps } from "@metro/filters";
|
||||||
import { Forms, Alert } from "@ui/components";
|
import { Alert, Forms } from "@ui/components";
|
||||||
|
|
||||||
const { FormInput } = Forms;
|
const { FormInput } = Forms;
|
||||||
const Alerts = findByProps("openLazy", "close");
|
const Alerts = findByProps("openLazy", "close");
|
||||||
|
|
@ -21,14 +21,14 @@ export default function InputAlert({ title, confirmText, confirmColor, onConfirm
|
||||||
const [error, setError] = React.useState("");
|
const [error, setError] = React.useState("");
|
||||||
|
|
||||||
function onConfirmWrapper() {
|
function onConfirmWrapper() {
|
||||||
const asyncOnConfirm = Promise.resolve(onConfirm(value))
|
const asyncOnConfirm = Promise.resolve(onConfirm(value));
|
||||||
|
|
||||||
asyncOnConfirm.then(() => {
|
asyncOnConfirm.then(() => {
|
||||||
Alerts.close();
|
Alerts.close();
|
||||||
}).catch((e: Error) => {
|
}).catch((e: Error) => {
|
||||||
setError(e.message);
|
setError(e.message);
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Alert
|
<Alert
|
||||||
|
|
@ -43,7 +43,7 @@ export default function InputAlert({ title, confirmText, confirmColor, onConfirm
|
||||||
<FormInput
|
<FormInput
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(v: string | { text: string }) => {
|
onChange={(v: string | { text: string; }) => {
|
||||||
setValue(typeof v === "string" ? v : v.text);
|
setValue(typeof v === "string" ? v : v.text);
|
||||||
if (error) setError("");
|
if (error) setError("");
|
||||||
}}
|
}}
|
||||||
|
|
@ -57,4 +57,4 @@ export default function InputAlert({ title, confirmText, confirmColor, onConfirm
|
||||||
/>
|
/>
|
||||||
</Alert>
|
</Alert>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
|
import { Strings } from "@core/i18n";
|
||||||
|
import { getAssetIDByName } from "@lib/api/assets";
|
||||||
import { ReactNative as RN } from "@metro/common";
|
import { ReactNative as RN } from "@metro/common";
|
||||||
import { findByProps } from "@metro/filters";
|
import { findByProps } from "@metro/filters";
|
||||||
import ErrorBoundary from "@ui/components/ErrorBoundary";
|
import ErrorBoundary from "@ui/components/ErrorBoundary";
|
||||||
import { getAssetIDByName } from "@/lib/api/assets";
|
|
||||||
import { Strings } from "@/core/i18n";
|
|
||||||
|
|
||||||
const { TextInput } = findByProps("TableRow");
|
const { TextInput } = findByProps("TableRow");
|
||||||
|
|
||||||
|
|
@ -14,16 +14,16 @@ export interface SearchProps {
|
||||||
|
|
||||||
|
|
||||||
function SearchIcon() {
|
function SearchIcon() {
|
||||||
return <RN.Image style={{ transform: [{ scale: 0.8 }]}} source={getAssetIDByName("search")} />
|
return <RN.Image style={{ transform: [{ scale: 0.8 }] }} source={getAssetIDByName("search")} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ({ onChangeText, placeholder, style }: SearchProps) => {
|
export default ({ onChangeText, placeholder, style }: SearchProps) => {
|
||||||
const [query, setQuery] = React.useState("");
|
const [query, setQuery] = React.useState("");
|
||||||
|
|
||||||
const onChange = (value: string) => {
|
const onChange = (value: string) => {
|
||||||
setQuery(value);
|
setQuery(value);
|
||||||
onChangeText?.(value);
|
onChangeText?.(value);
|
||||||
}
|
};
|
||||||
|
|
||||||
return <ErrorBoundary>
|
return <ErrorBoundary>
|
||||||
<RN.View style={style}>
|
<RN.View style={style}>
|
||||||
|
|
@ -38,5 +38,5 @@ export default ({ onChangeText, placeholder, style }: SearchProps) => {
|
||||||
value={query}
|
value={query}
|
||||||
/>
|
/>
|
||||||
</RN.View>
|
</RN.View>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
|
import { getAssetIDByName } from "@lib/api/assets";
|
||||||
|
import { findByProps } from "@lib/metro/filters";
|
||||||
import { ReactNative as RN } from "@metro/common";
|
import { ReactNative as RN } from "@metro/common";
|
||||||
import { getAssetIDByName } from "@/lib/api/assets";
|
|
||||||
import { findByProps } from "@/lib/metro/filters";
|
|
||||||
|
|
||||||
const { FormRow } = findByProps("FormRow");
|
const { FormRow } = findByProps("FormRow");
|
||||||
const { TableRow } = findByProps("TableRow");
|
const { TableRow } = findByProps("TableRow");
|
||||||
|
|
@ -30,5 +30,5 @@ export default function Summary({ label, icon, noPadding = false, noAnimation =
|
||||||
<RN.View style={!noPadding && { paddingHorizontal: 15 }}>{children}</RN.View>
|
<RN.View style={!noPadding && { paddingHorizontal: 15 }}>{children}</RN.View>
|
||||||
</>}
|
</>}
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import { ReactNative as RN } from "@metro/common";
|
|
||||||
import { findByDisplayName, findByName, findByProps } from "@metro/filters";
|
import { findByDisplayName, findByName, findByProps } from "@metro/filters";
|
||||||
|
|
||||||
// Discord
|
// Discord
|
||||||
|
|
@ -11,7 +10,7 @@ export const HelpMessage = findByName("HelpMessage");
|
||||||
export const SafeAreaView = findByProps("useSafeAreaInsets").SafeAreaView as any;
|
export const SafeAreaView = findByProps("useSafeAreaInsets").SafeAreaView as any;
|
||||||
|
|
||||||
// Vendetta
|
// Vendetta
|
||||||
export { default as Summary } from "@ui/components/Summary";
|
|
||||||
export { default as ErrorBoundary } from "@ui/components/ErrorBoundary";
|
|
||||||
export { default as Codeblock } from "@ui/components/Codeblock";
|
export { default as Codeblock } from "@ui/components/Codeblock";
|
||||||
export { default as Search } from "@ui/components/Search";
|
export { default as ErrorBoundary } from "@ui/components/ErrorBoundary";
|
||||||
|
export { default as Search } from "@ui/components/Search";
|
||||||
|
export { default as Summary } from "@ui/components/Summary";
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
export * as alerts from "./alerts";
|
export * as alerts from "./alerts";
|
||||||
export * as color from "./color";
|
export * as color from "./color";
|
||||||
export * as toasts from "./toasts";
|
|
||||||
export * as settings from "../../core/ui/settings";
|
|
||||||
export * as components from "./components";
|
export * as components from "./components";
|
||||||
|
export * as toasts from "./toasts";
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import { ReactNative as RN, stylesheet } from "@metro/common";
|
import { DeviceManager } from "@lib/api/native/modules";
|
||||||
import { findByName, findByProps, findByStoreName } from "@metro/filters";
|
|
||||||
import { after } from "@lib/api/patcher";
|
import { after } from "@lib/api/patcher";
|
||||||
import { toggleSafeMode } from "@lib/debug";
|
import { toggleSafeMode } from "@lib/debug";
|
||||||
import { DeviceManager } from "@lib/api/native/modules";
|
import { settings } from "@lib/settings";
|
||||||
|
import { ButtonColors } from "@lib/utils/types";
|
||||||
|
import { ReactNative as RN, stylesheet } from "@metro/common";
|
||||||
|
import { findByName, findByProps, findByStoreName } from "@metro/filters";
|
||||||
import { semanticColors } from "@ui/color";
|
import { semanticColors } from "@ui/color";
|
||||||
import { Button, Codeblock, ErrorBoundary as _ErrorBoundary, SafeAreaView } from "@ui/components";
|
import { Button, Codeblock, ErrorBoundary as _ErrorBoundary, SafeAreaView } from "@ui/components";
|
||||||
import { settings } from "@lib/settings";
|
|
||||||
import { ButtonColors } from "@/lib/utils/types";
|
|
||||||
|
|
||||||
const ErrorBoundary = findByName("ErrorBoundary");
|
const ErrorBoundary = findByName("ErrorBoundary");
|
||||||
|
|
||||||
|
|
@ -80,8 +80,8 @@ export default () => after("render", ErrorBoundary.prototype, function (this: an
|
||||||
{ text: "Restart Discord", onPress: this.handleReload },
|
{ text: "Restart Discord", onPress: this.handleReload },
|
||||||
...!settings.safeMode?.enabled ? [{ text: "Restart in Safe Mode", onPress: toggleSafeMode }] : [],
|
...!settings.safeMode?.enabled ? [{ text: "Restart in Safe Mode", onPress: toggleSafeMode }] : [],
|
||||||
{ text: "Retry Render", color: ButtonColors.RED, onPress: () => this.setState({ info: null, error: null }) },
|
{ text: "Retry Render", color: ButtonColors.RED, onPress: () => this.setState({ info: null, error: null }) },
|
||||||
]
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<_ErrorBoundary>
|
<_ErrorBoundary>
|
||||||
<SafeAreaView style={styles.container}>
|
<SafeAreaView style={styles.container}>
|
||||||
|
|
@ -98,7 +98,7 @@ export default () => after("render", ErrorBoundary.prototype, function (this: an
|
||||||
<BadgableTabBar
|
<BadgableTabBar
|
||||||
tabs={tabs}
|
tabs={tabs}
|
||||||
activeTab={this.state.activeTab}
|
activeTab={this.state.activeTab}
|
||||||
onTabSelected={(tab: string) => { this.setState({ activeTab: tab }) }}
|
onTabSelected={(tab: string) => { this.setState({ activeTab: tab }); }}
|
||||||
/>
|
/>
|
||||||
</RN.View>
|
</RN.View>
|
||||||
<Codeblock
|
<Codeblock
|
||||||
|
|
@ -122,10 +122,10 @@ export default () => after("render", ErrorBoundary.prototype, function (this: an
|
||||||
size={button.size ?? "small"}
|
size={button.size ?? "small"}
|
||||||
onPress={button.onPress}
|
onPress={button.onPress}
|
||||||
style={DeviceManager.isTablet ? { flex: `0.${buttons.length}`, marginLeft: buttonIndex } : { marginTop: buttonIndex }}
|
style={DeviceManager.isTablet ? { flex: `0.${buttons.length}`, marginLeft: buttonIndex } : { marginTop: buttonIndex }}
|
||||||
/>
|
/>;
|
||||||
})}
|
})}
|
||||||
</RN.View>
|
</RN.View>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
</_ErrorBoundary>
|
</_ErrorBoundary>
|
||||||
)
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { findByProps } from "@metro/filters";
|
import { getAssetIDByName } from "@lib/api/assets";
|
||||||
import { ReactNative, toasts } from "@metro/common";
|
import { ReactNative, toasts } from "@metro/common";
|
||||||
import { getAssetIDByName } from "@/lib/api/assets";
|
import { findByProps } from "@metro/filters";
|
||||||
|
|
||||||
const { uuid4 } = findByProps("uuid4");
|
const { uuid4 } = findByProps("uuid4");
|
||||||
|
|
||||||
export const showToast = (content: string, asset?: number) => toasts.open({
|
export const showToast = (content: string, asset?: number) => toasts.open({
|
||||||
//? In build 182205/44707, Discord changed their toasts, source is no longer used, rather icon, and a key is needed.
|
// ? In build 182205/44707, Discord changed their toasts, source is no longer used, rather icon, and a key is needed.
|
||||||
// TODO: We could probably have the developer specify a key themselves, but this works to fix toasts
|
// TODO: We could probably have the developer specify a key themselves, but this works to fix toasts
|
||||||
key: `vd-toast-${uuid4()}`,
|
key: `vd-toast-${uuid4()}`,
|
||||||
content: content,
|
content: content,
|
||||||
|
|
@ -18,4 +18,4 @@ showToast.showCopyToClipboard = (message = "Copied plugin URL to clipboard.") =>
|
||||||
if (ReactNative.Platform.OS !== "android" || ReactNative.Platform.Version <= 32) {
|
if (ReactNative.Platform.OS !== "android" || ReactNative.Platform.Version <= 32) {
|
||||||
showToast(message, getAssetIDByName("toast_copy_link"));
|
showToast(message, getAssetIDByName("toast_copy_link"));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
export const DISCORD_SERVER = "https://discord.gg/XjYgWXHb9Q";
|
export const DISCORD_SERVER = "https://discord.gg/XjYgWXHb9Q";
|
||||||
export const GITHUB = "https://github.com/pyoncord";
|
export const GITHUB = "https://github.com/pyoncord";
|
||||||
export const PROXY_PREFIX = "https://vd-plugins.github.io/proxy";
|
export const PROXY_PREFIX = "https://vd-plugins.github.io/proxy";
|
||||||
export const HTTP_REGEX = /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)$/;
|
export const HTTP_REGEX = /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)$/;
|
||||||
export const HTTP_REGEX_MULTI = /https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g;
|
export const HTTP_REGEX_MULTI = /https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_+.~#?&//=]*)/g;
|
||||||
|
|
||||||
// These are still Vendetta's
|
// These are still Vendetta's
|
||||||
export const DISCORD_SERVER_ID = "1015931589865246730";
|
export const DISCORD_SERVER_ID = "1015931589865246730";
|
||||||
export const PLUGINS_CHANNEL_ID = "1091880384561684561";
|
export const PLUGINS_CHANNEL_ID = "1091880384561684561";
|
||||||
export const THEMES_CHANNEL_ID = "1091880434939482202";
|
export const THEMES_CHANNEL_ID = "1091880434939482202";
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ export enum Events {
|
||||||
GET = "GET",
|
GET = "GET",
|
||||||
SET = "SET",
|
SET = "SET",
|
||||||
DEL = "DEL",
|
DEL = "DEL",
|
||||||
};
|
}
|
||||||
|
|
||||||
export type EmitterEvent = "SET" | "GET" | "DEL";
|
export type EmitterEvent = "SET" | "GET" | "DEL";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { findInTree } from "@lib/utils";
|
import { findInTree } from "@lib/utils";
|
||||||
import { SearchFilter } from "./findInTree";
|
import { SearchFilter } from "@lib/utils/findInTree";
|
||||||
|
|
||||||
export const findInReactTree = (tree: { [key: string]: any }, filter: SearchFilter): any => findInTree(tree, filter, {
|
export const findInReactTree = (tree: { [key: string]: any; }, filter: SearchFilter): any => findInTree(tree, filter, {
|
||||||
walkable: ["props", "children", "child", "sibling"],
|
walkable: ["props", "children", "child", "sibling"],
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
// Makes mass-importing utils cleaner, chosen over moving utils to one file
|
// Makes mass-importing utils cleaner, chosen over moving utils to one file
|
||||||
|
|
||||||
|
export * as constants from "@lib/utils/constants";
|
||||||
|
export { createEmitter } from "@lib/utils/emitter";
|
||||||
export { findInReactTree } from "@lib/utils/findInReactTree";
|
export { findInReactTree } from "@lib/utils/findInReactTree";
|
||||||
export { findInTree } from "@lib/utils/findInTree";
|
export { findInTree } from "@lib/utils/findInTree";
|
||||||
|
export * as logger from "@lib/utils/logger";
|
||||||
export { safeFetch } from "@lib/utils/safeFetch";
|
export { safeFetch } from "@lib/utils/safeFetch";
|
||||||
|
export * as types from "@lib/utils/types";
|
||||||
export { unfreeze } from "@lib/utils/unfreeze";
|
export { unfreeze } from "@lib/utils/unfreeze";
|
||||||
export { without } from "@lib/utils/without";
|
export { without } from "@lib/utils/without";
|
||||||
export { createEmitter } from "@lib/utils/emitter";
|
|
||||||
|
|
||||||
export * as constants from "@lib/utils/constants";
|
|
||||||
export * as logger from "@lib/utils/logger";
|
|
||||||
export * as types from "@lib/utils/types";
|
|
||||||
|
|
|
||||||
|
|
@ -14,4 +14,4 @@ export interface Logger {
|
||||||
export const logModule = findByProps("setLogFn").default;
|
export const logModule = findByProps("setLogFn").default;
|
||||||
const logger: Logger = new logModule("Bunny");
|
const logger: Logger = new logModule("Bunny");
|
||||||
|
|
||||||
export default logger;
|
export default logger;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
// A really basic fetch wrapper which throws on non-ok response codes
|
// A really basic fetch wrapper which throws on non-ok response codes
|
||||||
|
|
||||||
export async function safeFetch(input: RequestInfo | URL, options?: RequestInit, timeout = 10000) {
|
export async function safeFetch(input: RequestInfo | URL, options?: RequestInit, timeout = 10000) {
|
||||||
const req = await fetch(input, {
|
const req = await fetch(input, {
|
||||||
signal: timeoutSignal(timeout),
|
signal: timeoutSignal(timeout),
|
||||||
|
|
|
||||||
|
|
@ -13,4 +13,4 @@ export enum ButtonColors {
|
||||||
LIGHTGREY = "lightgrey",
|
LIGHTGREY = "lightgrey",
|
||||||
WHITE = "white",
|
WHITE = "white",
|
||||||
LINK = "link"
|
LINK = "link"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,4 +3,4 @@
|
||||||
export function unfreeze(obj: object) {
|
export function unfreeze(obj: object) {
|
||||||
if (Object.isFrozen(obj)) return Object.assign({}, obj);
|
if (Object.isFrozen(obj)) return Object.assign({}, obj);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
export function without<O extends object, K extends readonly (keyof O)[]>(object: O, ...keys: K): Omit<O, typeof keys[number]> {
|
export function without<O extends object, K extends readonly (keyof O)[]>(object: O, ...keys: K): Omit<O, typeof keys[number]> {
|
||||||
const cloned = { ...object };
|
const cloned = { ...object };
|
||||||
keys.forEach((k) => delete cloned[k]);
|
keys.forEach(k => delete cloned[k]);
|
||||||
return cloned;
|
return cloned;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { createVendettaObject } from "../core/vendettaObject";
|
import { createVendettaObject } from "@core/vendettaObject";
|
||||||
|
|
||||||
import * as api from "@lib/api";
|
import * as api from "@lib/api";
|
||||||
|
import patcher from "@lib/api/patcher";
|
||||||
import * as debug from "@lib/debug";
|
import * as debug from "@lib/debug";
|
||||||
import * as managers from "@lib/managers";
|
import * as managers from "@lib/managers";
|
||||||
import * as metro from "@lib/metro";
|
import * as metro from "@lib/metro";
|
||||||
|
|
@ -8,8 +8,6 @@ import * as settings from "@lib/settings";
|
||||||
import * as ui from "@lib/ui";
|
import * as ui from "@lib/ui";
|
||||||
import * as utils from "@lib/utils";
|
import * as utils from "@lib/utils";
|
||||||
|
|
||||||
import patcher from "@lib/api/patcher";
|
|
||||||
|
|
||||||
// You may now worry about my mental state.
|
// You may now worry about my mental state.
|
||||||
function createBunnyObject(unloads: any[]) {
|
function createBunnyObject(unloads: any[]) {
|
||||||
return {
|
return {
|
||||||
|
|
@ -59,7 +57,7 @@ function createBunnyObject(unloads: any[]) {
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
delete window.bunny;
|
delete window.bunny;
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BunnyObject = ReturnType<typeof createBunnyObject>;
|
export type BunnyObject = ReturnType<typeof createBunnyObject>;
|
||||||
|
|
|
||||||
2
src/modules.d.ts
vendored
2
src/modules.d.ts
vendored
|
|
@ -2,4 +2,4 @@ declare module "bunny-build" {
|
||||||
const version: string;
|
const version: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export * from "bunny-build";
|
export * from "bunny-build";
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"paths": {
|
"paths": {
|
||||||
"@*": ["src*"],
|
|
||||||
"@lib/*": ["src/lib/*"],
|
"@lib/*": ["src/lib/*"],
|
||||||
|
"@core/*": ["src/core/*"],
|
||||||
"@metro/*": ["src/lib/metro/*"],
|
"@metro/*": ["src/lib/metro/*"],
|
||||||
"@ui/*": ["src/lib/ui/*"],
|
"@ui/*": ["src/lib/ui/*"],
|
||||||
"@types": ["src/utils/types.ts"],
|
"@types": ["src/utils/types.ts"],
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue