diff --git a/src/index.ts b/src/index.ts
index ce354f1..010341e 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -2,10 +2,13 @@ import patcher from "@lib/patcher";
import logger from "@lib/logger";
import * as metro from "@metro/filters";
import * as common from "@metro/common";
+import initSettings from "./ui/settings";
console.log("Hello from Vendetta!");
try {
+ initSettings();
+
window.vendetta = {
patcher: patcher,
metro: { ...metro, common: common },
diff --git a/src/lib/metro/common.ts b/src/lib/metro/common.ts
index 9c276fd..5c89d25 100644
--- a/src/lib/metro/common.ts
+++ b/src/lib/metro/common.ts
@@ -3,4 +3,8 @@ import { findByProps } from "@metro/filters";
// Discord
export const Constants = findByProps("API_HOST");
export const channels = findByProps("getVoiceChannelId");
-export const i18n = findByProps("Messages");
\ No newline at end of file
+export const i18n = findByProps("Messages");
+
+// React
+export const React = findByProps("createElement") as typeof import("react");
+export const ReactNative = findByProps("Text", "Image") as typeof import("react-native");
\ No newline at end of file
diff --git a/src/ui/settings/components/Settings.tsx b/src/ui/settings/components/Settings.tsx
new file mode 100644
index 0000000..c5f7e9f
--- /dev/null
+++ b/src/ui/settings/components/Settings.tsx
@@ -0,0 +1,48 @@
+import { React, ReactNative as RN } from "@metro/common";
+import { findByProps } from "@metro/filters";
+import Version from "./Version";
+
+const { FormRow, FormSection, FormText } = findByProps("FormSection");
+const hermesProps = window.HermesInternal.getRuntimeProperties();
+const rnVer = RN.Platform.constants.reactNativeVersion;
+
+export default function Settings() {
+ const versions = [
+ {
+ label: "Discord",
+ version: RN.NativeModules.InfoDictionaryManager.Version,
+ },
+ {
+ label: "React",
+ version: React.version,
+ },
+ {
+ label: "React Native",
+ version: `${rnVer.major || 0}.${rnVer.minor || 0}.${rnVer.patch || 0}`,
+ },
+ {
+ label: "Hermes",
+ version: `${hermesProps["OSS Release Version"]} ${hermesProps["Build"]}`,
+ },
+ {
+ label: "Bytecode",
+ version: hermesProps["Bytecode Version"],
+ },
+ ];
+
+ return (
+ <>
+ {/* Why is there still a divider? */}
+
+ RN.NativeModules.BundleUpdaterManager.reload()}
+ />
+
+
+ {versions.map((v) => )}
+
+ >
+ )
+}
\ No newline at end of file
diff --git a/src/ui/settings/components/SettingsSection.tsx b/src/ui/settings/components/SettingsSection.tsx
new file mode 100644
index 0000000..0818822
--- /dev/null
+++ b/src/ui/settings/components/SettingsSection.tsx
@@ -0,0 +1,20 @@
+import { React } from "@metro/common";
+import { findByProps } from "@metro/filters";
+
+const { FormRow, FormSection } = findByProps("FormSection");
+
+interface SettingsSectionProps {
+ navigation: any;
+}
+
+export default function SettingsSection({ navigation }: SettingsSectionProps) {
+ return (
+
+ navigation.push("VendettaSettings")}
+ />
+
+ )
+}
\ No newline at end of file
diff --git a/src/ui/settings/components/Version.tsx b/src/ui/settings/components/Version.tsx
new file mode 100644
index 0000000..a5d5ad9
--- /dev/null
+++ b/src/ui/settings/components/Version.tsx
@@ -0,0 +1,18 @@
+import { React } from "@metro/common";
+import { findByProps } from "@metro/filters";
+
+interface VersionProps {
+ label: string;
+ version: string
+}
+
+const { FormRow, FormText } = findByProps("FormSection");
+
+export default function Version({ label, version }: VersionProps) {
+ return (
+ {version}}
+ />
+ )
+}
\ No newline at end of file
diff --git a/src/ui/settings/index.tsx b/src/ui/settings/index.tsx
new file mode 100644
index 0000000..994669c
--- /dev/null
+++ b/src/ui/settings/index.tsx
@@ -0,0 +1,38 @@
+import { React, i18n } from "@metro/common";
+import { findByDisplayName } from "@metro/filters";
+import { after } from "@lib/patcher";
+import findInReactTree from "@utils/findInReactTree";
+import Settings from "./components/Settings";
+import SettingsSection from "./components/SettingsSection";
+
+const screensModule = findByDisplayName("getScreens", false);
+const settingsModule = findByDisplayName("UserSettingsOverviewWrapper", false);
+
+export default function initSettings() {
+ const screensPatch = after("default", screensModule, (args, ret) => {
+ return {
+ ...ret,
+ VendettaSettings: {
+ title: "Vendetta Settings",
+ render: Settings
+ }
+ }
+ });
+
+ const settingsPatch = after("default", settingsModule, (args, _ret) => {
+ settingsPatch();
+ const toPatch = findInReactTree(_ret.props.children, i => i.type && i.type.name === "UserSettingsOverview");
+
+ // Upload logs button gone
+ after("renderSupportAndAcknowledgements", toPatch.type.prototype, (args, { props: { children } }) => {
+ const index = children.findIndex((c: any) => c?.type?.name === "UploadLogsButton");
+ if (index !== -1) children.splice(index, 1);
+ });
+
+ after("render", toPatch.type.prototype, (args, { props: { children } }) => {
+ const titles = [i18n.Messages["BILLING_SETTINGS"], i18n.Messages["PREMIUM_SETTINGS"]];
+ const index = children.findIndex((c: any) => titles.includes(c.props.title));
+ children.splice(index === -1 ? 4 : index, 0, );
+ });
+ });
+}
\ No newline at end of file