Move types to their respective files
This commit is contained in:
parent
dea9ecb084
commit
00b5be7130
33 changed files with 418 additions and 617 deletions
522
src/def.d.ts
vendored
522
src/def.d.ts
vendored
|
|
@ -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<P extends string | symbol> = Record<P, any> & Record<PropertyKey, any>;
|
||||
type PropsFinder = <T extends string | symbol>(...props: T[]) => PropIntellisense<T>;
|
||||
type PropsFinderAll = <T extends string | symbol>(...props: T[]) => PropIntellisense<T>[];
|
||||
|
||||
type LoggerFunction = (...messages: any[]) => void;
|
||||
interface Logger {
|
||||
log: LoggerFunction;
|
||||
info: LoggerFunction;
|
||||
warn: LoggerFunction;
|
||||
error: LoggerFunction;
|
||||
time: LoggerFunction;
|
||||
trace: LoggerFunction;
|
||||
verbose: LoggerFunction;
|
||||
}
|
||||
|
||||
type SearchTree = Record<string, any>;
|
||||
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<void>);
|
||||
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<string, (string | false)[]>;
|
||||
rawColors?: Record<string, string>;
|
||||
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<CommandResult> | Promise<void>;
|
||||
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<string | null>;
|
||||
/**
|
||||
* 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<Record<string, string>>;
|
||||
/**
|
||||
* 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<boolean>;
|
||||
/**
|
||||
* 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<boolean>;
|
||||
/**
|
||||
* @param path **Full** path to file
|
||||
* @param encoding Set to `base64` in order to encode response
|
||||
*/
|
||||
readFile(path: string, encoding: "base64" | "utf8"): Promise<string>;
|
||||
saveFileToGallery?(uri: string, fileName: string, fileType: "PNG" | "JPEG"): Promise<string>;
|
||||
/**
|
||||
* 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<string>;
|
||||
removeFile(storageDir: "cache" | "documents", path: string): Promise<unknown>;
|
||||
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<string, Set<EmitterListener>>
|
||||
|
||||
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<unknown>;
|
||||
set: (data: unknown) => void | Promise<void>;
|
||||
}
|
||||
|
||||
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: <T extends _RN.StyleSheet.NamedStyles<T>>(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<Response>;
|
||||
unfreeze: (obj: object) => object;
|
||||
without: <O extends object, K extends readonly (keyof O)[]>(object: O, ...keys: K) => Omit<O, typeof keys[number]>;
|
||||
};
|
||||
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<any> & { Looks: any, Colors: ButtonColors, Sizes: any };
|
||||
HelpMessage: _React.ComponentType;
|
||||
SafeAreaView: typeof _RN.SafeAreaView;
|
||||
// Vendetta
|
||||
Summary: _React.ComponentType<SummaryProps>;
|
||||
ErrorBoundary: _React.ComponentType<ErrorBoundaryProps>;
|
||||
Codeblock: _React.ComponentType<CodeblockProps>;
|
||||
Search: _React.ComponentType<SearchProps>;
|
||||
}
|
||||
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<string, Asset>;
|
||||
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<string, any>;
|
||||
rawColors: Record<string, any>;
|
||||
};
|
||||
plugins: {
|
||||
plugins: Record<string, Plugin>;
|
||||
fetchPlugin: (id: string) => Promise<void>;
|
||||
installPlugin: (id: string, enabled?: boolean) => Promise<void>;
|
||||
startPlugin: (id: string) => Promise<void>;
|
||||
stopPlugin: (id: string, disable?: boolean) => void;
|
||||
removePlugin: (id: string) => void;
|
||||
getSettings: (id: string) => JSX.Element;
|
||||
};
|
||||
themes: {
|
||||
themes: Record<string, Theme>;
|
||||
fetchTheme: (id: string, selected?: boolean) => Promise<void>;
|
||||
installTheme: (id: string) => Promise<void>;
|
||||
selectTheme: (id: string) => Promise<void>;
|
||||
removeTheme: (id: string) => Promise<boolean>;
|
||||
getCurrentTheme: () => Theme | null;
|
||||
updateThemes: () => Promise<void>;
|
||||
};
|
||||
commands: {
|
||||
registerCommand: (command: ApplicationCommand) => () => void;
|
||||
};
|
||||
storage: {
|
||||
createProxy: <T>(target: T) => { proxy: T, emitter: Emitter };
|
||||
useProxy: <T>(storage: T) => T;
|
||||
createStorage: <T>(backend: StorageBackend) => Promise<Awaited<T>>;
|
||||
wrapSync: <T extends Promise<any>>(store: T) => Awaited<T>;
|
||||
awaitSyncWrapper: (store: any) => Promise<void>;
|
||||
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<string, any>;
|
||||
}
|
||||
|
||||
declare global {
|
||||
type React = typeof _React;
|
||||
const __vendettaVersion: string;
|
||||
|
||||
interface Window {
|
||||
[key: PropertyKey]: any;
|
||||
modules: MetroModules;
|
||||
vendetta: VendettaObject;
|
||||
React: typeof _React;
|
||||
}
|
||||
}
|
||||
15
src/global.d.ts
vendored
Normal file
15
src/global.d.ts
vendored
Normal file
|
|
@ -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 {};
|
||||
|
|
@ -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<CommandResult> | Promise<void>;
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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<string, Set<EmitterListener>>
|
||||
|
||||
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<EmitterListeners>((acc, val: string) => ((acc[val] = new Set<EmitterListener>()), acc), {}) as EmitterListeners,
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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: <T extends import("react-native").StyleSheet.NamedStyles<T>>(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;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import { MetroModules, PropsFinder, PropsFinderAll } from "@types";
|
||||
import { instead } from "@lib/patcher";
|
||||
|
||||
export type MetroModules = { [id: number]: any };
|
||||
export type PropIntellisense<P extends string | symbol> = Record<P, any> & Record<PropertyKey, any>;
|
||||
export type PropsFinder = <T extends string | symbol>(...props: T[]) => PropIntellisense<T>;
|
||||
export type PropsFinderAll = <T extends string | symbol>(...props: T[]) => PropIntellisense<T>[];
|
||||
|
||||
// Metro require
|
||||
declare const __r: (moduleId: number) => any;
|
||||
|
||||
|
|
|
|||
|
|
@ -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<string | null>;
|
||||
/**
|
||||
* 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<Record<string, string>>;
|
||||
/**
|
||||
* 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<boolean>;
|
||||
/**
|
||||
* 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<boolean>;
|
||||
/**
|
||||
* @param path **Full** path to file
|
||||
* @param encoding Set to `base64` in order to encode response
|
||||
*/
|
||||
readFile(path: string, encoding: "base64" | "utf8"): Promise<string>;
|
||||
saveFileToGallery?(uri: string, fileName: string, fileType: "PNG" | "JPEG"): Promise<string>;
|
||||
/**
|
||||
* 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<string>;
|
||||
removeFile(storageDir: "cache" | "documents", path: string): Promise<unknown>;
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -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<Record<string, Plugin>>(createMMKVBackend("VENDETTA_PLUGINS")));
|
||||
const loadedPlugins: Record<string, EvaledPlugin> = {};
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Settings>(createMMKVBackend("VENDETTA_SETTINGS")));
|
||||
export const loaderConfig = wrapSync(createStorage<LoaderConfig>(createFileBackend(getLoaderConfigPath())));
|
||||
|
|
|
|||
|
|
@ -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<unknown>;
|
||||
set: (data: unknown) => void | Promise<void>;
|
||||
}
|
||||
|
||||
const ILLEGAL_CHARS_REGEX = /[<>:"\/\\|?*]/g;
|
||||
|
||||
const filePathFixer = (file: string): string => RN.Platform.select({
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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<string, (string | false)[]>;
|
||||
rawColors?: Record<string, string>;
|
||||
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?
|
||||
|
|
|
|||
16
src/lib/types.ts
Normal file
16
src/lib/types.ts
Normal file
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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"],
|
||||
|
|
|
|||
|
|
@ -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<string, any>;
|
||||
export type SearchFilter = (tree: SearchTree) => boolean;
|
||||
export interface FindInTreeOptions {
|
||||
walkable?: string[];
|
||||
ignore?: string[];
|
||||
maxDepth?: number;
|
||||
}
|
||||
|
||||
function treeSearch(tree: SearchTree, filter: SearchFilter, opts: Required<FindInTreeOptions>, depth: number): any {
|
||||
if (depth > opts.maxDepth) return;
|
||||
|
|
|
|||
|
|
@ -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<any>, 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: <T>(_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;
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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<ConfirmationAlertOptions
|
|||
body?: ConfirmationAlertOptions["content"];
|
||||
};
|
||||
|
||||
export 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;
|
||||
}
|
||||
|
||||
export function showConfirmationAlert(options: ConfirmationAlertOptions) {
|
||||
const internalOptions = options as InternalConfirmationAlertOptions;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
import { Asset } from "@types";
|
||||
import { assets } from "@metro/common";
|
||||
import { after } from "@lib/patcher";
|
||||
|
||||
export const all: Record<string, Asset> = {};
|
||||
|
||||
export interface Asset {
|
||||
name: string;
|
||||
id: number;
|
||||
}
|
||||
|
||||
export function patchAssets() {
|
||||
const unpatch = after("registerAsset", assets, (args: Asset[], id: number) => {
|
||||
const asset = args[0];
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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<void>);
|
||||
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("");
|
||||
|
|
|
|||
|
|
@ -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 <RN.Image style={{ transform: [{ scale: 0.8 }]}} source={getAssetIDByName("search")} />
|
||||
}
|
||||
|
||||
export default ({ onChangeText, placeholder, style }: SearchProps) => {
|
||||
const [query, setQuery] = React.useState("");
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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/*"]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue