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

210 lines
6.9 KiB
TypeScript

// Copyright (C) 2018 Avatao.com Innovative Learning Kft.
// All Rights Reserved. See LICENSE file for details.
import { Component, OnDestroy, OnInit, ChangeDetectorRef, ElementRef, ViewChild } from '@angular/core';
import { DeploymentNotificationService } from '../services/deployment-notification.service';
import { Subscription } from 'rxjs';
import { WebSocketService } from '../services/websocket.service';
import { WebSocketMessage } from '../message-types/websocket-message';
import { HideMessagesMessage, LayoutMessage, TerminalMenuItemMessage } from '../message-types/dashboard-messages';
import { config } from '../config';
import { ProcessLogService } from '../services/processlog.service';
import { LogMessage } from '../message-types/log-message';
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;
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;
showUrlBar = config.dashboard.showUrlBar;
actualIframeUrl: string = this.iframeUrl;
selectedTerminalMenuItem: string = config.dashboard.terminalOrConsole;
iframeReloadSubscription: Subscription;
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)
};
constructor(private deploymentNotificationService: DeploymentNotificationService,
private webSocketService: WebSocketService,
private changeDetectorRef: ChangeDetectorRef,
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 => {
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) {
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'}));
}
}
layoutHandler(message: LayoutMessage) {
if (config.dashboard.enabledLayouts.includes(message.value)) {
this.setLayout(message.value);
} else {
console.log('Invalid ide layout "' + message.value + '" received!');
}
}
hideMessagesHandler(message: HideMessagesMessage) {
this.hideMessages = message.value;
}
terminalMenuItemHandler(message: TerminalMenuItemMessage) {
this.selectTerminalMenuItem(message.value);
}
reloadFrontendHandlder(message: WebSocketMessage) {
setTimeout(() => window.location.reload(), 2000);
}
reloadIframeHandler(message: WebSocketMessage) {
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);
}
reloadIframe() {
setTimeout(() => {
this.webiframe.nativeElement.contentWindow.location.reload(true);
});
}
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;
}
setConsoleContentIfNoLiveLogs(logs: LogMessage) {
this.processLogService.emitNewLogsIfNoLiveLogs(logs);
if (config.ide.showConsoleOnDeploy) {
this.selectTerminalMenuItem('console');
}
}
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;
}
iframeLoad(): void {
if (this.webiframe) {
const href = this.webiframe.nativeElement.contentWindow.frames.location.href;
const niceURL = href.match(/.*?\/\/.*?(\/.*)/)[1];
this.actualIframeUrl = niceURL;
}
}
changeIframeURL() {
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;
}
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();
}
}
}