From 9f5e1affa874bdd74ebec268e9557ff67de7f1fe Mon Sep 17 00:00:00 2001 From: Matteo Gheza Date: Sun, 8 Aug 2021 16:11:22 +0200 Subject: [PATCH 01/14] Electron Error Page --- electron_error_page.html | 150 +++++++++++++++++++++++++++++++++++++++ index.js | 14 +++- main.js | 54 ++++++++++++-- 3 files changed, 209 insertions(+), 9 deletions(-) create mode 100644 electron_error_page.html diff --git a/electron_error_page.html b/electron_error_page.html new file mode 100644 index 00000000..61bb4917 --- /dev/null +++ b/electron_error_page.html @@ -0,0 +1,150 @@ + + + + + Unexpected error + + + + + +
+
+

:(

+

There was an unexpected error. Please open a bug report on the project's Github page or contact one of + the developers.

+

+

+
+ Open a bug report on Github +
+
+

Stacktrace:

+

+        
+
+
+

Logs:

+

+            
+
+

Config file:

+

+            
+
+
+ + + + \ No newline at end of file diff --git a/index.js b/index.js index ac74153d..4d850a7a 100644 --- a/index.js +++ b/index.js @@ -60,8 +60,10 @@ const socketupdates_Companion = ['sources', 'devices', 'device_sources', 'device const oscPort = 5958; const vmixEmulatorPort = '8099'; // Default 8099 var oscUDP = null; -var logFile = fs.openSync(getLogFilePath(), 'w'); // Setup Log file -var tallyDataFile = fs.openSync(getTallyDataPath(), 'w'); // Setup TallyData File +var logFilePath = getLogFilePath(); +var logFile = fs.openSync(logFilePath, 'w'); // Setup Log file +var tallyDataFilePath = getTallyDataPath(); +var tallyDataFile = fs.openSync(tallyDataFilePath, 'w'); // Setup TallyData File var vmix_emulator = null; //TCP server for VMix Emulator var vmix_clients = []; //Clients currently connected to the VMix Emulator const config_file = getConfigFilePath(); //local storage JSON file @@ -7162,3 +7164,11 @@ function getNetworkInterfaces() { // Get all network interfaces on host device } startUp(); + +exports.logs = Logs; +exports.logFilePath = logFilePath; +exports.tallyDataFilePath = tallyDataFilePath; +exports.getConfigFilePath = getConfigFilePath; +exports.getConfig = () => { + return JSON.parse(fs.readFileSync(getConfigFilePath())); +}; diff --git a/main.js b/main.js index 0f49f790..f6ddd50c 100644 --- a/main.js +++ b/main.js @@ -1,13 +1,56 @@ // This is the electron startup script -const { app, BrowserWindow, Tray, Menu, dialog } = require('electron'); +const { app, BrowserWindow, Tray, Menu, dialog, ipcMain, shell } = require('electron'); const { autoUpdater } = require("electron-updater"); const path = require("path"); +const fs = require('fs'); +let server; let mainWindow; let trayIcon; const gotTheLock = app.requestSingleInstanceLock() +function processError(err) { + console.error(err); + const errorWindow = new BrowserWindow({ + width: 800, + height: 600, + minHeight: 850, + minWidth: 1260, + webPreferences: { + nodeIntegration: true, + contextIsolation: false + }, + show: false + }); + errorWindow.setMenu(null); + + let logs = "No log obtained before the error." + let logsFilePath = server.logFilePath; + if (fs.existsSync(logsFilePath)) { + logs = fs.readFileSync(logsFilePath, 'utf8'); + } + ipcMain.on('pageLoaded', (event, arg) => { + event.reply("stacktrace", err.stack); + event.reply("logs", logs); + event.reply("config", JSON.stringify(server.getConfig(), null, 2)); + }); + ipcMain.on('bugReportButtonPressed', (event, arg) => { + let url = `https://github.com/josephdadams/TallyArbiter/issues/new?labels=bug&template=bug.yaml&title=%5BBug%5D%3A+&version=${app.getVersion()}&logs=${encodeURIComponent(logs)}`; + shell.openExternal(url); + }); + + errorWindow.loadFile("electron_error_page.html").then(() => { + errorWindow.show(); + if(mainWindow !== undefined) mainWindow.blur(); + errorWindow.focus(); + }).catch((err) => { console.error(err); }); +} + +process.on('uncaughtException', (err) => { + processError(err); +}) + function createWindow() { mainWindow = new BrowserWindow({ width: 800, @@ -30,12 +73,7 @@ function createWindow() { return false; }); // start the server - try { - require("./index"); - } catch(e) { - console.error(e); - dialog.showErrorBox("Unexpected error", "There was an unexpected error. Please open a bug report on the project's Github page or contact one of the developers. Stack Trace: " + e.toString()); - } + server = require("./index"); } function createTrayIcon() { @@ -112,6 +150,8 @@ if (!gotTheLock) { app.on('activate', function () { if (BrowserWindow.getAllWindows().length === 0) createWindow(); }); + }).catch((err) => { + processError(err); }); app.on('second-instance', () => { From 9794cc25d445144bd2dfd0deef9aebec8e519568 Mon Sep 17 00:00:00 2001 From: Matteo Gheza Date: Sun, 8 Aug 2021 23:58:57 +0200 Subject: [PATCH 02/14] Remove usernames, passwords and cloud from config --- index.js | 22 +++++++++++++++++++--- main.js | 2 +- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 4d850a7a..17837432 100644 --- a/index.js +++ b/index.js @@ -2232,6 +2232,23 @@ function SaveConfig() { } } +function getConfig() { + return JSON.parse(fs.readFileSync(getConfigFilePath())); +} + +function getConfigRedacted() { + let config = JSON.parse(fs.readFileSync(getConfigFilePath())); + config["security"] = { + username_settings: "admin", + password_settings: "12345", + username_producer: "producer", + password_producer: "12345" + }; + config["cloud_destinations"] = []; + config["cloud_keys"] = []; + return config; +} + function initializeDeviceStates() { // initializes each device state in the array upon server startup logger('Initializing Device States.', 'info-quiet'); @@ -7169,6 +7186,5 @@ exports.logs = Logs; exports.logFilePath = logFilePath; exports.tallyDataFilePath = tallyDataFilePath; exports.getConfigFilePath = getConfigFilePath; -exports.getConfig = () => { - return JSON.parse(fs.readFileSync(getConfigFilePath())); -}; +exports.getConfig = getConfig; +exports.getConfigRedacted = getConfigRedacted; diff --git a/main.js b/main.js index f6ddd50c..1c7cb405 100644 --- a/main.js +++ b/main.js @@ -33,7 +33,7 @@ function processError(err) { ipcMain.on('pageLoaded', (event, arg) => { event.reply("stacktrace", err.stack); event.reply("logs", logs); - event.reply("config", JSON.stringify(server.getConfig(), null, 2)); + event.reply("config", JSON.stringify(server.getConfigRedacted(), null, 2)); }); ipcMain.on('bugReportButtonPressed', (event, arg) => { let url = `https://github.com/josephdadams/TallyArbiter/issues/new?labels=bug&template=bug.yaml&title=%5BBug%5D%3A+&version=${app.getVersion()}&logs=${encodeURIComponent(logs)}`; From 9f71ca597307afa81eb6f4baddca3b223bd8380e Mon Sep 17 00:00:00 2001 From: Matteo Gheza Date: Mon, 9 Aug 2021 00:04:13 +0200 Subject: [PATCH 03/14] Custom style for scrollbars --- electron_error_page.html | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/electron_error_page.html b/electron_error_page.html index 61bb4917..a6653bcd 100644 --- a/electron_error_page.html +++ b/electron_error_page.html @@ -101,6 +101,41 @@ border-color: #005cbf; } + From 7c6434be728737b8e1ac7c1374c93e32210a086e Mon Sep 17 00:00:00 2001 From: Matteo Gheza Date: Mon, 9 Aug 2021 08:19:41 +0200 Subject: [PATCH 04/14] Pre-fill issue with config and stacktrace --- main.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/main.js b/main.js index 1c7cb405..38e0f9b5 100644 --- a/main.js +++ b/main.js @@ -30,13 +30,14 @@ function processError(err) { if (fs.existsSync(logsFilePath)) { logs = fs.readFileSync(logsFilePath, 'utf8'); } + let config = JSON.stringify(server.getConfigRedacted(), null, 2); ipcMain.on('pageLoaded', (event, arg) => { event.reply("stacktrace", err.stack); event.reply("logs", logs); - event.reply("config", JSON.stringify(server.getConfigRedacted(), null, 2)); + event.reply("config", config); }); ipcMain.on('bugReportButtonPressed', (event, arg) => { - let url = `https://github.com/josephdadams/TallyArbiter/issues/new?labels=bug&template=bug.yaml&title=%5BBug%5D%3A+&version=${app.getVersion()}&logs=${encodeURIComponent(logs)}`; + let url = `https://github.com/josephdadams/TallyArbiter/issues/new?labels=bug&template=bug.yaml&title=%5BBug%5D%3A+&version=${app.getVersion()}&config=${encodeURIComponent(config)}&logs=${encodeURIComponent(logs)}&stacktrace=${encodeURIComponent(err.stack)}`; shell.openExternal(url); }); From 6b2ecbfa499fd8c3f540a73e8fa6715cc053a538 Mon Sep 17 00:00:00 2001 From: Matteo Gheza Date: Thu, 12 Aug 2021 00:12:03 +0200 Subject: [PATCH 05/14] Rewrite everything, move error reports to server --- .../error-report/error-report.component.html | 37 ++++ .../error-report/error-report.component.scss | 123 ++++++++++++ .../error-report/error-report.component.ts | 47 +++++ .../error-reports-list.component.html | 14 ++ .../error-reports-list.component.scss | 0 .../error-reports-list.component.ts | 26 +++ .../app/_decorators/confirmable.decorator.ts | 4 +- UI/src/app/_models/ErrorReport.ts | 7 + UI/src/app/_models/ErrorReportsListElement.ts | 4 + UI/src/app/_services/socket.service.ts | 20 ++ UI/src/app/app-routing.module.ts | 4 + UI/src/app/app.component.ts | 13 +- UI/src/app/app.module.ts | 6 +- electron_error_page.html | 185 ------------------ index.js | 63 +++++- main.js | 44 ----- 16 files changed, 362 insertions(+), 235 deletions(-) create mode 100644 UI/src/app/_components/error-report/error-report.component.html create mode 100644 UI/src/app/_components/error-report/error-report.component.scss create mode 100644 UI/src/app/_components/error-report/error-report.component.ts create mode 100644 UI/src/app/_components/error-reports-list/error-reports-list.component.html create mode 100644 UI/src/app/_components/error-reports-list/error-reports-list.component.scss create mode 100644 UI/src/app/_components/error-reports-list/error-reports-list.component.ts create mode 100644 UI/src/app/_models/ErrorReport.ts create mode 100644 UI/src/app/_models/ErrorReportsListElement.ts delete mode 100644 electron_error_page.html diff --git a/UI/src/app/_components/error-report/error-report.component.html b/UI/src/app/_components/error-report/error-report.component.html new file mode 100644 index 00000000..5c1d9ef5 --- /dev/null +++ b/UI/src/app/_components/error-report/error-report.component.html @@ -0,0 +1,37 @@ +
+
+
+
+
+

:(

+

There was an unexpected error. Please open a bug report on the project's Github page or contact one of + the developers.

+
+ +
+

Stacktrace:

+
{{ currentReport.stacktrace }}
+
+
+

Error datetime

+

{{ currentReport.datetime | date:'YYYY-MM-dd HH:mm:ss' }}

+
+
+
+

Logs:

+
{{ currentReport.logs }}
+
+
+

Config file:

+
{{ currentReport.config | json }}
+
+
+
+
+
+

:(

+

Error report not found.

+
+
\ No newline at end of file diff --git a/UI/src/app/_components/error-report/error-report.component.scss b/UI/src/app/_components/error-report/error-report.component.scss new file mode 100644 index 00000000..590683d7 --- /dev/null +++ b/UI/src/app/_components/error-report/error-report.component.scss @@ -0,0 +1,123 @@ +.navbar { + display: none; +} + +body { + font-family: Calibri, Candara, Segoe, "Segoe UI", Optima, Arial, sans-serif; + background: #3973aa; + color: #fefeff; + height: 100vh; + margin: 0; +} + +#page { + height: 100%; + margin: 0 auto; + width: 80%; + font-size: 1.9vw; +} + +@media (min-width: 840px) { + #page { + font-size: 140%; + width: 800px; + } +} + +#text { + vertical-align: middle; +} + +h1, +h2, +h3, +h4, +h5 { + font-weight: normal; + padding: 0; + margin: 25px 0; + margin-top: 0; + font-weight: 300; +} + +h1 { + font-size: 6.5em; + margin-bottom: 10px; +} + +h2 { + font-size: 1.5em; +} + +h4 { + font-size: 1.4em; + line-height: 1.5em; +} + +h5 { + line-height: 1.1em; + font-size: 1.3em; +} + +.limit { + overflow-x: scroll; + width: 300px; +} + +#row { + display: flex; + flex-direction: row; + align-items: stretch; + justify-content: space-between; + height: 200px; +} + +.col { + padding: 1em; +} + +.btn-custom { + background-color: white; + color: #007bff; + border-color: #007bff; +} + +.btn-custom:hover { + color: #fff; + background-color: #0062cc; + border-color: #005cbf; +} + +::-webkit-scrollbar { + width: 8px; + height: 8px; +} +::-webkit-scrollbar-button { + width: 0px; + height: 0px; +} +::-webkit-scrollbar-thumb { + background: #e1e1e1; + border: 0px none #ffffff; + border-radius: 0px; +} +::-webkit-scrollbar-thumb:hover { + background: #ffffff; +} +::-webkit-scrollbar-thumb:active { + background: #ffffff; +} +::-webkit-scrollbar-track { + background: #868686; + border: 0px none #ffffff; + border-radius: 0px; +} +::-webkit-scrollbar-track:hover { + background: #868686; +} +::-webkit-scrollbar-track:active { + background: #727272; +} +::-webkit-scrollbar-corner { + background: transparent; +} \ No newline at end of file diff --git a/UI/src/app/_components/error-report/error-report.component.ts b/UI/src/app/_components/error-report/error-report.component.ts new file mode 100644 index 00000000..6c02b21d --- /dev/null +++ b/UI/src/app/_components/error-report/error-report.component.ts @@ -0,0 +1,47 @@ +import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { SocketService } from 'src/app/_services/socket.service'; +import { ErrorReport } from 'src/app/_models/ErrorReport'; + +@Component({ + selector: 'app-error-report', + templateUrl: './error-report.component.html', + styleUrls: ['./error-report.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class ErrorReportComponent implements OnInit { + public currentReportId: string = "blank"; + public currentReport: ErrorReport = {} as ErrorReport; + public loading = true; + public validReport = false; + public bugReportUrl = ""; + + constructor( + public route: ActivatedRoute, + public socketService: SocketService, + ) { + this.route.params.subscribe((params) => { + if (params.errorReportId) { + this.currentReportId = params.errorReportId; + } + }); + } + + ngOnInit(): void { + this.socketService.getErrorReportById(this.currentReportId) + .then((errorReport) => { + this.currentReport = errorReport as ErrorReport; + this.loading = false; + this.validReport = true; + let bugTitle = "[Bug] "+this.currentReport.stacktrace.split("\n")[0]; + this.bugReportUrl = `https://github.com/josephdadams/TallyArbiter/issues/new?labels=bug&template=bug.yaml&title=${encodeURIComponent(bugTitle)}&version=${this.socketService.version}&config=${encodeURIComponent(JSON.stringify(this.currentReport.config, null, 2))}&logs=${encodeURIComponent(this.currentReport.logs)}&stacktrace=${encodeURIComponent(this.currentReport.stacktrace)}`; + console.log("Error report found:"); + console.log(errorReport); + }) + .catch((response) => { + this.loading = false; + console.log("Error report not found"); + }); + } + +} diff --git a/UI/src/app/_components/error-reports-list/error-reports-list.component.html b/UI/src/app/_components/error-reports-list/error-reports-list.component.html new file mode 100644 index 00000000..91edbe6d --- /dev/null +++ b/UI/src/app/_components/error-reports-list/error-reports-list.component.html @@ -0,0 +1,14 @@ +
+
+
+
+
+
+ +
+
+

Hurray! No error report detected.

+
\ No newline at end of file diff --git a/UI/src/app/_components/error-reports-list/error-reports-list.component.scss b/UI/src/app/_components/error-reports-list/error-reports-list.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/UI/src/app/_components/error-reports-list/error-reports-list.component.ts b/UI/src/app/_components/error-reports-list/error-reports-list.component.ts new file mode 100644 index 00000000..bf55ab20 --- /dev/null +++ b/UI/src/app/_components/error-reports-list/error-reports-list.component.ts @@ -0,0 +1,26 @@ +import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { Router } from '@angular/router'; +import { SocketService } from 'src/app/_services/socket.service'; + +@Component({ + selector: 'app-error-reports-list', + templateUrl: './error-reports-list.component.html', + styleUrls: ['./error-reports-list.component.scss'] +}) +export class ErrorReportsListComponent implements OnInit { + constructor( + private router: Router, + public socketService: SocketService, + ) { + console.log(this.socketService.errorReports); + + } + + public selectErrorReport(id: any) { + this.router.navigate(["/", "errors", id.target.value]); + } + + ngOnInit(): void { + } + +} diff --git a/UI/src/app/_decorators/confirmable.decorator.ts b/UI/src/app/_decorators/confirmable.decorator.ts index 0422c30c..b03a5f31 100644 --- a/UI/src/app/_decorators/confirmable.decorator.ts +++ b/UI/src/app/_decorators/confirmable.decorator.ts @@ -1,7 +1,7 @@ import Swal, { SweetAlertOptions } from 'sweetalert2'; -export function Confirmable(text: string) { +export function Confirmable(text: string, focusCancel: boolean = true) { return (target: Object, propertyKey: string, descriptor: PropertyDescriptor) => { const originalMethod = descriptor.value; const config: SweetAlertOptions = { @@ -10,7 +10,7 @@ export function Confirmable(text: string) { showCancelButton: true, confirmButtonColor: "#2a70c7", icon: 'question', - focusCancel: true, + focusCancel: focusCancel, }; descriptor.value = async function (...args: any[]) { const res = await Swal.fire(config); diff --git a/UI/src/app/_models/ErrorReport.ts b/UI/src/app/_models/ErrorReport.ts new file mode 100644 index 00000000..d63554aa --- /dev/null +++ b/UI/src/app/_models/ErrorReport.ts @@ -0,0 +1,7 @@ +export interface ErrorReport { + id: string; + datetime: Date; + stacktrace: string; + logs: string; + config: object; +} \ No newline at end of file diff --git a/UI/src/app/_models/ErrorReportsListElement.ts b/UI/src/app/_models/ErrorReportsListElement.ts new file mode 100644 index 00000000..5dafef7e --- /dev/null +++ b/UI/src/app/_models/ErrorReportsListElement.ts @@ -0,0 +1,4 @@ +export interface ErrorReportsListElement { + id: string; + datetime: Date; +} \ No newline at end of file diff --git a/UI/src/app/_services/socket.service.ts b/UI/src/app/_services/socket.service.ts index 9ecaad4c..7c3ee937 100644 --- a/UI/src/app/_services/socket.service.ts +++ b/UI/src/app/_services/socket.service.ts @@ -20,6 +20,8 @@ import { SourceType } from '../_models/SourceType'; import { SourceTypeBusOptions } from '../_models/SourceTypeBusOptions'; import { SourceTypeDataFields } from '../_models/SourceTypeDataFields'; import { TSLClient } from '../_models/TSLClient'; +import { ErrorReport } from '../_models/ErrorReport'; +import { ErrorReportsListElement } from '../_models/ErrorReportsListElement'; @Injectable({ providedIn: 'root' @@ -55,6 +57,7 @@ export class SocketService { public cloudClients: CloudClient[] = []; public portsInUse: Port[] = []; public messages: Message[] = []; + public errorReports: ErrorReportsListElement[] = [] as ErrorReportsListElement[]; public dataLoaded = new Promise((resolve) => this._resolveDataLoadedPromise = resolve); private _resolveDataLoadedPromise!: () => void; @@ -258,6 +261,10 @@ export class SocketService { this.socket.on('PortsInUse', (ports: Port[]) => { this.portsInUse = ports; }); + this.socket.on('error_reports', (errorReports: ErrorReportsListElement[]) => { + this.errorReports = errorReports; + }); + this.socket.emit('get_error_reports'); this.socket.emit('version'); this.socket.emit('interfaces'); @@ -334,4 +341,17 @@ export class SocketService { let bus = this.busOptions.find(({id}: {id: string}) => id === busId); return bus?.type; } + + public getErrorReportById(id: string) { + return new Promise((resolve, reject) => { + this.socket.emit('get_error_report', id); + this.socket.once("error_report", (response: any) => { + if (response !== false) { + resolve(response); + } else { + reject(response); + } + }); + }); + } } diff --git a/UI/src/app/app-routing.module.ts b/UI/src/app/app-routing.module.ts index 32020f33..34cc9fdb 100644 --- a/UI/src/app/app-routing.module.ts +++ b/UI/src/app/app-routing.module.ts @@ -1,6 +1,8 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { AboutComponent } from './_components/about/about.component'; +import { ErrorReportComponent } from './_components/error-report/error-report.component'; +import { ErrorReportsListComponent } from './_components/error-reports-list/error-reports-list.component'; import { HomeComponent } from './_components/home/home.component'; import { LoginComponent } from './_components/login/login.component'; import { ProducerComponent } from './_components/producer/producer.component'; @@ -18,6 +20,8 @@ const routes: Routes = [ { path: "about", component: AboutComponent }, { path: "login/producer", component: LoginComponent }, { path: "login/settings", component: LoginComponent }, + { path: "errors/:errorReportId", component: ErrorReportComponent }, + { path: "errors", component: ErrorReportsListComponent }, // { path: "**", redirectTo: "/home", pathMatch: "full" }, ]; diff --git a/UI/src/app/app.component.ts b/UI/src/app/app.component.ts index 34d251ac..6aa54b34 100644 --- a/UI/src/app/app.component.ts +++ b/UI/src/app/app.component.ts @@ -1,5 +1,8 @@ import { Component } from '@angular/core'; import { WakeLockService } from './_services/wake-lock.service'; +import { SocketService } from './_services/socket.service'; +import { Router } from '@angular/router'; +import { Confirmable } from './_decorators/confirmable.decorator'; @Component({ selector: 'app-root', @@ -8,7 +11,15 @@ import { WakeLockService } from './_services/wake-lock.service'; }) export class AppComponent { public showMenu = false; - constructor(private wakeLockService: WakeLockService) { + constructor(private wakeLockService: WakeLockService, private router: Router, private SocketService: SocketService) { wakeLockService.init(); + this.SocketService.socket.on('server_error', (id: string) => { + this.show_error(id); + }); + } + + @Confirmable("There was an unexpected error. Do you want to view the bug report?", false) + public show_error(id: string) { + window.open(`/#/errors/${id}`, '_blank'); } } diff --git a/UI/src/app/app.module.ts b/UI/src/app/app.module.ts index 3c453b00..1e0930fa 100644 --- a/UI/src/app/app.module.ts +++ b/UI/src/app/app.module.ts @@ -14,6 +14,8 @@ import { ServiceWorkerModule } from '@angular/service-worker'; import { environment } from '../environments/environment'; import { LoginComponent } from './_components/login/login.component'; import { ChatComponent } from './_components/chat/chat.component'; +import { ErrorReportComponent } from './_components/error-report/error-report.component'; +import { ErrorReportsListComponent } from './_components/error-reports-list/error-reports-list.component'; @NgModule({ declarations: [ @@ -24,7 +26,9 @@ import { ChatComponent } from './_components/chat/chat.component'; TallyComponent, AboutComponent, LoginComponent, - ChatComponent + ChatComponent, + ErrorReportComponent, + ErrorReportsListComponent ], imports: [ BrowserModule, diff --git a/electron_error_page.html b/electron_error_page.html deleted file mode 100644 index a6653bcd..00000000 --- a/electron_error_page.html +++ /dev/null @@ -1,185 +0,0 @@ - - - - - Unexpected error - - - - - - -
-
-

:(

-

There was an unexpected error. Please open a bug report on the project's Github page or contact one of - the developers.

-

-

-
- Open a bug report on Github -
-
-

Stacktrace:

-

-        
-
-
-

Logs:

-

-            
-
-

Config file:

-

-            
-
-
- - - - \ No newline at end of file diff --git a/index.js b/index.js index 17837432..16a7fa41 100644 --- a/index.js +++ b/index.js @@ -759,9 +759,10 @@ function startUp() { initialSetup(); DeleteInactiveListenerClients(); - /*process.on('uncaughtException', function (err) { + process.on('uncaughtException', function (err) { logger(`Caught exception: ${err}`, 'error'); - });*/ + generateErrorReport(err.stack); + }); } //based on https://stackoverflow.com/a/37096512 @@ -1723,6 +1724,14 @@ function initialSetup() { SendMessage(type, socket.id, message); }); + socket.on('get_error_reports', function() { + socket.emit('error_reports', getErrorReportsList()); + }); + + socket.on('get_error_report', function(errorReportId) { + socket.emit('error_report', getErrorReport(errorReportId)); + }); + socket.on('disconnect', function() { // emitted when any socket.io client disconnects from the server DeactivateListenerClient(socket.id); CheckCloudClients(socket.id); @@ -7152,6 +7161,56 @@ function getTallyDataPath() { return path.join(TallyDataFolder, logName); } +function getErrorReportsList() { + try { + const ErrorReportsFolder = path.join(process.env.APPDATA || (process.platform == 'darwin' ? process.env.HOME + '/Library/Preferences/' : process.env.HOME + "/.local/share/"), "TallyArbiter/ErrorReports"); + const ErrorReportsFiles = fs.readdirSync(ErrorReportsFolder); + let errorReports = []; + ErrorReportsFiles.forEach((file) => { + let currentErrorReport = JSON.parse(fs.readFileSync(path.join(ErrorReportsFolder, file), "utf8")); + let reportId = file.replace(/\.[^/.]+$/, ""); + errorReports.push({ id: reportId, datetime: currentErrorReport.datetime }); + }); + return errorReports; + } catch (e) { + return []; + } +} + +function getErrorReport(reportId) { + try { + const ErrorReportsFolder = path.join(process.env.APPDATA || (process.platform == 'darwin' ? process.env.HOME + '/Library/Preferences/' : process.env.HOME + "/.local/share/"), "TallyArbiter/ErrorReports"); + const ErrorReportFile = path.join(ErrorReportsFolder, reportId + ".json"); + return JSON.parse(fs.readFileSync(ErrorReportFile, "utf8")); + } catch (e) { + return false; + } +} + +function getErrorReportPath(id) { + + const ErrorReportsFolder = path.join(process.env.APPDATA || (process.platform == 'darwin' ? process.env.HOME + '/Library/Preferences/' : process.env.HOME + "/.local/share/"), "TallyArbiter/ErrorReports"); + + if (!fs.existsSync(ErrorReportsFolder)) { + fs.mkdirSync(ErrorReportsFolder, { recursive: true }); + } + var errorReportName = id + ".json" + return path.join(ErrorReportsFolder, errorReportName); +} + +function generateErrorReport(stacktrace) { + let id = uuidv4(); + if(!stacktrace) stacktrace = "No stacktrace captured."; + var errorReport = { + "datetime": new Date(), + "stacktrace": stacktrace, + "logs": fs.readFileSync(logFilePath, 'utf8'), + "config": getConfigRedacted() + }; + fs.writeFileSync(getErrorReportPath(id), JSON.stringify(errorReport)); + io.emit("server_error", id); +} + function getNetworkInterfaces() { // Get all network interfaces on host device var interfaces = [] const networkInterfaces = os.networkInterfaces() diff --git a/main.js b/main.js index 38e0f9b5..b914d5a0 100644 --- a/main.js +++ b/main.js @@ -10,48 +10,6 @@ let trayIcon; const gotTheLock = app.requestSingleInstanceLock() -function processError(err) { - console.error(err); - const errorWindow = new BrowserWindow({ - width: 800, - height: 600, - minHeight: 850, - minWidth: 1260, - webPreferences: { - nodeIntegration: true, - contextIsolation: false - }, - show: false - }); - errorWindow.setMenu(null); - - let logs = "No log obtained before the error." - let logsFilePath = server.logFilePath; - if (fs.existsSync(logsFilePath)) { - logs = fs.readFileSync(logsFilePath, 'utf8'); - } - let config = JSON.stringify(server.getConfigRedacted(), null, 2); - ipcMain.on('pageLoaded', (event, arg) => { - event.reply("stacktrace", err.stack); - event.reply("logs", logs); - event.reply("config", config); - }); - ipcMain.on('bugReportButtonPressed', (event, arg) => { - let url = `https://github.com/josephdadams/TallyArbiter/issues/new?labels=bug&template=bug.yaml&title=%5BBug%5D%3A+&version=${app.getVersion()}&config=${encodeURIComponent(config)}&logs=${encodeURIComponent(logs)}&stacktrace=${encodeURIComponent(err.stack)}`; - shell.openExternal(url); - }); - - errorWindow.loadFile("electron_error_page.html").then(() => { - errorWindow.show(); - if(mainWindow !== undefined) mainWindow.blur(); - errorWindow.focus(); - }).catch((err) => { console.error(err); }); -} - -process.on('uncaughtException', (err) => { - processError(err); -}) - function createWindow() { mainWindow = new BrowserWindow({ width: 800, @@ -151,8 +109,6 @@ if (!gotTheLock) { app.on('activate', function () { if (BrowserWindow.getAllWindows().length === 0) createWindow(); }); - }).catch((err) => { - processError(err); }); app.on('second-instance', () => { From cfeb748dcc08aa0b5ef15252e26483a2e49922ce Mon Sep 17 00:00:00 2001 From: Matteo Gheza Date: Thu, 12 Aug 2021 00:35:58 +0200 Subject: [PATCH 06/14] Check reportId before opening file --- index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/index.js b/index.js index 16a7fa41..c9d2f6c8 100644 --- a/index.js +++ b/index.js @@ -7179,6 +7179,7 @@ function getErrorReportsList() { function getErrorReport(reportId) { try { + if(!reportId.match(/^[a-zA-Z0-9]+$/i)) return false; const ErrorReportsFolder = path.join(process.env.APPDATA || (process.platform == 'darwin' ? process.env.HOME + '/Library/Preferences/' : process.env.HOME + "/.local/share/"), "TallyArbiter/ErrorReports"); const ErrorReportFile = path.join(ErrorReportsFolder, reportId + ".json"); return JSON.parse(fs.readFileSync(ErrorReportFile, "utf8")); From a8b0ae527d6f8b2f07d07dfd68fdc1cda23dd263 Mon Sep 17 00:00:00 2001 From: Matteo Gheza Date: Thu, 12 Aug 2021 00:43:19 +0200 Subject: [PATCH 07/14] Add credits in the error report component css --- UI/src/app/_components/error-report/error-report.component.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/UI/src/app/_components/error-report/error-report.component.scss b/UI/src/app/_components/error-report/error-report.component.scss index 590683d7..a33f2e92 100644 --- a/UI/src/app/_components/error-report/error-report.component.scss +++ b/UI/src/app/_components/error-report/error-report.component.scss @@ -2,6 +2,7 @@ display: none; } +/* Based on https://codepen.io/xontab/pen/JrVaYR */ body { font-family: Calibri, Candara, Segoe, "Segoe UI", Optima, Arial, sans-serif; background: #3973aa; From 32ea9a47ef73e5d3fbcf6b62aa0d755b4f68e51f Mon Sep 17 00:00:00 2001 From: Matteo Gheza Date: Thu, 12 Aug 2021 12:19:53 +0200 Subject: [PATCH 08/14] Require login for viewing error reports --- .../app/_components/login/login.component.ts | 39 +++++++++++++++++-- .../settings/settings.component.ts | 11 +++++- UI/src/app/_guards/errors.guard.ts | 23 +++++++++++ UI/src/app/app-routing.module.ts | 9 +++-- UI/src/app/app.component.ts | 13 +------ 5 files changed, 74 insertions(+), 21 deletions(-) create mode 100644 UI/src/app/_guards/errors.guard.ts diff --git a/UI/src/app/_components/login/login.component.ts b/UI/src/app/_components/login/login.component.ts index cecc1000..af8eaa42 100644 --- a/UI/src/app/_components/login/login.component.ts +++ b/UI/src/app/_components/login/login.component.ts @@ -1,5 +1,5 @@ import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; -import { Router } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { AuthService, LoginResponse } from 'src/app/_services/auth.service'; @Component({ @@ -13,19 +13,50 @@ export class LoginComponent { public username = ""; public password = ""; private type!: "producer" | "settings"; + private redirectParam = ""; + private extraParam = ""; @ViewChild("inputPassword") public inputPassword!: ElementRef; - constructor(private router: Router, private authService: AuthService) { + constructor( + public route: ActivatedRoute, + private router: Router, + private authService: AuthService + ) { + this.route.params.subscribe((params) => { + if (params.redirect) { + this.redirectParam = params.redirect; + } + if (params.extraParam) { + this.extraParam = params.extraParam; + } + }); + console.log(this.redirectParam); + console.log(this.extraParam); + switch (this.redirectParam) { + case "producer": + this.type = "producer"; + break; + + case "errors": + case "settings": + this.type = "settings"; + break; + + default: + this.router.navigate(["/home"]); + break; + } } login(): void { this.loading = true; - this.type = this.router.url.endsWith("producer") ? "producer" : "settings"; this.authService.login(this.type, this.username, this.password).then((response: LoginResponse) => { this.loginResponse = response; this.loading = false; if (response.loginOk === true) { - this.router.navigate([this.type]); + let navigateParams = [this.redirectParam]; + if(this.extraParam !== "") navigateParams.push(this.extraParam); + this.router.navigate(navigateParams); } }); } diff --git a/UI/src/app/_components/settings/settings.component.ts b/UI/src/app/_components/settings/settings.component.ts index 9dd26dbf..d1b9e5e8 100644 --- a/UI/src/app/_components/settings/settings.component.ts +++ b/UI/src/app/_components/settings/settings.component.ts @@ -1,4 +1,5 @@ import { Component, ElementRef, ViewChild } from '@angular/core'; +import { Router } from '@angular/router'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { Confirmable } from 'src/app/_decorators/confirmable.decorator'; import { CloudClient } from 'src/app/_models/CloudClient'; @@ -66,7 +67,7 @@ export class SettingsComponent { public newCloudKey = ""; - constructor(private modalService: NgbModal, public socketService: SocketService) { + constructor(private modalService: NgbModal, public socketService: SocketService, private router: Router) { this.socketService.joinAdmins(); this.socketService.closeModals.subscribe(() => this.modalService.dismissAll()); this.socketService.scrollTallyDataSubject.subscribe(() => this.scrollToBottom(this.tallyDataContainer)); @@ -74,6 +75,14 @@ export class SettingsComponent { this.filterLogs(); this.scrollToBottom(this.logsContainer); }); + this.socketService.socket.on('server_error', (id: string) => { + this.show_error(id); + }); + } + + @Confirmable("There was an unexpected error. Do you want to view the bug report?", false) + public show_error(id: string) { + this.router.navigate(['/errors', id]); } private portInUse(portToCheck: number, sourceId: string) { diff --git a/UI/src/app/_guards/errors.guard.ts b/UI/src/app/_guards/errors.guard.ts new file mode 100644 index 00000000..de153fe1 --- /dev/null +++ b/UI/src/app/_guards/errors.guard.ts @@ -0,0 +1,23 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router'; +import { Observable } from 'rxjs'; +import { AuthService } from '../_services/auth.service'; + +@Injectable({ + providedIn: 'root' +}) +export class ErrorsGuard implements CanActivate { + constructor(private authService: AuthService, private router: Router) { } + canActivate( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot): Observable | Promise | boolean | UrlTree { + if (!this.authService.isAdmin()) { + let navigationParams = ["login", "errors"]; + if(route.paramMap.get('errorReportId') !== null) navigationParams.push(route.paramMap.get('errorReportId') as string); + this.router.navigate(navigationParams); + return false; + } + return true; + } + +} diff --git a/UI/src/app/app-routing.module.ts b/UI/src/app/app-routing.module.ts index 34cc9fdb..69763d67 100644 --- a/UI/src/app/app-routing.module.ts +++ b/UI/src/app/app-routing.module.ts @@ -10,6 +10,7 @@ import { SettingsComponent } from './_components/settings/settings.component'; import { TallyComponent } from './_components/tally/tally.component'; import { ProducerGuard } from './_guards/producer.guard'; import { SettingsGuard } from './_guards/settings.guard'; +import { ErrorsGuard } from './_guards/errors.guard'; const routes: Routes = [ { path: "home", component: HomeComponent }, @@ -17,11 +18,11 @@ const routes: Routes = [ { path: "tally", component: TallyComponent }, { path: "producer", component: ProducerComponent, canActivate: [ProducerGuard] }, { path: "settings", component: SettingsComponent, canActivate: [SettingsGuard] }, + { path: "errors/:errorReportId", component: ErrorReportComponent, canActivate: [ErrorsGuard] }, + { path: "errors", component: ErrorReportsListComponent, canActivate: [ErrorsGuard] }, { path: "about", component: AboutComponent }, - { path: "login/producer", component: LoginComponent }, - { path: "login/settings", component: LoginComponent }, - { path: "errors/:errorReportId", component: ErrorReportComponent }, - { path: "errors", component: ErrorReportsListComponent }, + { path: "login/:redirect/:extraParam", component: LoginComponent }, + { path: "login/:redirect", component: LoginComponent }, // { path: "**", redirectTo: "/home", pathMatch: "full" }, ]; diff --git a/UI/src/app/app.component.ts b/UI/src/app/app.component.ts index 6aa54b34..34d251ac 100644 --- a/UI/src/app/app.component.ts +++ b/UI/src/app/app.component.ts @@ -1,8 +1,5 @@ import { Component } from '@angular/core'; import { WakeLockService } from './_services/wake-lock.service'; -import { SocketService } from './_services/socket.service'; -import { Router } from '@angular/router'; -import { Confirmable } from './_decorators/confirmable.decorator'; @Component({ selector: 'app-root', @@ -11,15 +8,7 @@ import { Confirmable } from './_decorators/confirmable.decorator'; }) export class AppComponent { public showMenu = false; - constructor(private wakeLockService: WakeLockService, private router: Router, private SocketService: SocketService) { + constructor(private wakeLockService: WakeLockService) { wakeLockService.init(); - this.SocketService.socket.on('server_error', (id: string) => { - this.show_error(id); - }); - } - - @Confirmable("There was an unexpected error. Do you want to view the bug report?", false) - public show_error(id: string) { - window.open(`/#/errors/${id}`, '_blank'); } } From 1604166c4c577ee0e53627a97659d5efe9174956 Mon Sep 17 00:00:00 2001 From: Joseph Adams Date: Thu, 12 Aug 2021 13:16:38 -0400 Subject: [PATCH 09/14] fix tsl5 processing on Ross Acuity... None: LH = 0, RH = 0 PVW: LH = 2, RH = 0 PGM: LH = 0, RH = 1 PVW+PGM: LH = 3, RH = 3 --- index.js | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/index.js b/index.js index ac74153d..6f9fa5d7 100644 --- a/index.js +++ b/index.js @@ -2617,23 +2617,26 @@ function processTSL5Tally(sourceId, data) { tallyobj.TEXT = jspack.Unpack( "s".repeat(LENGTH), data, cursor) - //tally1 === lh tally - //tally2 === rh tally - let inPreview = 0; let inProgram = 0; - - if ((tallyobj.control.lh_tally === 2) && (tallyobj.control.rh_tally === 2)) { //device is in Preview only - inPreview = 1; - inProgram = 0; - } - else if ((tallyobj.control.lh_tally === 1) && (tallyobj.control.rh_tally === 1)) { //device is in Program only - inPreview = 0; - inProgram = 1; - } - else if ((tallyobj.control.lh_tally === 1) && (tallyobj.control.rh_tally === 2)) { //device is in PVW+PGM - inPreview = 1; - inProgram = 1; + + switch(tallyobj.control.text_tally) { + case 0: + inPreview = 0; + inProgram = 0; + break; + case 1: + inPreview = 0; + inProgram = 1; + break; + case 2: + inPreview = 1; + inProgram = 0; + break; + case 3: + inPreview = 1; + inProgram = 1; + break; } let newTallyObj = {}; From 48cd109d2e36fc0d6f7b332d29a8ee649008d9cf Mon Sep 17 00:00:00 2001 From: Matteo Gheza Date: Thu, 12 Aug 2021 22:00:42 +0200 Subject: [PATCH 10/14] Support for errors in main.js --- index.js | 14 ++++++++++---- main.js | 14 +++++++++++++- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index c9d2f6c8..4a99b40e 100644 --- a/index.js +++ b/index.js @@ -760,8 +760,9 @@ function startUp() { DeleteInactiveListenerClients(); process.on('uncaughtException', function (err) { - logger(`Caught exception: ${err}`, 'error'); - generateErrorReport(err.stack); + if (!process.versions.hasOwnProperty('electron')) { + generateErrorReport(err); + } }); } @@ -7199,9 +7200,13 @@ function getErrorReportPath(id) { return path.join(ErrorReportsFolder, errorReportName); } -function generateErrorReport(stacktrace) { +function generateErrorReport(error) { + logger(`Caught exception: ${error}`, 'error'); let id = uuidv4(); - if(!stacktrace) stacktrace = "No stacktrace captured."; + let stacktrace = "No stacktrace captured."; + if(error !== undefined){ + stacktrace = error.stack; + } var errorReport = { "datetime": new Date(), "stacktrace": stacktrace, @@ -7248,3 +7253,4 @@ exports.tallyDataFilePath = tallyDataFilePath; exports.getConfigFilePath = getConfigFilePath; exports.getConfig = getConfig; exports.getConfigRedacted = getConfigRedacted; +exports.generateErrorReport = generateErrorReport; diff --git a/main.js b/main.js index b914d5a0..4ef48670 100644 --- a/main.js +++ b/main.js @@ -1,5 +1,5 @@ // This is the electron startup script -const { app, BrowserWindow, Tray, Menu, dialog, ipcMain, shell } = require('electron'); +const { app, BrowserWindow, Tray, Menu, dialog } = require('electron'); const { autoUpdater } = require("electron-updater"); const path = require("path"); const fs = require('fs'); @@ -10,6 +10,16 @@ let trayIcon; const gotTheLock = app.requestSingleInstanceLock() +function processError(err){ + if(server !== undefined){ + server.generateErrorReport(err); + } else { + dialog.showErrorBox("Unexpected error", "There was an unexpected error, and there was an other error generating the error report. Please open a bug report on the project's Github page or contact one of the developers. Stack Trace: " + err.toString()); + } +} + +process.on('uncaughtException', processError); + function createWindow() { mainWindow = new BrowserWindow({ width: 800, @@ -109,6 +119,8 @@ if (!gotTheLock) { app.on('activate', function () { if (BrowserWindow.getAllWindows().length === 0) createWindow(); }); + }).catch((err) => { + processError(err); }); app.on('second-instance', () => { From 6a6c396141b7748159c91dcbd079c64e0f2e985b Mon Sep 17 00:00:00 2001 From: David Stevens Date: Thu, 12 Aug 2021 16:57:41 -0400 Subject: [PATCH 11/14] Adds are you sure prompt to quit --- .DS_Store | Bin 0 -> 10244 bytes main.js | 31 +++++++++++++++++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..9f945bc8e262109e6115ce7a5e4caf1cb3f784e2 GIT binary patch literal 10244 zcmeHMTWl0n82(^aR89%Vp6T9+bD&#v+J57`I?y|k?`Q8Q2k4v4iacJ(@E&>~^g!r=5f8BUL!2%q6TTc4lD|6W#*Y9bD@n}@ zJ@e$BWSl8TCVV+8Bx$IifRYqY^p6dWR)j73-o~H`GDml(7}KLG=c_M z`fDUymUJ3;q;GwUuI_>!J<%=8QElZc5Xow$Qxum%v-rBP4#EFPJZSLYV-TN~~j~##T#L3gNLMrIL z?x9B1NA73n=_Wq2+(L&~b;Z=;s=qF-I--n^?(FMVlIo-aEoN)hGacLAY2;jzNwvl$ z2Xv<$dt1(6?R{y-%4cjhsn(`7E1kDA&uFu(9`l&t_It(=k39{zPR{G#hW#dCnJ%fS zdDqx(5Wp-Lodk31MiwC+FmkSXO6-n#i<+0-b8qMR&G%oNFtJ8el*x5{1=BTCma$87 z(}t~^_Q4%PrtS?6kVkFLJYX8RNTe29du=UaT%0;>x*DAkomE5$+pAbvt(W`rDf4jN z^p1)#qVYKqS$$`ZPV?IA=&VPbw?w1yxw5>Q={s@;-Gy3-E?7tat=FZc`O|E=I}%ad z-b1wQ0;;sM$`MuHVQDGD@>MHVl4@gLLHO5Ku30P7>>K*iP9{U^afLeC-XY8Cg?()7 zO`7X%%^Efh&sP_Zu9M|mtffuYNvTeoKDB#8G&l4kQ7NPtkO7D^ctcN`mt66%BJqa(u zS$G}Zg%9BZd=8i38~7H!gCF2B{0@I%C62=?l(80P;7pu_vvCo&;!<3O?YI#);bz>5 zy|@?mVG=bwfDUHyFuHgYAHhfQ6h4km;q!PJ&)^IA8lJ=R_y)d(ALA$ZDSn1u;`jIy zUdCVWSNubYNQyLGs*~=L=1U8tg;I;OLSpY2DpE0q21LDhm)hy+<}u{IIi$m*#Zc_N zb?e7Q3|*>bQByl)G6l+%03JR_W z$F{^4DJsgMvRqlIsFf6EqPkjHtEj*tU)0-T9Wj*!&A*`UoA4fdMD)E#^!*WjhTq@{ zQFbCuC%VqV`M3ZZu#qUc0$1WHT#a404%g!bB5*J6#6H}G1DHmG=sSoe=Fr1D7Vsf_ zm`HpIA0rAsjnCk-_#B?am+)nL1z+W4ejDExL>!^#h9WVA#1C#x&q7bmwj7&%mKn)q zmd5bp;|PT+eR#`1n>@)V{cKjjCg_F^=z=ZK16=`YmR^)wXDE$eBaGHJOCd_FtI5vc zb4gOV!=CayRS3+9rxc~QT6(o;Jn_-sAiZcB2_+Jt8>A9Au_TqK4)kz693>HDI2pF_ zBqn{Gbc<57GW!Tw8#Gs)*2mJiOfWG-@?aUFiX&LUq)}2dN)mP>>#>%^EV(sFOu#1r zMoX{*y32XV7!&XlOUoN+A550&mRqw#6Hk_~V#mm`32s&q68im-$p2t69>B`Z zo=$ccEFKht{j%TM<8 { - app.isQuiting = true; - app.quit(); + dialog.showMessageBox(mainWindow, { + title: "Are you sure?", + message: "Are you sure you want to quit TallyArbiter?", + buttons: ["Yes", "No"], + }).then((v) => { + if (v.response == 0) { + app.isQuiting =true; + app.quit(); + } + }); }, }, ])); @@ -128,7 +139,19 @@ if (!gotTheLock) { }); app.on('window-all-closed', function () { - if (process.platform !== 'darwin') app.quit(); + if (process.platform !== 'darwin') { + app.preventDefault() // Prevents the window from closing + dialog.showMessageBox({ + type: 'question', + buttons: ['Yes', 'No'], + title: 'Confirm', + message: 'Are you sure you want to quit?' + }, function (response) { + if (response === 0) { // Runs the following if 'Yes' is clicked + app.quit(); + } + }) + } }); // Listen for web contents being created From 6967514f5b4c10c7be1e1dbcd6945c9025cd622a Mon Sep 17 00:00:00 2001 From: Matteo Gheza Date: Thu, 12 Aug 2021 23:19:09 +0200 Subject: [PATCH 12/14] Show popup for unreaded error reports --- .../settings/settings.component.ts | 12 +++++++ index.js | 32 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/UI/src/app/_components/settings/settings.component.ts b/UI/src/app/_components/settings/settings.component.ts index d1b9e5e8..efff8d6b 100644 --- a/UI/src/app/_components/settings/settings.component.ts +++ b/UI/src/app/_components/settings/settings.component.ts @@ -78,6 +78,13 @@ export class SettingsComponent { this.socketService.socket.on('server_error', (id: string) => { this.show_error(id); }); + this.socketService.socket.on('unreaded_error_reports', (list) => { + console.log(list); + if(list.length > 0) { + this.show_errors_list(); + } + }); + this.socketService.socket.emit('get_unreaded_error_reports'); } @Confirmable("There was an unexpected error. Do you want to view the bug report?", false) @@ -85,6 +92,11 @@ export class SettingsComponent { this.router.navigate(['/errors', id]); } + @Confirmable(`There are error reports that you haven't read yet. Do you want to open the list of errors now?`, false) + public show_errors_list() { + this.router.navigate(['/errors']); + } + private portInUse(portToCheck: number, sourceId: string) { for (const port of this.socketService.portsInUse) { if (port.port.toString() === portToCheck.toString()) { diff --git a/index.js b/index.js index 4a99b40e..3ea130b9 100644 --- a/index.js +++ b/index.js @@ -1729,7 +1729,12 @@ function initialSetup() { socket.emit('error_reports', getErrorReportsList()); }); + socket.on('get_unreaded_error_reports', function() { + socket.emit('unreaded_error_reports', getUnreadedErrorReportsList()); + }); + socket.on('get_error_report', function(errorReportId) { + markErrorReportAsReaded(errorReportId); socket.emit('error_report', getErrorReport(errorReportId)); }); @@ -7178,6 +7183,33 @@ function getErrorReportsList() { } } +function getReadedErrorReports() { + try { + const readedErrorReportsFilePath = path.join(process.env.APPDATA || (process.platform == 'darwin' ? process.env.HOME + '/Library/Preferences/' : process.env.HOME + "/.local/share/"), "TallyArbiter/readedErrorReports.json"); + return JSON.parse(fs.readFileSync(readedErrorReportsFilePath, 'utf8')); + } catch(e) { + return []; + } +} + +function markErrorReportAsReaded(errorReportId) { + try { + const readedErrorReportsFilePath = path.join(process.env.APPDATA || (process.platform == 'darwin' ? process.env.HOME + '/Library/Preferences/' : process.env.HOME + "/.local/share/"), "TallyArbiter/readedErrorReports.json"); + let readedErrorReportsList = getReadedErrorReports(); + readedErrorReportsList.push(errorReportId); + fs.writeFileSync(readedErrorReportsFilePath, JSON.stringify(readedErrorReportsList)); + return true; + } catch(e) { + return false; + } +} + +function getUnreadedErrorReportsList() { + let errorReports = getErrorReportsList(); + let readedErrorReports = getReadedErrorReports(); + return errorReports.filter((report) => { return !readedErrorReports.includes(report.id); }); +} + function getErrorReport(reportId) { try { if(!reportId.match(/^[a-zA-Z0-9]+$/i)) return false; From b7883b03419484d237f3c424ccb04987c028ed96 Mon Sep 17 00:00:00 2001 From: Matteo Gheza Date: Fri, 13 Aug 2021 00:54:01 +0200 Subject: [PATCH 13/14] Remove `encapsulation: ViewEncapsulation.None` and add back button --- .../error-report/error-report.component.html | 1 + .../error-report/error-report.component.scss | 108 ++++++++++-------- .../error-report/error-report.component.ts | 32 +++++- UI/src/app/_services/locationBack.service.ts | 27 +++++ .../_services/navbar-visibility.service.ts | 16 +++ UI/src/app/app.component.html | 2 +- UI/src/app/app.component.ts | 9 +- 7 files changed, 140 insertions(+), 55 deletions(-) create mode 100644 UI/src/app/_services/locationBack.service.ts create mode 100644 UI/src/app/_services/navbar-visibility.service.ts diff --git a/UI/src/app/_components/error-report/error-report.component.html b/UI/src/app/_components/error-report/error-report.component.html index 5c1d9ef5..5f11aa8a 100644 --- a/UI/src/app/_components/error-report/error-report.component.html +++ b/UI/src/app/_components/error-report/error-report.component.html @@ -1,6 +1,7 @@
+

:(

diff --git a/UI/src/app/_components/error-report/error-report.component.scss b/UI/src/app/_components/error-report/error-report.component.scss index a33f2e92..c06be23f 100644 --- a/UI/src/app/_components/error-report/error-report.component.scss +++ b/UI/src/app/_components/error-report/error-report.component.scss @@ -1,32 +1,22 @@ -.navbar { - display: none; -} - /* Based on https://codepen.io/xontab/pen/JrVaYR */ -body { - font-family: Calibri, Candara, Segoe, "Segoe UI", Optima, Arial, sans-serif; - background: #3973aa; - color: #fefeff; - height: 100vh; - margin: 0; -} - #page { - height: 100%; - margin: 0 auto; - width: 80%; - font-size: 1.9vw; + height: 100%; + margin: 0 auto; + width: 80%; + font-size: 1.9vw; + font-family: Calibri, Candara, Segoe, "Segoe UI", Optima, Arial, sans-serif; + color: #fefeff; } @media (min-width: 840px) { - #page { - font-size: 140%; - width: 800px; - } + #page { + font-size: 140%; + width: 800px; + } } #text { - vertical-align: middle; + vertical-align: middle; } h1, @@ -34,91 +24,113 @@ h2, h3, h4, h5 { - font-weight: normal; - padding: 0; - margin: 25px 0; - margin-top: 0; - font-weight: 300; + font-weight: normal; + padding: 0; + margin: 25px 0; + margin-top: 0; + font-weight: 300; } h1 { - font-size: 6.5em; - margin-bottom: 10px; + font-size: 6.5em; + margin-bottom: 10px; } h2 { - font-size: 1.5em; + font-size: 1.5em; } h4 { - font-size: 1.4em; - line-height: 1.5em; + font-size: 1.4em; + line-height: 1.5em; } h5 { - line-height: 1.1em; - font-size: 1.3em; + line-height: 1.1em; + font-size: 1.3em; } .limit { - overflow-x: scroll; - width: 300px; + overflow-x: scroll; + width: 300px; } #row { - display: flex; - flex-direction: row; - align-items: stretch; - justify-content: space-between; - height: 200px; + display: flex; + flex-direction: row; + align-items: stretch; + justify-content: space-between; + height: 200px; } .col { - padding: 1em; + padding: 1em; } .btn-custom { - background-color: white; - color: #007bff; - border-color: #007bff; + background-color: white; + color: #007bff; + border-color: #007bff; } .btn-custom:hover { - color: #fff; - background-color: #0062cc; - border-color: #005cbf; + color: #fff; + background-color: #0062cc; + border-color: #005cbf; } ::-webkit-scrollbar { width: 8px; height: 8px; } + ::-webkit-scrollbar-button { width: 0px; height: 0px; } + ::-webkit-scrollbar-thumb { background: #e1e1e1; border: 0px none #ffffff; border-radius: 0px; } + ::-webkit-scrollbar-thumb:hover { background: #ffffff; } + ::-webkit-scrollbar-thumb:active { background: #ffffff; } + ::-webkit-scrollbar-track { background: #868686; border: 0px none #ffffff; border-radius: 0px; } + ::-webkit-scrollbar-track:hover { background: #868686; } + ::-webkit-scrollbar-track:active { background: #727272; } + ::-webkit-scrollbar-corner { background: transparent; -} \ No newline at end of file +} + +#backBtn { + position: absolute; + z-index: 99; + border: none; + outline: none; + background-color: #fbfbfb; + color: black; + padding: 10px; + border-radius: 5px; + font-size: 18px; + top: 10px; + left: 10px; +} diff --git a/UI/src/app/_components/error-report/error-report.component.ts b/UI/src/app/_components/error-report/error-report.component.ts index 6c02b21d..f4d708f1 100644 --- a/UI/src/app/_components/error-report/error-report.component.ts +++ b/UI/src/app/_components/error-report/error-report.component.ts @@ -1,15 +1,16 @@ -import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, OnInit, OnDestroy, AfterViewInit, Renderer2, ElementRef } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { SocketService } from 'src/app/_services/socket.service'; import { ErrorReport } from 'src/app/_models/ErrorReport'; +import { NavbarVisibilityService } from 'src/app/_services/navbar-visibility.service'; +import { LocationBackService } from 'src/app/_services/locationBack.service'; @Component({ selector: 'app-error-report', templateUrl: './error-report.component.html', - styleUrls: ['./error-report.component.scss'], - encapsulation: ViewEncapsulation.None + styleUrls: ['./error-report.component.scss'] }) -export class ErrorReportComponent implements OnInit { +export class ErrorReportComponent implements OnInit, OnDestroy, AfterViewInit { public currentReportId: string = "blank"; public currentReport: ErrorReport = {} as ErrorReport; public loading = true; @@ -19,7 +20,12 @@ export class ErrorReportComponent implements OnInit { constructor( public route: ActivatedRoute, public socketService: SocketService, + public navbarVisibilityService: NavbarVisibilityService, + public locationBackService: LocationBackService, + private renderer: Renderer2, + private el: ElementRef ) { + navbarVisibilityService.hideNavbar(); this.route.params.subscribe((params) => { if (params.errorReportId) { this.currentReportId = params.errorReportId; @@ -27,7 +33,15 @@ export class ErrorReportComponent implements OnInit { }); } - ngOnInit(): void { + ngAfterViewInit() { + this.renderer.setStyle( + this.el.nativeElement.ownerDocument.body, + 'background', + '#3973aa' + ); + } + + ngOnInit() { this.socketService.getErrorReportById(this.currentReportId) .then((errorReport) => { this.currentReport = errorReport as ErrorReport; @@ -44,4 +58,12 @@ export class ErrorReportComponent implements OnInit { }); } + ngOnDestroy() { + this.renderer.removeStyle( + this.el.nativeElement.ownerDocument.body, + 'background' + ); + this.navbarVisibilityService.showNavbar(); + } + } diff --git a/UI/src/app/_services/locationBack.service.ts b/UI/src/app/_services/locationBack.service.ts new file mode 100644 index 00000000..ba0d3e29 --- /dev/null +++ b/UI/src/app/_services/locationBack.service.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@angular/core' +import { Location } from '@angular/common' +import { Router, NavigationEnd } from '@angular/router' + +/* Based on https://nils-mehlhorn.de/posts/angular-navigate-back-previous-page */ + +@Injectable({ providedIn: 'root' }) +export class LocationBackService { + private history: string[] = []; + + constructor(private router: Router, private location: Location) { + this.router.events.subscribe((event) => { + if (event instanceof NavigationEnd && !event.urlAfterRedirects.includes('login')) { + this.history.push(event.urlAfterRedirects); + } + }) + } + + goBack(): void { + this.history.pop(); + if (this.history.length > 0) { + this.location.back(); + } else { + this.router.navigateByUrl('/home'); + } + } +} \ No newline at end of file diff --git a/UI/src/app/_services/navbar-visibility.service.ts b/UI/src/app/_services/navbar-visibility.service.ts new file mode 100644 index 00000000..38b81295 --- /dev/null +++ b/UI/src/app/_services/navbar-visibility.service.ts @@ -0,0 +1,16 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class NavbarVisibilityService { + public navbarIsVisible: boolean = true; + + public hideNavbar() { + this.navbarIsVisible = false; + } + + public showNavbar() { + this.navbarIsVisible = true; + } +} diff --git a/UI/src/app/app.component.html b/UI/src/app/app.component.html index d6a97d05..022f3f0e 100644 --- a/UI/src/app/app.component.html +++ b/UI/src/app/app.component.html @@ -1,5 +1,5 @@ -