summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorParty Wumpus <48649272+PartyWumpus@users.noreply.github.com>2024-02-15 22:28:36 +0000
committerParty Wumpus <48649272+PartyWumpus@users.noreply.github.com>2024-02-15 22:28:36 +0000
commit867ce63f7bc67cb418d96d226a7e3eaf2b05bc87 (patch)
tree5424e3374c2f66e25d6fa0a80bc4f6f3e18fa84d
parentee6122b97d042e8bb50531511d6c0103e0d6a358 (diff)
downloaddecky-loader-867ce63f7bc67cb418d96d226a7e3eaf2b05bc87.tar.gz
decky-loader-867ce63f7bc67cb418d96d226a7e3eaf2b05bc87.zip
Add event handler
-rw-r--r--backend/decky_loader/updater.py4
-rw-r--r--backend/decky_loader/wsrouter.py15
-rw-r--r--frontend/src/components/settings/pages/general/Updater.tsx24
-rw-r--r--frontend/src/start.tsx2
-rw-r--r--frontend/src/updater.ts5
-rw-r--r--frontend/src/wsrouter.ts44
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;