import { FC, ReactNode } from 'react'; import { findSP } from '../utils'; import { Export, findModule, findModuleDetailsByExport, findModuleExport } from '../webpack'; // All of the popout options + strTitle are related. Proper usage is not yet known... export interface ShowModalProps { browserContext?: unknown; bForcePopOut?: boolean; bHideActionIcons?: boolean; bHideMainWindowForPopouts?: boolean; bNeverPopOut?: boolean; fnOnClose?: () => void; // Seems to be the same as "closeModal" callback, but only when the modal is a popout. Will no longer work after "Update" invocation! popupHeight?: number; popupWidth?: number; promiseRenderComplete?: Promise; // Invoked once the render is complete. Currently, it seems to be used as image loading success/error callback... strTitle?: string; } export interface ShowModalResult { // This method will not invoke any of the variations of "closeModal" callbacks! Close: () => void; // This method will replace the modal element completely and will not update the callback chains, // meaning that "closeModal" and etc. will not automatically close the modal anymore (also "fnOnClose" // will not be even called upon close anymore)! You have to manually call the "Close" method when, for example, // the "closeModal" is invoked in the newly updated modal: // { console.log("ABOUT TO CLOSE"); showModalRes.Close(); }} /> Update: (modal: ReactNode) => void; } const showModalRaw: ( modal: ReactNode, parent?: EventTarget, title?: string, props?: ShowModalProps, unknown1?: unknown, hideActions?: { bHideActions?: boolean }, modalManager?: unknown, ) => ShowModalResult = findModuleExport( (e: Export) => typeof e === 'function' && e.toString().includes('props.bDisableBackgroundDismiss') && !e?.prototype?.Cancel, ); export const showModal = ( modal: ReactNode, parent?: EventTarget, props: ShowModalProps = { strTitle: 'Decky Dialog', bHideMainWindowForPopouts: false, }, ): ShowModalResult => { return showModalRaw(modal, parent || findSP() || window, props.strTitle, props, undefined, { bHideActions: props.bHideActionIcons, }); }; export interface ModalRootProps { children?: ReactNode; onCancel?(): void; closeModal?(): void; onOK?(): void; onEscKeypress?(): void; className?: string; modalClassName?: string; bAllowFullSize?: boolean; bDestructiveWarning?: boolean; bDisableBackgroundDismiss?: boolean; bHideCloseIcon?: boolean; bOKDisabled?: boolean; bCancelDisabled?: boolean; } export interface ConfirmModalProps extends ModalRootProps { onMiddleButton?(): void; // setting this prop will enable the middle button strTitle?: ReactNode; strDescription?: ReactNode; strOKButtonText?: ReactNode; strCancelButtonText?: ReactNode; strMiddleButtonText?: ReactNode; bAlertDialog?: boolean; // This will open a modal with only OK button enabled bMiddleDisabled?: boolean; } export const ConfirmModal = findModuleExport( (e: Export) => !e?.prototype?.OK && e?.prototype?.Cancel && e?.prototype?.render, ) as FC; export const ModalRoot = Object.values( findModule((m: any) => { if (typeof m !== 'object') return false; for (let prop in m) { if (m[prop]?.m_mapModalManager && Object.values(m)?.find((x: any) => x?.type)) { return true; } } return false; }) || {}, )?.find((x: any) => x?.type?.toString?.()?.includes('((function(){')) as FC; interface SimpleModalProps { active?: boolean; children: ReactNode; } const [ModalModule, _ModalPosition] = findModuleDetailsByExport((e: Export) => e?.toString().includes('.ModalPosition'), 5) const ModalModuleProps = ModalModule ? Object.values(ModalModule) : []; export const SimpleModal = ModalModuleProps.find((prop) => { const string = prop?.toString(); return string?.includes('.ShowPortalModal()') && string?.includes('.OnElementReadyCallbacks.Register('); }) as FC; export const ModalPosition = _ModalPosition as FC;