From eb36802a2932e6c09218d523acbbcbc727e7edd2 Mon Sep 17 00:00:00 2001 From: satetsu888 Date: Sat, 15 Jun 2024 17:36:16 +0900 Subject: [PATCH] refactor to remove extend event target --- src/lib/veadotube/client.ts | 358 +++++++++++++++++++----------------- src/lib/veadotube/types.ts | 3 + 2 files changed, 190 insertions(+), 171 deletions(-) diff --git a/src/lib/veadotube/client.ts b/src/lib/veadotube/client.ts index 7ddf3ef..b729546 100644 --- a/src/lib/veadotube/client.ts +++ b/src/lib/veadotube/client.ts @@ -1,195 +1,211 @@ import { messages } from "./message"; -import { ServerConfig, State, WebsocketConnectingError, WebsocketError, WebsocketTimeoutError } from "./types"; +import { + Event, + EventListener, + ServerConfig, + State, + WebsocketConnectingError, + WebsocketError, + WebsocketTimeoutError, +} from "./types"; import { parseReceivedPayload } from "./util"; -class WebsocketStatusChangedEvent extends Event { - constructor() { - super("websocketStatusChanged"); - } -} +export class VeadotubeClient { + private config: ServerConfig; + public websocket: WebSocket | null; + private veadotubeStates: State[]; + private currentStateId: string | null; + private timeoutTimerId: number | null; -class StatesChangedEvent extends Event { - constructor() { - super("statesChanged"); - } -} + private subscribers: { + [key in Event]: Set; + } = { + websocketStatusChanged: new Set(), + statesChanged: new Set(), + stateChanged: new Set(), + }; -class StateChangedEvent extends Event { - constructor() { - super("stateChanged"); - } -} + constructor(config: ServerConfig) { + this.config = config; + this.websocket = null; + this.veadotubeStates = []; + this.currentStateId = null; -export class VeadotubeClient extends EventTarget { - private config: ServerConfig; - public websocket: WebSocket | null; - private veadotubeStates: State[]; - private currentStateId: string | null; - private timeoutTimerId: number | null; + this.timeoutTimerId = null; + } - constructor(config: ServerConfig) { - super(); - this.config = config; + addEventListener = (type: Event, listener: EventListener) => { + this.subscribers[type].add(listener); + }; - this.websocket = null; - this.veadotubeStates = []; - this.currentStateId = null; + removeEventListener = (type: Event, listener: EventListener) => { + this.subscribers[type].delete(listener); + }; - this.timeoutTimerId = null; + dispatchEvent(event: Event): void { + for (const listener of this.subscribers[event]) { + listener(); } + } - reset = () => { - this.stopTimeoutTimer(); - - this.websocket = null; - this.veadotubeStates = []; - this.currentStateId = null; - - this.dispatchEvent(new WebsocketStatusChangedEvent()); - this.dispatchEvent(new StatesChangedEvent()); - this.dispatchEvent(new StateChangedEvent()); - } + reset = () => { + this.stopTimeoutTimer(); - startTimeoutTimer = () => { - if (this.timeoutTimerId) { - return - } + this.websocket = null; + this.veadotubeStates = []; + this.currentStateId = null; - this.timeoutTimerId = window.setTimeout(() => { - if(this.config.onErrorCallback) { - this.config.onErrorCallback(WebsocketTimeoutError); - } else { - console.error("timeout occurred"); - } - }, 3000); - } + this.dispatchEvent("websocketStatusChanged"); + this.dispatchEvent("statesChanged"); + this.dispatchEvent("stateChanged"); + }; - stopTimeoutTimer = () => { - if (this.timeoutTimerId) { - clearTimeout(this.timeoutTimerId); - this.timeoutTimerId = null; - } + startTimeoutTimer = () => { + if (this.timeoutTimerId) { + return; } - onMessage = (event: MessageEvent) => { - this.stopTimeoutTimer() - const payloadEventResult = parseReceivedPayload(event); + this.timeoutTimerId = window.setTimeout(() => { + if (this.config.onErrorCallback) { + this.config.onErrorCallback(WebsocketTimeoutError); + } else { + console.error("timeout occurred"); + } + }, 3000); + }; - if (!payloadEventResult) { - return; - } - - switch (payloadEventResult.event) { - case "list": - this.veadotubeStates = payloadEventResult.states; - for (const state of this.veadotubeStates) { - this.callStateThumb(state.id); - } - this.dispatchEvent(new StatesChangedEvent()); - break; - case "peek": - // NOTE: do something with peek event ? - break; - case "thumb": - this.veadotubeStates = this.veadotubeStates.map((state) => { - if (state.id === payloadEventResult.stateId) { - return { - ...state, - image: { - width: payloadEventResult.width, - height: payloadEventResult.height, - data: payloadEventResult.png - } - } - } - return state; - }) - this.dispatchEvent(new StatesChangedEvent()); - break; - } + stopTimeoutTimer = () => { + if (this.timeoutTimerId) { + clearTimeout(this.timeoutTimerId); + this.timeoutTimerId = null; } + }; - open = (initializeWithListStates:boolean = true) => { - try { - this.innerOpen(initializeWithListStates); - } catch (e) { - this.reset(); - - if (this.config.onErrorCallback) { - this.config.onErrorCallback(WebsocketConnectingError); - } else { - console.error("failed to open websocket connection", e); - } - } - } - - innerOpen = (initializeWithListStates:boolean) => { - this.startTimeoutTimer(); - const ws = new WebSocket(`ws://${this.config.host}:${this.config.port}?n=${this.config.clientName}`); - console.debug("opening websocket connection to", this.config.host, this.config.port); - - ws.addEventListener("message", this.onMessage) - - ws.addEventListener("open", () => { - this.stopTimeoutTimer(); - if (initializeWithListStates) { - this.listStates(); - } - this.dispatchEvent(new WebsocketStatusChangedEvent()); - }) - - ws.addEventListener("close", (e) => { - if (this.config.onCloseCallback) { - this.config.onCloseCallback(e); - } - - this.reset(); - this.dispatchEvent(new WebsocketStatusChangedEvent()); - }) - - ws.addEventListener("error", (e) => { - this.reset(); - - if (this.config.onErrorCallback) { - this.config.onErrorCallback(WebsocketError); - } else { - console.error("websocket error", e); - } - }) - - this.websocket = ws; - this.dispatchEvent(new WebsocketStatusChangedEvent()); - } - - close = () => { - this.websocket?.close(); - } - - sendRaw = (message: string) => { - this.startTimeoutTimer(); - this.websocket?.send(message); - } + onMessage = (event: MessageEvent) => { + this.stopTimeoutTimer(); + const payloadEventResult = parseReceivedPayload(event); - setState = (stateId: string) => { - this.currentStateId = stateId; - this.dispatchEvent(new StateChangedEvent()); - this.sendRaw(messages.SET_STATE(stateId)); + if (!payloadEventResult) { + return; } - listStates = () => { - this.sendRaw(messages.LIST_STATES()); - } - - callStateThumb = (stateId: string) => { - this.sendRaw(messages.CALL_STATE_THUMB({ id: stateId })); - } - - getStates = () => { - return this.veadotubeStates; - } - - getCurrentStateId = () => { - return this.currentStateId; - } + switch (payloadEventResult.event) { + case "list": + this.veadotubeStates = payloadEventResult.states; + for (const state of this.veadotubeStates) { + this.callStateThumb(state.id); + } + this.dispatchEvent("statesChanged"); + break; + case "peek": + // NOTE: do something with peek event ? + break; + case "thumb": + this.veadotubeStates = this.veadotubeStates.map((state) => { + if (state.id === payloadEventResult.stateId) { + return { + ...state, + image: { + width: payloadEventResult.width, + height: payloadEventResult.height, + data: payloadEventResult.png, + }, + }; + } + return state; + }); + this.dispatchEvent("statesChanged"); + break; + } + }; + + open = (initializeWithListStates: boolean = true) => { + try { + this.innerOpen(initializeWithListStates); + } catch (e) { + this.reset(); + + if (this.config.onErrorCallback) { + this.config.onErrorCallback(WebsocketConnectingError); + } else { + console.error("failed to open websocket connection", e); + } + } + }; + + innerOpen = (initializeWithListStates: boolean) => { + this.startTimeoutTimer(); + const ws = new WebSocket( + `ws://${this.config.host}:${this.config.port}?n=${this.config.clientName}` + ); + console.debug( + "opening websocket connection to", + this.config.host, + this.config.port + ); + + ws.addEventListener("message", this.onMessage); + + ws.addEventListener("open", () => { + this.stopTimeoutTimer(); + if (initializeWithListStates) { + this.listStates(); + } + this.dispatchEvent("websocketStatusChanged"); + }); + + ws.addEventListener("close", (e) => { + if (this.config.onCloseCallback) { + this.config.onCloseCallback(e); + } + + this.reset(); + this.dispatchEvent("websocketStatusChanged"); + }); + + ws.addEventListener("error", (e) => { + this.reset(); + + if (this.config.onErrorCallback) { + this.config.onErrorCallback(WebsocketError); + } else { + console.error("websocket error", e); + } + }); + + this.websocket = ws; + this.dispatchEvent("websocketStatusChanged"); + }; + + close = () => { + this.websocket?.close(); + }; + + sendRaw = (message: string) => { + this.startTimeoutTimer(); + this.websocket?.send(message); + }; + + setState = (stateId: string) => { + this.currentStateId = stateId; + this.dispatchEvent("stateChanged"); + this.sendRaw(messages.SET_STATE(stateId)); + }; + + listStates = () => { + this.sendRaw(messages.LIST_STATES()); + }; + + callStateThumb = (stateId: string) => { + this.sendRaw(messages.CALL_STATE_THUMB({ id: stateId })); + }; + + getStates = () => { + return this.veadotubeStates; + }; + + getCurrentStateId = () => { + return this.currentStateId; + }; } diff --git a/src/lib/veadotube/types.ts b/src/lib/veadotube/types.ts index 0d3e954..acf5bb0 100644 --- a/src/lib/veadotube/types.ts +++ b/src/lib/veadotube/types.ts @@ -1,3 +1,6 @@ +export type Event = 'websocketStatusChanged' | 'statesChanged' | 'stateChanged'; +export type EventListener = () => void; + export type WebsocketConnectingErrorType = "WebsocketConnectingError" export type WebsocketErrorType = "WebsocketError" export type WebsocketTimeoutErrorType = "WebsocketTimeoutError"