diff options
author | megumin <megumin.bakaretsurie@gmail.com> | 2022-10-17 20:18:25 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-17 20:18:25 +0100 |
commit | 5625d63e46c43132676148a86739025c15fa5f2d (patch) | |
tree | 3699e126ad86cc2972b3aecfe4eeaef378e9e9f4 /src/utils | |
parent | ae730e83984cbf4dc804eebbf260a055bfe635c0 (diff) | |
download | Vencord-5625d63e46c43132676148a86739025c15fa5f2d.tar.gz Vencord-5625d63e46c43132676148a86739025c15fa5f2d.tar.bz2 Vencord-5625d63e46c43132676148a86739025c15fa5f2d.zip |
Settings 2.0 (#107)
Co-authored-by: Vendicated <vendicated@riseup.net>
Diffstat (limited to 'src/utils')
-rw-r--r-- | src/utils/modal.tsx | 91 | ||||
-rw-r--r-- | src/utils/types.ts | 89 |
2 files changed, 149 insertions, 31 deletions
diff --git a/src/utils/modal.tsx b/src/utils/modal.tsx index f142aee..4c8df6c 100644 --- a/src/utils/modal.tsx +++ b/src/utils/modal.tsx @@ -1,15 +1,6 @@ import { filters } from "../webpack"; -import { lazyWebpack } from "./misc"; import { mapMangledModuleLazy } from "../webpack/webpack"; -const ModalRoot = lazyWebpack(filters.byCode("headerIdIsManaged:")); -const Modals = mapMangledModuleLazy("onCloseRequest:null!=", { - openModal: filters.byCode("onCloseRequest:null!="), - closeModal: filters.byCode("onCloseCallback&&") -}); - -let modalId = 1337; - export enum ModalSize { SMALL = "small", MEDIUM = "medium", @@ -17,26 +8,64 @@ export enum ModalSize { DYNAMIC = "dynamic", } -/** - * Open a modal - * @param Component The component to render in the modal - * @returns The key of this modal. This can be used to close the modal later with closeModal - */ -export function openModal(Component: React.ComponentType, modalProps: Record<string, any>) { - let key = `Vencord${modalId++}`; - Modals.openModal(props => ( - <ModalRoot {...props} {...modalProps}> - <Component /> - </ModalRoot> - ), { modalKey: key }); - - return key; -} - -/** - * Close a modal by key. The id you need for this is returned by openModal. - * @param key The key of the modal to close - */ -export function closeModal(key: string) { - Modals.closeModal(key); +enum ModalTransitionState { + ENTERING, + ENTERED, + EXITING, + EXITED, + HIDDEN, +} + +export interface ModalProps { + transitionState: ModalTransitionState; + onClose(): Promise<void>; +} + +export interface ModalOptions { + modalKey?: string; + onCloseRequest?: (() => void); + onCloseCallback?: (() => void); +} + +interface ModalRootProps { + transitionState: ModalTransitionState; + children: React.ReactNode; + size?: ModalSize; + role?: "alertdialog" | "dialog"; + className?: string; + onAnimationEnd?(): string; +} + +type RenderFunction = (props: ModalProps) => React.ReactNode; + +export const Modals = mapMangledModuleLazy(".onAnimationEnd,", { + ModalRoot: filters.byCode("headerIdIsManaged:"), + ModalHeader: filters.byCode("children", "separator", "wrap", "NO_WRAP", "grow", "shrink", "id", "header"), + ModalContent: filters.byCode("scrollerRef", "content", "className", "children"), + ModalFooter: filters.byCode("HORIZONTAL_REVERSE", "START", "STRETCH", "NO_WRAP", "footerSeparator"), + ModalCloseButton: filters.byCode("closeWithCircleBackground", "hideOnFullscreen"), +}); + +export const ModalRoot = (props: ModalRootProps) => <Modals.ModalRoot {...props} />; +export const ModalHeader = (props: any) => <Modals.ModalHeader {...props} />; +export const ModalContent = (props: any) => <Modals.ModalContent {...props} />; +export const ModalFooter = (props: any) => <Modals.ModalFooter {...props} />; +export const ModalCloseButton = (props: any) => <Modals.ModalCloseButton {...props} />; + +const ModalAPI = mapMangledModuleLazy("onCloseRequest:null!=", { + openModal: filters.byCode("onCloseRequest:null!="), + closeModal: filters.byCode("onCloseCallback&&"), + openModalLazy: m => m?.length === 1 && filters.byCode(".apply(this,arguments)")(m), +}); + +export function openModalLazy(render: () => Promise<RenderFunction>, options?: ModalOptions & { contextKey?: string; }): Promise<string> { + return ModalAPI.openModalLazy(render, options); +} + +export function openModal(render: RenderFunction, options?: ModalOptions, contextKey?: string): string { + return ModalAPI.openModal(render, options, contextKey); +} + +export function closeModal(modalKey: string, contextKey?: string): void { + return ModalAPI.closeModal(modalKey, contextKey); } diff --git a/src/utils/types.ts b/src/utils/types.ts index f7ccdb6..5ed95e4 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -15,6 +15,7 @@ export interface Patch { find: string; replacement: PatchReplacement | PatchReplacement[]; all?: boolean; + predicate?(): boolean; } export interface PluginAuthor { @@ -34,13 +35,101 @@ interface PluginDef { start?(): void; stop?(): void; patches?: Omit<Patch, "plugin">[]; + /** + * List of commands. If you specify these, you must add CommandsAPI to dependencies + */ commands?: Command[]; + /** + * A list of other plugins that your plugin depends on. + * These will automatically be enabled and loaded before your plugin + * Common examples are CommandsAPI, MessageEventsAPI... + */ dependencies?: string[], + /** + * Whether this plugin is required and forcefully enabled + */ required?: boolean; /** * Set this if your plugin only works on Browser or Desktop, not both */ target?: "WEB" | "DESKTOP" | "BOTH"; + /** + * Optionally provide settings that the user can configure in the Plugins tab of settings. + */ + options?: Record<string, PluginOptionsItem>; + /** + * Allows you to specify a custom Component that will be rendered in your + * plugin's settings page + */ + settingsAboutComponent?: React.ComponentType; +} + +export enum OptionType { + STRING, + NUMBER, + BIGINT, + BOOLEAN, + SELECT, +} + +export type PluginOptionsItem = + | PluginOptionString + | PluginOptionNumber + | PluginOptionBoolean + | PluginOptionSelect; + +export interface PluginOptionBase { + description: string; + placeholder?: string; + onChange?(newValue: any): void; + disabled?(): boolean; + restartNeeded?: boolean; + componentProps?: Record<string, any>; + /** + * Set this if the setting only works on Browser or Desktop, not both + */ + target?: "WEB" | "DESKTOP" | "BOTH"; +} + +export interface PluginOptionString extends PluginOptionBase { + type: OptionType.STRING; + /** + * Prevents the user from saving settings if this is false or a string + */ + isValid?(value: string): boolean | string; + default?: string; +} + +export interface PluginOptionNumber extends PluginOptionBase { + type: OptionType.NUMBER | OptionType.BIGINT; + /** + * Prevents the user from saving settings if this is false or a string + */ + isValid?(value: number | BigInt): boolean | string; + default?: number; +} + +export interface PluginOptionBoolean extends PluginOptionBase { + type: OptionType.BOOLEAN; + /** + * Prevents the user from saving settings if this is false or a string + */ + isValid?(value: boolean): boolean | string; + default?: boolean; +} + +export interface PluginOptionSelect extends PluginOptionBase { + type: OptionType.SELECT; + /** + * Prevents the user from saving settings if this is false or a string + */ + isValid?(value: PluginOptionSelectOption): boolean | string; + options: PluginOptionSelectOption[]; +} +export interface PluginOptionSelectOption { + label: string; + value: string | number | boolean; + default?: boolean; } export type IpcRes<V = any> = { ok: true; value: V; } | { ok: false, error: any; }; |