mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-15 05:16:34 +00:00
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
This commit is contained in:
parent
474366c536
commit
5efe930d6c
@ -1 +1 @@
|
|||||||
Subproject commit 4f22ce18a788888bff1cbdafb3586edc715ba29e
|
Subproject commit bf4adea64549d0adb95597f1846d90bc47648c0e
|
@ -9,7 +9,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"setup_js_dev": "git submodule init && git submodule update && cd home-assistant-js && npm install",
|
"setup_js_dev": "git submodule init && git submodule update && cd home-assistant-js && npm install",
|
||||||
"clean": "rm -rf build/* build-temp/*",
|
"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_dev_demo": "BUILD_DEMO=1 npm run watch_ru_all",
|
||||||
"js_prod": "BUILD_DEV=0 npm run ru_all",
|
"js_prod": "BUILD_DEV=0 npm run ru_all",
|
||||||
"js_demo": "BUILD_DEV=0 BUILD_DEMO=1 npm run ru_all",
|
"js_demo": "BUILD_DEV=0 BUILD_DEMO=1 npm run ru_all",
|
||||||
|
@ -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);
|
|
@ -15,9 +15,10 @@ var path = require('path');
|
|||||||
var swPrecache = require('sw-precache');
|
var swPrecache = require('sw-precache');
|
||||||
var uglifyJS = require('uglify-js');
|
var uglifyJS = require('uglify-js');
|
||||||
|
|
||||||
|
const DEV = !!JSON.parse(process.env.BUILD_DEV || 'true');
|
||||||
|
|
||||||
var rootDir = '..';
|
var rootDir = '..';
|
||||||
var panelDir = rootDir + '/panels';
|
var panelDir = rootDir + '/panels';
|
||||||
// var panels = fs.readdirSync(panelDir);
|
|
||||||
|
|
||||||
var dynamicUrlToDependencies = {
|
var dynamicUrlToDependencies = {
|
||||||
'/': [rootDir + '/frontend.html', rootDir + '/core.js'],
|
'/': [rootDir + '/frontend.html', rootDir + '/core.js'],
|
||||||
@ -55,9 +56,9 @@ panelsFingerprinted.forEach(panel => {
|
|||||||
dynamicUrlToDependencies[url] = [fpath];
|
dynamicUrlToDependencies[url] = [fpath];
|
||||||
});
|
});
|
||||||
|
|
||||||
var options = {
|
const options = {
|
||||||
navigateFallback: '/',
|
navigateFallback: '/',
|
||||||
navigateFallbackWhitelist: [/^((?!(static|api|local|service_worker.js)).)*$/],
|
navigateFallbackWhitelist: [/^((?!(static|api|local|service_worker.js|manifest.json)).)*$/],
|
||||||
dynamicUrlToDependencies: dynamicUrlToDependencies,
|
dynamicUrlToDependencies: dynamicUrlToDependencies,
|
||||||
staticFileGlobs: [
|
staticFileGlobs: [
|
||||||
rootDir + '/icons/favicon.ico',
|
rootDir + '/icons/favicon.ico',
|
||||||
@ -74,9 +75,55 @@ var options = {
|
|||||||
verbose: true,
|
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(
|
genPromise = genPromise.then(
|
||||||
swString => uglifyJS.minify(swString, { fromString: true }).code);
|
swString => uglifyJS.minify(swString, { fromString: true }).code);
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@
|
|||||||
opacity: var(--dark-divider-opacity)
|
opacity: var(--dark-divider-opacity)
|
||||||
}
|
}
|
||||||
|
|
||||||
.streaming {
|
.setting {
|
||||||
@apply(--sidebar-text);
|
@apply(--sidebar-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,8 +120,18 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class='divider'></div>
|
<div class='divider'></div>
|
||||||
|
|
||||||
|
<template is='dom-if' if='[[supportPush]]'>
|
||||||
<paper-item class='horizontal layout justified'>
|
<paper-item class='horizontal layout justified'>
|
||||||
<div class='streaming'>Streaming updates</div>
|
<div class='setting'>Push Notifications</div>
|
||||||
|
<paper-toggle-button
|
||||||
|
on-change='handlePushChange'
|
||||||
|
checked='{{pushToggleChecked}}'
|
||||||
|
></paper-toggle-button>
|
||||||
|
</paper-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<paper-item class='horizontal layout justified'>
|
||||||
|
<div class='setting'>Streaming updates</div>
|
||||||
<stream-status hass='[[hass]]'></stream-status>
|
<stream-status hass='[[hass]]'></stream-status>
|
||||||
</paper-item>
|
</paper-item>
|
||||||
|
|
||||||
@ -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: {
|
panels: {
|
||||||
type: Array,
|
type: Array,
|
||||||
bindNuclear: function (hass) {
|
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 () {
|
created: function () {
|
||||||
@ -283,6 +292,56 @@ Polymer({
|
|||||||
this.debounce('updateStyles', this._boundUpdateStyles, 1);
|
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 () {
|
handleLogOut: function () {
|
||||||
this.hass.authActions.logOut();
|
this.hass.authActions.logOut();
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user