mirror of
https://github.com/SteamDeckHomebrew/decky-loader.git
synced 2026-06-13 09:03:20 +03:00
This reverts commit b27b625921.
These changes broke Decky's QAM injection when the lock screen is enabled and need to be revised
153 lines
4.5 KiB
TypeScript
153 lines
4.5 KiB
TypeScript
// TabsHook for versions after the Desktop merge
|
|
import { Patch, QuickAccessTab, afterPatch, findInReactTree, sleep } from 'decky-frontend-lib';
|
|
|
|
import { QuickAccessVisibleStateProvider } from './components/QuickAccessVisibleState';
|
|
import Logger from './logger';
|
|
|
|
declare global {
|
|
interface Window {
|
|
__TABS_HOOK_INSTANCE: any;
|
|
}
|
|
}
|
|
|
|
interface Tab {
|
|
id: QuickAccessTab | number;
|
|
title: any;
|
|
content: any;
|
|
icon: any;
|
|
}
|
|
|
|
class TabsHook extends Logger {
|
|
// private keys = 7;
|
|
tabs: Tab[] = [];
|
|
private qAMRoot?: any;
|
|
private qamPatch?: Patch;
|
|
|
|
constructor() {
|
|
super('TabsHook');
|
|
|
|
this.log('Initialized');
|
|
window.__TABS_HOOK_INSTANCE?.deinit?.();
|
|
window.__TABS_HOOK_INSTANCE = this;
|
|
}
|
|
|
|
init() {
|
|
const tree = (document.getElementById('root') as any)._reactRootContainer._internalRoot.current;
|
|
let qAMRoot: any;
|
|
const findQAMRoot = (currentNode: any, iters: number): any => {
|
|
if (iters >= 65) {
|
|
// currently 45
|
|
return null;
|
|
}
|
|
if (
|
|
typeof currentNode?.memoizedProps?.visible == 'boolean' &&
|
|
currentNode?.type?.toString()?.includes('QuickAccessMenuBrowserView')
|
|
) {
|
|
this.log(`QAM root was found in ${iters} recursion cycles`);
|
|
return currentNode;
|
|
}
|
|
if (currentNode.child) {
|
|
let node = findQAMRoot(currentNode.child, iters + 1);
|
|
if (node !== null) return node;
|
|
}
|
|
if (currentNode.sibling) {
|
|
let node = findQAMRoot(currentNode.sibling, iters + 1);
|
|
if (node !== null) return node;
|
|
}
|
|
return null;
|
|
};
|
|
(async () => {
|
|
qAMRoot = findQAMRoot(tree, 0);
|
|
while (!qAMRoot) {
|
|
this.error(
|
|
'Failed to find QAM root node, reattempting in 5 seconds. A developer may need to increase the recursion limit.',
|
|
);
|
|
await sleep(5000);
|
|
qAMRoot = findQAMRoot(tree, 0);
|
|
}
|
|
this.qAMRoot = qAMRoot;
|
|
let patchedInnerQAM: any;
|
|
this.qamPatch = afterPatch(qAMRoot.return, 'type', (_: any, ret: any) => {
|
|
try {
|
|
if (!qAMRoot?.child) {
|
|
qAMRoot = findQAMRoot(tree, 0);
|
|
this.qAMRoot = qAMRoot;
|
|
}
|
|
if (qAMRoot?.child && !qAMRoot?.child?.type?.decky) {
|
|
afterPatch(qAMRoot.child, 'type', (_: any, ret: any) => {
|
|
try {
|
|
const qamTabsRenderer = findInReactTree(ret, (x) => x?.props?.onFocusNavDeactivated);
|
|
if (patchedInnerQAM) {
|
|
qamTabsRenderer.type = patchedInnerQAM;
|
|
} else {
|
|
afterPatch(qamTabsRenderer, 'type', (innerArgs: any, ret: any) => {
|
|
const tabs = findInReactTree(ret, (x) => x?.props?.tabs);
|
|
this.render(tabs.props.tabs, innerArgs[0].visible);
|
|
return ret;
|
|
});
|
|
patchedInnerQAM = qamTabsRenderer.type;
|
|
}
|
|
} catch (e) {
|
|
this.error('Error patching QAM inner', e);
|
|
}
|
|
return ret;
|
|
});
|
|
qAMRoot.child.type.decky = true;
|
|
qAMRoot.child.alternate.type = qAMRoot.child.type;
|
|
}
|
|
} catch (e) {
|
|
this.error('Error patching QAM', e);
|
|
}
|
|
|
|
return ret;
|
|
});
|
|
|
|
if (qAMRoot.return.alternate) {
|
|
qAMRoot.return.alternate.type = qAMRoot.return.type;
|
|
}
|
|
this.log('Finished initial injection');
|
|
})();
|
|
}
|
|
|
|
deinit() {
|
|
this.qamPatch?.unpatch();
|
|
this.qAMRoot.return.alternate.type = this.qAMRoot.return.type;
|
|
}
|
|
|
|
add(tab: Tab) {
|
|
this.debug('Adding tab', tab.id, 'to render array');
|
|
this.tabs.push(tab);
|
|
}
|
|
|
|
removeById(id: number) {
|
|
this.debug('Removing tab', id);
|
|
this.tabs = this.tabs.filter((tab) => tab.id !== id);
|
|
}
|
|
|
|
render(existingTabs: any[], visible: boolean) {
|
|
let deckyTabAmount = existingTabs.reduce((prev: any, cur: any) => (cur.decky ? prev + 1 : prev), 0);
|
|
if (deckyTabAmount == this.tabs.length) {
|
|
for (let tab of existingTabs) {
|
|
if (tab?.decky && tab?.qAMVisibilitySetter) tab?.qAMVisibilitySetter(visible);
|
|
}
|
|
return;
|
|
}
|
|
for (const { title, icon, content, id } of this.tabs) {
|
|
const tab: any = {
|
|
key: id,
|
|
title,
|
|
tab: icon,
|
|
decky: true,
|
|
};
|
|
tab.panel = (
|
|
<QuickAccessVisibleStateProvider initial={visible} tab={tab}>
|
|
{content}
|
|
</QuickAccessVisibleStateProvider>
|
|
);
|
|
existingTabs.push(tab);
|
|
}
|
|
}
|
|
}
|
|
|
|
export default TabsHook;
|