feat: add poll result push notifications (#1227)

fixes one of the sub-tasks in #1130.

I also went ahead and removed the reply feature, because I cannot get it to work in Android 6.0.1 and I can't find any documentation for it in W3C/WHATWG, so I'm not sure how it is supposed to work.
This commit is contained in:
Nolan Lawson 2019-05-25 15:20:09 -07:00 committed by GitHub
parent a17948cf99
commit 12c5b732ae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 100 additions and 92 deletions

View file

@ -4,18 +4,21 @@
{: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>
{/if} {/if}
<form id="push-notification-settings" disabled="{!pushNotificationsSupport}" ref:pushNotificationsForm aria-label="Push notification settings"> <form id="push-notification-settings"
<input type="checkbox" id="push-notifications-follow" name="follow" disabled="{!pushNotificationsSupport}" on:change="onPushSettingsChange(event)"> disabled="{!pushNotificationsSupport}"
<label for="push-notifications-follow">New followers</label> ref:form
<br> aria-label="Push notification settings">
<input type="checkbox" id="push-notifications-favourite" name="favourite" disabled="{!pushNotificationsSupport}" on:change="onPushSettingsChange(event)"> {#each options as option, i (option.key)}
<label for="push-notifications-favourite">Favorites</label> {#if i > 0}
<br> <br>
<input type="checkbox" id="push-notifications-reblog" name="reblog" disabled="{!pushNotificationsSupport}" on:change="onPushSettingsChange(event)"> {/if}
<label for="push-notifications-reblog">Boosts</label> <input type="checkbox"
<br> id="push-notifications-{option.key}"
<input type="checkbox" id="push-notifications-mention" name="mention" disabled="{!pushNotificationsSupport}" on:change="onPushSettingsChange(event)"> name="{option.key}"
<label for="push-notifications-mention">Mentions</label> disabled="{!pushNotificationsSupport}"
on:change="onPushSettingsChange(event)">
<label for="push-notifications-{option.key}">{option.label}</label>
{/each}
</form> </form>
</div> </div>
<style> <style>
@ -27,11 +30,11 @@
padding: 20px; padding: 20px;
line-height: 2em; line-height: 2em;
} }
.push-notifications form[disabled="true"] { form[disabled="true"] {
opacity: 0.5; opacity: 0.5;
} }
.push-notifications p { p {
margin: 0; margin: 0 0 10px 0;
} }
</style> </style>
<script> <script>
@ -44,30 +47,52 @@
export default { export default {
async oncreate () { async oncreate () {
let { instanceName } = this.get() let { instanceName, options } = this.get()
await updatePushSubscriptionForInstance(instanceName) await updatePushSubscriptionForInstance(instanceName)
const form = this.refs.pushNotificationsForm const { form } = this.refs
const { pushSubscription } = this.store.get() const { pushSubscription } = this.store.get()
form.elements.follow.checked = get(pushSubscription, ['alerts', 'follow']) for (let { key } of options) {
form.elements.favourite.checked = get(pushSubscription, ['alerts', 'favourite']) form.elements[key].checked = get(pushSubscription, ['alerts', key])
form.elements.reblog.checked = get(pushSubscription, ['alerts', 'reblog']) }
form.elements.mention.checked = get(pushSubscription, ['alerts', 'mention'])
}, },
store: () => store, store: () => store,
data: () => ({
options: [
{
key: 'follow',
label: 'New Followers'
},
{
key: 'favourite',
label: 'Favorites'
},
{
key: 'reblog',
label: 'Boosts'
},
{
key: 'mention',
label: 'Mentions'
},
{
key: 'poll',
label: 'Poll results'
}
]
}),
computed: { computed: {
pushNotificationsSupport: ({ $pushNotificationsSupport }) => $pushNotificationsSupport pushNotificationsSupport: ({ $pushNotificationsSupport }) => $pushNotificationsSupport
}, },
methods: { methods: {
async onPushSettingsChange (e) { async onPushSettingsChange (e) {
const { instanceName } = this.get() const { instanceName, options } = this.get()
const form = this.refs.pushNotificationsForm const { form } = this.refs
const alerts = { const alerts = {}
follow: form.elements.follow.checked,
favourite: form.elements.favourite.checked, for (let { key } of options) {
reblog: form.elements.reblog.checked, alerts[key] = form.elements[key].checked
mention: form.elements.mention.checked
} }
try { try {

View file

@ -46,7 +46,7 @@ self.addEventListener('activate', event => {
// delete old asset/ondemand caches // delete old asset/ondemand caches
for (let key of keys) { for (let key of keys) {
if (key !== ASSETS && if (key !== ASSETS &&
!key.startsWith('webpack_assets_')) { !key.startsWith('webpack_assets_')) {
await caches.delete(key) await caches.delete(key)
} }
} }
@ -131,86 +131,71 @@ async function showSimpleNotification (data) {
} }
async function showRichNotification (data, notification) { async function showRichNotification (data, notification) {
const { origin } = new URL(data.icon) const { icon, body } = data
const tag = notification.id
const { origin } = self.location
switch (notification.type) { switch (notification.type) {
case 'follow': { case 'follow': {
await self.registration.showNotification(data.title, { await self.registration.showNotification(data.title, {
icon: data.icon, icon,
body: data.body, body,
tag: notification.id, tag,
data: { data: {
url: `${self.location.origin}/accounts/${notification.account.id}` url: `${origin}/accounts/${notification.account.id}`
} }
}) })
break break
} }
case 'mention': { case 'reblog':
const actions = [{ case 'favourite':
action: 'favourite', case 'poll':
title: 'Favourite' await self.registration.showNotification(data.title, {
}] icon,
body,
if ('reply' in NotificationEvent.prototype) { tag,
actions.splice(0, 0, { data: {
action: 'reply', url: `${origin}/statuses/${notification.status.id}`
type: 'text', }
title: 'Reply' })
}) break
} case 'mention':
const isPublic = ['public', 'unlisted'].includes(notification.status.visibility)
if (['public', 'unlisted'].includes(notification.status.visibility)) { const actions = [
actions.push({ {
action: 'favourite',
title: 'Favorite'
},
isPublic && {
action: 'reblog', action: 'reblog',
title: 'Boost' title: 'Boost'
}) }
} ].filter(Boolean)
await self.registration.showNotification(data.title, { await self.registration.showNotification(data.title, {
icon: data.icon, icon,
body: data.body, body,
tag: notification.id, tag,
data: { data: {
instance: origin, instance: new URL(data.icon).origin,
status_id: notification.status.id, status_id: notification.status.id,
access_token: data.access_token, access_token: data.access_token,
url: `${self.location.origin}/statuses/${notification.status.id}` url: `${origin}/statuses/${notification.status.id}`
}, },
actions actions
}) })
break break
}
case 'reblog': {
await self.registration.showNotification(data.title, {
icon: data.icon,
body: data.body,
tag: notification.id,
data: {
url: `${self.location.origin}/statuses/${notification.status.id}`
}
})
break
}
case 'favourite': {
await self.registration.showNotification(data.title, {
icon: data.icon,
body: data.body,
tag: notification.id,
data: {
url: `${self.location.origin}/statuses/${notification.status.id}`
}
})
break
}
} }
} }
const cloneNotification = notification => { const cloneNotification = notification => {
const clone = { } const clone = {}
// Object.assign() does not work with notifications // Object.assign() does not work with notifications
for (let k in notification) { for (let k in notification) {
clone[k] = notification[k] if (notification.hasOwnProperty(k)) {
clone[k] = notification[k]
}
} }
return clone return clone
@ -227,21 +212,19 @@ const updateNotificationWithoutAction = (notification, action) => {
self.addEventListener('notificationclick', event => { self.addEventListener('notificationclick', event => {
event.waitUntil((async () => { event.waitUntil((async () => {
switch (event.action) { switch (event.action) {
case 'reply': {
await post(`${event.notification.data.instance}/api/v1/statuses/`, {
status: event.reply,
in_reply_to_id: event.notification.data.status_id
}, { 'Authorization': `Bearer ${event.notification.data.access_token}` })
await updateNotificationWithoutAction(event.notification, 'reply')
break
}
case 'reblog': { case 'reblog': {
await post(`${event.notification.data.instance}/api/v1/statuses/${event.notification.data.status_id}/reblog`, null, { 'Authorization': `Bearer ${event.notification.data.access_token}` }) const url = `${event.notification.data.instance}/api/v1/statuses/${event.notification.data.status_id}/reblog`
await post(url, null, {
'Authorization': `Bearer ${event.notification.data.access_token}`
})
await updateNotificationWithoutAction(event.notification, 'reblog') await updateNotificationWithoutAction(event.notification, 'reblog')
break break
} }
case 'favourite': { case 'favourite': {
await post(`${event.notification.data.instance}/api/v1/statuses/${event.notification.data.status_id}/favourite`, null, { 'Authorization': `Bearer ${event.notification.data.access_token}` }) const url = `${event.notification.data.instance}/api/v1/statuses/${event.notification.data.status_id}/favourite`
await post(url, null, {
'Authorization': `Bearer ${event.notification.data.access_token}`
})
await updateNotificationWithoutAction(event.notification, 'favourite') await updateNotificationWithoutAction(event.notification, 'favourite')
break break
} }