mirror of
https://github.com/SteamDeckHomebrew/decky-loader.git
synced 2026-07-11 15:02:03 +00:00
API for patching existing routes, lower power use
This commit is contained in:
+2
-2
@@ -83,8 +83,8 @@ class PluginManager:
|
|||||||
|
|
||||||
async def loader_reinjector(self):
|
async def loader_reinjector(self):
|
||||||
while True:
|
while True:
|
||||||
await sleep(1)
|
await sleep(5)
|
||||||
if not await tab_has_global_var("SP", "DeckyPluginLoader"):
|
if not await tab_has_global_var("SP", "deckyHasLoaded"):
|
||||||
logger.info("Plugin loader isn't present in Steam anymore, reinjecting...")
|
logger.info("Plugin loader isn't present in Steam anymore, reinjecting...")
|
||||||
await self.inject_javascript()
|
await self.inject_javascript()
|
||||||
|
|
||||||
|
|||||||
@@ -6,17 +6,21 @@ export interface RouterEntry {
|
|||||||
component: ComponentType;
|
component: ComponentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type RoutePatch = (route: RouteProps) => RouteProps;
|
||||||
|
|
||||||
interface PublicDeckyRouterState {
|
interface PublicDeckyRouterState {
|
||||||
routes: Map<string, RouterEntry>;
|
routes: Map<string, RouterEntry>;
|
||||||
|
routePatches: Map<string, Set<RoutePatch>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DeckyRouterState {
|
export class DeckyRouterState {
|
||||||
private _routes = new Map<string, RouterEntry>();
|
private _routes = new Map<string, RouterEntry>();
|
||||||
|
private _routePatches = new Map<string, Set<RoutePatch>>();
|
||||||
|
|
||||||
public eventBus = new EventTarget();
|
public eventBus = new EventTarget();
|
||||||
|
|
||||||
publicState(): PublicDeckyRouterState {
|
publicState(): PublicDeckyRouterState {
|
||||||
return { routes: this._routes };
|
return { routes: this._routes, routePatches: this._routePatches };
|
||||||
}
|
}
|
||||||
|
|
||||||
addRoute(path: string, component: RouterEntry['component'], props: RouterEntry['props'] = {}) {
|
addRoute(path: string, component: RouterEntry['component'], props: RouterEntry['props'] = {}) {
|
||||||
@@ -24,6 +28,26 @@ export class DeckyRouterState {
|
|||||||
this.notifyUpdate();
|
this.notifyUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addPatch(path: string, patch: RoutePatch) {
|
||||||
|
let patchList = this._routePatches.get(path);
|
||||||
|
if (!patchList) {
|
||||||
|
patchList = new Set();
|
||||||
|
this._routePatches.set(path, patchList);
|
||||||
|
}
|
||||||
|
patchList.add(patch);
|
||||||
|
this.notifyUpdate();
|
||||||
|
return patch;
|
||||||
|
}
|
||||||
|
|
||||||
|
removePatch(path: string, patch: RoutePatch) {
|
||||||
|
const patchList = this._routePatches.get(path);
|
||||||
|
patchList?.delete(patch);
|
||||||
|
if (patchList?.size == 0) {
|
||||||
|
this._routePatches.delete(path);
|
||||||
|
}
|
||||||
|
this.notifyUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
removeRoute(path: string) {
|
removeRoute(path: string) {
|
||||||
this._routes.delete(path);
|
this._routes.delete(path);
|
||||||
this.notifyUpdate();
|
this.notifyUpdate();
|
||||||
@@ -36,6 +60,8 @@ export class DeckyRouterState {
|
|||||||
|
|
||||||
interface DeckyRouterStateContext extends PublicDeckyRouterState {
|
interface DeckyRouterStateContext extends PublicDeckyRouterState {
|
||||||
addRoute(path: string, component: RouterEntry['component'], props: RouterEntry['props']): void;
|
addRoute(path: string, component: RouterEntry['component'], props: RouterEntry['props']): void;
|
||||||
|
addPatch(path: string, patch: RoutePatch): RoutePatch;
|
||||||
|
removePatch(path: string, patch: RoutePatch): void;
|
||||||
removeRoute(path: string): void;
|
removeRoute(path: string): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +80,7 @@ export const DeckyRouterStateContextProvider: FC<Props> = ({ children, deckyRout
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function onUpdate() {
|
function onUpdate() {
|
||||||
|
console.log('test', deckyRouterState.publicState());
|
||||||
setPublicDeckyRouterState({ ...deckyRouterState.publicState() });
|
setPublicDeckyRouterState({ ...deckyRouterState.publicState() });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,12 +89,15 @@ export const DeckyRouterStateContextProvider: FC<Props> = ({ children, deckyRout
|
|||||||
return () => deckyRouterState.eventBus.removeEventListener('update', onUpdate);
|
return () => deckyRouterState.eventBus.removeEventListener('update', onUpdate);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const addRoute = (path: string, component: RouterEntry['component'], props: RouterEntry['props'] = {}) =>
|
const addRoute = deckyRouterState.addRoute.bind(deckyRouterState);
|
||||||
deckyRouterState.addRoute(path, component, props);
|
const addPatch = deckyRouterState.addPatch.bind(deckyRouterState);
|
||||||
const removeRoute = (path: string) => deckyRouterState.removeRoute(path);
|
const removePatch = deckyRouterState.removePatch.bind(deckyRouterState);
|
||||||
|
const removeRoute = deckyRouterState.removeRoute.bind(deckyRouterState);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DeckyRouterStateContext.Provider value={{ ...publicDeckyRouterState, addRoute, removeRoute }}>
|
<DeckyRouterStateContext.Provider
|
||||||
|
value={{ ...publicDeckyRouterState, addRoute, addPatch, removePatch, removeRoute }}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</DeckyRouterStateContext.Provider>
|
</DeckyRouterStateContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ declare global {
|
|||||||
DeckyUpdater?: DeckyUpdater;
|
DeckyUpdater?: DeckyUpdater;
|
||||||
importDeckyPlugin: Function;
|
importDeckyPlugin: Function;
|
||||||
syncDeckyPlugins: Function;
|
syncDeckyPlugins: Function;
|
||||||
|
deckyHasLoaded: boolean;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,3 +27,5 @@ window.syncDeckyPlugins = async function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
setTimeout(() => window.syncDeckyPlugins(), 5000);
|
setTimeout(() => window.syncDeckyPlugins(), 5000);
|
||||||
|
|
||||||
|
window.deckyHasLoaded = true;
|
||||||
|
|||||||
+11
-1
@@ -8,6 +8,16 @@ export const log = (name: string, ...args: any[]) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const debug = (name: string, ...args: any[]) => {
|
||||||
|
console.debug(
|
||||||
|
`%c Decky %c ${name} %c`,
|
||||||
|
'background: #16a085; color: black;',
|
||||||
|
'background: #1abc9c; color: black;',
|
||||||
|
'color: blue;',
|
||||||
|
...args,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const error = (name: string, ...args: any[]) => {
|
export const error = (name: string, ...args: any[]) => {
|
||||||
console.log(
|
console.log(
|
||||||
`%c Decky %c ${name} %c`,
|
`%c Decky %c ${name} %c`,
|
||||||
@@ -28,7 +38,7 @@ class Logger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
debug(...args: any[]) {
|
debug(...args: any[]) {
|
||||||
log(this.name, ...args);
|
debug(this.name, ...args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { afterPatch, findModuleChild, unpatch } from 'decky-frontend-lib';
|
import { afterPatch, findModuleChild, unpatch } from 'decky-frontend-lib';
|
||||||
import { ReactElement, createElement, memo } from 'react';
|
import { ReactElement, createElement, memo } from 'react';
|
||||||
import type { Route } from 'react-router';
|
import type { Route, RouteProps } from 'react-router';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DeckyRouterState,
|
DeckyRouterState,
|
||||||
DeckyRouterStateContextProvider,
|
DeckyRouterStateContextProvider,
|
||||||
|
RoutePatch,
|
||||||
RouterEntry,
|
RouterEntry,
|
||||||
useDeckyRouterState,
|
useDeckyRouterState,
|
||||||
} from './components/DeckyRouterState';
|
} from './components/DeckyRouterState';
|
||||||
@@ -38,19 +39,16 @@ class RouterHook extends Logger {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let Route: new () => Route;
|
let Route: new () => Route;
|
||||||
|
// Used to store the new replicated routes we create to allow routes to be unpatched.
|
||||||
|
let toReplace = new Map<string, Route>();
|
||||||
const DeckyWrapper = ({ children }: { children: ReactElement }) => {
|
const DeckyWrapper = ({ children }: { children: ReactElement }) => {
|
||||||
const { routes } = useDeckyRouterState();
|
const { routes, routePatches } = useDeckyRouterState();
|
||||||
|
|
||||||
let routerIndex = children.props.children[0].props.children.length;
|
const routeList = children.props.children[0].props.children;
|
||||||
if (
|
|
||||||
!children.props.children[0].props.children[routerIndex - 1]?.length ||
|
let routerIndex = routeList.length;
|
||||||
children.props.children[0].props.children[routerIndex - 1]?.length !== routes.size
|
if (!routeList[routerIndex - 1]?.length || routeList[routerIndex - 1]?.length !== routes.size) {
|
||||||
) {
|
if (routeList[routerIndex - 1]?.length && routeList[routerIndex - 1].length !== routes.size) routerIndex--;
|
||||||
if (
|
|
||||||
children.props.children[0].props.children[routerIndex - 1]?.length &&
|
|
||||||
children.props.children[0].props.children[routerIndex - 1].length !== routes.size
|
|
||||||
)
|
|
||||||
routerIndex--;
|
|
||||||
const newRouterArray: ReactElement[] = [];
|
const newRouterArray: ReactElement[] = [];
|
||||||
routes.forEach(({ component, props }, path) => {
|
routes.forEach(({ component, props }, path) => {
|
||||||
newRouterArray.push(
|
newRouterArray.push(
|
||||||
@@ -59,8 +57,26 @@ class RouterHook extends Logger {
|
|||||||
</Route>,
|
</Route>,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
children.props.children[0].props.children[routerIndex] = newRouterArray;
|
routeList[routerIndex] = newRouterArray;
|
||||||
}
|
}
|
||||||
|
routeList.forEach((route: Route, index: number) => {
|
||||||
|
const replaced = toReplace.get(route?.props?.path as string);
|
||||||
|
if (replaced) {
|
||||||
|
routeList[index] = replaced;
|
||||||
|
toReplace.delete(route?.props?.path as string);
|
||||||
|
}
|
||||||
|
if (route?.props?.path && routePatches.has(route.props.path as string)) {
|
||||||
|
toReplace.set(
|
||||||
|
route?.props?.path as string,
|
||||||
|
// @ts-ignore
|
||||||
|
createElement(routeList[index].type, routeList[index].props, routeList[index].props.children),
|
||||||
|
);
|
||||||
|
routePatches.get(route.props.path as string)?.forEach((patch) => {
|
||||||
|
routeList[index].props = patch(routeList[index].props);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.debug('Rerendered routes list');
|
||||||
return children;
|
return children;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -97,6 +113,14 @@ class RouterHook extends Logger {
|
|||||||
this.routerState.addRoute(path, component, props);
|
this.routerState.addRoute(path, component, props);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addPatch(path: string, patch: RoutePatch) {
|
||||||
|
return this.routerState.addPatch(path, patch);
|
||||||
|
}
|
||||||
|
|
||||||
|
removePatch(path: string, patch: RoutePatch) {
|
||||||
|
this.routerState.removePatch(path, patch);
|
||||||
|
}
|
||||||
|
|
||||||
removeRoute(path: string) {
|
removeRoute(path: string) {
|
||||||
this.routerState.removeRoute(path);
|
this.routerState.removeRoute(path);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,12 +110,12 @@ class TabsHook extends Logger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
add(tab: Tab) {
|
add(tab: Tab) {
|
||||||
this.log('Adding tab', tab.id, 'to render array');
|
this.debug('Adding tab', tab.id, 'to render array');
|
||||||
this.tabs.push(tab);
|
this.tabs.push(tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeById(id: number) {
|
removeById(id: number) {
|
||||||
this.log('Removing tab', id);
|
this.debug('Removing tab', id);
|
||||||
this.tabs = this.tabs.filter((tab) => tab.id !== id);
|
this.tabs = this.tabs.filter((tab) => tab.id !== id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user