mirror of
https://github.com/SteamDeckHomebrew/decky-loader.git
synced 2026-07-11 20:51:57 +00:00
react: Add Router hook & fix typescript issues (#68)
* add rollup watch command, add pnpm lockfile * wait for react * add WIP patcher, window hook, and webpack * fix typescript, fix React, lint, add pnpm to gitignore * actually fix react * show frontend JS errors in console * cleanup * Add Router hook * Remove console.log * Expose routerHook in createPluginAPI Co-authored-by: Jonas Dellinger <jonas@dellinger.dev>
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
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<RouteProps>;
|
||||
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(<Route path={path}>{createElement(Render)}</Route>);
|
||||
});
|
||||
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 = (
|
||||
<DeckyRouterStateContextProvider deckyRouterState={this.routerState}>
|
||||
<DeckyWrapper>{ret}</DeckyWrapper>
|
||||
</DeckyRouterStateContextProvider>
|
||||
);
|
||||
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;
|
||||
Reference in New Issue
Block a user