import { ChangeDetectorRef, Component, OnInit, EventEmitter, Output } from '@angular/core'; import { MessageConfig, MessageData, Message, MessageQueue } from '../message-types/bot-messages'; import { MarkdownService } from '../services/markdown.service'; import { WebSocketService } from '../services/websocket.service'; import { config } from '../config'; import { Subject, Observer, BehaviorSubject } from 'rxjs'; @Component({ selector: 'app-messages', templateUrl: './messages.component.html', styleUrls: ['./messages.component.scss'] }) export class MessagesComponent implements OnInit { @Output() newMessageEvent: EventEmitter = new EventEmitter(); newMessage: Subject = new Subject(); messageInQueue = true; originator = 'avataobot'; messages: MessageData[] = []; messageQueueAttender: MessageQueueAttender; command_handlers = { 'message.queue': this.handleQueueMessage.bind(this), 'message.config': this.configureMessages.bind(this), 'message.send': () => {} }; constructor( private markdownService: MarkdownService, private websocketService: WebSocketService, private changeDetectorRef: ChangeDetectorRef ) { this.messageQueueAttender = new MessageQueueAttender(this.newMessage); } ngOnInit() { this.newMessage.subscribe( message => { this.writeMessage(message); this.newMessageEvent.emit(); }); this.messageQueueAttender.messageInQueue.subscribe( (value) => this.messageInQueue = value ); this.websocketService.connect(); this.websocketService.observeKey('message.send').subscribe( message => this.newMessage.next(message) ); this.websocketService.observeKey('message').subscribe( message => this.command_handlers[message.key](message) ); } writeMessage(message: MessageData) { this.transformMessage(message); this.messages.push(message); this.changeDetectorRef.detectChanges(); } transformMessage(message: MessageData) { message.message = this.convertMarkdownToHTML(message.message); if (!message.originator) { message.originator = this.originator; } if (!message.timestamp) { message.timestamp = new Date(); } } handleQueueMessage(message: MessageQueue) { this.messageQueueAttender.queueMessages(message.value); } configureMessages(message: MessageConfig) { if (message.originator) { this.originator = message.originator; } } convertMarkdownToHTML(text: string) { return this.markdownService.convertToHtml(text); } } class MessageQueueAttender { public messageInQueue = new BehaviorSubject(false); private readonly charPerSecond: number; private lastMessageLength = 0; private queue: MessageData[] = []; constructor(private messageEmitter: Observer, wordPerMinute: number = config.messages.messageQueueWPM) { const charPerMinute = wordPerMinute * 5; this.charPerSecond = charPerMinute / 60; } queueMessages(messages: Array) { this.queue = this.queue.concat(messages); this.attendQueue(); } private processMessage() { if (this.queue.length > 0) { const lastMessage = this.queue.shift(); this.lastMessageLength = lastMessage.message.length; this.messageEmitter.next(lastMessage); } if (this.queue.length === 0) { this.lastMessageLength = 0; this.messageInQueue.next(false); } } private attendQueue() { if (this.queue.length > 0) { this.messageInQueue.next(true); const timeoutSeconds = this.lastMessageLength / this.charPerSecond; setTimeout( () => { this.processMessage(); this.attendQueue(); }, timeoutSeconds * 1000 ); } } }