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/toaster.tsx | 211 +++++++++++++++++++++++++++++------------------
 1 file changed, 132 insertions(+), 79 deletions(-)

(limited to 'frontend/src/toaster.tsx')

diff --git a/frontend/src/toaster.tsx b/frontend/src/toaster.tsx
index d7c0584f..94b08d70 100644
--- a/frontend/src/toaster.tsx
+++ b/frontend/src/toaster.tsx
@@ -1,8 +1,10 @@
-import { Patch, ToastData, afterPatch, findInReactTree, findModuleChild, sleep } from 'decky-frontend-lib';
-import { ReactNode } from 'react';
+import { Patch, ToastData, sleep } from 'decky-frontend-lib';
 
+import DeckyToaster from './components/DeckyToaster';
+import { DeckyToasterState, DeckyToasterStateContextProvider } from './components/DeckyToasterState';
 import Toast from './components/Toast';
 import Logger from './logger';
+import RouterHook from './router-hook';
 
 declare global {
   interface Window {
@@ -13,12 +15,15 @@ declare global {
 
 class Toaster extends Logger {
   private instanceRetPatch?: Patch;
+  private routerHook: RouterHook;
+  private toasterState: DeckyToasterState = new DeckyToasterState();
   private node: any;
   private settingsModule: any;
   private ready: boolean = false;
 
-  constructor() {
+  constructor(routerHook: RouterHook) {
     super('Toaster');
+    this.routerHook = routerHook;
 
     window.__TOASTER_INSTANCE?.deinit?.();
     window.__TOASTER_INSTANCE = this;
@@ -26,87 +31,135 @@ class Toaster extends Logger {
   }
 
   async init() {
-    let instance: any;
-
-    while (true) {
-      instance = findInReactTree(
-        (document.getElementById('root') as any)._reactRootContainer._internalRoot.current,
-        (x) => x?.memoizedProps?.className?.startsWith?.('toastmanager_ToastPlaceholder'),
-      );
-      if (instance) break;
-      this.debug('finding instance');
-      await sleep(2000);
-    }
-
-    this.node = instance.return.return;
-    let toast: any;
-    let renderedToast: ReactNode = null;
-    this.node.stateNode.render = (...args: any[]) => {
-      const ret = this.node.stateNode.__proto__.render.call(this.node.stateNode, ...args);
-      if (ret) {
-        this.instanceRetPatch = afterPatch(ret, 'type', (_: any, ret: any) => {
-          if (ret?.props?.children[1]?.children?.props) {
-            const currentToast = ret.props.children[1].children.props.notification;
-            if (currentToast?.decky) {
-              if (currentToast == toast) {
-                ret.props.children[1].children = renderedToast;
-              } else {
-                toast = currentToast;
-                renderedToast = <Toast toast={toast} />;
-                ret.props.children[1].children = renderedToast;
-              }
-            } else {
-              toast = null;
-              renderedToast = null;
-            }
-          }
-          return ret;
-        });
-        this.node.stateNode.shouldComponentUpdate = () => {
-          return false;
-        };
-        delete this.node.stateNode.render;
-      }
-      return ret;
-    };
-    this.settingsModule = findModuleChild((m) => {
-      if (typeof m !== 'object') return undefined;
-      for (let prop in m) {
-        if (typeof m[prop]?.settings && m[prop]?.communityPreferences) return m[prop];
-      }
-    });
-    this.log('Initialized');
-    this.ready = true;
+    this.routerHook.addGlobalComponent('DeckyToaster', () => (
+      <DeckyToasterStateContextProvider deckyToasterState={this.toasterState}>
+        <DeckyToaster />
+      </DeckyToasterStateContextProvider>
+    ));
+    // let instance: any;
+    // while (true) {
+    //   instance = findInReactTree(
+    //     (document.getElementById('root') as any)._reactRootContainer._internalRoot.current,
+    //     (x) => x?.memoizedProps?.className?.startsWith?.('toastmanager_ToastPlaceholder'),
+    //   );
+    //   if (instance) break;
+    //   this.debug('finding instance');
+    //   await sleep(2000);
+    // }
+    // // const windowManager = findModuleChild((m) => {
+    // //   if (typeof m !== 'object') return false;
+    // //   for (let prop in m) {
+    // //     if (m[prop]?.prototype?.GetRenderElement) return m[prop];
+    // //   }
+    // //   return false;
+    // // });
+    // this.node = instance.return.return;
+    // let toast: any;
+    // let renderedToast: ReactNode = null;
+    // console.log(instance, this.node);
+    // // replacePatch(window.SteamClient.BrowserView, "Destroy", (args: any[]) => {
+    // //   console.debug("destroy", args)
+    // //   return callOriginal;
+    // // })
+    // // let node = this.node.child.updateQueue.lastEffect;
+    // // while (node.next && !node.deckyPatched) {
+    // //   node = node.next;
+    // //   if (node.deps[1] == "notificationtoasts") {
+    // //     console.log("Deleting destroy");
+    // //     node.deckyPatched = true;
+    // //     node.create = () => {console.debug("VVVVVVVVVVV")};
+    // //     node.destroy = () => {console.debug("AAAAAAAAAAAAAAAAaaaaaaaaaaaaaaa")};
+    // //   }
+    // // }
+    // this.node.stateNode.render = (...args: any[]) => {
+    //   const ret = this.node.stateNode.__proto__.render.call(this.node.stateNode, ...args);
+    //   console.log('toast', ret);
+    //     if (ret) {
+    //       console.log(ret)
+    //       // this.instanceRetPatch = replacePatch(ret, 'type', (innerArgs: any) => {
+    //       //   console.log("inner toast", innerArgs)
+    //       //   // @ts-ignore
+    //       //   const oldEffect = window.SP_REACT.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher.current.useEffect;
+    //       //   // @ts-ignore
+    //       //   window.SP_REACT.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher.current.useEffect = (effect, deps) => {
+    //       //     console.log(effect, deps)
+    //       //     if (deps?.[1] == "notificationtoasts") {
+    //       //       console.log("run")
+    //       //       effect();
+    //       //     }
+    //       //     return oldEffect(effect, deps);
+    //       //   }
+    //       //   const ret = this.instanceRetPatch?.original(...args);
+    //       //   console.log("inner ret", ret)
+    //       //   // @ts-ignore
+    //       //   window.SP_REACT.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher.current.useEffect = oldEffect;
+    //       //   return ret
+    //       // });
+    //     }
+    //   //       console.log("toast ret", ret)
+    //   //       if (ret?.props?.children[1]?.children?.props) {
+    //   //         const currentToast = ret.props.children[1].children.props.notification;
+    //   //         if (currentToast?.decky) {
+    //   //           if (currentToast == toast) {
+    //   //             ret.props.children[1].children = renderedToast;
+    //   //           } else {
+    //   //             toast = currentToast;
+    //   //             renderedToast = <Toast toast={toast} />;
+    //   //             ret.props.children[1].children = renderedToast;
+    //   //           }
+    //   //         } else {
+    //   //           toast = null;
+    //   //           renderedToast = null;
+    //   //         }
+    //   //       }
+    //   //       return ret;
+    //   //     });
+    //   //   }
+    //   return ret;
+    // };
+    // this.settingsModule = findModuleChild((m) => {
+    //   if (typeof m !== 'object') return undefined;
+    //   for (let prop in m) {
+    //     if (typeof m[prop]?.settings && m[prop]?.communityPreferences) return m[prop];
+    //   }
+    // });
+    // // const idx = FocusNavController.m_ActiveContext.m_rgGamepadNavigationTrees.findIndex((x: any) => x.m_ID == "ToastContainer");
+    // // if (idx > -1) {
+    // //   FocusNavController.m_ActiveContext.m_rgGamepadNavigationTrees.splice(idx, 1)
+    // // }
+    // this.node.stateNode.forceUpdate();
+    // this.node.stateNode.shouldComponentUpdate = () => {
+    //   return false;
+    // };
+    // this.log('Initialized');
+    // this.ready = true;
   }
 
-  async toast(toast: ToastData) {
-    while (!this.ready) {
-      await sleep(100);
-    }
-    const settings = this.settingsModule?.settings;
-    let toastData = {
-      nNotificationID: window.NotificationStore.m_nNextTestNotificationID++,
-      rtCreated: Date.now(),
-      eType: 15,
-      nToastDurationMS: toast.duration || 5e3,
-      data: toast,
-      decky: true,
-    };
-    // @ts-ignore
-    toastData.data.appid = () => 0;
-    if (
-      (settings?.bDisableAllToasts && !toast.critical) ||
-      (settings?.bDisableToastsInGame && !toast.critical && window.NotificationStore.BIsUserInGame())
-    )
-      return;
-    window.NotificationStore.m_rgNotificationToasts.push(toastData);
-    window.NotificationStore.DispatchNextToast();
+  toast(toast: ToastData) {
+    toast.duration = toast.duration || 5e3;
+    this.toasterState.addToast(toast);
+    // const settings = this.settingsModule?.settings;
+    // let toastData = {
+    //   nNotificationID: window.NotificationStore.m_nNextTestNotificationID++,
+    //   rtCreated: Date.now(),
+    //   eType: 15,
+    //   nToastDurationMS: toast.duration || 5e3,
+    //   data: toast,
+    //   decky: true,
+    // };
+    // // @ts-ignore
+    // toastData.data.appid = () => 0;
+    // if (
+    //   (settings?.bDisableAllToasts && !toast.critical) ||
+    //   (settings?.bDisableToastsInGame && !toast.critical && window.NotificationStore.BIsUserInGame())
+    // )
+    //   return;
+    // window.NotificationStore.m_rgNotificationToasts.push(toastData);
+    // window.NotificationStore.DispatchNextToast();
   }
 
   deinit() {
-    this.instanceRetPatch?.unpatch();
-    this.node && delete this.node.stateNode.shouldComponentUpdate;
-    this.node && this.node.stateNode.forceUpdate();
+    this.routerHook.removeGlobalComponent('DeckyToaster');
   }
 }
 
-- 
cgit v1.2.3

