fix: push subscriptions per instance (#1277)

* fix: push subscriptions per instance

fixes #1274

* fixup

* add notice about one push notification per instance at a time
This commit is contained in:
Nolan Lawson 2019-06-19 23:00:27 -07:00 committed by GitHub
parent 51e7f703d3
commit 6980083ed0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 54 additions and 16 deletions

View file

@ -14,7 +14,9 @@ const {
currentInstance, currentInstance,
instanceThemes, instanceThemes,
disableCustomScrollbars, disableCustomScrollbars,
enableGrayscale enableGrayscale,
pushSubscription,
loggedInInstancesInOrder
} = storeLite.get() } = storeLite.get()
const theme = (instanceThemes && instanceThemes[currentInstance]) || DEFAULT_THEME const theme = (instanceThemes && instanceThemes[currentInstance]) || DEFAULT_THEME
@ -65,3 +67,18 @@ if (/iP(?:hone|ad|od)/.test(navigator.userAgent) &&
IntersectionObserver.toString().includes('[native code]'))) { IntersectionObserver.toString().includes('[native code]'))) {
document.head.removeChild(document.getElementById('theManifest')) document.head.removeChild(document.getElementById('theManifest'))
} }
if (pushSubscription) {
// Fix a bug in Pinafore <=v1.9.0 if we only have one instance we're logged in to
// (https://github.com/nolanlawson/pinafore/issues/1274)
if (loggedInInstancesInOrder && loggedInInstancesInOrder.length === 1) {
storeLite.set({
pushSubscriptions: {
[currentInstance]: pushSubscription
}
})
}
storeLite.set({
pushSubscription: null
})
}

View file

@ -5,10 +5,10 @@ import { urlBase64ToUint8Array } from '../_utils/base64'
const dummyApplicationServerKey = 'BImgAz4cF_yvNFp8uoBJCaGpCX4d0atNIFMHfBvAAXCyrnn9IMAFQ10DW_ZvBCzGeR4fZI5FnEi2JVcRE-L88jY=' const dummyApplicationServerKey = 'BImgAz4cF_yvNFp8uoBJCaGpCX4d0atNIFMHfBvAAXCyrnn9IMAFQ10DW_ZvBCzGeR4fZI5FnEi2JVcRE-L88jY='
export async function updatePushSubscriptionForInstance (instanceName) { export async function updatePushSubscriptionForInstance (instanceName) {
const { loggedInInstances, pushSubscription } = store.get() const { loggedInInstances, currentPushSubscription } = store.get()
const accessToken = loggedInInstances[instanceName].access_token const accessToken = loggedInInstances[instanceName].access_token
if (pushSubscription === null) { if (currentPushSubscription === null) {
return return
} }
@ -16,7 +16,7 @@ export async function updatePushSubscriptionForInstance (instanceName) {
const subscription = await registration.pushManager.getSubscription() const subscription = await registration.pushManager.getSubscription()
if (subscription === null) { if (subscription === null) {
store.set({ pushSubscription: null }) store.setInstanceData(instanceName, 'pushSubscriptions', null)
store.save() store.save()
return return
} }
@ -28,16 +28,16 @@ export async function updatePushSubscriptionForInstance (instanceName) {
if (btoa(urlBase64ToUint8Array(backendSubscription.server_key).buffer) !== btoa(subscription.options.applicationServerKey)) { if (btoa(urlBase64ToUint8Array(backendSubscription.server_key).buffer) !== btoa(subscription.options.applicationServerKey)) {
await subscription.unsubscribe() await subscription.unsubscribe()
await deleteSubscription(instanceName, accessToken) await deleteSubscription(instanceName, accessToken)
await updateAlerts(instanceName, pushSubscription.alerts) await updateAlerts(instanceName, currentPushSubscription.alerts)
} else { } else {
store.set({ pushSubscription: backendSubscription }) store.setInstanceData(instanceName, 'pushSubscriptions', backendSubscription)
store.save() store.save()
} }
} catch (e) { } catch (e) {
// TODO: Better way to detect 404 // TODO: Better way to detect 404
if (e.message.startsWith('404:')) { if (e.message.startsWith('404:')) {
await subscription.unsubscribe() await subscription.unsubscribe()
store.set({ pushSubscription: null }) store.setInstanceData(instanceName, 'pushSubscriptions', null)
store.save() store.save()
} }
} }
@ -73,16 +73,16 @@ export async function updateAlerts (instanceName, alerts) {
backendSubscription = await postSubscription(instanceName, accessToken, subscription, alerts) backendSubscription = await postSubscription(instanceName, accessToken, subscription, alerts)
store.set({ pushSubscription: backendSubscription }) store.setInstanceData(instanceName, 'pushSubscriptions', backendSubscription)
store.save() store.save()
} else { } else {
try { try {
const backendSubscription = await putSubscription(instanceName, accessToken, alerts) const backendSubscription = await putSubscription(instanceName, accessToken, alerts)
store.set({ pushSubscription: backendSubscription }) store.setInstanceData(instanceName, 'pushSubscriptions', backendSubscription)
store.save() store.save()
} catch (e) { } catch (e) {
const backendSubscription = await postSubscription(instanceName, accessToken, subscription, alerts) const backendSubscription = await postSubscription(instanceName, accessToken, subscription, alerts)
store.set({ pushSubscription: backendSubscription }) store.setInstanceData(instanceName, 'pushSubscriptions', backendSubscription)
store.save() store.save()
} }
} }

View file

@ -1,8 +1,10 @@
<div class="push-notifications"> <div class="push-notifications">
{#if pushNotificationsSupport === false} {#if pushNotificationsSupport === false}
<p>Your browser doesn't support push notifications.</p> <p>Your browser doesn't support push notifications.</p>
{:elseif $notificationPermission === "denied"} {:elseif $notificationPermission === "denied"}
<p role="alert">You have denied permission to show notifications.</p> <p role="alert">You have denied permission to show notifications.</p>
{:elseif $loggedInInstancesInOrder.length > 1}
<p>Note that you can only have push notifications for one instance at a time.</p>
{/if} {/if}
<form id="push-notification-settings" <form id="push-notification-settings"
disabled="{!pushNotificationsSupport}" disabled="{!pushNotificationsSupport}"
@ -51,7 +53,7 @@
await updatePushSubscriptionForInstance(instanceName) await updatePushSubscriptionForInstance(instanceName)
const { form } = this.refs const { form } = this.refs
const { pushSubscription } = this.store.get() const pushSubscription = this.store.getInstanceData(instanceName, 'pushSubscriptions')
for (let { key } of options) { for (let { key } of options) {
form.elements[key].checked = get(pushSubscription, ['alerts', key]) form.elements[key].checked = get(pushSubscription, ['alerts', key])

View file

@ -15,6 +15,7 @@ export function instanceComputations (store) {
computeForInstance(store, 'currentStatusModifications', 'statusModifications', null) computeForInstance(store, 'currentStatusModifications', 'statusModifications', null)
computeForInstance(store, 'currentCustomEmoji', 'customEmoji', []) computeForInstance(store, 'currentCustomEmoji', 'customEmoji', [])
computeForInstance(store, 'currentComposeData', 'composeData', {}) computeForInstance(store, 'currentComposeData', 'composeData', {})
computeForInstance(store, 'currentPushSubscription', 'pushSubscriptions', null)
store.compute( store.compute(
'isUserLoggedIn', 'isUserLoggedIn',

View file

@ -36,4 +36,15 @@ export function instanceMixins (Store) {
instanceSettings[instanceName][settingName] = value instanceSettings[instanceName][settingName] = value
this.set({ instanceSettings }) this.set({ instanceSettings })
} }
Store.prototype.setInstanceData = function (instanceName, key, value) {
let instanceData = this.get()[key] || {}
instanceData[instanceName] = value
this.set({ [key]: instanceData })
}
Store.prototype.getInstanceData = function (instanceName, key) {
let instanceData = this.get()[key] || {}
return instanceData[instanceName]
}
} }

View file

@ -32,7 +32,7 @@ const persistedState = {
neverMarkMediaAsSensitive: false, neverMarkMediaAsSensitive: false,
omitEmojiInDisplayNames: undefined, omitEmojiInDisplayNames: undefined,
pinnedPages: {}, pinnedPages: {},
pushSubscription: null, pushSubscriptions: {},
reduceMotion: reduceMotion:
!process.browser || !process.browser ||
window.matchMedia('(prefers-reduced-motion: reduce)').matches, window.matchMedia('(prefers-reduced-motion: reduce)').matches,

View file

@ -1,5 +1,4 @@
// "lite" version of the store used in the inline script. Purely read-only, // "lite" version of the store used in the inline script.
// does not implement non-LocalStorage store features.
import { safeParse } from './safeParse' import { safeParse } from './safeParse'
import { testHasLocalStorageOnce } from '../_utils/testStorage' import { testHasLocalStorageOnce } from '../_utils/testStorage'
@ -16,5 +15,13 @@ export const storeLite = {
return obj[prop] return obj[prop]
} }
}) })
},
set (obj) {
if (hasLocalStorage) {
for (let [key, value] of Object.entries(obj)) {
localStorage.setItem(`store_${key}`, JSON.stringify(value))
}
}
} }
} }