import User from "./User"; import { getTimeline, getTimelineNames } from "./timeline"; import { getCurrentTime } from "./util"; export interface RoomOptions { seekTime: number; timelineName: string; } export interface TickerMessage { user: User; message: string; } // FIXME: dedupe with frontend export interface SerializedRoom { id: number; userCount: number; isLeader: boolean; running: boolean; startTime?: number; seekTime: number; timelineName: string; readyToParticipate: boolean; speedFactor: number; ticker: string[]; users?: { id: string; readyToParticipate: boolean }[]; } export default class Room { id = 0; users: User[] = []; leader: User | null = null; ticker: TickerMessage[] = []; running = false; startTime: number | undefined = undefined; seekTime = 0; timelineName = "Centurion"; // For debugging purposes speedFactor = 1; constructor(name: number) { this.id = name; } serialize(user?: User) { const obj: SerializedRoom = { id: this.id, userCount: this.users.length, isLeader: this.leader === user, running: this.running, startTime: this.startTime, timelineName: this.timelineName, seekTime: this.seekTime, readyToParticipate: this.getLeader()?.readyToParticipate || false, speedFactor: this.speedFactor, ticker: this.ticker.map((i) => i.message), }; if (typeof user === "undefined" || this.leader === user) { obj["users"] = this.users.map((u) => u.serialize()); } return obj; } serializeTimeline() { return getTimeline(this.timelineName); } sync() { this.users.forEach((u) => u.sync()); } async join(user: User) { this.users.push(user); await user.setRoom(this); if (!this.hasLeader()) { this.setLeader(user); } this.sync(); } async leave(user: User) { this.removeTickerMessageForUser(user); this.users.splice(this.users.indexOf(user), 1); await user.setRoom(null); if (this.leader == user) { this.setRandomLeader(); } this.sync(); } setOptions(options: { seekTime: number; timelineName: string }) { this.seekTime = Math.max(0, Math.min(options.seekTime, 250 * 60 * 1000)); if (getTimelineNames().indexOf(options.timelineName) >= 0) { this.timelineName = options.timelineName; } this.sync(); } start() { this.running = true; this.startTime = getCurrentTime() - this.seekTime; this.sync(); } /** * * @returns {boolean} */ hasUsers() { return this.users.length !== 0; } setRandomLeader() { if (this.hasUsers()) { this.leader = this.users[0]; } } hasLeader(): boolean { return this.leader != null; } setLeader(user: User) { this.leader = user; } getLeader(): User | null { return this.leader; } submitTickerMessage(user: User, message: string) { message = message.replace("\n", ""); this.removeTickerMessageForUser(user); this.ticker.push({ user: user, message: message, }); this.sync(); } removeTickerMessageForUser(user: User) { let existing = -1; for (let i = 0; i < this.ticker.length; i++) { if (this.ticker[i].user === user) { existing = i; break; } } if (existing >= 0) { this.ticker.splice(existing, 1); } } }