summaryrefslogtreecommitdiff
path: root/frontend/src/router-hook.tsx
diff options
context:
space:
mode:
authorAAGaming <aagaming00@protonmail.com>2022-05-30 14:26:54 -0400
committerGitHub <noreply@github.com>2022-05-30 20:26:54 +0200
commit007860f8f771a7ee62b1c384fbe4f741528a75d5 (patch)
treee901ec893500f0531ebb66b5066b2003086570b3 /frontend/src/router-hook.tsx
parent44776b393e984e5968c8b092fade56644c39a4a7 (diff)
downloaddecky-loader-007860f8f771a7ee62b1c384fbe4f741528a75d5.tar.gz
decky-loader-007860f8f771a7ee62b1c384fbe4f741528a75d5.zip
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>
Diffstat (limited to 'frontend/src/router-hook.tsx')
-rw-r--r--frontend/src/router-hook.tsx92
1 files changed, 92 insertions, 0 deletions
diff --git a/frontend/src/router-hook.tsx b/frontend/src/router-hook.tsx
new file mode 100644
index 00000000..ef2844b6
--- /dev/null
+++ b/frontend/src/router-hook.tsx
@@ -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;