diff --git a/src/opnsense/www/js/opnsense_widget_manager.js b/src/opnsense/www/js/opnsense_widget_manager.js index 45c34572d..1ce21aac8 100644 --- a/src/opnsense/www/js/opnsense_widget_manager.js +++ b/src/opnsense/www/js/opnsense_widget_manager.js @@ -188,6 +188,10 @@ class WidgetManager { widget.setId(id); this.widgetClasses[id] = widget; + document.addEventListener('visibilitychange', (e) => { + this.widgetClasses[id].onVisibilityChanged(!document.hidden); + }); + if (!id in this.widgetTranslations) { console.error('Missing translations for widget', id); } diff --git a/src/opnsense/www/js/widgets/BaseWidget.js b/src/opnsense/www/js/widgets/BaseWidget.js index 55b4474b7..a4dcf7a0b 100644 --- a/src/opnsense/www/js/widgets/BaseWidget.js +++ b/src/opnsense/www/js/widgets/BaseWidget.js @@ -31,6 +31,9 @@ export default class BaseWidget { this.translations = {}; this.tickTimeout = 5000; // Default tick timeout this.resizeHandles = "all" + this.eventSource = null; + this.eventSourceUrl = null; + this.eventSourceOnData = null; } getResizeHandles() { @@ -67,7 +70,40 @@ export default class BaseWidget { } onWidgetClose() { - return null; + this.closeEventSource(); + } + + openEventSource(url, onMessage) { + this.closeEventSource(); + + this.eventSourceUrl = url; + this.eventSourceOnData = onMessage; + this.eventSource = new EventSource(url); + this.eventSource.onmessage = onMessage; + this.eventSource.onerror = (e) => { + if (this.eventSource.readyState == EventSource.closed) { + this.closeEventSource(); + } else { + console.error('Unknown error occurred during streaming operation', e); + } + }; + } + + closeEventSource() { + if (this.eventSource !== null) { + this.eventSource.close(); + this.eventSource = null; + } + } + + onVisibilityChanged(visible) { + if (this.eventSourceUrl !== null) { + if (visible) { + this.openEventSource(this.eventSourceUrl, this.eventSourceOnData); + } else if (this.eventSource !== null) { + this.closeEventSource(); + } + } } /* For testing purposes */ diff --git a/src/opnsense/www/js/widgets/Cpu.js b/src/opnsense/www/js/widgets/Cpu.js index 0d5257cf5..f0374cbb8 100644 --- a/src/opnsense/www/js/widgets/Cpu.js +++ b/src/opnsense/www/js/widgets/Cpu.js @@ -32,7 +32,6 @@ export default class Cpu extends BaseWidget { constructor() { super(); this.resizeHandles = "e, w"; - this.eventSource = null; } _createChart(selector, timeSeries) { @@ -85,12 +84,6 @@ export default class Cpu extends BaseWidget { return $container; } - onwidgetClose() { - if (this.eventSource !== null) { - this.eventSource.close(); - } - } - async onMarkupRendered() { ajaxGet('/api/diagnostics/cpu_usage/getcputype', {}, (data, status) => { $('.cpu-type').text(data); @@ -104,11 +97,9 @@ export default class Cpu extends BaseWidget { this._createChart('cpu-usage-intr', intr_ts); this._createChart('cpu-usage-user', user_ts); this._createChart('cpu-usage-sys', sys_ts); - this.eventSource = new EventSource('/api/diagnostics/cpu_usage/stream'); - - this.eventSource.onmessage = function(event) { + super.openEventSource('/api/diagnostics/cpu_usage/stream', (event) => { if (!event) { - this.eventSource.close(); + super.closeEventSource(); } const data = JSON.parse(event.data); let date = Date.now(); @@ -116,7 +107,7 @@ export default class Cpu extends BaseWidget { intr_ts.append(date, data.intr); user_ts.append(date, data.user); sys_ts.append(date, data.sys); - }; + }); } onWidgetResize(elem, width, height) { diff --git a/src/opnsense/www/js/widgets/Firewall.js b/src/opnsense/www/js/widgets/Firewall.js index 63c60ae87..72990d431 100644 --- a/src/opnsense/www/js/widgets/Firewall.js +++ b/src/opnsense/www/js/widgets/Firewall.js @@ -33,7 +33,6 @@ export default class Firewall extends BaseTableWidget { constructor(config) { super(); this.config = config; - this.eventSource = null; this.ifMap = {}; this.counters = {}; this.chart = null; @@ -86,7 +85,7 @@ export default class Firewall extends BaseTableWidget { _onMessage(event) { if (!event) { - this.eventSource.close(); + super.closeEventSource(); } let actIcons = { @@ -190,8 +189,7 @@ export default class Firewall extends BaseTableWidget { return; } - this.eventSource = new EventSource('/api/diagnostics/firewall/streamLog'); - this.eventSource.onmessage = this._onMessage.bind(this); + super.openEventSource('/api/diagnostics/firewall/streamLog', this._onMessage.bind(this)); let context = document.getElementById('fw-chart').getContext('2d'); let config = { @@ -285,9 +283,7 @@ export default class Firewall extends BaseTableWidget { } onWidgetClose() { - if (this.eventSource !== null) { - this.eventSource.close(); - } + super.onWidgetClose(); if (this.chart !== null) { this.chart.destroy(); diff --git a/src/opnsense/www/js/widgets/Traffic.js b/src/opnsense/www/js/widgets/Traffic.js index 4a32523bf..adc5ad2e8 100644 --- a/src/opnsense/www/js/widgets/Traffic.js +++ b/src/opnsense/www/js/widgets/Traffic.js @@ -37,7 +37,6 @@ export default class Traffic extends BaseWidget { trafficOut: null }; this.initialized = false; - this.eventSource = null; this.datasets = {inbytes: [], outbytes: []}; } @@ -155,7 +154,7 @@ export default class Traffic extends BaseWidget { _onMessage(event) { if (!event) { - this.eventSource.close(); + super.closeEventSource(); } const data = JSON.parse(event.data); @@ -199,15 +198,12 @@ export default class Traffic extends BaseWidget { } async onMarkupRendered() { - this.eventSource = new EventSource('/api/diagnostics/traffic/stream/2'); - this.eventSource.onmessage = this._onMessage.bind(this); + super.openEventSource('/api/diagnostics/traffic/stream/1', this._onMessage.bind(this)); } onWidgetClose() { + super.onWidgetClose(); this.charts.trafficIn.destroy(); this.charts.trafficOut.destroy(); - if (this.eventSource !== null) { - this.eventSource.close(); - } } }