import { Socket } from "socket.io"; import Room, { SerializedRoom } from "./Room"; import { getTimelineNames } from "./timeline"; export interface Config { availableTimelines: string[]; } export default class User { socket: Socket; id: string; room: Room | null = null; readyToParticipate = false; constructor(socket: Socket) { this.socket = socket; this.id = socket.id; } serialize() { return { id: this.id, readyToParticipate: this.readyToParticipate, }; } async setRoom(room: Room | null) { if (this.room === room) return; if (this.room !== null) { await this.socket.leave(this.room.id.toString()); this.readyToParticipate = false; } this.room = room; if (this.room !== null) { await this.socket.join(this.room.id.toString()); } this.sync(); } getConfig() { return { availableTimelines: getTimelineNames(), }; } sentConfig: Config | null = null; sentRoom: SerializedRoom | null = null; sentTimelineName: string | null = null; sync() { // Config const config = this.getConfig(); if (!this.syncEquals(this.sentConfig, config)) { this.sentConfig = Object.assign({}, config); this.emit("config", { config: this.sentConfig, }); } // Room if (!this.syncEquals(this.sentRoom, this.room?.serialize(this))) { this.sentRoom = this.room ? (JSON.parse( JSON.stringify(this.room.serialize(this)) ) as SerializedRoom) : null; this.emit("room", { room: this.sentRoom, }); } // Timeline if (!this.syncEquals(this.sentTimelineName, this.room?.timelineName)) { this.sentTimelineName = this.room?.timelineName || null; this.emit("timeline", { timeline: this.sentTimelineName == null ? null : this.room?.serializeTimeline(), }); } } emit(eventName: string, obj: unknown) { this.socket.emit(eventName, obj); } syncEquals(obj1: unknown, obj2: unknown): boolean { if (typeof obj1 !== typeof obj2) { return false; } if (typeof obj1 !== "object") { // Both are not 'object' return Object.is(obj1, obj2); } if (obj1 === null && obj2 === null) { return true; } if (obj1 === null || obj2 === null) { return false; } if (typeof obj2 !== "object") { // This can not happen ;) throw new TypeError("Obj2 is not object while obj1 is."); } if (Object.keys(obj1).length !== Object.keys(obj2).length) { return false; } return Object.keys(obj1).every((key: string) => { if (!(key in obj1) || !(key in obj2)) { return false; } return this.syncEquals( obj1[key as keyof object], obj2[key as keyof object] ); }); } }