-
-
diff --git a/src/app/messages/messages.component.scss b/src/app/messages/messages.component.scss
index 307aeef..9b39263 100644
--- a/src/app/messages/messages.component.scss
+++ b/src/app/messages/messages.component.scss
@@ -3,18 +3,8 @@
@import "../../assets/scss/variables.scss";
-.tfw-messages-main {
- .tfw-grid-messages-header {
- display: grid;
- grid-template-columns: 1fr 1fr;
- margin-bottom: $small;
-
- span {
- color: $tao-blue-500;
- font-weight: 500;
- font-size: $font-size-h3;
- }
- }
+.tfw-next-button {
+ text-align: center;
}
.tfw-grid-message {
@@ -28,6 +18,51 @@
padding: $tiny;
font-size: $font-size-base;
margin-bottom: $hair;
+
+ animation-name: inflate;
+ animation-duration: 0.5s;
+ animation-timing-function: cubic-bezier(0.01, 0.1, 0, 1);
+}
+
+.highlighted-message {
+ box-shadow: 0px 0px 3px 0px rgba($tao-gray-300,0.65);
+}
+
+@keyframes inflate {
+ 0% { transform: scale(0,0); }
+ 100% { transform: scale(1,1); }
+}
+
+.jumping-circle-container {
+ display: flex;
+ padding-top: 1.3em;
+ padding-bottom: 1em;
+}
+
+.jumping-circle {
+ width: 0.35em;
+ height: 0.35em;
+ border-radius: 50%;
+ background-color: gray;
+ margin-top: 0.3em;
+ margin-left: 0.3em;
+
+ animation-name: float;
+ animation-duration: 1.7s;
+ animation-timing-function: ease-in-out;
+ animation-iteration-count: infinite;
+}
+
+#jc2 { animation-delay: 0.2s; }
+#jc3 { animation-delay: 0.45s; }
+
+@keyframes float {
+ 0% { transform: translateY(0em); }
+ 30% { transform: translateY(-0.5em); }
+ 60% { transform: translateY(0.2em); }
+ 80% { transform: translateY(0em); }
+ 90% { transform: translateY(-0.5em); }
+ 100% { transform: translateY(0em); }
}
.tfw-grid-message-header {
diff --git a/src/app/messages/messages.component.ts b/src/app/messages/messages.component.ts
index 3a8105e..53a42eb 100644
--- a/src/app/messages/messages.component.ts
+++ b/src/app/messages/messages.component.ts
@@ -1,7 +1,7 @@
// Copyright (C) 2018 Avatao.com Innovative Learning Kft.
// All Rights Reserved. See LICENSE file for details.
-import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
+import { ChangeDetectorRef, Component, OnInit, EventEmitter, Output } from '@angular/core';
import { MarkdownService } from '../services/markdown.service';
import { WebSocketService } from '../services/websocket.service';
@@ -9,6 +9,8 @@ import { MessagesMessage } from '../message-types/messages-message';
import { MessagesControlCommand } from '../message-types/messages-control-command';
import { config } from '../config';
import { CommandMessage } from '../message-types/command-message';
+import { MessageQueueMessage } from '../message-types/message-queue-message';
+import { Subject, Observer, BehaviorSubject } from 'rxjs';
@Component({
selector: 'app-messages',
@@ -16,7 +18,12 @@ import { CommandMessage } from '../message-types/command-message';
styleUrls: ['./messages.component.scss']
})
export class MessagesComponent implements OnInit {
+ @Output() newMessageEvent: EventEmitter
= new EventEmitter();
+ newMessage: Subject = new Subject();
+ messageInQueue = true;
+
messages: MessagesMessage[] = [];
+ messageQueueAttender: MessageQueueAttender;
showNextButton: boolean = config.messages.showNextButton;
command_handlers = {
@@ -27,23 +34,50 @@ export class MessagesComponent implements OnInit {
private markdownService: MarkdownService,
private websocketService: WebSocketService,
private changeDetectorRef: ChangeDetectorRef
- ) {}
-
- ngOnInit() {
- this.websocketService.connect();
- this.websocketService.observeKey('message').subscribe(
- (event) => {
- this.messages.push(event.data);
- event.data.message = this.convert(event.data.message);
- this.changeDetectorRef.detectChanges();
- });
- this.websocketService.observeKey('messagecontrol').subscribe(
- (event) => {
- this.command_handlers[event.data.command](event.data);
- });
+ ) {
+ this.messageQueueAttender = new MessageQueueAttender(this.newMessage);
}
- convert(text: string) {
+ 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').subscribe(
+ (event) => this.newMessage.next(event.data)
+ );
+ this.websocketService.observeKey('queueMessages').subscribe(
+ (event) => this.handleQueueMessage(event.data)
+ );
+ this.websocketService.observeKey('messagecontrol').subscribe(
+ (event) => this.command_handlers[event.data.command](event.data)
+ );
+ }
+
+ writeMessage(message: MessagesMessage) {
+ this.transformMessage(message);
+ this.messages.push(message);
+ this.changeDetectorRef.detectChanges();
+ }
+
+ transformMessage(message: MessagesMessage) {
+ message.message = this.convertMarkdownToHTML(message.message);
+ if (!message.timestamp) {
+ message.timestamp = new Date();
+ }
+ }
+
+ handleQueueMessage(data: MessageQueueMessage) {
+ this.messageQueueAttender.queueMessages(data.messages);
+ }
+
+ convertMarkdownToHTML(text: string) {
return this.markdownService.convertToHtml(text);
}
@@ -55,3 +89,48 @@ export class MessagesComponent implements OnInit {
this.websocketService.sendJSON({key: '', trigger: 'step_next'});
}
}
+
+
+class MessageQueueAttender {
+ public messageInQueue = new BehaviorSubject(false);
+
+ private readonly charPerSecond: number;
+ private lastMessageLength = 0;
+ private queue: MessagesMessage[] = [];
+
+ 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
+ );
+ }
+ }
+}