mirror of
https://github.com/SteamDeckHomebrew/decky-frontend-lib.git
synced 2026-05-20 10:00:08 +02:00
144 lines
4.6 KiB
TypeScript
Executable File
144 lines
4.6 KiB
TypeScript
Executable File
import { FC, ReactNode } from 'react';
|
|
|
|
import { findSP } from '../utils';
|
|
import { findModule, findModuleChild } from '../webpack';
|
|
|
|
// All of the popout options + strTitle are related. Proper usage is not yet known...
|
|
export interface ShowModalProps {
|
|
browserContext?: unknown; // This is another Deck Object that is yet to be found
|
|
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<void>; // 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:
|
|
// <ModalRoot closeModal={() => { 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,
|
|
) => Promise<ShowModalResult>)
|
|
| void = findModuleChild((m) => {
|
|
if (typeof m !== 'object') return undefined;
|
|
for (let prop in m) {
|
|
if (
|
|
typeof m[prop] === 'function' &&
|
|
m[prop].toString().includes('props.bDisableBackgroundDismiss') &&
|
|
!m[prop]?.prototype?.Cancel
|
|
) {
|
|
return m[prop];
|
|
}
|
|
}
|
|
});
|
|
|
|
const oldShowModalRaw:
|
|
| ((modal: ReactNode, parent?: EventTarget, props?: ShowModalProps) => Promise<ShowModalResult>)
|
|
| void = findModuleChild((m) => {
|
|
if (typeof m !== 'object') return undefined;
|
|
for (let prop in m) {
|
|
if (typeof m[prop] === 'function' && m[prop].toString().includes('bHideMainWindowForPopouts:!0')) {
|
|
return m[prop];
|
|
}
|
|
}
|
|
});
|
|
|
|
export const showModal = (
|
|
modal: ReactNode,
|
|
parent?: EventTarget,
|
|
props: ShowModalProps = {
|
|
strTitle: 'Decky Dialog',
|
|
bHideMainWindowForPopouts: false,
|
|
},
|
|
): Promise<ShowModalResult> => {
|
|
if (showModalRaw) {
|
|
return showModalRaw(modal, parent || findSP(), props.strTitle, props, undefined, {
|
|
bHideActions: props.bHideActionIcons,
|
|
});
|
|
} else if (oldShowModalRaw) {
|
|
return oldShowModalRaw(modal, parent || findSP(), props);
|
|
} else {
|
|
throw new Error('[DFL:Modals]: Cannot find showModal function');
|
|
}
|
|
};
|
|
|
|
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 = findModuleChild((m) => {
|
|
if (typeof m !== 'object') return undefined;
|
|
for (let prop in m) {
|
|
if (!m[prop]?.prototype?.OK && m[prop]?.prototype?.Cancel && m[prop]?.prototype?.render) {
|
|
return m[prop];
|
|
}
|
|
}
|
|
}) as FC<ConfirmModalProps>;
|
|
|
|
// new
|
|
export const ModalRoot = (Object.values(
|
|
findModule((m: any) => {
|
|
if (typeof m !== 'object') return false;
|
|
|
|
for (let prop in m) {
|
|
if (m[prop]?.toString()?.includes('"ModalManager","DialogWrapper"')) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}) || {},
|
|
)?.find((x: any) => x?.type?.toString()?.includes('((function(){')) ||
|
|
// old
|
|
findModuleChild((m) => {
|
|
if (typeof m !== 'object') return undefined;
|
|
for (let prop in m) {
|
|
if (m[prop]?.prototype?.OK && m[prop]?.prototype?.Cancel && m[prop]?.prototype?.render) {
|
|
return m[prop];
|
|
}
|
|
}
|
|
})) as FC<ModalRootProps>;
|