From 5efe930d6cfa1d24bf444d35499f98023b00e64e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 14 Aug 2016 00:31:52 -0700 Subject: [PATCH] Add HTML 5 push notifcations support (#89) * Add push notification handling to service worker * Add push registration to sidebar * Whitelist manifest.json * Remove unused property * Catch if no url specified * Fix eslint * Fix bug * Fix some bugs * More Firefox proof * Moar fixes * Fix semi --- home-assistant-js | 2 +- package.json | 2 +- script/sw-dev.js | 10 ---- script/sw-precache.js | 57 +++++++++++++++++++-- src/components/ha-sidebar.html | 91 ++++++++++++++++++++++++++++------ 5 files changed, 129 insertions(+), 33 deletions(-) delete mode 100755 script/sw-dev.js diff --git a/home-assistant-js b/home-assistant-js index 4f22ce18a7..bf4adea645 160000 --- a/home-assistant-js +++ b/home-assistant-js @@ -1 +1 @@ -Subproject commit 4f22ce18a788888bff1cbdafb3586edc715ba29e +Subproject commit bf4adea64549d0adb95597f1846d90bc47648c0e diff --git a/package.json b/package.json index 50de0901b5..9a406fffbe 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "scripts": { "setup_js_dev": "git submodule init && git submodule update && cd home-assistant-js && npm install", "clean": "rm -rf build/* build-temp/*", - "js_dev": "script/sw-dev.js && npm run watch_ru_all", + "js_dev": "script/sw-precache.js && npm run watch_ru_all", "js_dev_demo": "BUILD_DEMO=1 npm run watch_ru_all", "js_prod": "BUILD_DEV=0 npm run ru_all", "js_demo": "BUILD_DEV=0 BUILD_DEMO=1 npm run ru_all", diff --git a/script/sw-dev.js b/script/sw-dev.js deleted file mode 100755 index 0d7c304835..0000000000 --- a/script/sw-dev.js +++ /dev/null @@ -1,10 +0,0 @@ -#! /usr/bin/env node - -var fs = require('fs'); -var path = require('path'); - -var content = ` -console.warn('Service worker disabled in dev mode'); -`; - -fs.writeFileSync(path.join('build', 'service_worker.js'), content); diff --git a/script/sw-precache.js b/script/sw-precache.js index e9dfe3bc75..ce85f898d3 100755 --- a/script/sw-precache.js +++ b/script/sw-precache.js @@ -15,9 +15,10 @@ var path = require('path'); var swPrecache = require('sw-precache'); var uglifyJS = require('uglify-js'); +const DEV = !!JSON.parse(process.env.BUILD_DEV || 'true'); + var rootDir = '..'; var panelDir = rootDir + '/panels'; -// var panels = fs.readdirSync(panelDir); var dynamicUrlToDependencies = { '/': [rootDir + '/frontend.html', rootDir + '/core.js'], @@ -55,9 +56,9 @@ panelsFingerprinted.forEach(panel => { dynamicUrlToDependencies[url] = [fpath]; }); -var options = { +const options = { navigateFallback: '/', - navigateFallbackWhitelist: [/^((?!(static|api|local|service_worker.js)).)*$/], + navigateFallbackWhitelist: [/^((?!(static|api|local|service_worker.js|manifest.json)).)*$/], dynamicUrlToDependencies: dynamicUrlToDependencies, staticFileGlobs: [ rootDir + '/icons/favicon.ico', @@ -74,9 +75,55 @@ var options = { verbose: true, }; -var genPromise = swPrecache.generate(options); +const devBase = 'console.warn("Service worker caching disabled in development")'; -if (true) { +const notify = ` +self.addEventListener("push", function(event) { + var data; + if (event.data) { + data = event.data.json(); + event.waitUntil(self.registration.showNotification(data.title, data)); + } +}); +self.addEventListener('notificationclick', function(event) { + var url; + + if (!event.notification.data || !event.notification.data.url) { + return; + } + + event.notification.close(); + url = event.notification.data.url; + + if (!url) return; + + event.waitUntil( + clients.matchAll({ + type: 'window', + }) + .then(function (windowClients) { + var i; + var client; + for (i = 0; i < windowClients.length; i++) { + client = windowClients[i]; + if (client.url === url && 'focus' in client) { + return client.focus(); + } + } + if (clients.openWindow) { + return clients.openWindow(url); + } + return undefined; + }) + ); +}); +`; + +let genPromise = DEV ? Promise.resolve(devBase) : swPrecache.generate(options); + +genPromise = genPromise.then(swString => swString + '\n' + notify); + +if (!DEV) { genPromise = genPromise.then( swString => uglifyJS.minify(swString, { fromString: true }).code); } diff --git a/src/components/ha-sidebar.html b/src/components/ha-sidebar.html index f50abbdc2d..69b2aa4822 100644 --- a/src/components/ha-sidebar.html +++ b/src/components/ha-sidebar.html @@ -78,7 +78,7 @@ opacity: var(--dark-divider-opacity) } - .streaming { + .setting { @apply(--sidebar-text); } @@ -120,8 +120,18 @@
+ + -
Streaming updates
+
Streaming updates
@@ -180,20 +190,6 @@ Polymer({ }, }, - hasHistoryComponent: { - type: Boolean, - bindNuclear: function (hass) { - return hass.configGetters.isComponentLoaded('history'); - }, - }, - - hasLogbookComponent: { - type: Boolean, - bindNuclear: function (hass) { - return hass.configGetters.isComponentLoaded('logbook'); - }, - }, - panels: { type: Array, bindNuclear: function (hass) { @@ -203,6 +199,19 @@ Polymer({ ]; }, }, + + supportPush: { + type: Boolean, + value: 'PushManager' in window && + (document.location.protocol === 'https:' || + document.location.hostname === 'localhost'), + }, + + pushToggleChecked: { + type: Boolean, + value: 'Notification' in window && + Notification.permission === 'granted', + }, }, created: function () { @@ -283,6 +292,56 @@ Polymer({ this.debounce('updateStyles', this._boundUpdateStyles, 1); }, + handlePushChange: function (ev) { + var subscribe = ev.target.checked; + + // MVP, will move later. + var promise = navigator.serviceWorker.getRegistration().then(function (reg) { + if (!reg) { + throw new Error('No service worker registered'); + } + + return reg.pushManager.subscribe({ + userVisibleOnly: true, + }); + }); + + if (!subscribe) { + promise.then(function (sub) { + sub.unsubscribe(); + }); + return; + } + + promise.then(function (sub) { + var browserName; + if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) { + browserName = 'firefox'; + } else { + browserName = 'chrome'; + } + + return this.hass.callApi('POST', 'notify.html5', { + subscription: sub, + browser: browserName, + }); + }.bind(this)).catch(function (err) { + var message; + + if (err.message && err.message.indexOf('gcm_sender_id') !== -1) { + message = 'Please setup the notify.html5 platform.'; + } else { + message = 'Notification registration failed.'; + } + + /* eslint-disable no-console */ + console.error(err); + /* eslint-enable no-console */ + this.hass.notificationActions.createNotification(message); + this.pushToggleChecked = false; + }.bind(this)); + }, + handleLogOut: function () { this.hass.authActions.logOut(); },