From d0b47488fb69b2b8449fa73e8d94e2cdcc9adda8 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Thu, 20 Dec 2018 09:15:30 +0300
Subject: [PATCH 1/4] default webPush to false, because having it at true leads
 to some problems with local dev mode. Instances can re-enable it, and BE can
 default it to true in config.exs

---
 src/modules/config.js | 2 +-
 static/config.json    | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/modules/config.js b/src/modules/config.js
index ccfd0190..c9528f6f 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -24,7 +24,7 @@ const defaultState = {
     likes: true,
     repeats: true
   },
-  webPushNotifications: true,
+  webPushNotifications: false,
   muteWords: [],
   highlight: {},
   interfaceLanguage: browserLocale,
diff --git a/static/config.json b/static/config.json
index 7887f930..f508eea1 100644
--- a/static/config.json
+++ b/static/config.json
@@ -16,5 +16,6 @@
   "alwaysShowSubjectInput": true,
   "hidePostStats": false,
   "hideUserStats": false,
-  "loginMethod": "password"
+  "loginMethod": "password",
+  "webPushNotifications": false
 }

From 957b2a6f7ef8ca381fc44b79a17639e400407b67 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Thu, 20 Dec 2018 09:17:59 +0300
Subject: [PATCH 2/4] simplified some code, made it possible to unregister
 serviceworker altogether

---
 src/main.js               | 23 ++++++++++-------------
 src/modules/users.js      |  5 ++++-
 src/services/push/push.js | 17 ++++++++++++++++-
 3 files changed, 30 insertions(+), 15 deletions(-)

diff --git a/src/main.js b/src/main.js
index bf92e78e..c22a762e 100644
--- a/src/main.js
+++ b/src/main.js
@@ -54,24 +54,21 @@ const persistedStateOptions = {
 const registerPushNotifications = store => {
   store.subscribe((mutation, state) => {
     const vapidPublicKey = state.instance.vapidPublicKey
+    const webPushNotification = state.config.webPushNotifications
     const permission = state.interface.notificationPermission === 'granted'
-    const isUserMutation = mutation.type === 'setCurrentUser'
-
-    if (isUserMutation && vapidPublicKey && permission) {
-      return store.dispatch('registerPushNotifications')
-    }
-
     const user = state.users.currentUser
+
+    const isUserMutation = mutation.type === 'setCurrentUser'
     const isVapidMutation = mutation.type === 'setInstanceOption' && mutation.payload.name === 'vapidPublicKey'
-
-    if (isVapidMutation && user && permission) {
-      return store.dispatch('registerPushNotifications')
-    }
-
     const isPermMutation = mutation.type === 'setNotificationPermission' && mutation.payload === 'granted'
+    const isUserConfigMutation = mutation.type === 'setOption' && mutation.payload.name === 'webPushNotifications'
 
-    if (isPermMutation && user && vapidPublicKey) {
-      return store.dispatch('registerPushNotifications')
+    if (isUserMutation || isVapidMutation || isPermMutation || isUserConfigMutation) {
+      if (user && vapidPublicKey && permission && webPushNotification) {
+        return store.dispatch('registerPushNotifications')
+      } else if (isUserConfigMutation && !webPushNotification) {
+        return store.dispatch('unregisterPushNotifications')
+      }
     }
   })
 }
diff --git a/src/modules/users.js b/src/modules/users.js
index 13d3f26e..2ea0919e 100644
--- a/src/modules/users.js
+++ b/src/modules/users.js
@@ -1,7 +1,7 @@
 import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
 import { compact, map, each, merge } from 'lodash'
 import { set } from 'vue'
-import registerPushNotifications from '../services/push/push.js'
+import { registerPushNotifications, unregisterPushNotifications } from '../services/push/push.js'
 import oauthApi from '../services/new_api/oauth'
 import { humanizeErrors } from './errors'
 
@@ -116,6 +116,9 @@ const users = {
 
       registerPushNotifications(isEnabled, vapidPublicKey, token)
     },
+    unregisterPushNotifications (store) {
+      unregisterPushNotifications()
+    },
     addNewStatuses (store, { statuses }) {
       const users = map(statuses, 'user')
       const retweetedUsers = compact(map(statuses, 'retweeted_status.user'))
diff --git a/src/services/push/push.js b/src/services/push/push.js
index 1ac304d1..ff67fd5a 100644
--- a/src/services/push/push.js
+++ b/src/services/push/push.js
@@ -19,6 +19,12 @@ function registerServiceWorker () {
     .catch((err) => console.error('Unable to register service worker.', err))
 }
 
+function unregisterServiceWorker () {
+  return runtime.register()
+    .then((registration) => registration.unregister())
+    .catch((err) => console.error('Unable to unregister serviceworker', err))
+}
+
 function subscribe (registration, isEnabled, vapidPublicKey) {
   if (!isEnabled) return Promise.reject(new Error('Web Push is disabled in config'))
   if (!vapidPublicKey) return Promise.reject(new Error('VAPID public key is not found'))
@@ -59,7 +65,7 @@ function sendSubscriptionToBackEnd (subscription, token) {
     })
 }
 
-export default function registerPushNotifications (isEnabled, vapidPublicKey, token) {
+export function registerPushNotifications (isEnabled, vapidPublicKey, token) {
   if (isPushSupported()) {
     registerServiceWorker()
       .then((registration) => subscribe(registration, isEnabled, vapidPublicKey))
@@ -67,3 +73,12 @@ export default function registerPushNotifications (isEnabled, vapidPublicKey, to
       .catch((e) => console.warn(`Failed to setup Web Push Notifications: ${e.message}`))
   }
 }
+
+export function unregisterPushNotifications (isEnabled, vapidPublicKey, token) {
+  if (isPushSupported()) {
+    unregisterServiceWorker()
+      .then((registration) => subscribe(registration, isEnabled, vapidPublicKey))
+      .then((subscription) => sendSubscriptionToBackEnd(subscription, token))
+      .catch((e) => console.warn(`Failed to setup Web Push Notifications: ${e.message}`))
+  }
+}

From a4f09029260100f6d5baea67ac333593c5c4432c Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Tue, 25 Dec 2018 03:46:19 +0300
Subject: [PATCH 3/4] small refactor, added push unsub notice for BE

---
 src/modules/users.js      |  4 +-
 src/services/push/push.js | 77 +++++++++++++++++++++++++++------------
 2 files changed, 56 insertions(+), 25 deletions(-)

diff --git a/src/modules/users.js b/src/modules/users.js
index 2ea0919e..f2b59aaa 100644
--- a/src/modules/users.js
+++ b/src/modules/users.js
@@ -117,7 +117,9 @@ const users = {
       registerPushNotifications(isEnabled, vapidPublicKey, token)
     },
     unregisterPushNotifications (store) {
-      unregisterPushNotifications()
+      const token = store.state.currentUser.credentials
+
+      unregisterPushNotifications(token)
     },
     addNewStatuses (store, { statuses }) {
       const users = map(statuses, 'user')
diff --git a/src/services/push/push.js b/src/services/push/push.js
index ff67fd5a..9142895f 100644
--- a/src/services/push/push.js
+++ b/src/services/push/push.js
@@ -14,18 +14,12 @@ function isPushSupported () {
   return 'serviceWorker' in navigator && 'PushManager' in window
 }
 
-function registerServiceWorker () {
+function getOrCreateServiceWorker () {
   return runtime.register()
-    .catch((err) => console.error('Unable to register service worker.', err))
+    .catch((err) => console.error('Unable to get or create a service worker.', err))
 }
 
-function unregisterServiceWorker () {
-  return runtime.register()
-    .then((registration) => registration.unregister())
-    .catch((err) => console.error('Unable to unregister serviceworker', err))
-}
-
-function subscribe (registration, isEnabled, vapidPublicKey) {
+function subscribePush (registration, isEnabled, vapidPublicKey) {
   if (!isEnabled) return Promise.reject(new Error('Web Push is disabled in config'))
   if (!vapidPublicKey) return Promise.reject(new Error('VAPID public key is not found'))
 
@@ -36,6 +30,30 @@ function subscribe (registration, isEnabled, vapidPublicKey) {
   return registration.pushManager.subscribe(subscribeOptions)
 }
 
+function unsubscribePush (registration) {
+  return registration.pushManager.getSubscription()
+    .then((subscribtion) => {
+      if (subscribtion === null) { return }
+      return subscribtion.unsubscribe()
+    })
+}
+
+function deleteSubscriptionFromBackEnd (token) {
+  return window.fetch('/api/v1/push/subscription/', {
+    method: 'DELETE',
+    headers: {
+      'Content-Type': 'application/json',
+      'Authorization': `Bearer ${token}`
+    }
+  }).then((response) => {
+    if (!response.ok) throw new Error('Bad status code from server.')
+    return response.json()
+  }).then((responseData) => {
+    if (!responseData.id) throw new Error('Bad response from server.')
+    return responseData
+  })
+}
+
 function sendSubscriptionToBackEnd (subscription, token) {
   return window.fetch('/api/v1/push/subscription/', {
     method: 'POST',
@@ -54,31 +72,42 @@ function sendSubscriptionToBackEnd (subscription, token) {
         }
       }
     })
+  }).then((response) => {
+    if (!response.ok) throw new Error('Bad status code from server.')
+    return response.json()
+  }).then((responseData) => {
+    if (!responseData.id) throw new Error('Bad response from server.')
+    return responseData
   })
-    .then((response) => {
-      if (!response.ok) throw new Error('Bad status code from server.')
-      return response.json()
-    })
-    .then((responseData) => {
-      if (!responseData.id) throw new Error('Bad response from server.')
-      return responseData
-    })
 }
 
 export function registerPushNotifications (isEnabled, vapidPublicKey, token) {
   if (isPushSupported()) {
-    registerServiceWorker()
-      .then((registration) => subscribe(registration, isEnabled, vapidPublicKey))
+    getOrCreateServiceWorker()
+      .then((registration) => subscribePush(registration, isEnabled, vapidPublicKey))
       .then((subscription) => sendSubscriptionToBackEnd(subscription, token))
       .catch((e) => console.warn(`Failed to setup Web Push Notifications: ${e.message}`))
   }
 }
 
-export function unregisterPushNotifications (isEnabled, vapidPublicKey, token) {
+export function unregisterPushNotifications (token) {
   if (isPushSupported()) {
-    unregisterServiceWorker()
-      .then((registration) => subscribe(registration, isEnabled, vapidPublicKey))
-      .then((subscription) => sendSubscriptionToBackEnd(subscription, token))
-      .catch((e) => console.warn(`Failed to setup Web Push Notifications: ${e.message}`))
+    Promise.all([
+      deleteSubscriptionFromBackEnd(token),
+      getOrCreateServiceWorker()
+        .then((registration) => {
+          return unsubscribePush(registration).then((result) => [registration, result])
+        })
+        .then(([registration, unsubResult]) => {
+          if (!unsubResult) {
+            console.warn('Push subscription cancellation wasn\'t successful, killing SW anyway...')
+          }
+          return registration.unregister().then((result) => {
+            if (!result) {
+              console.warn('Failed to kill SW')
+            }
+          })
+        })
+    ]).catch((e) => console.warn(`Failed to disable Web Push Notifications: ${e.message}`))
   }
 }

From 500b4bd37412960c7b2278b930794b2ccf00c971 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Tue, 25 Dec 2018 14:28:49 +0300
Subject: [PATCH 4/4] fix

---
 src/services/push/push.js | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/src/services/push/push.js b/src/services/push/push.js
index 9142895f..bf0c9680 100644
--- a/src/services/push/push.js
+++ b/src/services/push/push.js
@@ -47,10 +47,7 @@ function deleteSubscriptionFromBackEnd (token) {
     }
   }).then((response) => {
     if (!response.ok) throw new Error('Bad status code from server.')
-    return response.json()
-  }).then((responseData) => {
-    if (!responseData.id) throw new Error('Bad response from server.')
-    return responseData
+    return response
   })
 }