initial work on showing notifications through serviceworkers
This commit is contained in:
parent
4c11ac9a27
commit
73fbe89a4b
|
@ -16,6 +16,7 @@ import backendInteractorService from '../services/backend_interactor_service/bac
|
||||||
import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js'
|
import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js'
|
||||||
import { applyTheme, applyConfig } from '../services/style_setter/style_setter.js'
|
import { applyTheme, applyConfig } from '../services/style_setter/style_setter.js'
|
||||||
import FaviconService from '../services/favicon_service/favicon_service.js'
|
import FaviconService from '../services/favicon_service/favicon_service.js'
|
||||||
|
import { initServiceWorker, updateFocus } from '../services/sw/sw.js'
|
||||||
|
|
||||||
let staticInitialResults = null
|
let staticInitialResults = null
|
||||||
|
|
||||||
|
@ -344,6 +345,9 @@ const afterStoreSetup = async ({ store, i18n }) => {
|
||||||
store.dispatch('setLayoutHeight', windowHeight())
|
store.dispatch('setLayoutHeight', windowHeight())
|
||||||
|
|
||||||
FaviconService.initFaviconService()
|
FaviconService.initFaviconService()
|
||||||
|
initServiceWorker()
|
||||||
|
|
||||||
|
window.addEventListener('focus', () => updateFocus())
|
||||||
|
|
||||||
const overrides = window.___pleromafe_dev_overrides || {}
|
const overrides = window.___pleromafe_dev_overrides || {}
|
||||||
const server = (typeof overrides.target !== 'undefined') ? overrides.target : window.location.origin
|
const server = (typeof overrides.target !== 'undefined') ? overrides.target : window.location.origin
|
||||||
|
|
|
@ -2,7 +2,7 @@ import backendInteractorService from '../services/backend_interactor_service/bac
|
||||||
import { windowWidth, windowHeight } from '../services/window_utils/window_utils'
|
import { windowWidth, windowHeight } from '../services/window_utils/window_utils'
|
||||||
import oauthApi from '../services/new_api/oauth.js'
|
import oauthApi from '../services/new_api/oauth.js'
|
||||||
import { compact, map, each, mergeWith, last, concat, uniq, isArray } from 'lodash'
|
import { compact, map, each, mergeWith, last, concat, uniq, isArray } from 'lodash'
|
||||||
import { registerPushNotifications, unregisterPushNotifications } from '../services/push/push.js'
|
import { registerPushNotifications, unregisterPushNotifications } from '../services/sw/sw.js'
|
||||||
|
|
||||||
// TODO: Unify with mergeOrAdd in statuses.js
|
// TODO: Unify with mergeOrAdd in statuses.js
|
||||||
export const mergeOrAdd = (arr, obj, item) => {
|
export const mergeOrAdd = (arr, obj, item) => {
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
|
import { showDesktopNotification as swDesktopNotification } from '../sw/sw.js'
|
||||||
|
|
||||||
export const showDesktopNotification = (rootState, desktopNotificationOpts) => {
|
export const showDesktopNotification = (rootState, desktopNotificationOpts) => {
|
||||||
if (!('Notification' in window && window.Notification.permission === 'granted')) return
|
if (!('Notification' in window && window.Notification.permission === 'granted')) return
|
||||||
if (rootState.statuses.notifications.desktopNotificationSilence) { return }
|
if (rootState.statuses.notifications.desktopNotificationSilence) { return }
|
||||||
|
|
||||||
const desktopNotification = new window.Notification(desktopNotificationOpts.title, desktopNotificationOpts)
|
swDesktopNotification(desktopNotificationOpts)
|
||||||
// Chrome is known for not closing notifications automatically
|
|
||||||
// according to MDN, anyway.
|
|
||||||
setTimeout(desktopNotification.close.bind(desktopNotification), 5000)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,12 @@ function urlBase64ToUint8Array (base64String) {
|
||||||
return Uint8Array.from([...rawData].map((char) => char.charCodeAt(0)))
|
return Uint8Array.from([...rawData].map((char) => char.charCodeAt(0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isSWSupported () {
|
||||||
|
return 'serviceWorker' in navigator
|
||||||
|
}
|
||||||
|
|
||||||
function isPushSupported () {
|
function isPushSupported () {
|
||||||
return 'serviceWorker' in navigator && 'PushManager' in window
|
return 'PushManager' in window
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOrCreateServiceWorker () {
|
function getOrCreateServiceWorker () {
|
||||||
|
@ -39,7 +43,7 @@ function unsubscribePush (registration) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteSubscriptionFromBackEnd (token) {
|
function deleteSubscriptionFromBackEnd (token) {
|
||||||
return window.fetch('/api/v1/push/subscription/', {
|
return fetch('/api/v1/push/subscription/', {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
@ -78,6 +82,20 @@ function sendSubscriptionToBackEnd (subscription, token, notificationVisibility)
|
||||||
return responseData
|
return responseData
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
export function initServiceWorker () {
|
||||||
|
if (!isSWSupported()) return
|
||||||
|
getOrCreateServiceWorker()
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function showDesktopNotification (content) {
|
||||||
|
const { active: sw } = await window.navigator.serviceWorker.getRegistration()
|
||||||
|
sw.postMessage({ type: 'desktopNotification', content })
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateFocus () {
|
||||||
|
const { active: sw } = await window.navigator.serviceWorker.getRegistration()
|
||||||
|
sw.postMessage({ type: 'updateFocus' })
|
||||||
|
}
|
||||||
|
|
||||||
export function registerPushNotifications (isEnabled, vapidPublicKey, token, notificationVisibility) {
|
export function registerPushNotifications (isEnabled, vapidPublicKey, token, notificationVisibility) {
|
||||||
if (isPushSupported()) {
|
if (isPushSupported()) {
|
||||||
|
@ -98,13 +116,8 @@ export function unregisterPushNotifications (token) {
|
||||||
})
|
})
|
||||||
.then(([registration, unsubResult]) => {
|
.then(([registration, unsubResult]) => {
|
||||||
if (!unsubResult) {
|
if (!unsubResult) {
|
||||||
console.warn('Push subscription cancellation wasn\'t successful, killing SW anyway...')
|
console.warn('Push subscription cancellation wasn\'t successful')
|
||||||
}
|
}
|
||||||
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}`))
|
]).catch((e) => console.warn(`Failed to disable Web Push Notifications: ${e.message}`))
|
||||||
}
|
}
|
37
src/sw.js
37
src/sw.js
|
@ -13,9 +13,9 @@ const i18n = createI18n({
|
||||||
messages
|
messages
|
||||||
})
|
})
|
||||||
|
|
||||||
function isEnabled () {
|
const state = {
|
||||||
return localForage.getItem('vuex-lz')
|
lastFocused: null,
|
||||||
.then(data => data.config.webPushNotifications)
|
notificationIds: new Set()
|
||||||
}
|
}
|
||||||
|
|
||||||
function getWindowClients () {
|
function getWindowClients () {
|
||||||
|
@ -29,11 +29,11 @@ const setLocale = async () => {
|
||||||
i18n.locale = locale
|
i18n.locale = locale
|
||||||
}
|
}
|
||||||
|
|
||||||
const maybeShowNotification = async (event) => {
|
const showPushNotification = async (event) => {
|
||||||
const enabled = await isEnabled()
|
|
||||||
const activeClients = await getWindowClients()
|
const activeClients = await getWindowClients()
|
||||||
await setLocale()
|
await setLocale()
|
||||||
if (enabled && (activeClients.length === 0)) {
|
// Only show push notifications if all tabs/windows are closed
|
||||||
|
if (activeClients.length === 0) {
|
||||||
const data = event.data.json()
|
const data = event.data.json()
|
||||||
|
|
||||||
const url = `${self.registration.scope}api/v1/notifications/${data.notification_id}`
|
const url = `${self.registration.scope}api/v1/notifications/${data.notification_id}`
|
||||||
|
@ -48,8 +48,27 @@ const maybeShowNotification = async (event) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.addEventListener('push', async (event) => {
|
self.addEventListener('push', async (event) => {
|
||||||
|
console.log(event)
|
||||||
if (event.data) {
|
if (event.data) {
|
||||||
event.waitUntil(maybeShowNotification(event))
|
event.waitUntil(showPushNotification(event))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
self.addEventListener('message', async (event) => {
|
||||||
|
const { type, content } = event.data
|
||||||
|
console.log(event)
|
||||||
|
|
||||||
|
if (type === 'desktopNotification') {
|
||||||
|
const { title, body, icon, id } = content
|
||||||
|
if (state.notificationIds.has(id)) return
|
||||||
|
state.notificationIds.add(id)
|
||||||
|
setTimeout(() => state.notificationIds.remove(id), 10000)
|
||||||
|
self.registration.showNotification('SWTEST: ' + title, { body, icon })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'updateFocus') {
|
||||||
|
state.lastFocused = event.source.id
|
||||||
|
console.log(state)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -59,7 +78,9 @@ self.addEventListener('notificationclick', (event) => {
|
||||||
event.waitUntil(getWindowClients().then((list) => {
|
event.waitUntil(getWindowClients().then((list) => {
|
||||||
for (let i = 0; i < list.length; i++) {
|
for (let i = 0; i < list.length; i++) {
|
||||||
const client = list[i]
|
const client = list[i]
|
||||||
if (client.url === '/' && 'focus' in client) { return client.focus() }
|
if (state.lastFocused === null || client.id === state.lastFocused) {
|
||||||
|
if ('focus' in client) return client.focus()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clients.openWindow) return clients.openWindow('/')
|
if (clients.openWindow) return clients.openWindow('/')
|
||||||
|
|
Loading…
Reference in New Issue