From e67a1a6a1b1c726aae67c5a09c77656ff1b032de Mon Sep 17 00:00:00 2001 From: jeffvli Date: Fri, 10 Oct 2025 11:26:17 -0700 Subject: [PATCH] add global event emitter --- src/renderer/events/event-emitter.ts | 63 ++++++++++++++++++++++++++++ src/renderer/events/events.ts | 30 +++++++++++++ src/renderer/events/types.ts | 15 +++++++ 3 files changed, 108 insertions(+) create mode 100644 src/renderer/events/event-emitter.ts create mode 100644 src/renderer/events/events.ts create mode 100644 src/renderer/events/types.ts diff --git a/src/renderer/events/event-emitter.ts b/src/renderer/events/event-emitter.ts new file mode 100644 index 000000000..25ede0f6e --- /dev/null +++ b/src/renderer/events/event-emitter.ts @@ -0,0 +1,63 @@ +import { ErrorHandler, EventCallback, TypedEventEmitter } from './types'; + +import { EventMap } from '/@/renderer/events/events'; + +class TypedEventEmitterImpl implements TypedEventEmitter { + private errorHandler: ErrorHandler | null = null; + private events: Map = new Map(); + + emit(event: K, payload: EventMap[K]): void { + const callbacks = this.events.get(String(event)); + if (callbacks) { + callbacks.forEach((callback) => { + try { + callback(payload); + } catch (error) { + this.handleError(error as Error, String(event), payload); + } + }); + } + } + + off(event: K, callback: EventCallback): void { + const callbacks = this.events.get(String(event)); + if (callbacks) { + const index = callbacks.indexOf(callback); + if (index > -1) { + callbacks.splice(index, 1); + } + } + } + + on(event: K, callback: EventCallback): void { + const eventKey = String(event); + if (!this.events.has(eventKey)) { + this.events.set(eventKey, []); + } + this.events.get(eventKey)!.push(callback); + } + + removeAllListeners(event?: K): void { + if (event) { + // Remove specific event listeners + this.events.delete(String(event)); + } else { + // Remove all listeners + this.events.clear(); + } + } + + setErrorHandler(handler: ErrorHandler): void { + this.errorHandler = handler; + } + + private handleError(error: Error, event: string, payload: any): void { + if (this.errorHandler) { + this.errorHandler(error, event, payload); + } else { + console.error(`Event emitter error for event "${event}":`, error, payload); + } + } +} + +export const eventEmitter = new TypedEventEmitterImpl(); diff --git a/src/renderer/events/events.ts b/src/renderer/events/events.ts new file mode 100644 index 000000000..9e093ca5c --- /dev/null +++ b/src/renderer/events/events.ts @@ -0,0 +1,30 @@ +import { LibraryItem } from '/@/shared/types/domain-types'; + +export type EventMap = { + ITEM_LIST_REFRESH: ItemListRefreshEventPayload; + ITEM_LIST_UPDATE_ITEM: ItemListUpdateItemEventPayload; + USER_FAVORITE: UserFavoriteEventPayload; + USER_RATING: UserRatingEventPayload; +}; + +export type ItemListRefreshEventPayload = { + key: string; +}; + +export type ItemListUpdateItemEventPayload = { + index: number; + item: unknown; + key: string; +}; + +export type UserFavoriteEventPayload = { + favorite: boolean; + id: string[]; + itemType: LibraryItem; +}; + +export type UserRatingEventPayload = { + id: string[]; + itemType: LibraryItem; + rating: null | number; +}; diff --git a/src/renderer/events/types.ts b/src/renderer/events/types.ts new file mode 100644 index 000000000..bb128b534 --- /dev/null +++ b/src/renderer/events/types.ts @@ -0,0 +1,15 @@ +export type ErrorHandler = (error: Error, event: string, payload: any) => void; + +export type EventCallback = (payload: T) => void; + +export interface TypedEventEmitter> { + emit(event: K, payload: T[K]): void; + + off(event: K, callback: EventCallback): void; + + on(event: K, callback: EventCallback): void; + + removeAllListeners(event?: K): void; + + setErrorHandler(handler: ErrorHandler): void; +}