@decky/ui 4.6.0 → 4.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { FC, ReactNode } from 'react';
1
+ import { ReactNode } from 'react';
2
2
  import { FooterLegendProps } from './FooterLegend';
3
3
  export interface Tab {
4
4
  id: string;
@@ -13,4 +13,4 @@ export interface TabsProps {
13
13
  onShowTab: (tab: string) => void;
14
14
  autoFocusContents?: boolean;
15
15
  }
16
- export declare const Tabs: FC<TabsProps>;
16
+ export declare const Tabs: any;
@@ -1,48 +1,3 @@
1
- import { createElement, useEffect, useState } from 'react';
2
- import { fakeRenderComponent, findInReactTree, sleep } from '../utils';
3
1
  import { findModuleByExport } from '../webpack';
4
- import { SteamSpinner } from './SteamSpinner';
5
- let tabsComponent;
6
- const getTabs = async () => {
7
- if (tabsComponent)
8
- return tabsComponent;
9
- while (!window?.DeckyPluginLoader?.routerHook?.routes) {
10
- console.debug('[DFL:Tabs]: Waiting for Decky router...');
11
- await sleep(500);
12
- }
13
- return (tabsComponent = fakeRenderComponent(() => {
14
- return findInReactTree(findInReactTree(window.DeckyPluginLoader.routerHook.routes
15
- .find((x) => x.props.path == '/library/app/:appid/achievements')
16
- .props.children.type(), (x) => x?.props?.scrollTabsTop).type({ appid: 1 }), (x) => x?.props?.tabs).type;
17
- }, {
18
- useRef: () => ({ current: { reaction: { track: () => { } } } }),
19
- useContext: () => ({ match: { params: { appid: 1 } } }),
20
- useMemo: () => ({ data: {} }),
21
- }));
22
- };
23
- let oldTabs;
24
- try {
25
- const oldTabsModule = findModuleByExport((e) => e.Unbleed);
26
- if (oldTabsModule)
27
- oldTabs = Object.values(oldTabsModule).find((x) => x?.type?.toString()?.includes('((function(){'));
28
- }
29
- catch (e) {
30
- console.error('Error finding oldTabs:', e);
31
- }
32
- export const Tabs = (oldTabs ||
33
- ((props) => {
34
- const found = tabsComponent;
35
- const [tc, setTC] = useState(found);
36
- useEffect(() => {
37
- if (found)
38
- return;
39
- (async () => {
40
- console.debug('[DFL:Tabs]: Finding component...');
41
- const t = await getTabs();
42
- console.debug('[DFL:Tabs]: Found!');
43
- setTC(t);
44
- })();
45
- }, []);
46
- console.log('tc', tc);
47
- return tc ? createElement(tc, props) : window.SP_REACT.createElement(SteamSpinner, null);
48
- }));
2
+ const tabsModule = findModuleByExport(e => e?.toString()?.includes(".TabRowTabs") && e?.toString()?.includes("activeTab:"));
3
+ export const Tabs = tabsModule && Object.values(tabsModule).find((e) => e?.type?.toString()?.includes("((function()"));
@@ -1,3 +1,4 @@
1
+ import { WindowRouter } from '../modules/Router';
1
2
  import { AppDetails, LogoPosition, SteamAppOverview } from './SteamClient';
2
3
  declare global {
3
4
  interface Window {
@@ -46,5 +47,11 @@ declare global {
46
47
  GetCustomLogoPosition: (app: SteamAppOverview) => LogoPosition | null;
47
48
  SaveCustomLogoPosition: (app: SteamAppOverview, logoPositions: LogoPosition) => any;
48
49
  };
50
+ SteamUIStore: {
51
+ GetFocusedWindowInstance: () => WindowRouter;
52
+ };
53
+ securitystore: {
54
+ IsLockScreenActive: () => boolean;
55
+ };
49
56
  }
50
57
  }
@@ -1,4 +1,4 @@
1
- import { sleep } from '../utils';
1
+ import Logger from '../logger';
2
2
  import { findModuleExport } from '../webpack';
3
3
  export var SideMenu;
4
4
  (function (SideMenu) {
@@ -59,44 +59,53 @@ export var DisplayStatus;
59
59
  })(DisplayStatus || (DisplayStatus = {}));
60
60
  export const Router = findModuleExport((e) => e.Navigate && e.NavigationManager);
61
61
  export let Navigation = {};
62
+ const logger = new Logger("Navigation");
62
63
  try {
63
- (async () => {
64
- let InternalNavigators = {};
65
- if (!Router.NavigateToAppProperties || Router.deckyShim) {
66
- function initInternalNavigators() {
64
+ function createNavigationFunction(fncName, handler) {
65
+ return (...args) => {
66
+ let win;
67
+ try {
68
+ win = window.SteamUIStore.GetFocusedWindowInstance();
69
+ }
70
+ catch (e) {
71
+ logger.warn("Navigation interface failed to call GetFocusedWindowInstance", e);
72
+ }
73
+ if (!win) {
74
+ logger.warn("Navigation interface could not find any focused window. Falling back to GamepadUIMainWindowInstance");
75
+ win = Router.WindowStore?.GamepadUIMainWindowInstance;
76
+ }
77
+ if (win) {
67
78
  try {
68
- InternalNavigators = findModuleExport((e) => e.GetNavigator && e.SetNavigator)?.GetNavigator();
79
+ const thisObj = handler && handler(win);
80
+ (thisObj || win)[fncName](...args);
69
81
  }
70
82
  catch (e) {
71
- console.error('[DFL:Router]: Failed to init internal navigators, trying again');
83
+ logger.error("Navigation handler failed", e);
72
84
  }
73
85
  }
74
- initInternalNavigators();
75
- while (!InternalNavigators?.AppProperties) {
76
- console.log('[DFL:Router]: Trying to init internal navigators again');
77
- await sleep(2000);
78
- initInternalNavigators();
86
+ else {
87
+ logger.error("Navigation interface could not find a window to navigate");
79
88
  }
80
- }
81
- const newNavigation = {
82
- Navigate: Router.Navigate?.bind(Router),
83
- NavigateBack: Router.WindowStore?.GamepadUIMainWindowInstance?.NavigateBack?.bind(Router.WindowStore.GamepadUIMainWindowInstance),
84
- NavigateToAppProperties: InternalNavigators?.AppProperties || Router.NavigateToAppProperties?.bind(Router),
85
- NavigateToExternalWeb: InternalNavigators?.ExternalWeb || Router.NavigateToExternalWeb?.bind(Router),
86
- NavigateToInvites: InternalNavigators?.Invites || Router.NavigateToInvites?.bind(Router),
87
- NavigateToChat: InternalNavigators?.Chat || Router.NavigateToChat?.bind(Router),
88
- NavigateToLibraryTab: InternalNavigators?.LibraryTab || Router.NavigateToLibraryTab?.bind(Router),
89
- NavigateToLayoutPreview: Router.NavigateToLayoutPreview?.bind(Router),
90
- NavigateToSteamWeb: Router.WindowStore?.GamepadUIMainWindowInstance?.NavigateToSteamWeb?.bind(Router.WindowStore.GamepadUIMainWindowInstance),
91
- OpenSideMenu: Router.WindowStore?.GamepadUIMainWindowInstance?.MenuStore.OpenSideMenu?.bind(Router.WindowStore.GamepadUIMainWindowInstance.MenuStore),
92
- OpenQuickAccessMenu: Router.WindowStore?.GamepadUIMainWindowInstance?.MenuStore.OpenQuickAccessMenu?.bind(Router.WindowStore.GamepadUIMainWindowInstance.MenuStore),
93
- OpenMainMenu: Router.WindowStore?.GamepadUIMainWindowInstance?.MenuStore.OpenMainMenu?.bind(Router.WindowStore.GamepadUIMainWindowInstance.MenuStore),
94
- CloseSideMenus: Router.CloseSideMenus?.bind(Router),
95
- OpenPowerMenu: Router.OpenPowerMenu?.bind(Router),
96
89
  };
97
- Object.assign(Navigation, newNavigation);
98
- })();
90
+ }
91
+ const newNavigation = {
92
+ Navigate: createNavigationFunction("Navigate"),
93
+ NavigateBack: createNavigationFunction("NavigateBack"),
94
+ NavigateToAppProperties: createNavigationFunction("AppProperties", win => win.Navigator),
95
+ NavigateToExternalWeb: createNavigationFunction("ExternalWeb", win => win.Navigator),
96
+ NavigateToInvites: createNavigationFunction("Invites", win => win.Navigator),
97
+ NavigateToChat: createNavigationFunction("Chat", win => win.Navigator),
98
+ NavigateToLibraryTab: createNavigationFunction("LibraryTab", win => win.Navigator),
99
+ NavigateToLayoutPreview: Router.NavigateToLayoutPreview?.bind(Router),
100
+ NavigateToSteamWeb: createNavigationFunction("NavigateToSteamWeb"),
101
+ OpenSideMenu: createNavigationFunction("OpenSideMenu", win => win.MenuStore),
102
+ OpenQuickAccessMenu: createNavigationFunction("OpenQuickAccessMenu", win => win.MenuStore),
103
+ OpenMainMenu: createNavigationFunction("OpenMainMenu", win => win.MenuStore),
104
+ CloseSideMenus: createNavigationFunction("CloseSideMenus", win => win.MenuStore),
105
+ OpenPowerMenu: Router.OpenPowerMenu?.bind(Router),
106
+ };
107
+ Object.assign(Navigation, newNavigation);
99
108
  }
100
109
  catch (e) {
101
- console.error('[DFL:Router]: Error initializing Navigation interface', e);
110
+ logger.error('Error initializing Navigation interface', e);
102
111
  }
@@ -25,6 +25,7 @@ export function injectFCTrampoline(component, customHooks) {
25
25
  applyHookStubs(customHooks);
26
26
  window.SP_REACT.createElement = () => {
27
27
  loggingEnabled && logger.debug("createElement hook called");
28
+ loggingEnabled && console.trace("createElement trace");
28
29
  return Object.create(component.prototype);
29
30
  };
30
31
  }
@@ -37,26 +38,36 @@ export function injectFCTrampoline(component, customHooks) {
37
38
  window.SP_REACT.createElement = oldCreateElement;
38
39
  }
39
40
  };
41
+ let renderHookStep = 0;
40
42
  Object.defineProperty(component, "contextType", {
41
43
  configurable: true,
42
44
  get: function () {
43
- loggingEnabled && logger.debug("get contexttype", this, stubsApplied);
44
- applyStubsIfNeeded();
45
+ loggingEnabled && logger.debug("get contexttype", this, stubsApplied, renderHookStep);
46
+ loggingEnabled && console.trace("contextType trace");
47
+ if (renderHookStep == 0)
48
+ renderHookStep = 1;
49
+ else if (renderHookStep == 3)
50
+ renderHookStep = 4;
45
51
  return this._contextType;
46
52
  },
47
53
  set: function (value) {
48
54
  this._contextType = value;
49
55
  }
50
56
  });
51
- Object.defineProperty(component, "getDerivedStateFromProps", {
57
+ Object.defineProperty(component, "contextTypes", {
52
58
  configurable: true,
53
59
  get: function () {
54
- loggingEnabled && logger.debug("get getDerivedStateFromProps", this, stubsApplied);
55
- removeStubsIfNeeded();
56
- return this._getDerivedStateFromProps;
60
+ loggingEnabled && logger.debug("get contexttypes", this, stubsApplied, renderHookStep);
61
+ loggingEnabled && console.trace("contextTypes trace");
62
+ if (renderHookStep == 1) {
63
+ renderHookStep = 2;
64
+ applyStubsIfNeeded();
65
+ }
66
+ ;
67
+ return this._contextTypes;
57
68
  },
58
69
  set: function (value) {
59
- this._getDerivedStateFromProps = value;
70
+ this._contextTypes = value;
60
71
  }
61
72
  });
62
73
  Object.defineProperty(component.prototype, "updater", {
@@ -65,10 +76,29 @@ export function injectFCTrampoline(component, customHooks) {
65
76
  return this._updater;
66
77
  },
67
78
  set: function (value) {
68
- loggingEnabled && logger.debug("set updater", this, value, stubsApplied);
69
- removeStubsIfNeeded();
79
+ loggingEnabled && logger.debug("set updater", this, value, stubsApplied, renderHookStep);
80
+ loggingEnabled && console.trace("updater trace");
81
+ if (renderHookStep == 2) {
82
+ renderHookStep = 0;
83
+ removeStubsIfNeeded();
84
+ }
70
85
  return this._updater = value;
71
86
  }
72
87
  });
88
+ Object.defineProperty(component, "getDerivedStateFromProps", {
89
+ configurable: true,
90
+ get: function () {
91
+ loggingEnabled && logger.debug("get getDerivedStateFromProps", this, stubsApplied, renderHookStep);
92
+ loggingEnabled && console.trace("getDerivedStateFromProps trace");
93
+ if (renderHookStep == 2) {
94
+ renderHookStep = 0;
95
+ removeStubsIfNeeded();
96
+ }
97
+ return this._getDerivedStateFromProps;
98
+ },
99
+ set: function (value) {
100
+ this._getDerivedStateFromProps = value;
101
+ }
102
+ });
73
103
  return userComponent;
74
104
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decky/ui",
3
- "version": "4.6.0",
3
+ "version": "4.7.1",
4
4
  "description": "A library for interacting with the Steam frontend in Decky plugins and elsewhere.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,9 +1,6 @@
1
- import { FC, ReactNode, createElement, useEffect, useState } from 'react';
2
-
3
- import { fakeRenderComponent, findInReactTree, sleep } from '../utils';
4
- import { Export, findModuleByExport } from '../webpack';
1
+ import { FC, ReactNode } from 'react';
2
+ import { findModuleByExport } from '../webpack';
5
3
  import { FooterLegendProps } from './FooterLegend';
6
- import { SteamSpinner } from './SteamSpinner';
7
4
 
8
5
  /**
9
6
  * Individual tab objects for the Tabs component
@@ -65,63 +62,9 @@ export interface TabsProps {
65
62
  autoFocusContents?: boolean;
66
63
  }
67
64
 
68
- let tabsComponent: any;
69
-
70
- const getTabs = async () => {
71
- if (tabsComponent) return tabsComponent;
72
- // @ts-ignore
73
- while (!window?.DeckyPluginLoader?.routerHook?.routes) {
74
- console.debug('[DFL:Tabs]: Waiting for Decky router...');
75
- await sleep(500);
76
- }
77
- return (tabsComponent = fakeRenderComponent(
78
- () => {
79
- return findInReactTree(
80
- findInReactTree(
81
- // @ts-ignore
82
- window.DeckyPluginLoader.routerHook.routes
83
- .find((x: any) => x.props.path == '/library/app/:appid/achievements')
84
- .props.children.type(),
85
- (x) => x?.props?.scrollTabsTop,
86
- ).type({ appid: 1 }),
87
- (x) => x?.props?.tabs,
88
- ).type;
89
- },
90
- {
91
- useRef: () => ({ current: { reaction: { track: () => {} } } }),
92
- useContext: () => ({ match: { params: { appid: 1 } } }),
93
- useMemo: () => ({ data: {} }),
94
- },
95
- ));
96
- };
97
-
98
- let oldTabs: any;
99
-
100
- try {
101
- const oldTabsModule = findModuleByExport((e: Export) => e.Unbleed);
102
- if (oldTabsModule)
103
- oldTabs = Object.values(oldTabsModule).find((x: any) => x?.type?.toString()?.includes('((function(){'));
104
- } catch (e) {
105
- console.error('Error finding oldTabs:', e);
106
- }
65
+ const tabsModule = findModuleByExport(e => e?.toString()?.includes(".TabRowTabs") && e?.toString()?.includes("activeTab:"));
107
66
 
108
67
  /**
109
68
  * Tabs component as used in the library and media tabs. See {@link TabsProps}.
110
- * Unlike other components in `decky-frontend-lib`, this requires Decky Loader to be running.
111
69
  */
112
- export const Tabs = (oldTabs ||
113
- ((props: TabsProps) => {
114
- const found = tabsComponent;
115
- const [tc, setTC] = useState<FC<TabsProps>>(found);
116
- useEffect(() => {
117
- if (found) return;
118
- (async () => {
119
- console.debug('[DFL:Tabs]: Finding component...');
120
- const t = await getTabs();
121
- console.debug('[DFL:Tabs]: Found!');
122
- setTC(t);
123
- })();
124
- }, []);
125
- console.log('tc', tc);
126
- return tc ? createElement(tc, props) : <SteamSpinner />;
127
- })) as FC<TabsProps>;
70
+ export const Tabs = tabsModule && Object.values(tabsModule).find((e: any) => e?.type?.toString()?.includes("((function()")) as FC<TabsProps>;
@@ -1,3 +1,4 @@
1
+ import { WindowRouter } from '../modules/Router';
1
2
  import { AppDetails, LogoPosition, SteamAppOverview } from './SteamClient';
2
3
  declare global {
3
4
  interface Window {
@@ -46,5 +47,11 @@ declare global {
46
47
  GetCustomLogoPosition: (app: SteamAppOverview) => LogoPosition | null;
47
48
  SaveCustomLogoPosition: (app: SteamAppOverview, logoPositions: LogoPosition) => any;
48
49
  };
50
+ SteamUIStore: {
51
+ GetFocusedWindowInstance: () => WindowRouter;
52
+ };
53
+ securitystore: {
54
+ IsLockScreenActive: () => boolean;
55
+ };
49
56
  }
50
57
  }
@@ -1,4 +1,4 @@
1
- import { sleep } from '../utils';
1
+ import Logger from '../logger';
2
2
  import { Export, findModuleExport } from '../webpack';
3
3
 
4
4
  export enum SideMenu {
@@ -88,14 +88,23 @@ export interface WindowStore {
88
88
 
89
89
  export interface Router {
90
90
  WindowStore?: WindowStore;
91
+ /** @deprecated use {@link Navigation} instead */
91
92
  CloseSideMenus(): void;
93
+ /** @deprecated use {@link Navigation} instead */
92
94
  Navigate(path: string): void;
95
+ /** @deprecated use {@link Navigation} instead */
93
96
  NavigateToAppProperties(): void;
97
+ /** @deprecated use {@link Navigation} instead */
94
98
  NavigateToExternalWeb(url: string): void;
99
+ /** @deprecated use {@link Navigation} instead */
95
100
  NavigateToInvites(): void;
101
+ /** @deprecated use {@link Navigation} instead */
96
102
  NavigateToChat(): void;
103
+ /** @deprecated use {@link Navigation} instead */
97
104
  NavigateToLibraryTab(): void;
105
+ /** @deprecated use {@link Navigation} instead */
98
106
  NavigateToLayoutPreview(e: unknown): void;
107
+ /** @deprecated use {@link Navigation} instead */
99
108
  OpenPowerMenu(unknown?: any): void;
100
109
  get RunningApps(): AppOverview[];
101
110
  get MainRunningApp(): AppOverview | undefined;
@@ -117,58 +126,58 @@ export interface Navigation {
117
126
  OpenQuickAccessMenu(quickAccessTab?: QuickAccessTab): void;
118
127
  OpenMainMenu(): void;
119
128
  OpenPowerMenu(unknown?: any): void;
129
+ /** if calling this to perform navigation, call it after Navigate to prevent a race condition in desktop Big Picture mode that hides the overlay unintentionally */
120
130
  CloseSideMenus(): void;
121
131
  }
122
132
 
123
133
  export let Navigation = {} as Navigation;
124
134
 
135
+ const logger = new Logger("Navigation");
136
+
125
137
  try {
126
- (async () => {
127
- let InternalNavigators: any = {};
128
- if (!Router.NavigateToAppProperties || (Router as unknown as any).deckyShim) {
129
- function initInternalNavigators() {
138
+ function createNavigationFunction(fncName: string, handler?: (win: any) => any) {
139
+ return (...args: any) => {
140
+ let win: WindowRouter | undefined;
141
+ try {
142
+ win = window.SteamUIStore.GetFocusedWindowInstance();
143
+ } catch (e) {
144
+ logger.warn("Navigation interface failed to call GetFocusedWindowInstance", e);
145
+ }
146
+ if (!win) {
147
+ logger.warn("Navigation interface could not find any focused window. Falling back to GamepadUIMainWindowInstance");
148
+ win = Router.WindowStore?.GamepadUIMainWindowInstance;
149
+ }
150
+
151
+ if (win) {
130
152
  try {
131
- InternalNavigators = findModuleExport((e: Export) => e.GetNavigator && e.SetNavigator)?.GetNavigator();
153
+ const thisObj = handler && handler(win);
154
+ (thisObj || win)[fncName](...args);
132
155
  } catch (e) {
133
- console.error('[DFL:Router]: Failed to init internal navigators, trying again');
156
+ logger.error("Navigation handler failed", e);
134
157
  }
135
- }
136
- initInternalNavigators();
137
- while (!InternalNavigators?.AppProperties) {
138
- console.log('[DFL:Router]: Trying to init internal navigators again');
139
- await sleep(2000);
140
- initInternalNavigators();
158
+ } else {
159
+ logger.error("Navigation interface could not find a window to navigate");
141
160
  }
142
161
  }
143
- const newNavigation = {
144
- Navigate: Router.Navigate?.bind(Router),
145
- NavigateBack: Router.WindowStore?.GamepadUIMainWindowInstance?.NavigateBack?.bind(
146
- Router.WindowStore.GamepadUIMainWindowInstance,
147
- ),
148
- NavigateToAppProperties: InternalNavigators?.AppProperties || Router.NavigateToAppProperties?.bind(Router),
149
- NavigateToExternalWeb: InternalNavigators?.ExternalWeb || Router.NavigateToExternalWeb?.bind(Router),
150
- NavigateToInvites: InternalNavigators?.Invites || Router.NavigateToInvites?.bind(Router),
151
- NavigateToChat: InternalNavigators?.Chat || Router.NavigateToChat?.bind(Router),
152
- NavigateToLibraryTab: InternalNavigators?.LibraryTab || Router.NavigateToLibraryTab?.bind(Router),
153
- NavigateToLayoutPreview: Router.NavigateToLayoutPreview?.bind(Router),
154
- NavigateToSteamWeb: Router.WindowStore?.GamepadUIMainWindowInstance?.NavigateToSteamWeb?.bind(
155
- Router.WindowStore.GamepadUIMainWindowInstance,
156
- ),
157
- OpenSideMenu: Router.WindowStore?.GamepadUIMainWindowInstance?.MenuStore.OpenSideMenu?.bind(
158
- Router.WindowStore.GamepadUIMainWindowInstance.MenuStore,
159
- ),
160
- OpenQuickAccessMenu: Router.WindowStore?.GamepadUIMainWindowInstance?.MenuStore.OpenQuickAccessMenu?.bind(
161
- Router.WindowStore.GamepadUIMainWindowInstance.MenuStore,
162
- ),
163
- OpenMainMenu: Router.WindowStore?.GamepadUIMainWindowInstance?.MenuStore.OpenMainMenu?.bind(
164
- Router.WindowStore.GamepadUIMainWindowInstance.MenuStore,
165
- ),
166
- CloseSideMenus: Router.CloseSideMenus?.bind(Router),
167
- OpenPowerMenu: Router.OpenPowerMenu?.bind(Router),
168
- } as Navigation;
162
+ }
163
+ const newNavigation = {
164
+ Navigate: createNavigationFunction("Navigate"),
165
+ NavigateBack: createNavigationFunction("NavigateBack"),
166
+ NavigateToAppProperties: createNavigationFunction("AppProperties", win => win.Navigator),
167
+ NavigateToExternalWeb: createNavigationFunction("ExternalWeb", win => win.Navigator),
168
+ NavigateToInvites: createNavigationFunction("Invites", win => win.Navigator),
169
+ NavigateToChat: createNavigationFunction("Chat", win => win.Navigator),
170
+ NavigateToLibraryTab: createNavigationFunction("LibraryTab", win => win.Navigator),
171
+ NavigateToLayoutPreview: Router.NavigateToLayoutPreview?.bind(Router),
172
+ NavigateToSteamWeb: createNavigationFunction("NavigateToSteamWeb"),
173
+ OpenSideMenu: createNavigationFunction("OpenSideMenu", win => win.MenuStore),
174
+ OpenQuickAccessMenu: createNavigationFunction("OpenQuickAccessMenu", win => win.MenuStore),
175
+ OpenMainMenu: createNavigationFunction("OpenMainMenu", win => win.MenuStore),
176
+ CloseSideMenus: createNavigationFunction("CloseSideMenus", win => win.MenuStore),
177
+ OpenPowerMenu: Router.OpenPowerMenu?.bind(Router),
178
+ } as Navigation;
169
179
 
170
- Object.assign(Navigation, newNavigation);
171
- })();
180
+ Object.assign(Navigation, newNavigation);
172
181
  } catch (e) {
173
- console.error('[DFL:Router]: Error initializing Navigation interface', e);
182
+ logger.error('Error initializing Navigation interface', e);
174
183
  }
@@ -52,6 +52,7 @@ export function injectFCTrampoline(component: FC, customHooks?: any): FCTrampoli
52
52
  // we have to redirect this to return an object with component's prototype as a constructor returning a value overrides its prototype
53
53
  window.SP_REACT.createElement = () => {
54
54
  loggingEnabled && logger.debug("createElement hook called");
55
+ loggingEnabled && console.trace("createElement trace");
55
56
  return Object.create(component.prototype);
56
57
  };
57
58
  }
@@ -66,12 +67,16 @@ export function injectFCTrampoline(component: FC, customHooks?: any): FCTrampoli
66
67
  }
67
68
  }
68
69
 
70
+ let renderHookStep = 0;
71
+
69
72
  // Accessed two times, once directly before class instantiation, and again in some extra logic we don't need to worry about that we hanlde below just in case.
70
73
  Object.defineProperty(component, "contextType", {
71
74
  configurable: true,
72
75
  get: function () {
73
- loggingEnabled && logger.debug("get contexttype", this, stubsApplied);
74
- applyStubsIfNeeded();
76
+ loggingEnabled && logger.debug("get contexttype", this, stubsApplied, renderHookStep);
77
+ loggingEnabled && console.trace("contextType trace");
78
+ if (renderHookStep == 0) renderHookStep = 1;
79
+ else if (renderHookStep == 3) renderHookStep = 4;
75
80
  return this._contextType;
76
81
  },
77
82
  set: function (value) {
@@ -79,16 +84,20 @@ export function injectFCTrampoline(component: FC, customHooks?: any): FCTrampoli
79
84
  }
80
85
  });
81
86
 
82
- // Undoes the second contextType access we can't detect shortly before render before it's able to cause any damage
83
- Object.defineProperty(component, "getDerivedStateFromProps", {
87
+ // Always accessed directly after contextType for the path we want to catch.
88
+ Object.defineProperty(component, "contextTypes", {
84
89
  configurable: true,
85
90
  get: function () {
86
- loggingEnabled && logger.debug("get getDerivedStateFromProps", this, stubsApplied);
87
- removeStubsIfNeeded();
88
- return this._getDerivedStateFromProps;
91
+ loggingEnabled && logger.debug("get contexttypes", this, stubsApplied, renderHookStep);
92
+ loggingEnabled && console.trace("contextTypes trace");
93
+ if (renderHookStep == 1) {
94
+ renderHookStep = 2;
95
+ applyStubsIfNeeded();
96
+ };
97
+ return this._contextTypes;
89
98
  },
90
99
  set: function (value) {
91
- this._getDerivedStateFromProps = value;
100
+ this._contextTypes = value;
92
101
  }
93
102
  });
94
103
 
@@ -99,11 +108,32 @@ export function injectFCTrampoline(component: FC, customHooks?: any): FCTrampoli
99
108
  return this._updater;
100
109
  },
101
110
  set: function (value) {
102
- loggingEnabled && logger.debug("set updater", this, value, stubsApplied);
103
- removeStubsIfNeeded();
111
+ loggingEnabled && logger.debug("set updater", this, value, stubsApplied, renderHookStep);
112
+ loggingEnabled && console.trace("updater trace");
113
+ if (renderHookStep == 2) {
114
+ renderHookStep = 0;
115
+ removeStubsIfNeeded();
116
+ }
104
117
  return this._updater = value;
105
118
  }
106
119
  });
107
120
 
121
+ // Prevents the second contextType+contextTypes access from leaving its hooks around
122
+ Object.defineProperty(component, "getDerivedStateFromProps", {
123
+ configurable: true,
124
+ get: function () {
125
+ loggingEnabled && logger.debug("get getDerivedStateFromProps", this, stubsApplied, renderHookStep);
126
+ loggingEnabled && console.trace("getDerivedStateFromProps trace");
127
+ if (renderHookStep == 2) {
128
+ renderHookStep = 0;
129
+ removeStubsIfNeeded();
130
+ }
131
+ return this._getDerivedStateFromProps;
132
+ },
133
+ set: function (value) {
134
+ this._getDerivedStateFromProps = value;
135
+ }
136
+ });
137
+
108
138
  return userComponent;
109
139
  }
@@ -104,19 +104,15 @@ export function wrapReactClass(node: any, prop: any = 'type') {
104
104
 
105
105
  export function getReactRoot(o: HTMLElement | Element | Node) {
106
106
  return (
107
- // @ts-expect-error 7053
108
- o[Object.keys(o).find((k) => k.startsWith('__reactContainer$')) as string] ||
109
- // @ts-expect-error 7053
110
- o['_reactRootContainer']?._internalRoot?.current
107
+ (o as any)[Object.keys(o).find((k) => k.startsWith('__reactContainer$')) as string] ||
108
+ (o as any)['_reactRootContainer']?._internalRoot?.current
111
109
  );
112
110
  }
113
111
 
114
112
  export function getReactInstance(o: HTMLElement | Element | Node) {
115
113
  return (
116
- // @ts-expect-error 7053
117
- o[Object.keys(o).find((k) => k.startsWith('__reactFiber')) as string] ||
118
- // @ts-expect-error 7053
119
- o[Object.keys(o).find((k) => k.startsWith('__reactInternalInstance')) as string]
114
+ (o as any)[Object.keys(o).find((k) => k.startsWith('__reactFiber')) as string] ||
115
+ (o as any)[Object.keys(o).find((k) => k.startsWith('__reactInternalInstance')) as string]
120
116
  );
121
117
  }
122
118