From 00b5be7130e2701443448975ebf9d3827fcfdd96 Mon Sep 17 00:00:00 2001 From: amsyarasyiq <82711525+amsyarasyiq@users.noreply.github.com> Date: Fri, 23 Feb 2024 15:11:35 +0800 Subject: [PATCH] Move types to their respective files --- src/def.d.ts | 522 -------------------- src/global.d.ts | 15 + src/lib/commands.ts | 61 ++- src/lib/debug.ts | 20 +- src/lib/emitter.ts | 25 +- src/lib/loader.ts | 20 +- src/lib/logger.ts | 12 +- src/lib/metro/common.ts | 7 +- src/lib/metro/filters.ts | 6 +- src/lib/native.ts | 78 ++- src/lib/plugins.ts | 23 +- src/lib/settings.ts | 18 +- src/lib/storage/backends.ts | 6 +- src/lib/storage/index.ts | 4 +- src/lib/themes.ts | 26 +- src/lib/types.ts | 16 + src/lib/utils/findInReactTree.ts | 2 +- src/lib/utils/findInTree.ts | 8 +- src/lib/vendettaObject.ts | 54 +- src/ui/alerts.ts | 17 +- src/ui/assets.ts | 6 +- src/ui/components/Codeblock.tsx | 7 +- src/ui/components/ErrorBoundary.tsx | 5 +- src/ui/components/InputAlert.tsx | 13 +- src/ui/components/Search.tsx | 34 +- src/ui/components/Summary.tsx | 8 +- src/ui/safeMode.tsx | 2 +- src/ui/settings/components/AssetDisplay.tsx | 2 +- src/ui/settings/components/PluginCard.tsx | 4 +- src/ui/settings/components/ThemeCard.tsx | 4 +- src/ui/settings/pages/Plugins.tsx | 3 +- src/ui/settings/pages/Themes.tsx | 4 +- tsconfig.json | 3 +- 33 files changed, 418 insertions(+), 617 deletions(-) delete mode 100644 src/def.d.ts create mode 100644 src/global.d.ts create mode 100644 src/lib/types.ts diff --git a/src/def.d.ts b/src/def.d.ts deleted file mode 100644 index 68702bc..0000000 --- a/src/def.d.ts +++ /dev/null @@ -1,522 +0,0 @@ -import * as _spitroast from "spitroast"; -import _React from "react"; -import _RN from "react-native"; -import _Clipboard from "@react-native-clipboard/clipboard"; -import _moment from "moment"; -import _chroma from "chroma-js"; -import _lodash from "lodash"; - -type MetroModules = { [id: number]: any }; - -// Component types -interface SummaryProps { - label: string; - icon?: string; - noPadding?: boolean; - noAnimation?: boolean; - children: JSX.Element | JSX.Element[]; -} - -interface ErrorBoundaryProps { - children: JSX.Element | JSX.Element[]; -} - -interface CodeblockProps { - selectable?: boolean; - style?: _RN.TextStyle; - children?: string; -} - -interface SearchProps { - onChangeText?: (v: string) => void; - placeholder?: string; - style?: _RN.TextStyle; -} - -// Helper types for API functions -type PropIntellisense

= Record & Record; -type PropsFinder = (...props: T[]) => PropIntellisense; -type PropsFinderAll = (...props: T[]) => PropIntellisense[]; - -type LoggerFunction = (...messages: any[]) => void; -interface Logger { - log: LoggerFunction; - info: LoggerFunction; - warn: LoggerFunction; - error: LoggerFunction; - time: LoggerFunction; - trace: LoggerFunction; - verbose: LoggerFunction; -} - -type SearchTree = Record; -type SearchFilter = (tree: SearchTree) => boolean; -interface FindInTreeOptions { - walkable?: string[]; - ignore?: string[]; - maxDepth?: number; -} - -interface Asset { - name: string; - id: number; -} - -export enum ButtonColors { - BRAND = "brand", - RED = "red", - GREEN = "green", - PRIMARY = "primary", - TRANSPARENT = "transparent", - GREY = "grey", - LIGHTGREY = "lightgrey", - WHITE = "white", - LINK = "link" -} - -interface ConfirmationAlertOptions { - title?: string; - content: string | JSX.Element | (string | JSX.Element)[]; - confirmText?: string; - confirmColor?: ButtonColors; - onConfirm: () => void; - secondaryConfirmText?: string; - onConfirmSecondary?: () => void; - cancelText?: string; - onCancel?: () => void; - isDismissable?: boolean; -} - -interface InputAlertProps { - title?: string; - confirmText?: string; - confirmColor?: ButtonColors; - onConfirm: (input: string) => (void | Promise); - cancelText?: string; - placeholder?: string; - initialValue?: string; - secureTextEntry?: boolean; -} - -interface Author { - name: string; - id?: string; -} - -// See https://github.com/vendetta-mod/polymanifest -interface PluginManifest { - name: string; - description: string; - authors: Author[]; - main: string; - hash: string; - // Vendor-specific field, contains our own data - vendetta?: { - icon?: string; - }; -} - -interface Plugin { - id: string; - manifest: PluginManifest; - enabled: boolean; - update: boolean; - js: string; -} - -interface ThemeData { - name: string; - description?: string; - authors?: Author[]; - spec: number; - semanticColors?: Record; - rawColors?: Record; - background?: { - url: string; - blur?: number; - /** - * The alpha value of the background. - * `CHAT_BACKGROUND` of semanticColors alpha value will be ignored when this is specified - */ - alpha?: number; - } -} - -interface Theme { - id: string; - selected: boolean; - data: ThemeData; -} - -interface Settings { - debuggerUrl: string; - developerSettings: boolean; - safeMode?: { - enabled: boolean; - currentThemeId?: string; - }; -} - -interface ApplicationCommand { - description: string; - name: string; - options: ApplicationCommandOption[]; - execute: (args: any[], ctx: CommandContext) => CommandResult | void | Promise | Promise; - id?: string; - applicationId: string; - displayName: string; - displayDescription: string; - inputType: ApplicationCommandInputType; - type: ApplicationCommandType; -} - -export enum ApplicationCommandInputType { - BUILT_IN, - BUILT_IN_TEXT, - BUILT_IN_INTEGRATION, - BOT, - PLACEHOLDER, -} - -interface ApplicationCommandOption { - name: string; - description: string; - required?: boolean; - type: ApplicationCommandOptionType; - displayName: string; - displayDescription: string; -} - -export enum ApplicationCommandOptionType { - SUB_COMMAND = 1, - SUB_COMMAND_GROUP, - STRING, - INTEGER, - BOOLEAN, - USER, - CHANNEL, - ROLE, - MENTIONABLE, - NUMBER, - ATTACHMENT, -} - -export enum ApplicationCommandType { - CHAT = 1, - USER, - MESSAGE, -} - -interface CommandContext { - channel: any; - guild: any; -} - -interface CommandResult { - content: string; - tts?: boolean; -} - -interface RNConstants extends _RN.PlatformConstants { - // Android - Version: number; - Release: string; - Serial: string; - Fingerprint: string; - Model: string; - Brand: string; - Manufacturer: string; - ServerHost?: string; - - // iOS - forceTouchAvailable: boolean; - interfaceIdiom: string; - osVersion: string; - systemName: string; -} - -/** - * A key-value storage based upon `SharedPreferences` on Android. - * - * These types are based on Android though everything should be the same between - * platforms. - */ -interface MMKVManager { - /** - * Get the value for the given `key`, or null - * @param key The key to fetch - */ - getItem: (key: string) => Promise; - /** - * Deletes the value for the given `key` - * @param key The key to delete - */ - removeItem: (key: string) => void; - /** - * Sets the value of `key` to `value` - */ - setItem: (key: string, value: string) => void; - /** - * Goes through every item in storage and returns it, excluding the - * keys specified in `exclude`. - * @param exclude A list of items to exclude from result - */ - refresh: (exclude: string[]) => Promise>; - /** - * You will be murdered if you use this function. - * Clears ALL of Discord's settings. - */ - clear: () => void; -} - -interface FileManager { - /** - * @param path **Full** path to file - */ - fileExists: (path: string) => Promise; - /** - * Allowed URI schemes on Android: `file://`, `content://` ([See here](https://developer.android.com/reference/android/content/ContentResolver#accepts-the-following-uri-schemes:_3)) - */ - getSize: (uri: string) => Promise; - /** - * @param path **Full** path to file - * @param encoding Set to `base64` in order to encode response - */ - readFile(path: string, encoding: "base64" | "utf8"): Promise; - saveFileToGallery?(uri: string, fileName: string, fileType: "PNG" | "JPEG"): Promise; - /** - * Beware! This function has differing functionality on iOS and Android. - * @param storageDir Either `cache` or `documents`. - * @param path Path in `storageDir`, parents are recursively created. - * @param data The data to write to the file - * @param encoding Set to `base64` if `data` is base64 encoded. - * @returns Promise that resolves to path of the file once it got written - */ - writeFile(storageDir: "cache" | "documents", path: string, data: string, encoding: "base64" | "utf8"): Promise; - removeFile(storageDir: "cache" | "documents", path: string): Promise; - getConstants: () => { - /** - * The path the `documents` storage dir (see {@link writeFile}) represents. - */ - DocumentsDirPath: string; - CacheDirPath: string; - }; - /** - * Will apparently cease to exist some time in the future so please use {@link getConstants} instead. - * @deprecated - */ - DocumentsDirPath: string; -} - -type EmitterEvent = "SET" | "GET" | "DEL"; - -interface EmitterListenerData { - path: string[]; - value?: any; -} - -type EmitterListener = ( - event: EmitterEvent, - data: EmitterListenerData | any -) => any; - -type EmitterListeners = Record> - -interface Emitter { - listeners: EmitterListeners; - on: (event: EmitterEvent, listener: EmitterListener) => void; - off: (event: EmitterEvent, listener: EmitterListener) => void; - once: (event: EmitterEvent, listener: EmitterListener) => void; - emit: (event: EmitterEvent, data: EmitterListenerData) => void; -} - -interface StorageBackend { - get: () => unknown | Promise; - set: (data: unknown) => void | Promise; -} - -interface LoaderConfig { - customLoadUrl: { - enabled: boolean; - url: string; - }; - loadReactDevTools: boolean; -} - -interface LoaderIdentity { - name: string; - features: { - loaderConfig?: boolean; - devtools?: { - prop: string; - version: string; - }, - themes?: { - prop: string; - } - } -} - -interface DiscordStyleSheet { - [index: string]: any, - createStyles: >(sheet: T | (() => T)) => () => T; - createThemedStyleSheet: typeof import("react-native").StyleSheet.create; -} - -interface VendettaObject { - patcher: { - after: typeof _spitroast.after; - before: typeof _spitroast.before; - instead: typeof _spitroast.instead; - }; - metro: { - find: (filter: (m: any) => boolean) => any; - findAll: (filter: (m: any) => boolean) => any[]; - findByProps: PropsFinder; - findByPropsAll: PropsFinderAll; - findByName: (name: string, defaultExp?: boolean) => any; - findByNameAll: (name: string, defaultExp?: boolean) => any[]; - findByDisplayName: (displayName: string, defaultExp?: boolean) => any; - findByDisplayNameAll: (displayName: string, defaultExp?: boolean) => any[]; - findByTypeName: (typeName: string, defaultExp?: boolean) => any; - findByTypeNameAll: (typeName: string, defaultExp?: boolean) => any[]; - findByStoreName: (name: string) => any; - common: { - constants: PropIntellisense<"Fonts" | "Permissions">; - channels: PropIntellisense<"getVoiceChannelId">; - i18n: PropIntellisense<"Messages">; - url: PropIntellisense<"openURL">; - toasts: PropIntellisense<"open" | "close">; - stylesheet: DiscordStyleSheet; - clipboard: typeof _Clipboard; - assets: PropIntellisense<"registerAsset">; - invites: PropIntellisense<"acceptInviteAndTransitionToInviteChannel">; - commands: PropIntellisense<"getBuiltInCommands">; - navigation: PropIntellisense<"pushLazy">; - navigationStack: PropIntellisense<"createStackNavigator">; - NavigationNative: PropIntellisense<"NavigationContainer">; - // You may ask: "Why not just install Flux's types?" - // Answer: Discord have a (presumably proprietary) fork. It's wildly different. - Flux: PropIntellisense<"connectStores">; - FluxDispatcher: PropIntellisense<"_currentDispatchActionType">; - React: typeof _React; - ReactNative: typeof _RN; - moment: typeof _moment; - chroma: typeof _chroma; - lodash: typeof _lodash; - util: PropIntellisense<"inspect" | "isNullOrUndefined">; - }; - }; - constants: { - DISCORD_SERVER: string; - DISCORD_SERVER_ID: string; - PLUGINS_CHANNEL_ID: string; - THEMES_CHANNEL_ID: string; - GITHUB: string; - PROXY_PREFIX: string; - HTTP_REGEX: RegExp; - HTTP_REGEX_MULTI: RegExp; - }; - utils: { - findInReactTree: (tree: SearchTree, filter: SearchFilter) => any; - findInTree: (tree: SearchTree, filter: SearchFilter, options: FindInTreeOptions) => any; - safeFetch: (input: RequestInfo | URL, options?: RequestInit, timeout?: number) => Promise; - unfreeze: (obj: object) => object; - without: (object: O, ...keys: K) => Omit; - }; - debug: { - connectToDebugger: (url: string) => void; - // TODO: Type output? - getDebugInfo: () => void; - } - ui: { - components: { - // Discord - Forms: PropIntellisense<"Form" | "FormSection">; - General: PropIntellisense<"Button" | "Text" | "View">; - Alert: _React.ComponentType; - Button: _React.ComponentType & { Looks: any, Colors: ButtonColors, Sizes: any }; - HelpMessage: _React.ComponentType; - SafeAreaView: typeof _RN.SafeAreaView; - // Vendetta - Summary: _React.ComponentType; - ErrorBoundary: _React.ComponentType; - Codeblock: _React.ComponentType; - Search: _React.ComponentType; - } - toasts: { - showToast: (content: string, asset?: number) => void; - }; - alerts: { - showConfirmationAlert: (options: ConfirmationAlertOptions) => void; - showCustomAlert: (component: _React.ComponentType, props: any) => void; - showInputAlert: (options: InputAlertProps) => void; - }; - assets: { - all: Record; - find: (filter: (a: any) => void) => Asset | null | undefined; - getAssetByName: (name: string) => Asset; - getAssetByID: (id: number) => Asset; - getAssetIDByName: (name: string) => number; - }; - // TODO: Make a vain attempt to type these - semanticColors: Record; - rawColors: Record; - }; - plugins: { - plugins: Record; - fetchPlugin: (id: string) => Promise; - installPlugin: (id: string, enabled?: boolean) => Promise; - startPlugin: (id: string) => Promise; - stopPlugin: (id: string, disable?: boolean) => void; - removePlugin: (id: string) => void; - getSettings: (id: string) => JSX.Element; - }; - themes: { - themes: Record; - fetchTheme: (id: string, selected?: boolean) => Promise; - installTheme: (id: string) => Promise; - selectTheme: (id: string) => Promise; - removeTheme: (id: string) => Promise; - getCurrentTheme: () => Theme | null; - updateThemes: () => Promise; - }; - commands: { - registerCommand: (command: ApplicationCommand) => () => void; - }; - storage: { - createProxy: (target: T) => { proxy: T, emitter: Emitter }; - useProxy: (storage: T) => T; - createStorage: (backend: StorageBackend) => Promise>; - wrapSync: >(store: T) => Awaited; - awaitSyncWrapper: (store: any) => Promise; - createMMKVBackend: (store: string) => StorageBackend; - createFileBackend: (file: string) => StorageBackend; - }; - settings: Settings; - loader: { - identity?: LoaderIdentity; - config: LoaderConfig; - }; - logger: Logger; - version: string; - unload: () => void; -} - -interface VendettaPluginObject { - id: string; - manifest: PluginManifest; - storage: Record; -} - -declare global { - type React = typeof _React; - const __vendettaVersion: string; - - interface Window { - [key: PropertyKey]: any; - modules: MetroModules; - vendetta: VendettaObject; - React: typeof _React; - } -} diff --git a/src/global.d.ts b/src/global.d.ts new file mode 100644 index 0000000..5640bca --- /dev/null +++ b/src/global.d.ts @@ -0,0 +1,15 @@ +import { MetroModules } from "./lib/metro/filters"; + +declare global { + type React = typeof import("react"); + const __vendettaVersion: string; + + interface Window { + [key: string]: any; + modules: MetroModules; + vendetta: any; + React: typeof import("react"); + } +} + +export {}; diff --git a/src/lib/commands.ts b/src/lib/commands.ts index 4717d0b..9fa7be0 100644 --- a/src/lib/commands.ts +++ b/src/lib/commands.ts @@ -1,9 +1,68 @@ -import { ApplicationCommand, ApplicationCommandType } from "@types"; import { commands as commandsModule } from "@metro/common"; import { after } from "@lib/patcher"; let commands: ApplicationCommand[] = []; +export interface ApplicationCommand { + description: string; + name: string; + options: ApplicationCommandOption[]; + execute: (args: any[], ctx: CommandContext) => CommandResult | void | Promise | Promise; + id?: string; + applicationId: string; + displayName: string; + displayDescription: string; + inputType: ApplicationCommandInputType; + type: ApplicationCommandType; +} + +export enum ApplicationCommandInputType { + BUILT_IN, + BUILT_IN_TEXT, + BUILT_IN_INTEGRATION, + BOT, + PLACEHOLDER, +} + +export interface ApplicationCommandOption { + name: string; + description: string; + required?: boolean; + type: ApplicationCommandOptionType; + displayName: string; + displayDescription: string; +} + +export enum ApplicationCommandOptionType { + SUB_COMMAND = 1, + SUB_COMMAND_GROUP, + STRING, + INTEGER, + BOOLEAN, + USER, + CHANNEL, + ROLE, + MENTIONABLE, + NUMBER, + ATTACHMENT, +} + +export enum ApplicationCommandType { + CHAT = 1, + USER, + MESSAGE, +} + +export interface CommandContext { + channel: any; + guild: any; +} + +export interface CommandResult { + content: string; + tts?: boolean; +} + export function patchCommands() { const unpatch = after("getBuiltInCommands", commandsModule, ([type], res: ApplicationCommand[]) => { if (type === ApplicationCommandType.CHAT) return res.concat(commands); diff --git a/src/lib/debug.ts b/src/lib/debug.ts index 7d63b89..8dc35e2 100644 --- a/src/lib/debug.ts +++ b/src/lib/debug.ts @@ -1,4 +1,3 @@ -import { RNConstants } from "@types"; import { ReactNative as RN } from "@metro/common"; import { after } from "@lib/patcher"; import { getThemeFromLoader, selectTheme } from "@lib/themes"; @@ -8,8 +7,27 @@ import { showToast } from "@ui/toasts"; import settings from "@lib/settings"; import logger from "@lib/logger"; import { getLoaderName, isThemeSupported } from "./loader"; +import type { PlatformConstants } from "react-native"; export let socket: WebSocket; +export interface RNConstants extends PlatformConstants { + // Android + Version: number; + Release: string; + Serial: string; + Fingerprint: string; + Model: string; + Brand: string; + Manufacturer: string; + ServerHost?: string; + + // iOS + forceTouchAvailable: boolean; + interfaceIdiom: string; + osVersion: string; + systemName: string; +} + export async function toggleSafeMode() { settings.safeMode = { ...settings.safeMode, enabled: !settings.safeMode?.enabled } if (isThemeSupported()) { diff --git a/src/lib/emitter.ts b/src/lib/emitter.ts index 1bfbc08..4b64555 100644 --- a/src/lib/emitter.ts +++ b/src/lib/emitter.ts @@ -1,11 +1,32 @@ -import { Emitter, EmitterEvent, EmitterListener, EmitterListenerData, EmitterListeners } from "@types"; - export enum Events { GET = "GET", SET = "SET", DEL = "DEL", }; +export type EmitterEvent = "SET" | "GET" | "DEL"; + +export interface EmitterListenerData { + path: string[]; + value?: any; +} + +export type EmitterListener = ( + event: EmitterEvent, + data: EmitterListenerData | any +) => any; + +export type EmitterListeners = Record> + +export interface Emitter { + listeners: EmitterListeners; + on: (event: EmitterEvent, listener: EmitterListener) => void; + off: (event: EmitterEvent, listener: EmitterListener) => void; + once: (event: EmitterEvent, listener: EmitterListener) => void; + emit: (event: EmitterEvent, data: EmitterListenerData) => void; +} + + export default function createEmitter(): Emitter { return { listeners: Object.values(Events).reduce((acc, val: string) => ((acc[val] = new Set()), acc), {}) as EmitterListeners, diff --git a/src/lib/loader.ts b/src/lib/loader.ts index a96867a..3e454f2 100644 --- a/src/lib/loader.ts +++ b/src/lib/loader.ts @@ -1,10 +1,24 @@ -import type { LoaderConfig, LoaderIdentity, Theme } from "@types"; +import { Theme } from "./themes"; // @ts-ignore const pyonLoaderIdentity = globalThis.__PYON_LOADER__; // @ts-ignore const vendettaLoaderIdentity = globalThis.__vendetta_loader; +export interface VendettaLoaderIdentity { + name: string; + features: { + loaderConfig?: boolean; + devtools?: { + prop: string; + version: string; + }, + themes?: { + prop: string; + } + } +} + export function isVendettaLoader() { return vendettaLoaderIdentity != null; } @@ -48,10 +62,10 @@ function polyfillVendettaLoaderIdentity() { configurable: true }); - return loader as LoaderIdentity; + return loader as VendettaLoaderIdentity; } -export function getVendettaLoaderIdentity(): LoaderIdentity | null { +export function getVendettaLoaderIdentity(): VendettaLoaderIdentity | null { // @ts-ignore if (globalThis.__vendetta_loader) return globalThis.__vendetta_loader; return polyfillVendettaLoaderIdentity(); diff --git a/src/lib/logger.ts b/src/lib/logger.ts index 1e107df..2b5fbce 100644 --- a/src/lib/logger.ts +++ b/src/lib/logger.ts @@ -1,6 +1,16 @@ -import { Logger } from "@types"; import { findByProps } from "@metro/filters"; +export type LoggerFunction = (...messages: any[]) => void; +export interface Logger { + log: LoggerFunction; + info: LoggerFunction; + warn: LoggerFunction; + error: LoggerFunction; + time: LoggerFunction; + trace: LoggerFunction; + verbose: LoggerFunction; +} + export const logModule = findByProps("setLogFn").default; const logger: Logger = new logModule("Vendetta"); diff --git a/src/lib/metro/common.ts b/src/lib/metro/common.ts index 0eeef10..1f86f77 100644 --- a/src/lib/metro/common.ts +++ b/src/lib/metro/common.ts @@ -1,8 +1,13 @@ import { find, findByProps, findByStoreName } from "@metro/filters"; -import { DiscordStyleSheet } from "@types"; import { ReactNative as RN } from "@lib/preinit"; import type { StyleSheet } from "react-native"; +export interface DiscordStyleSheet { + [index: string]: any, + createStyles: >(sheet: T | (() => T)) => () => T; + createThemedStyleSheet: typeof import("react-native").StyleSheet.create; +} + const ThemeStore = findByStoreName("ThemeStore"); const colorModule = findByProps("colors", "unsafe_rawColors"); const colorResolver = colorModule.internal ?? colorModule.meta; diff --git a/src/lib/metro/filters.ts b/src/lib/metro/filters.ts index 97a9bd6..98d6305 100644 --- a/src/lib/metro/filters.ts +++ b/src/lib/metro/filters.ts @@ -1,6 +1,10 @@ -import { MetroModules, PropsFinder, PropsFinderAll } from "@types"; import { instead } from "@lib/patcher"; +export type MetroModules = { [id: number]: any }; +export type PropIntellisense

= Record & Record; +export type PropsFinder = (...props: T[]) => PropIntellisense; +export type PropsFinderAll = (...props: T[]) => PropIntellisense[]; + // Metro require declare const __r: (moduleId: number) => any; diff --git a/src/lib/native.ts b/src/lib/native.ts index 622033b..b365b0b 100644 --- a/src/lib/native.ts +++ b/src/lib/native.ts @@ -1,9 +1,81 @@ -import { MMKVManager as _MMKVManager, FileManager as _FileManager } from "@types"; const nmp = window.nativeModuleProxy; -export const MMKVManager = nmp.MMKVManager as _MMKVManager; +/** + * A key-value storage based upon `SharedPreferences` on Android. + * + * These types are based on Android though everything should be the same between + * platforms. + */ +export interface MMKVManager { + /** + * Get the value for the given `key`, or null + * @param key The key to fetch + */ + getItem: (key: string) => Promise; + /** + * Deletes the value for the given `key` + * @param key The key to delete + */ + removeItem: (key: string) => void; + /** + * Sets the value of `key` to `value` + */ + setItem: (key: string, value: string) => void; + /** + * Goes through every item in storage and returns it, excluding the + * keys specified in `exclude`. + * @param exclude A list of items to exclude from result + */ + refresh: (exclude: string[]) => Promise>; + /** + * You will be murdered if you use this function. + * Clears ALL of Discord's settings. + */ + clear: () => void; +} + +export interface FileManager { + /** + * @param path **Full** path to file + */ + fileExists: (path: string) => Promise; + /** + * Allowed URI schemes on Android: `file://`, `content://` ([See here](https://developer.android.com/reference/android/content/ContentResolver#accepts-the-following-uri-schemes:_3)) + */ + getSize: (uri: string) => Promise; + /** + * @param path **Full** path to file + * @param encoding Set to `base64` in order to encode response + */ + readFile(path: string, encoding: "base64" | "utf8"): Promise; + saveFileToGallery?(uri: string, fileName: string, fileType: "PNG" | "JPEG"): Promise; + /** + * Beware! This function has differing functionality on iOS and Android. + * @param storageDir Either `cache` or `documents`. + * @param path Path in `storageDir`, parents are recursively created. + * @param data The data to write to the file + * @param encoding Set to `base64` if `data` is base64 encoded. + * @returns Promise that resolves to path of the file once it got written + */ + writeFile(storageDir: "cache" | "documents", path: string, data: string, encoding: "base64" | "utf8"): Promise; + removeFile(storageDir: "cache" | "documents", path: string): Promise; + getConstants: () => { + /** + * The path the `documents` storage dir (see {@link writeFile}) represents. + */ + DocumentsDirPath: string; + CacheDirPath: string; + }; + /** + * Will apparently cease to exist some time in the future so please use {@link getConstants} instead. + * @deprecated + */ + DocumentsDirPath: string; +} + +export const MMKVManager = nmp.MMKVManager as MMKVManager; //! 173.10 renamed this to RTNFileManager. -export const FileManager = (nmp.DCDFileManager ?? nmp.RTNFileManager) as _FileManager; +export const FileManager = (nmp.DCDFileManager ?? nmp.RTNFileManager) as FileManager; //! 173.13 renamed this to RTNClientInfoManager. export const ClientInfoManager = nmp.InfoDictionaryManager ?? nmp.RTNClientInfoManager; //! 173.14 renamed this to RTNDeviceManager. diff --git a/src/lib/plugins.ts b/src/lib/plugins.ts index 9d0b346..f207da8 100644 --- a/src/lib/plugins.ts +++ b/src/lib/plugins.ts @@ -1,9 +1,9 @@ -import { PluginManifest, Plugin } from "@types"; import { safeFetch } from "@lib/utils"; import { awaitSyncWrapper, createMMKVBackend, createStorage, purgeStorage, wrapSync } from "@lib/storage"; import { allSettled } from "@lib/polyfills"; import logger, { logModule } from "@lib/logger"; import settings from "@lib/settings"; +import { Author } from "./types"; type EvaledPlugin = { onLoad?(): void; @@ -11,6 +11,27 @@ type EvaledPlugin = { settings: JSX.Element; }; +// See https://github.com/vendetta-mod/polymanifest +export interface PluginManifest { + name: string; + description: string; + authors: Author[]; + main: string; + hash: string; + // Vendor-specific field, contains our own data + vendetta?: { + icon?: string; + }; +} + +export interface Plugin { + id: string; + manifest: PluginManifest; + enabled: boolean; + update: boolean; + js: string; +} + export const plugins = wrapSync(createStorage>(createMMKVBackend("VENDETTA_PLUGINS"))); const loadedPlugins: Record = {}; diff --git a/src/lib/settings.ts b/src/lib/settings.ts index af9c5d1..0ec3ad7 100644 --- a/src/lib/settings.ts +++ b/src/lib/settings.ts @@ -1,6 +1,22 @@ -import { LoaderConfig, Settings } from "@types"; import { createFileBackend, createMMKVBackend, createStorage, wrapSync } from "@lib/storage"; import { getLoaderConfigPath } from "./loader"; +export interface Settings { + debuggerUrl: string; + developerSettings: boolean; + safeMode?: { + enabled: boolean; + currentThemeId?: string; + }; +} + +export interface LoaderConfig { + customLoadUrl: { + enabled: boolean; + url: string; + }; + loadReactDevTools: boolean; +} + export default wrapSync(createStorage(createMMKVBackend("VENDETTA_SETTINGS"))); export const loaderConfig = wrapSync(createStorage(createFileBackend(getLoaderConfigPath()))); diff --git a/src/lib/storage/backends.ts b/src/lib/storage/backends.ts index 6296181..c3bcbb7 100644 --- a/src/lib/storage/backends.ts +++ b/src/lib/storage/backends.ts @@ -1,7 +1,11 @@ -import { StorageBackend } from "@types"; import { MMKVManager, FileManager } from "@lib/native"; import { ReactNative as RN } from "@metro/common"; +export interface StorageBackend { + get: () => unknown | Promise; + set: (data: unknown) => void | Promise; +} + const ILLEGAL_CHARS_REGEX = /[<>:"\/\\|?*]/g; const filePathFixer = (file: string): string => RN.Platform.select({ diff --git a/src/lib/storage/index.ts b/src/lib/storage/index.ts index fb72e35..88ada11 100644 --- a/src/lib/storage/index.ts +++ b/src/lib/storage/index.ts @@ -1,5 +1,5 @@ -import { Emitter, StorageBackend } from "@types"; -import createEmitter from "@lib/emitter"; +import createEmitter, { Emitter } from "@lib/emitter"; +import { StorageBackend } from "@lib/storage/backends"; const emitterSymbol = Symbol.for("vendetta.storage.emitter"); const syncAwaitSymbol = Symbol.for("vendetta.storage.accessor"); diff --git a/src/lib/themes.ts b/src/lib/themes.ts index 13fabc4..250d56b 100644 --- a/src/lib/themes.ts +++ b/src/lib/themes.ts @@ -1,4 +1,3 @@ -import { Theme, ThemeData } from "@types"; import { ReactNative as RN, chroma } from "@metro/common"; import { findInReactTree, safeFetch } from "@lib/utils"; import { find, findByName, findByProps, findByStoreName } from "@metro/filters"; @@ -7,6 +6,31 @@ import { createFileBackend, createMMKVBackend, createStorage, wrapSync, awaitSyn import logger from "@lib/logger"; import { ThemeManager } from "./native"; import { getStoredTheme, getThemeFilePath } from "./loader"; +import { Author } from "./types"; + +export interface ThemeData { + name: string; + description?: string; + authors?: Author[]; + spec: number; + semanticColors?: Record; + rawColors?: Record; + background?: { + url: string; + blur?: number; + /** + * The alpha value of the background. + * `CHAT_BACKGROUND` of semanticColors alpha value will be ignored when this is specified + */ + alpha?: number; + } +} + +export interface Theme { + id: string; + selected: boolean; + data: ThemeData; +} //! As of 173.10, early-finding this does not work. // Somehow, this is late enough, though? diff --git a/src/lib/types.ts b/src/lib/types.ts new file mode 100644 index 0000000..830070f --- /dev/null +++ b/src/lib/types.ts @@ -0,0 +1,16 @@ +export interface Author { + name: string; + id?: string; +} + +export enum ButtonColors { + BRAND = "brand", + RED = "red", + GREEN = "green", + PRIMARY = "primary", + TRANSPARENT = "transparent", + GREY = "grey", + LIGHTGREY = "lightgrey", + WHITE = "white", + LINK = "link" +} \ No newline at end of file diff --git a/src/lib/utils/findInReactTree.ts b/src/lib/utils/findInReactTree.ts index 002f3e3..9d4ea53 100644 --- a/src/lib/utils/findInReactTree.ts +++ b/src/lib/utils/findInReactTree.ts @@ -1,5 +1,5 @@ -import { SearchFilter } from "@types"; import { findInTree } from "@lib/utils"; +import { SearchFilter } from "./findInTree"; export default (tree: { [key: string]: any }, filter: SearchFilter): any => findInTree(tree, filter, { walkable: ["props", "children", "child", "sibling"], diff --git a/src/lib/utils/findInTree.ts b/src/lib/utils/findInTree.ts index f8a19cd..3c3c9c4 100644 --- a/src/lib/utils/findInTree.ts +++ b/src/lib/utils/findInTree.ts @@ -1,7 +1,13 @@ // This has been completely reimplemented at this point, but the disclaimer at the end of disclaimers still counts. // https://github.com/Cordwood/Cordwood/blob/91c0b971bbf05e112927df75415df99fa105e1e7/src/lib/utils/findInTree.ts -import { FindInTreeOptions, SearchTree, SearchFilter } from "@types"; +export type SearchTree = Record; +export type SearchFilter = (tree: SearchTree) => boolean; +export interface FindInTreeOptions { + walkable?: string[]; + ignore?: string[]; + maxDepth?: number; +} function treeSearch(tree: SearchTree, filter: SearchFilter, opts: Required, depth: number): any { if (depth > opts.maxDepth) return; diff --git a/src/lib/vendettaObject.ts b/src/lib/vendettaObject.ts index c6531f6..0508ee5 100644 --- a/src/lib/vendettaObject.ts +++ b/src/lib/vendettaObject.ts @@ -1,4 +1,3 @@ -import { VendettaObject } from "@types"; import patcher from "@lib/patcher"; import { getVendettaLoaderIdentity } from "@lib/loader"; import settings, { loaderConfig } from "@lib/settings"; @@ -16,23 +15,23 @@ import * as assets from "@ui/assets"; import * as color from "@ui/color"; import * as utils from "@lib/utils"; -export const createVendettaObject = (unloads: any[]): VendettaObject => ({ +export const createVendettaObject = (unloads: any[]): any => ({ patcher: { before: patcher.before, after: patcher.after, instead: patcher.instead }, metro: { - find: (filter) => metro.find(filter), - findAll: (filter) => metro.findAll(filter), - findByProps: (filter) => metro.findByProps(filter), - findByPropsAll: (filter) => metro.findByPropsAll(filter), - findByName: (name, defaultExp?) => metro.findByName(name, defaultExp), - findByNameAll: (name, defaultExp?) => metro.findByNameAll(name, defaultExp), - findByDisplayName: (displayName, defaultExp?) => metro.findByDisplayName(displayName, defaultExp), - findByDisplayNameAll: (displayName, defaultExp?) => metro.findByDisplayNameAll(displayName, defaultExp), - findByTypeName: (typeName, defaultExp?) => metro.findByTypeName(typeName, defaultExp), - findByTypeNameAll: (typeName, defaultExp?) => metro.findByTypeNameAll(typeName, defaultExp), + find: (filter: (m: any) => boolean) => metro.find(filter), + findAll: (filter: (m: any) => boolean) => metro.findAll(filter), + findByProps: (filter: any) => metro.findByProps(filter), + findByPropsAll: (filter: any) => metro.findByPropsAll(filter), + findByName: (name: string, defaultExp?: boolean | undefined) => metro.findByName(name, defaultExp), + findByNameAll: (name: string, defaultExp?: boolean | undefined) => metro.findByNameAll(name, defaultExp), + findByDisplayName: (displayName: string, defaultExp?: boolean | undefined) => metro.findByDisplayName(displayName, defaultExp), + findByDisplayNameAll: (displayName: string, defaultExp?: boolean | undefined) => metro.findByDisplayNameAll(displayName, defaultExp), + findByTypeName: (typeName: string, defaultExp?: boolean | undefined) => metro.findByTypeName(typeName, defaultExp), + findByTypeNameAll: (typeName: string, defaultExp?: boolean | undefined) => metro.findByTypeNameAll(typeName, defaultExp), findByStoreName: (name: string) => metro.findByStoreName(name), common: { constants: common.constants, @@ -69,11 +68,11 @@ export const createVendettaObject = (unloads: any[]): VendettaObject => ({ THEMES_CHANNEL_ID: "1091880434939482202", }, utils: { - findInReactTree: (tree, filter) => utils.findInReactTree(tree, filter), - findInTree: (tree, filter, options) => utils.findInTree(tree, filter, options), - safeFetch: (input, options?, timeout?) => utils.safeFetch(input, options, timeout), + findInReactTree: (tree: { [key: string]: any; }, filter: any) => utils.findInReactTree(tree, filter), + findInTree: (tree: any, filter: any, options: any) => utils.findInTree(tree, filter, options), + safeFetch: (input: RequestInfo | URL, options?: RequestInit | undefined, timeout?: number | undefined) => utils.safeFetch(input, options, timeout), unfreeze: (obj: object) => utils.unfreeze(obj), - without: (object, ...keys) => utils.without(object, ...keys) + without: (object: any, ...keys: any) => utils.without(object, ...keys) }, debug: { connectToDebugger: (url: string) => debug.connectToDebugger(url), @@ -96,9 +95,9 @@ export const createVendettaObject = (unloads: any[]): VendettaObject => ({ showToast: (content: string, asset?: number) => toasts.showToast(content, asset) }, alerts: { - showConfirmationAlert: (options) => alerts.showConfirmationAlert(options), - showCustomAlert: (component, props: any) => alerts.showCustomAlert(component, props), - showInputAlert: (options) => alerts.showInputAlert(options) + showConfirmationAlert: (options: any) => alerts.showConfirmationAlert(options), + showCustomAlert: (component: React.ComponentType, props: any) => alerts.showCustomAlert(component, props), + showInputAlert: (options: any) => alerts.showInputAlert(options) }, assets: { all: assets.all, @@ -132,7 +131,7 @@ export const createVendettaObject = (unloads: any[]): VendettaObject => ({ registerCommand: commands.registerCommand }, storage: { - createProxy: (target) => storage.createProxy(target), + createProxy: (target: any) => storage.createProxy(target), useProxy: (_storage: T) => storage.useProxy(_storage), createStorage: (backend: any) => storage.createStorage(backend), wrapSync: (store: any) => storage.wrapSync(store), @@ -146,18 +145,17 @@ export const createVendettaObject = (unloads: any[]): VendettaObject => ({ config: loaderConfig, }, logger: { - log: (...message) => console.log(...message), - info: (...message) => console.info(...message), - warn: (...message) => console.warn(...message), - error: (...message) => console.error(...message), - time: (...message) => console.time(...message), - trace: (...message) => console.trace(...message), - verbose: (...message) => console.log(...message) + log: (...message: any) => console.log(...message), + info: (...message: any) => console.info(...message), + warn: (...message: any) => console.warn(...message), + error: (...message: any) => console.error(...message), + time: (...message: any) => console.time(...message), + trace: (...message: any) => console.trace(...message), + verbose: (...message: any) => console.log(...message) }, version: debug.versionHash, unload: () => { unloads.filter(i => typeof i === "function").forEach(p => p()); - // @ts-expect-error explode delete window.vendetta; }, }); diff --git a/src/ui/alerts.ts b/src/ui/alerts.ts index b35a3fe..03cac07 100644 --- a/src/ui/alerts.ts +++ b/src/ui/alerts.ts @@ -1,6 +1,6 @@ -import { ConfirmationAlertOptions, InputAlertProps } from "@types"; import { findByProps } from "@metro/filters"; -import InputAlert from "@ui/components/InputAlert"; +import InputAlert, { InputAlertProps } from "@ui/components/InputAlert"; +import { ButtonColors } from "@/lib/types"; const Alerts = findByProps("openLazy", "close"); @@ -9,6 +9,19 @@ interface InternalConfirmationAlertOptions extends Omit void; + secondaryConfirmText?: string; + onConfirmSecondary?: () => void; + cancelText?: string; + onCancel?: () => void; + isDismissable?: boolean; +} + export function showConfirmationAlert(options: ConfirmationAlertOptions) { const internalOptions = options as InternalConfirmationAlertOptions; diff --git a/src/ui/assets.ts b/src/ui/assets.ts index 842abe9..11ff78b 100644 --- a/src/ui/assets.ts +++ b/src/ui/assets.ts @@ -1,9 +1,13 @@ -import { Asset } from "@types"; import { assets } from "@metro/common"; import { after } from "@lib/patcher"; export const all: Record = {}; +export interface Asset { + name: string; + id: number; +} + export function patchAssets() { const unpatch = after("registerAsset", assets, (args: Asset[], id: number) => { const asset = args[0]; diff --git a/src/ui/components/Codeblock.tsx b/src/ui/components/Codeblock.tsx index d3e9540..3c51001 100644 --- a/src/ui/components/Codeblock.tsx +++ b/src/ui/components/Codeblock.tsx @@ -1,7 +1,12 @@ -import { CodeblockProps } from "@types"; import { ReactNative as RN, stylesheet, constants } from "@metro/common"; import { semanticColors } from "@ui/color"; +export interface CodeblockProps { + selectable?: boolean; + style?: import("react-native").TextStyle; + children?: string; +} + const styles = stylesheet.createThemedStyleSheet({ codeBlock: { fontFamily: constants.Fonts.CODE_SEMIBOLD, diff --git a/src/ui/components/ErrorBoundary.tsx b/src/ui/components/ErrorBoundary.tsx index 12b068d..4929501 100644 --- a/src/ui/components/ErrorBoundary.tsx +++ b/src/ui/components/ErrorBoundary.tsx @@ -1,4 +1,3 @@ -import { ErrorBoundaryProps } from "@types"; import { React, ReactNative as RN, stylesheet } from "@metro/common"; import { Forms, Button, Codeblock } from "@ui/components"; @@ -7,6 +6,10 @@ interface ErrorBoundaryState { errText?: string; } +export interface ErrorBoundaryProps { + children: JSX.Element | JSX.Element[]; +} + const styles = stylesheet.createThemedStyleSheet({ view: { flex: 1, diff --git a/src/ui/components/InputAlert.tsx b/src/ui/components/InputAlert.tsx index 75efc7d..59d5e2c 100644 --- a/src/ui/components/InputAlert.tsx +++ b/src/ui/components/InputAlert.tsx @@ -1,10 +1,21 @@ -import { InputAlertProps } from "@types"; +import { ButtonColors } from "@/lib/types"; import { findByProps } from "@metro/filters"; import { Forms, Alert } from "@ui/components"; const { FormInput } = Forms; const Alerts = findByProps("openLazy", "close"); +export interface InputAlertProps { + title?: string; + confirmText?: string; + confirmColor?: ButtonColors; + onConfirm: (input: string) => (void | Promise); + cancelText?: string; + placeholder?: string; + initialValue?: string; + secureTextEntry?: boolean; +} + export default function InputAlert({ title, confirmText, confirmColor, onConfirm, cancelText, placeholder, initialValue = "", secureTextEntry }: InputAlertProps) { const [value, setValue] = React.useState(initialValue); const [error, setError] = React.useState(""); diff --git a/src/ui/components/Search.tsx b/src/ui/components/Search.tsx index cd25346..d0517e4 100644 --- a/src/ui/components/Search.tsx +++ b/src/ui/components/Search.tsx @@ -1,41 +1,21 @@ -import { SearchProps } from "@types"; import { ReactNative as RN } from "@metro/common"; import { findByProps } from "@metro/filters"; import ErrorBoundary from "@ui/components/ErrorBoundary"; import { getAssetIDByName } from "@ui/assets"; -const { FormIcon } = findByProps("FormIcon"); const { TextInput } = findByProps("TableRow"); -const props = { - "placeholder": "Search", - "returnKeyType": "search", - "size": "md", - "onFocus": "ƒ () {}", - "value": "", - "grow": true, - "autoCorrect": false, - "autoCapitalize": "none", - "accessibilityRole": "search", - "leadingIcon": "ƒ () {}", - "isClearable": true, - "onChangeText": "ƒ () {}", - "editable": true, - "onBlur": "ƒ () {}", - "style": [ - "{padding: 12, paddingTop: 12}", - "{color: \"#e4e5e8\", fontFamily: \"ggsans-Medium, Noto…}", - "{paddingEnd: undefined, paddingStart: 36.3636360168…}", - "{padding: 12, paddingTop: 12}", - "{color: \"#e4e5e8\", fontFamily: \"ggsans-Medium, Noto…}", - "{paddingEnd: undefined, paddingStart: 36.3636360168…}" - ], - "placeholderTextColor": "#828391" -}; +export interface SearchProps { + onChangeText?: (v: string) => void; + placeholder?: string; + style?: import("react-native").TextStyle; +} + function SearchIcon() { return } + export default ({ onChangeText, placeholder, style }: SearchProps) => { const [query, setQuery] = React.useState(""); diff --git a/src/ui/components/Summary.tsx b/src/ui/components/Summary.tsx index 68c610c..2cb8152 100644 --- a/src/ui/components/Summary.tsx +++ b/src/ui/components/Summary.tsx @@ -1,10 +1,16 @@ -import { SummaryProps } from "@types"; import { ReactNative as RN } from "@metro/common"; import { getAssetIDByName } from "@ui/assets"; import { findByProps } from "@/lib/metro/filters"; const { FormRow } = findByProps("FormRow"); const { TableRow } = findByProps("TableRow"); +export interface SummaryProps { + label: string; + icon?: string; + noPadding?: boolean; + noAnimation?: boolean; + children: JSX.Element | JSX.Element[]; +} export default function Summary({ label, icon, noPadding = false, noAnimation = false, children }: SummaryProps) { const [hidden, setHidden] = React.useState(true); diff --git a/src/ui/safeMode.tsx b/src/ui/safeMode.tsx index 4142be3..21e34a9 100644 --- a/src/ui/safeMode.tsx +++ b/src/ui/safeMode.tsx @@ -1,4 +1,3 @@ -import { ButtonColors } from "@types"; import { ReactNative as RN, stylesheet } from "@metro/common"; import { findByName, findByProps, findByStoreName } from "@metro/filters"; import { after } from "@lib/patcher"; @@ -7,6 +6,7 @@ import { DeviceManager } from "@lib/native"; import { semanticColors } from "@ui/color"; import { Button, Codeblock, ErrorBoundary as _ErrorBoundary, SafeAreaView } from "@ui/components"; import settings from "@lib/settings"; +import { ButtonColors } from "@/lib/types"; const ErrorBoundary = findByName("ErrorBoundary"); diff --git a/src/ui/settings/components/AssetDisplay.tsx b/src/ui/settings/components/AssetDisplay.tsx index 3b85e82..3a6c4bf 100644 --- a/src/ui/settings/components/AssetDisplay.tsx +++ b/src/ui/settings/components/AssetDisplay.tsx @@ -1,7 +1,7 @@ -import { Asset } from "@types"; import { ReactNative as RN, clipboard } from "@metro/common"; import { showToast } from "@ui/toasts"; import { Forms } from "@ui/components"; +import { Asset } from "@/ui/assets"; interface AssetDisplayProps { asset: Asset } diff --git a/src/ui/settings/components/PluginCard.tsx b/src/ui/settings/components/PluginCard.tsx index be43cce..bb302e2 100644 --- a/src/ui/settings/components/PluginCard.tsx +++ b/src/ui/settings/components/PluginCard.tsx @@ -1,12 +1,12 @@ -import { ButtonColors, Plugin } from "@types"; import { NavigationNative, clipboard } from "@metro/common"; -import { removePlugin, startPlugin, stopPlugin, getSettings, fetchPlugin } from "@lib/plugins"; +import { removePlugin, startPlugin, stopPlugin, getSettings, fetchPlugin, Plugin } from "@lib/plugins"; import { MMKVManager } from "@lib/native"; import { getAssetIDByName } from "@ui/assets"; import { showToast } from "@ui/toasts"; import { showConfirmationAlert } from "@ui/alerts"; import Card, { CardWrapper } from "@ui/settings/components/Card"; import { useProxy } from "@/lib/storage"; +import { ButtonColors } from "@/lib/types"; async function stopThenStart(plugin: Plugin, callback: Function) { if (plugin.enabled) stopPlugin(plugin.id, false); diff --git a/src/ui/settings/components/ThemeCard.tsx b/src/ui/settings/components/ThemeCard.tsx index 015fd20..acc0b96 100644 --- a/src/ui/settings/components/ThemeCard.tsx +++ b/src/ui/settings/components/ThemeCard.tsx @@ -1,6 +1,5 @@ -import { ButtonColors, Theme } from "@types"; import { clipboard } from "@metro/common"; -import { applyTheme, fetchTheme, removeTheme, selectTheme, themes } from "@lib/themes"; +import { Theme, applyTheme, fetchTheme, removeTheme, selectTheme, themes } from "@lib/themes"; import { useProxy } from "@lib/storage"; import { BundleUpdaterManager } from "@lib/native"; import { getAssetIDByName } from "@ui/assets"; @@ -8,6 +7,7 @@ import { showConfirmationAlert } from "@ui/alerts"; import { showToast } from "@ui/toasts"; import settings from "@lib/settings"; import Card, { CardWrapper } from "@ui/settings/components/Card"; +import { ButtonColors } from "@/lib/types"; async function selectAndApply(value: boolean, id: string) { try { diff --git a/src/ui/settings/pages/Plugins.tsx b/src/ui/settings/pages/Plugins.tsx index c5ac722..e3c5e22 100644 --- a/src/ui/settings/pages/Plugins.tsx +++ b/src/ui/settings/pages/Plugins.tsx @@ -1,6 +1,5 @@ -import { Plugin } from "@types"; import { useProxy } from "@lib/storage"; -import { installPlugin, plugins } from "@lib/plugins"; +import { Plugin, installPlugin, plugins } from "@lib/plugins"; import settings from "@lib/settings"; import AddonPage from "@ui/settings/components/AddonPage"; import PluginCard from "@ui/settings/components/PluginCard"; diff --git a/src/ui/settings/pages/Themes.tsx b/src/ui/settings/pages/Themes.tsx index e68ba61..bc1c9c6 100644 --- a/src/ui/settings/pages/Themes.tsx +++ b/src/ui/settings/pages/Themes.tsx @@ -1,10 +1,10 @@ -import { Theme, ButtonColors } from "@types"; import { useProxy } from "@lib/storage"; -import { installTheme, themes } from "@lib/themes"; +import { Theme, installTheme, themes } from "@lib/themes"; import { Button } from "@ui/components"; import settings from "@lib/settings"; import AddonPage from "@ui/settings/components/AddonPage"; import ThemeCard from "@ui/settings/components/ThemeCard"; +import { ButtonColors } from "@/lib/types"; export default function Themes() { useProxy(settings); diff --git a/tsconfig.json b/tsconfig.json index 9410367..d4a6b86 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,8 +13,7 @@ "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "paths": { - "@/*": ["src/*"], - "@types": ["src/def.d.ts"], + "@*": ["src*"], "@lib/*": ["src/lib/*"], "@metro/*": ["src/lib/metro/*"], "@ui/*": ["src/ui/*"]