diff options
| -rw-r--r-- | backend/decky_loader/updater.py | 4 | ||||
| -rw-r--r-- | backend/decky_loader/wsrouter.py | 15 | ||||
| -rw-r--r-- | frontend/src/components/settings/pages/general/Updater.tsx | 24 | ||||
| -rw-r--r-- | frontend/src/start.tsx | 2 | ||||
| -rw-r--r-- | frontend/src/updater.ts | 5 | ||||
| -rw-r--r-- | frontend/src/wsrouter.ts | 44 |
6 files changed, 62 insertions, 32 deletions
diff --git a/backend/decky_loader/updater.py b/backend/decky_loader/updater.py index eadd49af..5ead49b4 100644 --- a/backend/decky_loader/updater.py +++ b/backend/decky_loader/updater.py @@ -189,7 +189,7 @@ class Updater: raw += len(c) new_progress = round((raw / total) * 100) if progress != new_progress: - self.context.loop.create_task(tab.evaluate_js(f"window.DeckyUpdater.updateProgress({new_progress})", False, False, False)) + self.context.loop.create_task(self.context.ws.emit("frontend/update_download_percentage", new_progress)) progress = new_progress if ON_LINUX: @@ -202,7 +202,7 @@ class Updater: logger.info(f"Setting the executable flag with chcon returned {await process.wait()}") logger.info("Updated loader installation.") - await tab.evaluate_js("window.DeckyUpdater.finish()", False, False) + await self.context.ws.emit("frontend/finish_download") await self.do_restart() await tab.close_websocket() diff --git a/backend/decky_loader/wsrouter.py b/backend/decky_loader/wsrouter.py index 8b145dc3..4874e967 100644 --- a/backend/decky_loader/wsrouter.py +++ b/backend/decky_loader/wsrouter.py @@ -7,9 +7,7 @@ from aiohttp.web import Application, WebSocketResponse, Request, Response, get from enum import IntEnum -from typing import Callable, Coroutine, Dict, Any, cast, TypeVar, Type - -from dataclasses import asdict, is_dataclass +from typing import Callable, Coroutine, Dict, Any, cast, TypeVar from traceback import format_exc @@ -127,11 +125,6 @@ class WSRouter: self.logger.debug('Websocket connection closed') return ws - # DataType defaults to None so that if a plugin opts in to strict pyright checking and attempts to pass data witbout specifying the type (or any), the type check fails - async def emit(self, event: str, data: DataType | None = None, data_type: Type[DataType] | None = None): - self.logger.debug('Firing frontend event %s with args %s', data) - sent_data: Dict[Any, Any] | None = cast(Dict[Any, Any], data) - if is_dataclass(data): - sent_data = asdict(data) # type: ignore Argument of type "DataclassInstance | type[DataclassInstance]" cannot be assigned to parameter "obj" of type "DataclassInstance" in function "asdict" - - await self.write({ "type": MessageType.EVENT.value, "event": event, "data": sent_data }) + async def emit(self, event: str, *args: Any): + self.logger.debug(f'Firing frontend event {event} with args {args}') + await self.write({ "type": MessageType.EVENT.value, "event": event, "args": args }) diff --git a/frontend/src/components/settings/pages/general/Updater.tsx b/frontend/src/components/settings/pages/general/Updater.tsx index 26537b58..645252fe 100644 --- a/frontend/src/components/settings/pages/general/Updater.tsx +++ b/frontend/src/components/settings/pages/general/Updater.tsx @@ -77,16 +77,20 @@ export default function UpdaterSettings() { const { t } = useTranslation(); useEffect(() => { - window.DeckyUpdater = { - updateProgress: (i) => { - setUpdateProgress(i); - setIsLoaderUpdating(true); - }, - finish: async () => { - setUpdateProgress(0); - setReloading(true); - await doRestart(); - }, + const a = DeckyBackend.addEventListener('frontend/update_download_percentage', (percentage) => { + setUpdateProgress(percentage); + setIsLoaderUpdating(true); + }); + + const b = DeckyBackend.addEventListener('frontend/finish_download', async () => { + setUpdateProgress(0); + setReloading(true); + await doRestart(); + }); + + return () => { + DeckyBackend.removeEventListener('frontend/update_download_percentage', a); + DeckyBackend.removeEventListener('frontend/finish_download', b); }; }, []); diff --git a/frontend/src/start.tsx b/frontend/src/start.tsx index e8c489f2..0803f46e 100644 --- a/frontend/src/start.tsx +++ b/frontend/src/start.tsx @@ -3,11 +3,9 @@ import Backend from 'i18next-http-backend'; import { initReactI18next } from 'react-i18next'; import PluginLoader from './plugin-loader'; -import { DeckyUpdater } from './updater'; declare global { export var DeckyPluginLoader: PluginLoader; - export var DeckyUpdater: DeckyUpdater | undefined; // TODO get rid of this export var importDeckyPlugin: Function; export var deckyHasLoaded: boolean; export var deckyHasConnectedRDT: boolean | undefined; diff --git a/frontend/src/updater.ts b/frontend/src/updater.ts index d6c23832..2df1f2a5 100644 --- a/frontend/src/updater.ts +++ b/frontend/src/updater.ts @@ -4,11 +4,6 @@ export enum Branches { // Testing, } -export interface DeckyUpdater { - updateProgress: (val: number) => void; - finish: () => void; -} - export interface RemoteVerInfo { assets: { browser_download_url: string; diff --git a/frontend/src/wsrouter.ts b/frontend/src/wsrouter.ts index 32037eaf..aada457c 100644 --- a/frontend/src/wsrouter.ts +++ b/frontend/src/wsrouter.ts @@ -34,7 +34,13 @@ interface ErrorMessage { id: number; } -type Message = CallMessage | ReplyMessage | ErrorMessage; +interface EventMessage { + type: MessageType.EVENT; + event: string; + args: any; +} + +type Message = CallMessage | ReplyMessage | ErrorMessage | EventMessage; // Helper to resolve a promise from the outside interface PromiseResolver<T> { @@ -46,6 +52,7 @@ interface PromiseResolver<T> { export class WSRouter extends Logger { routes: Map<string, (...args: any) => any> = new Map(); runningCalls: Map<number, PromiseResolver<any>> = new Map(); + eventListeners: Map<string, Set<(...args: any) => any>> = new Map(); ws?: WebSocket; connectPromise?: Promise<void>; // Used to map results and errors to calls @@ -87,7 +94,7 @@ export class WSRouter extends Logger { this.ws?.send(JSON.stringify(data)); } - addRoute(name: string, route: (args: any) => any) { + addRoute(name: string, route: (...args: any) => any) { this.routes.set(name, route); } @@ -95,6 +102,25 @@ export class WSRouter extends Logger { this.routes.delete(name); } + addEventListener(event: string, listener: (...args: any) => any) { + if (!this.eventListeners.has(event)) { + this.eventListeners.set(event, new Set([listener])); + } else { + this.eventListeners.get(event)?.add(listener); + } + return listener; + } + + removeEventListener(event: string, listener: (...args: any) => any) { + if (this.eventListeners.has(event)) { + const set = this.eventListeners.get(event); + set?.delete(listener); + if (set?.size === 0) { + this.eventListeners.delete(event); + } + } + } + async onMessage(msg: MessageEvent) { try { const data = JSON.parse(msg.data) as Message; @@ -129,6 +155,20 @@ export class WSRouter extends Logger { } break; + case MessageType.EVENT: + if (this.eventListeners.has(data.event)) { + for (const listener of this.eventListeners.get(data.event)!) { + try { + listener(...data.args); + } catch (e) { + this.error(`error in event ${data.event}`, e, listener); + } + } + } else { + this.debug(`event ${data.event} has no listeners`); + } + break; + default: this.error('Unknown message type', data); break; |
