Skip to content

Commit

Permalink
refactor to remove extend event target
Browse files Browse the repository at this point in the history
  • Loading branch information
satetsu888 committed Jun 15, 2024
1 parent 6f994b1 commit eb36802
Show file tree
Hide file tree
Showing 2 changed files with 190 additions and 171 deletions.
358 changes: 187 additions & 171 deletions src/lib/veadotube/client.ts
Original file line number Diff line number Diff line change
@@ -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<EventListener>;
} = {
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<string>) => {
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<string>) => {
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;
};
}
3 changes: 3 additions & 0 deletions src/lib/veadotube/types.ts
Original file line number Diff line number Diff line change
@@ -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"
Expand Down

0 comments on commit eb36802

Please sign in to comment.