mirror of
https://github.com/morpheus65535/bazarr.git
synced 2025-04-24 22:57:13 -04:00
173 lines
4.4 KiB
TypeScript
173 lines
4.4 KiB
TypeScript
import { debounce, forIn, remove, uniq } from "lodash";
|
|
import { onlineManager } from "react-query";
|
|
import { Socket, io } from "socket.io-client";
|
|
import { Environment, isDevEnv, isTestEnv } from "../../utilities";
|
|
import { ENSURE, GROUP, LOG } from "../../utilities/console";
|
|
import { createDefaultReducer } from "./reducer";
|
|
|
|
class SocketIOClient {
|
|
private socket: Socket;
|
|
private events: SocketIO.Event[];
|
|
private debounceReduce: () => void;
|
|
|
|
private reducers: SocketIO.Reducer[];
|
|
|
|
constructor() {
|
|
this.socket = io({
|
|
path: `${Environment.baseUrl}/api/socket.io`,
|
|
transports: ["polling", "websocket"],
|
|
upgrade: true,
|
|
rememberUpgrade: true,
|
|
autoConnect: false,
|
|
});
|
|
|
|
this.socket.on("connect", this.onConnect.bind(this));
|
|
this.socket.on("disconnect", this.onDisconnect.bind(this));
|
|
this.socket.on("connect_error", this.onConnectError.bind(this));
|
|
this.socket.on("data", this.onEvent.bind(this));
|
|
|
|
this.events = [];
|
|
this.debounceReduce = debounce(this.reduce, 20);
|
|
this.reducers = [];
|
|
|
|
onlineManager.setOnline(false);
|
|
|
|
if (isDevEnv) {
|
|
window.socketIO = {
|
|
dump: () => {
|
|
GROUP("Socket.IO Reducers", (logger) => {
|
|
this.reducers.forEach((reducer) => {
|
|
logger(reducer.key);
|
|
});
|
|
});
|
|
},
|
|
emit: (e) => {
|
|
if (e) {
|
|
this.onEvent(e);
|
|
}
|
|
},
|
|
};
|
|
}
|
|
}
|
|
|
|
initialize() {
|
|
if (isTestEnv) {
|
|
return;
|
|
}
|
|
|
|
LOG("info", "Initializing Socket.IO client...");
|
|
this.reducers.push(...createDefaultReducer());
|
|
|
|
window.addEventListener("app-auth-changed", (ev) => {
|
|
const authenticated = ev.detail.authenticated;
|
|
LOG("info", "Authentication status change to", authenticated);
|
|
if (authenticated) {
|
|
this.connect();
|
|
} else {
|
|
this.disconnect();
|
|
}
|
|
});
|
|
}
|
|
|
|
connect() {
|
|
LOG("info", "Connecting Socket.IO client...");
|
|
this.socket.connect();
|
|
}
|
|
|
|
disconnect() {
|
|
LOG("info", "Disconnecting Socket.IO client...");
|
|
this.socket.disconnect();
|
|
}
|
|
|
|
addReducer(reducer: SocketIO.Reducer) {
|
|
this.reducers.push(reducer);
|
|
}
|
|
|
|
removeReducer(reducer: SocketIO.Reducer) {
|
|
const removed = remove(this.reducers, (r) => r === reducer);
|
|
ENSURE(removed.length === 0, "Fail to remove reducer", reducer);
|
|
}
|
|
|
|
private reduce() {
|
|
const events = [...this.events];
|
|
this.events = [];
|
|
|
|
const records: SocketIO.ActionRecord = {};
|
|
|
|
events.forEach((e) => {
|
|
if (!(e.type in records)) {
|
|
records[e.type] = {};
|
|
}
|
|
|
|
const record = records[e.type];
|
|
if (record === undefined) {
|
|
return;
|
|
}
|
|
|
|
if (!(e.action in record)) {
|
|
record[e.action] = [];
|
|
}
|
|
|
|
if (e.payload) {
|
|
record[e.action]?.push(e.payload);
|
|
}
|
|
});
|
|
|
|
forIn(records, (element, type) => {
|
|
if (element) {
|
|
const handlers = this.reducers.filter((v) => v.key === type);
|
|
if (handlers.length === 0) {
|
|
LOG("warning", "Unhandled SocketIO event", type);
|
|
return;
|
|
}
|
|
|
|
handlers.forEach((handler) => {
|
|
const anyAction = handler.any;
|
|
if (anyAction) {
|
|
anyAction();
|
|
}
|
|
|
|
forIn(element, (ids, key) => {
|
|
ids = uniq(ids);
|
|
const action = handler[key as SocketIO.ActionType];
|
|
if (action) {
|
|
// FIXME: type
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
action(ids as any[]);
|
|
} else if (anyAction === undefined) {
|
|
LOG("warning", "Unhandled SocketIO event", key, type);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
private onConnect() {
|
|
LOG("info", "Socket.IO has connected");
|
|
onlineManager.setOnline(true);
|
|
this.onEvent({ type: "connect", action: "update" });
|
|
}
|
|
|
|
private onConnectError() {
|
|
LOG("warning", "Socket.IO has error connecting backend");
|
|
onlineManager.setOnline(false);
|
|
this.onEvent({ type: "connect_error", action: "update" });
|
|
}
|
|
|
|
private onDisconnect() {
|
|
LOG("warning", "Socket.IO has disconnected");
|
|
onlineManager.setOnline(false);
|
|
this.onEvent({ type: "disconnect", action: "update" });
|
|
}
|
|
|
|
private onEvent(event: SocketIO.Event) {
|
|
LOG("info", "Socket.IO receives", event);
|
|
this.events.push(event);
|
|
this.debounceReduce();
|
|
}
|
|
}
|
|
|
|
const socketIOClient = new SocketIOClient();
|
|
|
|
export default socketIOClient;
|