183 lines
5.2 KiB
JavaScript
183 lines
5.2 KiB
JavaScript
// Push Notifications Client-side JavaScript
|
|
class PushNotificationManager {
|
|
constructor() {
|
|
this.vapidPublicKey = null
|
|
this.serviceWorkerRegistration = null
|
|
this.isSupported = "serviceWorker" in navigator && "PushManager" in window
|
|
}
|
|
|
|
async init(vapidPublicKey) {
|
|
if (!this.isSupported) {
|
|
console.warn("Push notifications are not supported in this browser")
|
|
return false
|
|
}
|
|
|
|
this.vapidPublicKey = vapidPublicKey
|
|
|
|
try {
|
|
// Register service worker
|
|
this.serviceWorkerRegistration = await navigator.serviceWorker.register("/static/js/sw.js")
|
|
console.log("Service Worker registered successfully")
|
|
return true
|
|
} catch (error) {
|
|
console.error("Service Worker registration failed:", error)
|
|
return false
|
|
}
|
|
}
|
|
|
|
async requestPermission() {
|
|
if (!this.isSupported) {
|
|
return "not-supported"
|
|
}
|
|
|
|
const permission = await Notification.requestPermission()
|
|
console.log("Notification permission:", permission)
|
|
return permission
|
|
}
|
|
|
|
async subscribe() {
|
|
if (!this.serviceWorkerRegistration) {
|
|
throw new Error("Service Worker not registered")
|
|
}
|
|
|
|
try {
|
|
const subscription = await this.serviceWorkerRegistration.pushManager.subscribe({
|
|
userVisibleOnly: true,
|
|
applicationServerKey: this.urlBase64ToUint8Array(this.vapidPublicKey),
|
|
})
|
|
|
|
console.log("Push subscription successful:", subscription)
|
|
return subscription
|
|
} catch (error) {
|
|
console.error("Push subscription failed:", error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
async unsubscribe() {
|
|
if (!this.serviceWorkerRegistration) {
|
|
return false
|
|
}
|
|
|
|
try {
|
|
const subscription = await this.serviceWorkerRegistration.pushManager.getSubscription()
|
|
if (subscription) {
|
|
await subscription.unsubscribe()
|
|
console.log("Push unsubscription successful")
|
|
return true
|
|
}
|
|
return false
|
|
} catch (error) {
|
|
console.error("Push unsubscription failed:", error)
|
|
return false
|
|
}
|
|
}
|
|
|
|
async getSubscription() {
|
|
if (!this.serviceWorkerRegistration) {
|
|
return null
|
|
}
|
|
|
|
try {
|
|
return await this.serviceWorkerRegistration.pushManager.getSubscription()
|
|
} catch (error) {
|
|
console.error("Failed to get subscription:", error)
|
|
return null
|
|
}
|
|
}
|
|
|
|
async sendSubscriptionToServer(subscription, deviceType = "web") {
|
|
try {
|
|
const response = await fetch("/api/communications/push-devices/", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
Authorization: `Bearer ${localStorage.getItem("access_token")}`,
|
|
},
|
|
body: JSON.stringify({
|
|
device_token: JSON.stringify(subscription),
|
|
device_type: deviceType,
|
|
}),
|
|
})
|
|
|
|
if (response.ok) {
|
|
console.log("Subscription sent to server successfully")
|
|
return true
|
|
} else {
|
|
console.error("Failed to send subscription to server:", response.statusText)
|
|
return false
|
|
}
|
|
} catch (error) {
|
|
console.error("Error sending subscription to server:", error)
|
|
return false
|
|
}
|
|
}
|
|
|
|
async removeSubscriptionFromServer(subscription) {
|
|
try {
|
|
const response = await fetch("/api/communications/push-devices/", {
|
|
method: "DELETE",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
Authorization: `Bearer ${localStorage.getItem("access_token")}`,
|
|
},
|
|
body: JSON.stringify({
|
|
device_token: JSON.stringify(subscription),
|
|
}),
|
|
})
|
|
|
|
if (response.ok) {
|
|
console.log("Subscription removed from server successfully")
|
|
return true
|
|
} else {
|
|
console.error("Failed to remove subscription from server:", response.statusText)
|
|
return false
|
|
}
|
|
} catch (error) {
|
|
console.error("Error removing subscription from server:", error)
|
|
return false
|
|
}
|
|
}
|
|
|
|
urlBase64ToUint8Array(base64String) {
|
|
const padding = "=".repeat((4 - (base64String.length % 4)) % 4)
|
|
const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/")
|
|
|
|
const rawData = window.atob(base64)
|
|
const outputArray = new Uint8Array(rawData.length)
|
|
|
|
for (let i = 0; i < rawData.length; ++i) {
|
|
outputArray[i] = rawData.charCodeAt(i)
|
|
}
|
|
return outputArray
|
|
}
|
|
|
|
// Helper method to setup push notifications for authenticated users
|
|
async setupPushNotifications(vapidPublicKey) {
|
|
const initialized = await this.init(vapidPublicKey)
|
|
if (!initialized) return false
|
|
|
|
const permission = await this.requestPermission()
|
|
if (permission !== "granted") {
|
|
console.log("Push notification permission denied")
|
|
return false
|
|
}
|
|
|
|
try {
|
|
const subscription = await this.subscribe()
|
|
const sent = await this.sendSubscriptionToServer(subscription)
|
|
return sent
|
|
} catch (error) {
|
|
console.error("Failed to setup push notifications:", error)
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
// Global instance
|
|
window.pushNotificationManager = new PushNotificationManager()
|
|
|
|
// Auto-setup for authenticated users (call this after user login)
|
|
window.setupPushNotifications = async (vapidPublicKey) =>
|
|
await window.pushNotificationManager.setupPushNotifications(vapidPublicKey)
|