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:
AAGaming
2022-05-30 14:26:54 -04:00
committed by GitHub
parent 44776b393e
commit 007860f8f7
9 changed files with 185 additions and 13 deletions
+92
View File
@@ -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;