From 84c3b039c385ad872bb0f22eba7a3d2cd4a5ea10 Mon Sep 17 00:00:00 2001
From: AAGaming <aa@mail.catvibers.me>
Date: Mon, 24 Oct 2022 19:14:56 -0400
Subject: preview 10/21/2022 fixes (#234)

* initial fixes: everything working except toasts and patch notes

* tabshook changes, disable toaster for now

* prettier

* oops

* implement custom toaster because I am tired of Valve's shit

also fix QAM not injecting sometimes

* remove extra logging

* add findSP, fix patch notes, fix vscode screwup

* fix patch notes

* show error when plugin frontends fail to load

* add get_tab_lambda

* add css and has_element helpers to Tab

* small modals fixup

* Don't forceUpdate QuickAccess on stable

* add routes prop used to get tabs component

* add more dev utils to DFL global
---
 frontend/src/router-hook.tsx | 89 +++++++++++++++++++++++++++++++++-----------
 1 file changed, 67 insertions(+), 22 deletions(-)

(limited to 'frontend/src/router-hook.tsx')

diff --git a/frontend/src/router-hook.tsx b/frontend/src/router-hook.tsx
index 8414db2c..bf3ae0cb 100644
--- a/frontend/src/router-hook.tsx
+++ b/frontend/src/router-hook.tsx
@@ -1,7 +1,12 @@
 import { Patch, afterPatch, findModuleChild } from 'decky-frontend-lib';
-import { ReactElement, ReactNode, cloneElement, createElement, memo } from 'react';
+import { FC, ReactElement, ReactNode, cloneElement, createElement, memo } from 'react';
 import type { Route } from 'react-router';
 
+import {
+  DeckyGlobalComponentsState,
+  DeckyGlobalComponentsStateContextProvider,
+  useDeckyGlobalComponentsState,
+} from './components/DeckyGlobalComponentsState';
 import {
   DeckyRouterState,
   DeckyRouterStateContextProvider,
@@ -22,8 +27,10 @@ class RouterHook extends Logger {
   private memoizedRouter: any;
   private gamepadWrapper: any;
   private routerState: DeckyRouterState = new DeckyRouterState();
+  private globalComponentsState: DeckyGlobalComponentsState = new DeckyGlobalComponentsState();
   private wrapperPatch: Patch;
   private routerPatch?: Patch;
+  public routes?: any[];
 
   constructor() {
     super('RouterHook');
@@ -42,24 +49,28 @@ class RouterHook extends Logger {
 
     let Route: new () => Route;
     // Used to store the new replicated routes we create to allow routes to be unpatched.
-    let toReplace = new Map<string, ReactNode>();
-    const DeckyWrapper = ({ children }: { children: ReactElement }) => {
-      const { routes, routePatches } = useDeckyRouterState();
-
-      const routeList = children.props.children[0].props.children;
-
+    const processList = (
+      routeList: any[],
+      routes: Map<string, RouterEntry> | null,
+      routePatches: Map<string, Set<RoutePatch>>,
+      save: boolean,
+    ) => {
+      this.debug('Route list: ', routeList);
+      if (save) this.routes = routeList;
       let routerIndex = routeList.length;
-      if (!routeList[routerIndex - 1]?.length || routeList[routerIndex - 1]?.length !== routes.size) {
-        if (routeList[routerIndex - 1]?.length && routeList[routerIndex - 1].length !== routes.size) routerIndex--;
-        const newRouterArray: ReactElement[] = [];
-        routes.forEach(({ component, props }, path) => {
-          newRouterArray.push(
-            <Route path={path} {...props}>
-              {createElement(component)}
-            </Route>,
-          );
-        });
-        routeList[routerIndex] = newRouterArray;
+      if (routes) {
+        if (!routeList[routerIndex - 1]?.length || routeList[routerIndex - 1]?.length !== routes.size) {
+          if (routeList[routerIndex - 1]?.length && routeList[routerIndex - 1].length !== routes.size) routerIndex--;
+          const newRouterArray: ReactElement[] = [];
+          routes.forEach(({ component, props }, path) => {
+            newRouterArray.push(
+              <Route path={path} {...props}>
+                {createElement(component)}
+              </Route>,
+            );
+          });
+          routeList[routerIndex] = newRouterArray;
+        }
       }
       routeList.forEach((route: Route, index: number) => {
         const replaced = toReplace.get(route?.props?.path as string);
@@ -85,19 +96,40 @@ class RouterHook extends Logger {
           });
         }
       });
+    };
+    let toReplace = new Map<string, ReactNode>();
+    const DeckyWrapper = ({ children }: { children: ReactElement }) => {
+      const { routes, routePatches } = useDeckyRouterState();
+      const mainRouteList = children.props.children[0].props.children;
+      const ingameRouteList = children.props.children[1].props.children; // /appoverlay and /apprunning
+      processList(mainRouteList, routes, routePatches, true);
+      processList(ingameRouteList, null, routePatches, false);
+
       this.debug('Rerendered routes list');
       return children;
     };
 
+    let renderedComponents: ReactElement[] = [];
+
+    const DeckyGlobalComponentsWrapper = () => {
+      const { components } = useDeckyGlobalComponentsState();
+      if (renderedComponents.length != components.size) {
+        this.debug('Rerendering global components');
+        renderedComponents = Array.from(components.values()).map((GComponent) => <GComponent />);
+      }
+      return <>{renderedComponents}</>;
+    };
+
     this.wrapperPatch = afterPatch(this.gamepadWrapper, 'render', (_: any, ret: any) => {
-      if (ret?.props?.children?.props?.children?.length == 5) {
+      if (ret?.props?.children?.props?.children?.length == 5 || ret?.props?.children?.props?.children?.length == 4) {
+        const idx = ret?.props?.children?.props?.children?.length == 4 ? 1 : 2;
         if (
-          ret.props.children.props.children[2]?.props?.children?.[0]?.type?.type
+          ret.props.children.props.children[idx]?.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;
+            this.router = ret.props.children.props.children[idx]?.props?.children?.[0]?.type;
             this.routerPatch = afterPatch(this.router, 'type', (_: any, ret: any) => {
               if (!Route)
                 Route = ret.props.children[0].props.children.find((x: any) => x.props.path == '/createaccount').type;
@@ -111,7 +143,12 @@ class RouterHook extends Logger {
             this.memoizedRouter = memo(this.router.type);
             this.memoizedRouter.isDeckyRouter = true;
           }
-          ret.props.children.props.children[2].props.children[0].type = this.memoizedRouter;
+          ret.props.children.props.children.push(
+            <DeckyGlobalComponentsStateContextProvider deckyGlobalComponentsState={this.globalComponentsState}>
+              <DeckyGlobalComponentsWrapper />
+            </DeckyGlobalComponentsStateContextProvider>,
+          );
+          ret.props.children.props.children[idx].props.children[0].type = this.memoizedRouter;
         }
       }
       return ret;
@@ -126,6 +163,14 @@ class RouterHook extends Logger {
     return this.routerState.addPatch(path, patch);
   }
 
+  addGlobalComponent(name: string, component: FC) {
+    this.globalComponentsState.addComponent(name, component);
+  }
+
+  removeGlobalComponent(name: string) {
+    this.globalComponentsState.removeComponent(name);
+  }
+
   removePatch(path: string, patch: RoutePatch) {
     this.routerState.removePatch(path, patch);
   }
-- 
cgit v1.2.3

