diff options
| author | AAGaming <aagaming00@protonmail.com> | 2022-05-30 14:26:54 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-05-30 20:26:54 +0200 |
| commit | 007860f8f771a7ee62b1c384fbe4f741528a75d5 (patch) | |
| tree | e901ec893500f0531ebb66b5066b2003086570b3 /frontend/src/router-hook.tsx | |
| parent | 44776b393e984e5968c8b092fade56644c39a4a7 (diff) | |
| download | decky-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.tsx | 92 |
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; |
