import { afterPatch, findModuleChild, unpatch } from 'decky-frontend-lib'; import { FC, ReactElement, createElement } from 'react'; import { DeckyRouterState, DeckyRouterStateContextProvider, useDeckyRouterState } from './components/DeckyRouterState'; import Logger from './logger'; declare global { interface Window { __ROUTER_HOOK_INSTANCE: any; } } interface RouteProps { path: string; children: ReactElement; } class RouterHook extends Logger { private router: any; private memoizedRouter: any; private gamepadWrapper: any; private routerState: DeckyRouterState = new DeckyRouterState(); constructor() { super('RouterHook'); this.log('Initialized'); window.__ROUTER_HOOK_INSTANCE?.deinit?.(); window.__ROUTER_HOOK_INSTANCE = this; this.gamepadWrapper = findModuleChild((m) => { if (typeof m !== 'object') return undefined; for (let prop in m) { if (m[prop]?.render?.toString()?.includes('["flow-children","onActivate","onCancel","focusClassName",')) return m[prop]; } }); let Route: FC; const DeckyWrapper = ({ children }: { children: ReactElement }) => { const { routes } = useDeckyRouterState(); const routerIndex = children.props.children[0].props.children.length - 1; if ( !children.props.children[0].props.children[routerIndex].length || children.props.children[0].props.children !== routes.size ) { const newRouterArray: ReactElement[] = []; routes.forEach((Render, path) => { newRouterArray.push({createElement(Render)}); }); children.props.children[0].props.children[routerIndex] = newRouterArray; } return children; }; afterPatch(this.gamepadWrapper, 'render', (_: any, ret: any) => { if (ret?.props?.children?.props?.children?.length == 5) { if ( ret.props.children.props.children[2]?.props?.children?.[0]?.type?.type ?.toString() ?.includes('GamepadUI.Settings.Root()') ) { if (!this.router) { this.router = ret.props.children.props.children[2]?.props?.children?.[0]?.type; afterPatch(this.router, 'type', (_: any, ret: any) => { if (!Route) Route = ret.props.children[0].props.children.find((x: any) => x.props.path == '/createaccount').type; const returnVal = ( {ret} ); return returnVal; }); this.memoizedRouter = window.SP_REACT.memo(this.router.type); this.memoizedRouter.isDeckyRouter = true; } ret.props.children.props.children[2].props.children[0].type = this.memoizedRouter; } } return ret; }); } deinit() { unpatch(this.gamepadWrapper, 'render'); this.router && unpatch(this.router, 'type'); } } export default RouterHook;