From 3b0efcac9d09606d2195b9c9f558efc9fd478012 Mon Sep 17 00:00:00 2001 From: Oliver Giles Date: Fri, 29 Dec 2017 17:18:43 +0200 Subject: [PATCH] resolves #23: In-browser notification This implements web notifications on job completion. Opt-in by default, the preference is saved in js localStorage and can be toggled directly on any page --- src/resources/index.html | 12 +++++++----- src/resources/js/app.js | 30 +++++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/resources/index.html b/src/resources/index.html index 62bba13..b8e5e71 100644 --- a/src/resources/index.html +++ b/src/resources/index.html @@ -25,6 +25,9 @@ a.navbar-btn.active { color: #fff; } a.navbar-btn:hover { color: #fff; text-decoration: none; } a.navbar-btn:focus { color: #fff; } + .bell { margin: 8px 15px; color: #9d9d9d; } + .bell:hover { text-decoration: none; color: #9d9d9d; cursor: pointer; } + .bell.active { color: #333; } dt,dd { line-height: 2; } canvas { width: 100% !important; @@ -219,13 +222,12 @@
+ 🔔
diff --git a/src/resources/js/app.js b/src/resources/js/app.js index eb6990e..0830bc0 100644 --- a/src/resources/js/app.js +++ b/src/resources/js/app.js @@ -32,8 +32,11 @@ const WebsocketHandler = function() { // at this point, the component must be defined if (!this.comp) return console.error("Page component was undefined"); - else if (typeof this.comp[msg.type] === 'function') - this.comp[msg.type](msg.data); + else { + this.comp.$root.showNotify(msg.type, msg.data); + if(typeof this.comp[msg.type] === 'function') + this.comp[msg.type](msg.data); + } } }; ws.onclose = function(ev) { @@ -513,7 +516,28 @@ new Vue({ el: '#app', data: { title: '', // populated by status ws message - connected: false + connected: false, + notify: 'localStorage' in window && localStorage.getItem('showNotifications') == 1 + }, + computed: { + supportsNotifications() { + return 'Notification' in window && Notification.permission !== 'denied'; + } + }, + methods: { + toggleNotifications(en) { + if(Notification.permission !== 'granted') + Notification.requestPermission(p => this.notify = (p === 'granted')) + else + this.notify = en; + }, + showNotify(msg, data) { + if(this.notify && msg === 'job_completed') + new Notification(data.name + ' ' + '#' + data.number +' completed'); + } + }, + watch: { + notify(e) { localStorage.setItem('showNotifications', e ? 1 : 0); } }, router: new VueRouter({ mode: 'history',