Add event handler

This commit is contained in:
Party Wumpus
2024-02-15 22:28:36 +00:00
parent ee6122b97d
commit 867ce63f7b
6 changed files with 62 additions and 32 deletions
+2 -2
View File
@@ -189,7 +189,7 @@ class Updater:
raw += len(c) raw += len(c)
new_progress = round((raw / total) * 100) new_progress = round((raw / total) * 100)
if progress != new_progress: 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 progress = new_progress
if ON_LINUX: if ON_LINUX:
@@ -202,7 +202,7 @@ class Updater:
logger.info(f"Setting the executable flag with chcon returned {await process.wait()}") logger.info(f"Setting the executable flag with chcon returned {await process.wait()}")
logger.info("Updated loader installation.") 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 self.do_restart()
await tab.close_websocket() await tab.close_websocket()
+4 -11
View File
@@ -7,9 +7,7 @@ from aiohttp.web import Application, WebSocketResponse, Request, Response, get
from enum import IntEnum from enum import IntEnum
from typing import Callable, Coroutine, Dict, Any, cast, TypeVar, Type from typing import Callable, Coroutine, Dict, Any, cast, TypeVar
from dataclasses import asdict, is_dataclass
from traceback import format_exc from traceback import format_exc
@@ -127,11 +125,6 @@ class WSRouter:
self.logger.debug('Websocket connection closed') self.logger.debug('Websocket connection closed')
return ws 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, *args: Any):
async def emit(self, event: str, data: DataType | None = None, data_type: Type[DataType] | None = None): self.logger.debug(f'Firing frontend event {event} with args {args}')
self.logger.debug('Firing frontend event %s with args %s', data) await self.write({ "type": MessageType.EVENT.value, "event": event, "args": args })
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 })
@@ -77,16 +77,20 @@ export default function UpdaterSettings() {
const { t } = useTranslation(); const { t } = useTranslation();
useEffect(() => { useEffect(() => {
window.DeckyUpdater = { const a = DeckyBackend.addEventListener('frontend/update_download_percentage', (percentage) => {
updateProgress: (i) => { setUpdateProgress(percentage);
setUpdateProgress(i); setIsLoaderUpdating(true);
setIsLoaderUpdating(true); });
},
finish: async () => { const b = DeckyBackend.addEventListener('frontend/finish_download', async () => {
setUpdateProgress(0); setUpdateProgress(0);
setReloading(true); setReloading(true);
await doRestart(); await doRestart();
}, });
return () => {
DeckyBackend.removeEventListener('frontend/update_download_percentage', a);
DeckyBackend.removeEventListener('frontend/finish_download', b);
}; };
}, []); }, []);
-2
View File
@@ -3,11 +3,9 @@ import Backend from 'i18next-http-backend';
import { initReactI18next } from 'react-i18next'; import { initReactI18next } from 'react-i18next';
import PluginLoader from './plugin-loader'; import PluginLoader from './plugin-loader';
import { DeckyUpdater } from './updater';
declare global { declare global {
export var DeckyPluginLoader: PluginLoader; export var DeckyPluginLoader: PluginLoader;
export var DeckyUpdater: DeckyUpdater | undefined; // TODO get rid of this
export var importDeckyPlugin: Function; export var importDeckyPlugin: Function;
export var deckyHasLoaded: boolean; export var deckyHasLoaded: boolean;
export var deckyHasConnectedRDT: boolean | undefined; export var deckyHasConnectedRDT: boolean | undefined;
-5
View File
@@ -4,11 +4,6 @@ export enum Branches {
// Testing, // Testing,
} }
export interface DeckyUpdater {
updateProgress: (val: number) => void;
finish: () => void;
}
export interface RemoteVerInfo { export interface RemoteVerInfo {
assets: { assets: {
browser_download_url: string; browser_download_url: string;
+42 -2
View File
@@ -34,7 +34,13 @@ interface ErrorMessage {
id: number; 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 // Helper to resolve a promise from the outside
interface PromiseResolver<T> { interface PromiseResolver<T> {
@@ -46,6 +52,7 @@ interface PromiseResolver<T> {
export class WSRouter extends Logger { export class WSRouter extends Logger {
routes: Map<string, (...args: any) => any> = new Map(); routes: Map<string, (...args: any) => any> = new Map();
runningCalls: Map<number, PromiseResolver<any>> = new Map(); runningCalls: Map<number, PromiseResolver<any>> = new Map();
eventListeners: Map<string, Set<(...args: any) => any>> = new Map();
ws?: WebSocket; ws?: WebSocket;
connectPromise?: Promise<void>; connectPromise?: Promise<void>;
// Used to map results and errors to calls // Used to map results and errors to calls
@@ -87,7 +94,7 @@ export class WSRouter extends Logger {
this.ws?.send(JSON.stringify(data)); 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); this.routes.set(name, route);
} }
@@ -95,6 +102,25 @@ export class WSRouter extends Logger {
this.routes.delete(name); 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) { async onMessage(msg: MessageEvent) {
try { try {
const data = JSON.parse(msg.data) as Message; const data = JSON.parse(msg.data) as Message;
@@ -129,6 +155,20 @@ export class WSRouter extends Logger {
} }
break; 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: default:
this.error('Unknown message type', data); this.error('Unknown message type', data);
break; break;