From b3455649c53034e01725977260e69cff59c47e87 Mon Sep 17 00:00:00 2001
From: Egor Kislitsyn <egor@kislitsyn.com>
Date: Thu, 13 Dec 2018 18:04:09 +0700
Subject: [PATCH] improve notification subscription

---
 src/main.js               | 35 +++++++++++++++++++++++++++--------
 src/modules/interface.js  | 11 +++++++++--
 src/modules/users.js      | 11 +++++++++++
 src/services/push/push.js | 25 +++----------------------
 4 files changed, 50 insertions(+), 32 deletions(-)

diff --git a/src/main.js b/src/main.js
index e4621482..23ea854b 100644
--- a/src/main.js
+++ b/src/main.js
@@ -50,6 +50,32 @@ const persistedStateOptions = {
     'oauth'
   ]
 }
+
+const registerPushNotifications = store => {
+  store.subscribe((mutation, state) => {
+    const vapidPublicKey = state.instance.vapidPublicKey
+    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 isVapidMutation = mutation.type === 'setInstanceOption' && mutation.payload.name === 'vapidPublicKey'
+
+    if (isVapidMutation && user && permission) {
+      return store.dispatch('registerPushNotifications')
+    }
+
+    const isPermMutation = mutation.type === 'setNotificationPermission' && mutation.payload === 'granted'
+
+    if (isPermMutation && user && vapidPublicKey) {
+      return store.dispatch('registerPushNotifications')
+    }
+  })
+}
+
 createPersistedState(persistedStateOptions).then((persistedState) => {
   const store = new Vuex.Store({
     modules: {
@@ -62,17 +88,10 @@ createPersistedState(persistedStateOptions).then((persistedState) => {
       chat: chatModule,
       oauth: oauthModule
     },
-    plugins: [persistedState],
+    plugins: [persistedState, registerPushNotifications],
     strict: false // Socket modifies itself, let's ignore this for now.
     // strict: process.env.NODE_ENV !== 'production'
   })
 
-  store.subscribe((mutation, state) => {
-    if ((mutation.type === 'setCurrentUser' && state.instance.vapidPublicKey) || // Login + existing key
-      (mutation.type === 'setInstanceOption' && mutation.payload.name === 'vapidPublicKey' && state.users.currentUser)) { // Logged in, key arrives late
-      store.dispatch('registerPushNotifications')
-    }
-  })
-
   afterStoreSetup({ store, i18n })
 })
diff --git a/src/modules/interface.js b/src/modules/interface.js
index 07489685..5abc2c81 100644
--- a/src/modules/interface.js
+++ b/src/modules/interface.js
@@ -3,7 +3,8 @@ import { set, delete as del } from 'vue'
 const defaultState = {
   settings: {
     currentSaveStateNotice: null,
-    noticeClearTimeout: null
+    noticeClearTimeout: null,
+    notificationPermission: null
   }
 }
 
@@ -17,10 +18,13 @@ const interfaceMod = {
         }
         set(state.settings, 'currentSaveStateNotice', { error: false, data: success })
         set(state.settings, 'noticeClearTimeout',
-            setTimeout(() => del(state.settings, 'currentSaveStateNotice'), 2000))
+          setTimeout(() => del(state.settings, 'currentSaveStateNotice'), 2000))
       } else {
         set(state.settings, 'currentSaveStateNotice', { error: true, errorData: error })
       }
+    },
+    setNotificationPermission (state, permission) {
+      state.notificationPermission = permission
     }
   },
   actions: {
@@ -29,6 +33,9 @@ const interfaceMod = {
     },
     settingsSaved ({ commit, dispatch }, { success, error }) {
       commit('settingsSaved', { success, error })
+    },
+    setNotificationPermission ({ commit }, permission) {
+      commit('setNotificationPermission', permission)
     }
   }
 }
diff --git a/src/modules/users.js b/src/modules/users.js
index 5e0c087d..e4fa472d 100644
--- a/src/modules/users.js
+++ b/src/modules/users.js
@@ -19,6 +19,14 @@ export const mergeOrAdd = (arr, obj, item) => {
   }
 }
 
+const getNotificationPermission = () => {
+  const Notification = window.Notification
+
+  if (!Notification) return Promise.resolve(null)
+  if (Notification.permission === 'default') return Notification.requestPermission()
+  return Promise.resolve(Notification.permission)
+}
+
 export const mutations = {
   setMuted (state, { user: {id}, muted }) {
     const user = state.usersObject[id]
@@ -108,6 +116,9 @@ const users = {
                   commit('setCurrentUser', user)
                   commit('addNewUsers', [user])
 
+                  getNotificationPermission()
+                    .then(permission => commit('setNotificationPermission', permission))
+
                   // Set our new backend interactor
                   commit('setBackendInteractor', backendInteractorService(accessToken))
 
diff --git a/src/services/push/push.js b/src/services/push/push.js
index 58017ed7..1ac304d1 100644
--- a/src/services/push/push.js
+++ b/src/services/push/push.js
@@ -19,22 +19,6 @@ function registerServiceWorker () {
     .catch((err) => console.error('Unable to register service worker.', err))
 }
 
-function askPermission () {
-  return new Promise((resolve, reject) => {
-    const Notification = window.Notification
-
-    if (!Notification) return reject(new Error('Notifications disabled'))
-    if (Notification.permission !== 'default') return resolve(Notification.permission)
-
-    const permissionResult = Notification.requestPermission(resolve)
-
-    if (permissionResult) permissionResult.then(resolve, reject)
-  }).then((permissionResult) => {
-    if (permissionResult !== 'granted') throw new Error('We weren\'t granted permission.')
-    return permissionResult
-  })
-}
-
 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'))
@@ -78,11 +62,8 @@ function sendSubscriptionToBackEnd (subscription, token) {
 export default function registerPushNotifications (isEnabled, vapidPublicKey, token) {
   if (isPushSupported()) {
     registerServiceWorker()
-      .then((registration) => {
-        return askPermission()
-          .then(() => subscribe(registration, isEnabled, vapidPublicKey))
-          .then((subscription) => sendSubscriptionToBackEnd(subscription, token))
-          .catch((e) => console.warn(`Failed to setup Web Push Notifications: ${e.message}`))
-      })
+      .then((registration) => subscribe(registration, isEnabled, vapidPublicKey))
+      .then((subscription) => sendSubscriptionToBackEnd(subscription, token))
+      .catch((e) => console.warn(`Failed to setup Web Push Notifications: ${e.message}`))
   }
 }