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 ab6ec98160
6 changed files with 90 additions and 23 deletions
+2 -2
View File
@@ -83,8 +83,8 @@ class PluginManager:
async def loader_reinjector(self):
while True:
await sleep(1)
if not await tab_has_global_var("SP", "DeckyPluginLoader"):
await sleep(5)
if not await tab_has_global_var("SP", "deckyHasLoaded"):
logger.info("Plugin loader isn't present in Steam anymore, reinjecting...")
await self.inject_javascript()
+35 -5
View File
@@ -6,17 +6,21 @@ export interface RouterEntry {
component: ComponentType;
}
export type RoutePatch = (route: RouteProps) => RouteProps;
interface PublicDeckyRouterState {
routes: Map<string, RouterEntry>;
routePatches: Map<string, Set<RoutePatch>>;
}
export class DeckyRouterState {
private _routes = new Map<string, RouterEntry>();
private _routePatches = new Map<string, Set<RoutePatch>>();
public eventBus = new EventTarget();
publicState(): PublicDeckyRouterState {
return { routes: this._routes };
return { routes: this._routes, routePatches: this._routePatches };
}
addRoute(path: string, component: RouterEntry['component'], props: RouterEntry['props'] = {}) {
@@ -24,6 +28,26 @@ export class DeckyRouterState {
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) {
this._routes.delete(path);
this.notifyUpdate();
@@ -36,6 +60,8 @@ export class DeckyRouterState {
interface DeckyRouterStateContext extends PublicDeckyRouterState {
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;
}
@@ -54,6 +80,7 @@ export const DeckyRouterStateContextProvider: FC<Props> = ({ children, deckyRout
useEffect(() => {
function onUpdate() {
console.log('test', deckyRouterState.publicState());
setPublicDeckyRouterState({ ...deckyRouterState.publicState() });
}
@@ -62,12 +89,15 @@ export const DeckyRouterStateContextProvider: FC<Props> = ({ children, deckyRout
return () => deckyRouterState.eventBus.removeEventListener('update', onUpdate);
}, []);
const addRoute = (path: string, component: RouterEntry['component'], props: RouterEntry['props'] = {}) =>
deckyRouterState.addRoute(path, component, props);
const removeRoute = (path: string) => deckyRouterState.removeRoute(path);
const addRoute = deckyRouterState.addRoute.bind(deckyRouterState);
const addPatch = deckyRouterState.addPatch.bind(deckyRouterState);
const removePatch = deckyRouterState.removePatch.bind(deckyRouterState);
const removeRoute = deckyRouterState.removeRoute.bind(deckyRouterState);
return (
<DeckyRouterStateContext.Provider value={{ ...publicDeckyRouterState, addRoute, removeRoute }}>
<DeckyRouterStateContext.Provider
value={{ ...publicDeckyRouterState, addRoute, addPatch, removePatch, removeRoute }}
>
{children}
</DeckyRouterStateContext.Provider>
);
+3
View File
@@ -7,6 +7,7 @@ declare global {
DeckyUpdater?: DeckyUpdater;
importDeckyPlugin: Function;
syncDeckyPlugins: Function;
deckyHasLoaded: boolean;
}
}
@@ -26,3 +27,5 @@ window.syncDeckyPlugins = async function () {
};
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[]) => {
console.log(
`%c Decky %c ${name} %c`,
@@ -28,7 +38,7 @@ class Logger {
}
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 { ReactElement, createElement, memo } from 'react';
import type { Route } from 'react-router';
import type { Route, RouteProps } from 'react-router';
import {
DeckyRouterState,
DeckyRouterStateContextProvider,
RoutePatch,
RouterEntry,
useDeckyRouterState,
} from './components/DeckyRouterState';
@@ -38,19 +39,16 @@ class RouterHook extends Logger {
});
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 { routes } = useDeckyRouterState();
const { routes, routePatches } = useDeckyRouterState();
let routerIndex = children.props.children[0].props.children.length;
if (
!children.props.children[0].props.children[routerIndex - 1]?.length ||
children.props.children[0].props.children[routerIndex - 1]?.length !== routes.size
) {
if (
children.props.children[0].props.children[routerIndex - 1]?.length &&
children.props.children[0].props.children[routerIndex - 1].length !== routes.size
)
routerIndex--;
const routeList = children.props.children[0].props.children;
let routerIndex = routeList.length;
if (!routeList[routerIndex - 1]?.length || routeList[routerIndex - 1]?.length !== routes.size) {
if (routeList[routerIndex - 1]?.length && routeList[routerIndex - 1].length !== routes.size) routerIndex--;
const newRouterArray: ReactElement[] = [];
routes.forEach(({ component, props }, path) => {
newRouterArray.push(
@@ -59,8 +57,26 @@ class RouterHook extends Logger {
</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;
};
@@ -97,6 +113,14 @@ class RouterHook extends Logger {
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) {
this.routerState.removeRoute(path);
}
+2 -2
View File
@@ -110,12 +110,12 @@ class TabsHook extends Logger {
}
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);
}
removeById(id: number) {
this.log('Removing tab', id);
this.debug('Removing tab', id);
this.tabs = this.tabs.filter((tab) => tab.id !== id);
}