API for patching existing routes, lower power use

This commit is contained in:
AAGaming
2022-08-02 18:54:55 -04:00
parent f1e809781a
commit 03eab07cbf
6 changed files with 90 additions and 23 deletions
+2 -2
View File
@@ -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()
+35 -5
View File
@@ -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>
); );
+3
View File
@@ -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
View File
@@ -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);
} }
} }
+37 -13
View File
@@ -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);
} }
+2 -2
View File
@@ -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);
} }