Improved push notifications

This commit is contained in:
Eduard Urbach 2017-07-15 01:32:06 +02:00
parent 92a540e024
commit 460d90d957
7 changed files with 78 additions and 22 deletions

View File

@ -1,10 +1,10 @@
package notifications package notifications
import ( import (
"fmt"
"net/http" "net/http"
"github.com/aerogo/aero" "github.com/aerogo/aero"
"github.com/animenotifier/arn"
"github.com/animenotifier/notify.moe/utils" "github.com/animenotifier/notify.moe/utils"
) )
@ -16,13 +16,13 @@ func Test(ctx *aero.Context) string {
return ctx.Error(http.StatusBadRequest, "Not logged in", nil) return ctx.Error(http.StatusBadRequest, "Not logged in", nil)
} }
for _, sub := range user.PushSubscriptions().Items { notification := &arn.Notification{
err := sub.SendNotification("Yay, it works!") Title: "Anime Notifier",
Message: "Yay, it works!",
Icon: "https://" + ctx.App.Config.Domain + "/images/brand/300",
}
if err != nil { user.SendNotification(notification)
fmt.Println(err)
}
}
return "ok" return "ok"
} }

View File

@ -26,19 +26,19 @@ component Settings(user *arn.User)
Icon("bell") Icon("bell")
span Notifications span Notifications
.widget-input #enable-notifications.widget-input
label Enable: label Enable:
button.action(data-action="enableNotifications", data-trigger="click") button.action(data-action="enableNotifications", data-trigger="click")
Icon("toggle-on") Icon("toggle-off")
span Enable notifications span Enable notifications
.widget-input #disable-notifications.widget-input
label Disable: label Disable:
button.action(data-action="disableNotifications", data-trigger="click") button.action(data-action="disableNotifications", data-trigger="click")
Icon("toggle-off") Icon("toggle-on")
span Disable notifications span Disable notifications
.widget-input #test-notification.widget-input
label Test: label Test:
button.action(data-action="testNotification", data-trigger="click") button.action(data-action="testNotification", data-trigger="click")
Icon("paper-plane") Icon("paper-plane")

View File

@ -215,13 +215,15 @@ export function search(arn: AnimeNotifier, search: HTMLInputElement, e: Keyboard
} }
// Enable notifications // Enable notifications
export function enableNotifications(arn: AnimeNotifier, button: HTMLElement) { export async function enableNotifications(arn: AnimeNotifier, button: HTMLElement) {
arn.pushManager.subscribe(arn.user.dataset.id) await arn.pushManager.subscribe(arn.user.dataset.id)
arn.updatePushUI()
} }
// Disable notifications // Disable notifications
export function disableNotifications(arn: AnimeNotifier, button: HTMLElement) { export async function disableNotifications(arn: AnimeNotifier, button: HTMLElement) {
arn.pushManager.unsubscribe(arn.user.dataset.id) await arn.pushManager.unsubscribe(arn.user.dataset.id)
arn.updatePushUI()
} }
// Test notification // Test notification

View File

@ -111,7 +111,7 @@ export class AnimeNotifier {
this.pushManager = new PushManager() this.pushManager = new PushManager()
} }
onContentLoaded() { async onContentLoaded() {
// Stop watching all the objects from the previous page. // Stop watching all the objects from the previous page.
this.visibilityObserver.disconnect() this.visibilityObserver.disconnect()
@ -120,9 +120,11 @@ export class AnimeNotifier {
Promise.resolve().then(() => this.lazyLoadImages()), Promise.resolve().then(() => this.lazyLoadImages()),
Promise.resolve().then(() => this.displayLocalDates()), Promise.resolve().then(() => this.displayLocalDates()),
Promise.resolve().then(() => this.setSelectBoxValue()), Promise.resolve().then(() => this.setSelectBoxValue()),
Promise.resolve().then(() => this.assignActions()) Promise.resolve().then(() => this.assignActions()),
Promise.resolve().then(() => this.updatePushUI())
]) ])
// Apply page title
let headers = document.getElementsByTagName("h1") let headers = document.getElementsByTagName("h1")
if(this.app.currentPath === "/" || headers.length === 0) { if(this.app.currentPath === "/" || headers.length === 0) {
@ -134,6 +136,22 @@ export class AnimeNotifier {
} }
} }
async updatePushUI() {
if(!this.pushManager.pushSupported) {
return
}
let subscription = await this.pushManager.subscription()
if(subscription) {
this.app.find("enable-notifications").style.display = "none"
this.app.find("disable-notifications").style.display = "flex"
} else {
this.app.find("enable-notifications").style.display = "flex"
this.app.find("disable-notifications").style.display = "none"
}
}
onIdle() { onIdle() {
this.pushAnalytics() this.pushAnalytics()
} }

View File

@ -5,6 +5,21 @@ export class PushManager {
this.pushSupported = ("serviceWorker" in navigator) && ("PushManager" in window) this.pushSupported = ("serviceWorker" in navigator) && ("PushManager" in window)
} }
async subscription(): Promise<PushSubscription> {
if(!this.pushSupported) {
return Promise.resolve(null)
}
let registration = await navigator.serviceWorker.ready
let subscription = await registration.pushManager.getSubscription()
if(subscription) {
return Promise.resolve(subscription)
}
return Promise.resolve(null)
}
async subscribe(userId: string) { async subscribe(userId: string) {
if(!this.pushSupported) { if(!this.pushSupported) {
return return
@ -59,6 +74,7 @@ export class PushManager {
p256dh: key, p256dh: key,
auth: secret, auth: secret,
platform: navigator.platform, platform: navigator.platform,
userAgent: navigator.userAgent,
screen: { screen: {
width: window.screen.width, width: window.screen.width,
height: window.screen.height height: window.screen.height

View File

@ -17,8 +17,25 @@ self.addEventListener("install", (evt: InstallEvent) => {
self.addEventListener("activate", (evt: any) => { self.addEventListener("activate", (evt: any) => {
console.log("Service worker activate") console.log("Service worker activate")
// Delete old cache
let cacheWhitelist = [CACHE]
let deleteOldCache = caches.keys().then(keyList => {
return Promise.all(keyList.map(key => {
if(cacheWhitelist.indexOf(key) === -1) {
return caches.delete(key)
}
}))
})
let immediateClaim = (self as any).clients.claim()
// Immediate claim
evt.waitUntil( evt.waitUntil(
(self as any).clients.claim() Promise.all([
deleteOldCache,
immediateClaim
])
) )
}) })
@ -111,11 +128,13 @@ self.addEventListener("fetch", async (evt: FetchEvent) => {
}) })
self.addEventListener("push", (evt: PushEvent) => { self.addEventListener("push", (evt: PushEvent) => {
var payload = evt.data ? evt.data.text() : "no payload" var payload = evt.data ? evt.data.json() : {}
evt.waitUntil( evt.waitUntil(
(self as any).registration.showNotification("beta.notify.moe Service Worker", { (self as any).registration.showNotification(payload.title, {
body: payload body: payload.message,
icon: payload.icon,
image: payload.image
}) })
) )
}) })

View File

@ -185,6 +185,7 @@ var routeTests = map[string][]string{
"/import/myanimelist/animelist/finish": nil, "/import/myanimelist/animelist/finish": nil,
"/import/kitsu/animelist": nil, "/import/kitsu/animelist": nil,
"/import/kitsu/animelist/finish": nil, "/import/kitsu/animelist/finish": nil,
"/api/test/notification": nil,
"/anime/:id/edit": nil, "/anime/:id/edit": nil,
"/new/thread": nil, "/new/thread": nil,
"/new/soundtrack": nil, "/new/soundtrack": nil,