diff --git a/build.mjs b/build.mjs index 6ee549c..fbeb166 100644 --- a/build.mjs +++ b/build.mjs @@ -1,11 +1,16 @@ import { build } from "esbuild"; +import { promisify } from "util"; +import { exec as _exec } from "child_process"; +import { replace } from "esbuild-plugin-replace"; import alias from "esbuild-plugin-alias"; import esg from "esbuild-plugin-external-global"; import fs from "fs/promises"; import path from "path"; +const exec = promisify(_exec); const tsconfig = JSON.parse(await fs.readFile("./tsconfig.json")); const aliases = Object.fromEntries(Object.entries(tsconfig.compilerOptions.paths).map(([alias, [target]]) => [alias, path.resolve(target)])); +const commit = (await exec("git rev-parse HEAD")).stdout.trim().substring(0, 7) || "custom"; try { await build({ @@ -19,6 +24,9 @@ try { alias(aliases), esg.externalGlobalPlugin({ "react": "window.React", + }), + replace({ + "__vendettaVersion": commit, }) ], legalComments: "external", diff --git a/package.json b/package.json index 6199fe2..d16dbea 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "esbuild": "^0.15.11", "esbuild-plugin-alias": "^0.2.1", "esbuild-plugin-external-global": "^1.0.1", + "esbuild-plugin-replace": "^1.3.0", "redux": "^4.2.0", "typescript": "^4.8.4", "zustand": "^4.1.2" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6f707b1..a5dcf49 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,7 @@ specifiers: esbuild: ^0.15.11 esbuild-plugin-alias: ^0.2.1 esbuild-plugin-external-global: ^1.0.1 + esbuild-plugin-replace: ^1.3.0 redux: ^4.2.0 spitroast: ^1.4.2 typescript: ^4.8.4 @@ -22,6 +23,7 @@ devDependencies: esbuild: 0.15.11 esbuild-plugin-alias: 0.2.1 esbuild-plugin-external-global: 1.0.1 + esbuild-plugin-replace: 1.3.0 redux: 4.2.0 typescript: 4.8.4 zustand: 4.1.2 @@ -242,6 +244,12 @@ packages: resolution: {integrity: sha512-NDzYHRoShpvLqNcrgV8ZQh61sMIFAry5KLTQV83BPG5iTXCCu7h72SCfJ97bW0GqtuqDD/1aqLbKinI/rNgUsg==} dev: true + /esbuild-plugin-replace/1.3.0: + resolution: {integrity: sha512-i9v5FDfKUaxzpCLn+avq3I6lxJDL5616/tzP93/GFAI7REFwkMTy/pilEIvqlH8RTVpjlwJDOqUOYvJOf5jnXg==} + dependencies: + magic-string: 0.25.9 + dev: true + /esbuild-sunos-64/0.15.11: resolution: {integrity: sha512-EuBdTGlsMTjEl1sQnBX2jfygy7iR6CKfvOzi+gEOfhDqbHXsmY1dcpbVtcwHAg9/2yUZSfMJHMAgf1z8M4yyyw==} engines: {node: '>=12'} @@ -313,6 +321,12 @@ packages: engines: {node: '>=8'} dev: true + /magic-string/0.25.9: + resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} + dependencies: + sourcemap-codec: 1.4.8 + dev: true + /merge-options/3.0.4: resolution: {integrity: sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==} engines: {node: '>=10'} @@ -330,6 +344,11 @@ packages: resolution: {integrity: sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==} dev: true + /sourcemap-codec/1.4.8: + resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} + deprecated: Please use @jridgewell/sourcemap-codec instead + dev: true + /spitroast/1.4.2: resolution: {integrity: sha512-iEBsKg3EXTQj2nikYIMtOE5YSqbI5CtRxVYI+Gh+9HeQxf4u86UWF5yC5eTVAoReZSogbD2M37JYG8TYGBnFTg==} dev: false diff --git a/src/def.d.ts b/src/def.d.ts index 01daf00..3960ed0 100644 --- a/src/def.d.ts +++ b/src/def.d.ts @@ -127,6 +127,7 @@ interface VendettaObject { } settings: Settings; logger: Logger; + version: string; } declare global { diff --git a/src/index.ts b/src/index.ts index 00da721..f3eb61d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,7 +11,7 @@ import * as toasts from "@ui/toasts"; import { patchAssets, all, find, getAssetByID, getAssetByName, getAssetIDByName } from "@ui/assets"; import initSettings from "@ui/settings"; import { fixTheme } from "@ui/fixTheme"; -import { connectToDebugger, patchLogHook } from "@lib/debug"; +import { connectToDebugger, patchLogHook, versionHash } from "@lib/debug"; import { plugins, fetchPlugin, evalPlugin, stopPlugin, removePlugin, getSettings } from "@lib/plugins"; import settings from "@lib/settings"; @@ -54,12 +54,13 @@ async function init() { }, settings: settings, logger: logger, + version: versionHash, }; - initSettings(); - patchAssets(); patchLogHook(); + patchAssets(); fixTheme(); + initSettings(); } catch (e: Error | any) { erroredOnLoad = true; alert(`Vendetta failed to initialize... ${e.stack || e.toString()}`); diff --git a/src/lib/debug.ts b/src/lib/debug.ts index e513599..5dbdc98 100644 --- a/src/lib/debug.ts +++ b/src/lib/debug.ts @@ -1,3 +1,4 @@ +import { ReactNative as RN } from "@metro/common"; import { after } from "@lib/patcher"; import { getAssetIDByName } from "@ui/assets"; import { showToast } from "@ui/toasts"; @@ -41,3 +42,30 @@ export function patchLogHook() { logger.log(args[0]); }); } + +export const versionHash = "__vendettaVersion"; + +export function getDebugInfo(string: boolean = false) { + const InfoDictionaryManager = RN.NativeModules.InfoDictionaryManager; + const hermesProps = window.HermesInternal.getRuntimeProperties(); + const rnVer = RN.Platform.constants.reactNativeVersion; + + return { + vendetta: { + version: versionHash, + }, + discord: { + version: InfoDictionaryManager.Version, + build: InfoDictionaryManager.Build, + }, + react: { + version: React.version, + nativeVersion: `${rnVer.major || 0}.${rnVer.minor || 0}.${rnVer.patch || 0}`, + }, + hermes: { + version: hermesProps["OSS Release Version"], + buildType: hermesProps["Build"], + bytecodeVersion: hermesProps["Bytecode Version"], + } + } +} \ No newline at end of file diff --git a/src/ui/components.ts b/src/ui/components.ts index 6e88552..9564579 100644 --- a/src/ui/components.ts +++ b/src/ui/components.ts @@ -1,4 +1,5 @@ -import { findByProps } from "@metro/filters"; +import { findByDisplayName, findByProps } from "@metro/filters"; export const Forms = findByProps("Form", "FormSection"); -export const General = findByProps("Button", "Text", "View"); \ No newline at end of file +export const General = findByProps("Button", "Text", "View"); +export const Search = findByDisplayName("StaticSearchBarContainer"); \ No newline at end of file diff --git a/src/ui/settings/components/PluginCard.tsx b/src/ui/settings/components/PluginCard.tsx index cd6cfef..740a977 100644 --- a/src/ui/settings/components/PluginCard.tsx +++ b/src/ui/settings/components/PluginCard.tsx @@ -53,7 +53,7 @@ export default function PluginCard({ plugin }: PluginCardProps) { leading={} trailing={ { if (v) startPlugin(plugin.id); else stopPlugin(plugin.id); setEnabled(v); @@ -76,11 +76,11 @@ export default function PluginCard({ plugin }: PluginCardProps) { { plugin.update = !plugin.update; - setUpdate(plugin.update); showToast(`${plugin.update ? "Enabled" : "Disabled"} updates for ${plugin.manifest.name}.`, getAssetIDByName("toast_image_saved")); + setUpdate(plugin.update); }} > - + {getSettings(plugin.id) && showSettings(plugin)}> diff --git a/src/ui/settings/pages/Developer.tsx b/src/ui/settings/pages/Developer.tsx index a05bd99..ccc97b0 100644 --- a/src/ui/settings/pages/Developer.tsx +++ b/src/ui/settings/pages/Developer.tsx @@ -1,19 +1,33 @@ -import { ReactNative as RN } from "@metro/common"; -import { Forms } from "@ui/components"; +import { ReactNative as RN, stylesheet } from "@metro/common"; +import { Forms, Search } from "@ui/components"; import { getAssetIDByName } from "@ui/assets"; +import { showToast } from "@/ui/toasts"; import { connectToDebugger } from "@lib/debug"; import { all } from "@ui/assets"; import settings from "@lib/settings"; +import logger from "@lib/logger"; import AssetDisplay from "@ui/settings/components/AssetDisplay"; const { FormSection, FormRow, FormInput, FormDivider } = Forms; +const { connectToDevTools } = window.__vendetta_rdc; + +const styles = stylesheet.createThemedStyleSheet({ + search: { + margin: 0, + padding: 0, + paddingRight: 15, + paddingLeft: 15, + borderBottomWidth: 0, + backgroundColor: "none" + } +}) export default function Developer() { const [debuggerUrl, setDebuggerUrl] = React.useState(settings.debuggerUrl || ""); const [searchName, setSearchName] = React.useState(""); return ( - <> + connectToDebugger(debuggerUrl)} /> + + {connectToDevTools && } + trailing={FormRow.Arrow} + onPress={() => { + try { + connectToDevTools({ + host: debuggerUrl.split(":")[0], + resolveRNStyle: RN.StyleSheet.flatten, + }); + } catch(e) { + logger.error("Failed to connect to React DevTools!", e); + showToast("Failed to connect to React DevTools!", getAssetIDByName("Small")); + } + }} + />} - setSearchName(v)} - title="SEARCH" + setSearchName(v)} + placeholder="Search..." /> a.name.includes(searchName))} @@ -48,6 +79,6 @@ export default function Developer() { keyExtractor={item => item.name} /> - + ) } \ No newline at end of file diff --git a/src/ui/settings/pages/General.tsx b/src/ui/settings/pages/General.tsx index 96fb455..da90aac 100644 --- a/src/ui/settings/pages/General.tsx +++ b/src/ui/settings/pages/General.tsx @@ -2,36 +2,40 @@ import { ReactNative as RN, url } from "@metro/common"; import { DISCORD_SERVER, GITHUB } from "@lib/constants"; import { getAssetIDByName } from "@ui/assets"; import { Forms } from "@ui/components"; +import { getDebugInfo } from "@lib/debug"; import Version from "@ui/settings/components/Version"; import settings from "@lib/settings"; -const { FormRow, FormSection, FormDivider, FormSwitch } = Forms; -const InfoDictionaryManager = RN.NativeModules.InfoDictionaryManager; -const hermesProps = window.HermesInternal.getRuntimeProperties(); -const rnVer = RN.Platform.constants.reactNativeVersion; +const { FormRow, FormSwitchRow, FormSection, FormDivider } = Forms; +const debugInfo = getDebugInfo() export default function General() { const [devSettings, setDevSettings] = React.useState(settings.developerSettings || false); const versions = [ + { + label: "Vendetta", + version: debugInfo.vendetta.version, + icon: "ic_progress_wrench_24px" + }, { label: "Discord", - version: `${InfoDictionaryManager.Version} (${InfoDictionaryManager.Build})`, + version: `${debugInfo.discord.version} (${debugInfo.discord.build})`, icon: "Discord", }, { label: "React", - version: React.version, + version: debugInfo.react.version, icon: "ic_category_16px", }, { label: "React Native", - version: `${rnVer.major || 0}.${rnVer.minor || 0}.${rnVer.patch || 0}`, + version: debugInfo.react.nativeVersion, icon: "mobile", }, { label: "Hermes", - version: `${hermesProps["OSS Release Version"]} ${hermesProps["Build"]} | Bytecode ${hermesProps["Bytecode Version"]}`, + version: `${debugInfo.hermes.version} ${debugInfo.hermes.buildType} | Bytecode ${debugInfo.hermes.bytecodeVersion}`, icon: "ic_badge_staff", }, ]; @@ -69,16 +73,14 @@ export default function General() { onPress={() => RN.NativeModules.BundleUpdaterManager.reload()} /> - } - trailing={ { - settings.developerSettings = v; - setDevSettings(v); - }} - />} + value={devSettings} + onValueChange={(v: boolean) => { + settings.developerSettings = v; + setDevSettings(v); + }} /> diff --git a/src/ui/settings/pages/Plugins.tsx b/src/ui/settings/pages/Plugins.tsx index 32a6e82..b6f81ab 100644 --- a/src/ui/settings/pages/Plugins.tsx +++ b/src/ui/settings/pages/Plugins.tsx @@ -12,7 +12,7 @@ export default function Plugins() { const [pluginList, setPluginList] = React.useState(plugins); return ( - <> + setPluginUrl(v)} @@ -20,7 +20,7 @@ export default function Plugins() { /> } + leading={} trailing={FormRow.Arrow} onPress={() => { fetchPlugin(pluginUrl).then(() => { @@ -37,6 +37,6 @@ export default function Plugins() { renderItem={({ item }) => } keyExtractor={item => item.id} /> - + ) } \ No newline at end of file