frontend-tutorial-framework/src/app/dashboard/dashboard.component.ts

210 lines
6.9 KiB
TypeScript
Raw Normal View History

2018-05-11 16:00:26 +00:00
// Copyright (C) 2018 Avatao.com Innovative Learning Kft.
// All Rights Reserved. See LICENSE file for details.
2018-05-17 13:58:08 +00:00
import { Component, OnDestroy, OnInit, ChangeDetectorRef, ElementRef, ViewChild } from '@angular/core';
2018-03-14 15:50:37 +00:00
import { DeploymentNotificationService } from '../services/deployment-notification.service';
2018-05-18 12:53:45 +00:00
import { Subscription } from 'rxjs';
import { WebSocketService } from '../services/websocket.service';
import { WebSocketMessage } from '../message-types/websocket-message';
2019-08-08 12:44:40 +00:00
import { HideMessagesMessage, LayoutMessage, TerminalMenuItemMessage } from '../message-types/dashboard-messages';
2018-03-21 15:32:56 +00:00
import { config } from '../config';
import { ProcessLogService } from '../services/processlog.service';
import { LogMessage } from '../message-types/log-message';
2019-05-13 11:57:08 +00:00
import { HttpClient } from '@angular/common/http';
import { delay, retryWhen, tap } from 'rxjs/operators';
@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.scss']
})
export class DashboardComponent implements OnInit, OnDestroy {
deploying = false;
2019-01-22 12:57:43 +00:00
polling = false;
deploymentNotificationSubscription: Subscription;
@ViewChild('webiframe', {static: true}) webiframe: ElementRef;
@ViewChild('tfwmessages', {static: true}) messages: ElementRef;
@ViewChild('urlbar', {static: true}) urlbar: ElementRef;
layout: string = config.dashboard.currentLayout;
hideMessages: boolean = config.dashboard.hideMessages;
iframeUrl: string = config.dashboard.iframeUrl;
2019-06-25 09:12:18 +00:00
showUrlBar = config.dashboard.showUrlBar;
2019-01-21 16:24:02 +00:00
actualIframeUrl: string = this.iframeUrl;
selectedTerminalMenuItem: string = config.dashboard.terminalOrConsole;
2019-01-22 12:57:43 +00:00
iframeReloadSubscription: Subscription;
2018-05-25 11:33:37 +00:00
2018-06-19 12:26:12 +00:00
command_handlers = {
'dashboard.layout': this.layoutHandler.bind(this),
'dashboard.hideMessages': this.hideMessagesHandler.bind(this),
'dashboard.terminalMenuItem': this.terminalMenuItemHandler.bind(this),
'dashboard.reloadFrontend': this.reloadFrontendHandlder.bind(this),
'dashboard.reloadIframe': this.reloadIframeHandler.bind(this)
2018-06-19 12:26:12 +00:00
};
constructor(private deploymentNotificationService: DeploymentNotificationService,
private webSocketService: WebSocketService,
private changeDetectorRef: ChangeDetectorRef,
2019-01-22 12:57:43 +00:00
private processLogService: ProcessLogService,
private http: HttpClient) {}
ngOnInit() {
this.webSocketService.connect();
this.initCommandHandling();
this.initDeploymentNotifications();
this.recoverIfNeeded();
this.triggerFirstFSMStepIfNeeded();
}
initCommandHandling() {
this.webSocketService.observeKey<WebSocketMessage>('dashboard').subscribe(message => {
2019-08-08 12:44:40 +00:00
this.command_handlers[message.key](message);
this.changeDetectorRef.detectChanges();
});
}
initDeploymentNotifications() {
this.deploymentNotificationSubscription = this.deploymentNotificationService.deploying.subscribe(
(deploying) => {
this.deploying = deploying;
if (!deploying && config.ide.reloadIframeOnDeploy) {
2019-01-22 12:57:43 +00:00
if (this.polling) {
this.iframeReloadSubscription.unsubscribe();
}
this.pollingServerForIframeReload();
}
});
}
triggerFirstFSMStepIfNeeded() {
if (config.dashboard.triggerFirstFSMStep) {
setTimeout(() => {
this.webSocketService.sendJSON({
'key': 'fsm.step',
'trigger': config.dashboard.triggerFirstFSMStep
});
});
}
}
recoverIfNeeded() {
if (config.dashboard.recoverAfterPageReload) {
setTimeout(() => this.webSocketService.sendJSON({'key': 'recover'}));
}
}
2019-08-08 12:44:40 +00:00
layoutHandler(message: LayoutMessage) {
if (config.dashboard.enabledLayouts.includes(message.value)) {
this.setLayout(message.value);
} else {
2019-08-08 12:44:40 +00:00
console.log('Invalid ide layout "' + message.value + '" received!');
}
}
2019-08-08 12:44:40 +00:00
hideMessagesHandler(message: HideMessagesMessage) {
this.hideMessages = message.value;
}
2019-08-08 12:44:40 +00:00
terminalMenuItemHandler(message: TerminalMenuItemMessage) {
this.selectTerminalMenuItem(message.value);
}
2019-08-08 12:44:40 +00:00
reloadFrontendHandlder(message: WebSocketMessage) {
setTimeout(() => window.location.reload(), 2000);
}
2019-08-08 12:44:40 +00:00
reloadIframeHandler(message: WebSocketMessage) {
2019-02-24 00:11:54 +00:00
setTimeout(() => this.reloadIframeNoSubmit(), 200);
}
setLayout(layout: string) {
this.layout = layout;
// We need to trigger a 'resize' event manually, otherwise editor stays collapsed
// editor 'resize' event listener requires a parameter of force=true
setTimeout(() => window.dispatchEvent(new Event('resize', {force: true} as any)), 0);
}
2018-05-17 13:58:08 +00:00
reloadIframe() {
setTimeout(() => {
this.webiframe.nativeElement.contentWindow.location.reload(true);
});
}
2019-02-24 00:11:54 +00:00
reloadIframeNoSubmit() {
// Sometimes it is needed to reload the iframe without resending the previous form data
setTimeout(() => {
this.webiframe.nativeElement.contentWindow.location = this.webiframe.nativeElement.contentWindow.location.href;
})
}
selectTerminalMenuItem(item: string) {
if (!item.match('(terminal|console)')) {
return;
}
this.selectedTerminalMenuItem = item;
}
2018-05-30 12:19:19 +00:00
setConsoleContentIfNoLiveLogs(logs: LogMessage) {
this.processLogService.emitNewLogsIfNoLiveLogs(logs);
if (config.ide.showConsoleOnDeploy) {
this.selectTerminalMenuItem('console');
}
}
2018-06-26 12:13:29 +00:00
scrollMessagesToBottom(): void {
const element = this.messages.nativeElement;
// This must be done in the Angular event loop to avoid messing up
// change detection (not in the template like ConsoleComponent does)
element.scrollTop = element.scrollHeight;
}
2019-01-21 15:49:44 +00:00
iframeLoad(): void {
2019-01-21 16:24:02 +00:00
if (this.webiframe) {
2019-07-10 07:21:23 +00:00
const href = this.webiframe.nativeElement.contentWindow.frames.location.href;
const niceURL = href.match(/.*?\/\/.*?(\/.*)/)[1];
this.actualIframeUrl = niceURL;
2019-01-21 16:24:02 +00:00
}
2019-01-21 15:49:44 +00:00
}
2019-01-21 16:00:13 +00:00
changeIframeURL() {
2019-07-10 07:21:23 +00:00
const userGivenValue = this.urlbar.nativeElement.value.trim();
if(
userGivenValue == '/' ||
userGivenValue.startsWith('dashboard') ||
userGivenValue.startsWith('/dashboard')
)
return;
this.webiframe.nativeElement.contentWindow.frames.location.href = this.urlbar.nativeElement.value;
2019-01-21 16:00:13 +00:00
}
2019-01-22 12:57:43 +00:00
pollingServerForIframeReload() {
this.polling = true;
this.iframeReloadSubscription = this.http.get(this.actualIframeUrl, {observe: 'response'}).pipe(
retryWhen(errors =>
errors.pipe(
tap(
response => {
if (response.status === 200) {
this.iframeReloadSubscription.unsubscribe();
this.polling = false;
this.reloadIframe();
}
}
),
delay(1000)
)
)
).subscribe();
}
ngOnDestroy() {
if (this.deploymentNotificationSubscription) {
this.deploymentNotificationSubscription.unsubscribe();
}
if (this.iframeReloadSubscription) {
this.iframeReloadSubscription.unsubscribe();
}
}
}