+const url = 'https://binomiant.duckdns.org/mVk7Yr3k/v1';
+const prodStatus = '/status';
+const prodEvents = '/events';
+const prodControl = '/control';
+const prodNotification = '/test/notification';
+const testStatus = '/test/status';
+const testEvents = '/test/events';
+const testControl = '/test/control';
+const base64PubKey = 'BLDSdGasI5sLks30brbIWvlLMFqzoxxkOs7'
+ + 'aW_E9PDBzIO_mDs6-tvtb2U0-BVFDafNd58DJgoXxdK5711FF29c';
+
function setItem(key, value) {
if (key === null) {
return;
hasItem: hasItem,
};
+function urlBase64ToUint8Array(base64String) {
+ const padding = '='.repeat((4 - base64String.length % 4) % 4);
+ const base64 = (base64String + padding)
+ .replace(/\-/g, '+')
+ .replace(/_/g, '/');
+ const rawData = window.atob(base64);
+ const outputArray = new Uint8Array(rawData.length);
+
+ for (let i = 0; i < rawData.length; ++i) {
+ outputArray[i] = rawData.charCodeAt(i);
+ }
+
+ return outputArray;
+}
+
+function register() {
+ const permission = Notification.permission;
+ switch (permission) {
+ case 'granted':
+ return new Promise((resolve, reject) => {
+ navigator.serviceWorker.ready
+ .then(registration => {
+ if (registration) {
+ registration.pushManager.getSubscription()
+ .then(subscription => {
+ if (subscription) {
+ resolve(subscription);
+ } else {
+ const publicKey = urlBase64ToUint8Array(base64PubKey);
+ registration.pushManager.subscribe({
+ userVisibleOnly: true,
+ applicationServerKey: publicKey,
+ })
+ .then(subscription => {
+ if (subscription) {
+ resolve(subscription);
+ } else {
+ reject(null);
+ }
+ });
+ }
+ })
+ .catch(err => {
+ reject(err);
+ });
+ } else {
+ reject(null);
+ }
+ })
+ .catch(err => {
+ reject(err);
+ });
+ });
+ break;
+ case 'default':
+ return new Promise((resolve, reject) => {
+ Notification.requestPermission()
+ .then(status => {
+ if (status) {
+ switch (status) {
+ case 'granted':
+ navigator.serviceWorker.ready
+ .then(registration => {
+ if (registration) {
+ const publicKey = urlBase64ToUint8Array(base64PubKey);
+ registration.pushManager.subscribe({
+ userVisibleOnly: true,
+ applicationServerKey: publicKey,
+ })
+ .then(subscription => {
+ if (subscription) {
+ resolve(subscription);
+ } else {
+ reject(null);
+ }
+ });
+ } else {
+ reject(null);
+ }
+ })
+ .catch(err => {
+ reject(err);
+ });
+ break;
+ case 'denied':
+ case 'default':
+ default:
+ reject(null);
+ break;
+ }
+ } else {
+ reject(null);
+ }
+ })
+ .catch(err => {
+ reject(err);
+ });
+ });
+ break;
+ case 'denied':
+ default:
+ return Promise.reject();
+ break;
+ }
+}
+
+function subscribe() {
+ var apiKey = getItem('apiKey');
+
+ var data = {
+ path: prodNotification,
+ query: '',
+ method: 'POST',
+ apiKey: apiKey,
+ requestContentType: 'json',
+ responseContentType: 'json',
+ body: '',
+ }
+
+ return new Promise((resolve, reject) => {
+ register()
+ .then(subscription => {
+ if (subscription === null) {
+ reject(null);
+ } else {
+ data.body = JSON.stringify(subscription);
+ sendRequest(data)
+ .then(response => {
+ resolve(response);
+ })
+ .catch(err => {
+ reject(err);
+ });
+ }
+ })
+ .catch(err => {
+ reject(err);
+ });
+ });
+}
+
+function unsubscribe() {
+ return new Promise((resolve, reject) => {
+ navigator.serviceWorker.ready
+ .then(registration => {
+ registration.pushManager.getSubscription()
+ .then(subscription => {
+ subscription.unsubscribe()
+ .then(result => {
+ resolve(result);
+ })
+ .catch(err => {
+ reject(err);
+ });
+ })
+ .catch(err => {
+ reject(err);
+ });
+ })
+ .catch(err => {
+ reject(err);
+ });
+ });
+}
+
export const notification = {
+ subscribe: subscribe,
+ unsubscribe: unsubscribe,
};
-const url = 'https://binomiant.duckdns.org/mVk7Yr3k/v1';
-const prodStatus = '/status';
-const prodEvents = '/events';
-const prodControl = '/control';
-const testStatus = '/test/status';
-const testEvents = '/test/events';
-const testControl = '/test/control';
-
async function sendRequest(data) {
var init = {
method: data.method,
if (data.requestContentType != null) {
init.headers['Content-Type'] = 'application/json';
}
- if (data.apiKey == null) {
+ if (data.apiKey === '') {
init.headers['X-API-Key-Test'] = '2TTqCD4mNNny';
} else {
init.headers['X-API-Key'] = data.apiKey;
}
+ if (data.body) {
+ init.body = data.body;
+ }
const response = await fetch(url + data.path + data.query, init);
if (response.status >= 200 && response.status <= 399) {
function getStatus() {
const testMode = getItem('testMode');
var path = '',
- apiKey = null;
+ apiKey = '';
var query = new URLSearchParams('');
if (testMode) {
function getEvents() {
const testMode = getItem('testMode');
var path = '',
- apiKey = null;
+ apiKey = '';
var query = new URLSearchParams('');
if (testMode) {
function postControl() {
const testMode = getItem('testMode');
var path = '',
- apiKey = null;
+ apiKey = '';
var query = new URLSearchParams('');
if (testMode) {
--- /dev/null
+/* Custom part */
+
+workbox.core.setCacheNameDetails({prefix: "garnod-pwa.git"});
+
+self.addEventListener('message', (event) => {
+ if (event.data && event.data.type === 'SKIP_WAITING') {
+ self.skipWaiting();
+ }
+});
+
+self.addEventListener('push', event => {
+ const notificationTitle = 'Garage Node';
+
+ var message = 'Door is open!';
+ var icon = null;
+ if (event.data) {
+ message = event.data.json().message;
+ icon = event.data.json().icon;
+ }
+
+ var notificationOptions = {
+ body: message,
+ tag: 'notification'
+ };
+ if (icon) {
+ notificationOptions.badge = icon;
+ notificationOptions.icon = icon;
+ }
+
+ event.waitUntil(
+ Promise.all([
+ self.registration.showNotification(
+ notificationTitle, notificationOptions)
+ ])
+ );
+});
+
+self.addEventListener('notificationclick', event => {
+ event.notification.close();
+
+ var clickResponsePromise = Promise.resolve();
+ if (event.notification.data && event.notification.data.url) {
+ clickResponsePromise = clients.openWindow(event.notification.data.url);
+ }
+
+ event.waitUntil(
+ Promise.all([
+ clickResponsePromise
+ ])
+ );
+});
+
+/**
+ * The workboxSW.precacheAndRoute() method efficiently caches and responds to
+ * requests for URLs in the manifest.
+ * See https://goo.gl/S9QRab
+ */
+self.__precacheManifest = [].concat(self.__precacheManifest || []);
+workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
:showApiKeyDialog="showApiKeyDialog"
v-on:apiKeySetEvent="setApiKey"
/>
+
+ <v-snackbar
+ v-model="snackbar.show"
+ :timeout="snackbar.timeout"
+ :color="snackbar.color"
+ bottom
+ >
+ {{snackbar.text}}
+ </v-snackbar>
</v-content>
</template>
<script>
import ApiKeyDialog from '@/components/ApiKey'
-import { storage } from '@/lib/lib.js'
+import { storage, notification } from '@/lib/lib.js'
+
+const messages = {
+ subscribeFailed: {
+ text: 'Subscribe notification failed',
+ color: 'error',
+ },
+};
export default {
components: {
},
],
showApiKeyDialog: false,
+ snackbar: {
+ show: false,
+ timeout: 5000,
+ color: messages.subscribeFailed.color,
+ text: messages.subscribeFailed.text,
+ },
};
},
created() {
this.listItems[1].value = !this.listItems[1].value;
this.settings[1].value = this.listItems[1].value;
storage.setItem('pushNotification', this.settings[1].value);
+
+ if (this.settings[1].value) {
+ notification.subscribe()
+ .then(result => {
+ if (result === null) {
+ this.listItems[1].value = !this.listItems[1].value;
+ this.settings[1].value = this.listItems[1].value;
+ storage.setItem('pushNotification', this.settings[1].value);
+ this.snackbar.show = true;
+ }
+ })
+ .catch(err => {
+ this.listItems[1].value = !this.listItems[1].value;
+ this.settings[1].value = this.listItems[1].value;
+ storage.setItem('pushNotification', this.settings[1].value);
+ this.snackbar.show = true;
+ });
+ } else {
+ notification.unsubscribe()
+ .then(result => {
+ if (result === null) {
+ this.listItems[1].value = !this.listItems[1].value;
+ this.settings[1].value = this.listItems[1].value;
+ storage.setItem('pushNotification', this.settings[1].value);
+ this.snackbar.show = true;
+ }
+ })
+ .catch(err => {
+ this.listItems[1].value = !this.listItems[1].value;
+ this.settings[1].value = this.listItems[1].value;
+ storage.setItem('pushNotification', this.settings[1].value);
+ this.snackbar.show = true;
+ });
+ }
},
setPushNotificationInSwitch() {
this.listItems[1].value = !this.listItems[1].value;