feat: restructure into XFlow monorepo with Nuxt 4, Modular Onion Monolith, Android client, and Desktop CLI
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
export const usePush = () => {
|
||||
const isSupported = ref(false)
|
||||
const subscription = ref<PushSubscription | null>(null)
|
||||
|
||||
const checkSupport = () => {
|
||||
isSupported.value = 'serviceWorker' in navigator && 'PushManager' in window
|
||||
}
|
||||
|
||||
const getRegistration = async (): Promise<ServiceWorkerRegistration | null> => {
|
||||
if (!('serviceWorker' in navigator)) return null
|
||||
return navigator.serviceWorker.ready
|
||||
}
|
||||
|
||||
const subscribe = async (): Promise<boolean> => {
|
||||
const registration = await getRegistration()
|
||||
if (!registration) return false
|
||||
|
||||
try {
|
||||
const existing = await registration.pushManager.getSubscription()
|
||||
if (existing) {
|
||||
subscription.value = existing
|
||||
await sendSubscriptionToServer(existing)
|
||||
return true
|
||||
}
|
||||
|
||||
// In production, get VAPID public key from server
|
||||
const vapidKey = await $fetch<string>('/api/push/vapid-public-key').catch(() => null)
|
||||
const applicationServerKey = vapidKey
|
||||
? (urlBase64ToUint8Array(vapidKey) as unknown as ArrayBuffer)
|
||||
: undefined
|
||||
|
||||
const sub = await registration.pushManager.subscribe({
|
||||
userVisibleOnly: true,
|
||||
applicationServerKey
|
||||
})
|
||||
|
||||
subscription.value = sub
|
||||
await sendSubscriptionToServer(sub)
|
||||
return true
|
||||
} catch (err) {
|
||||
console.error('Push subscription failed:', err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const sendSubscriptionToServer = async (sub: PushSubscription) => {
|
||||
await $fetch('/api/push/subscribe', {
|
||||
method: 'POST',
|
||||
body: { subscription: sub.toJSON() }
|
||||
})
|
||||
}
|
||||
|
||||
const unsubscribe = async (): Promise<boolean> => {
|
||||
const registration = await getRegistration()
|
||||
if (!registration) return false
|
||||
|
||||
const existing = await registration.pushManager.getSubscription()
|
||||
if (existing) {
|
||||
await existing.unsubscribe()
|
||||
subscription.value = null
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
const urlBase64ToUint8Array = (base64String: string): Uint8Array => {
|
||||
const padding = '='.repeat((4 - (base64String.length % 4)) % 4)
|
||||
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/')
|
||||
const rawData = atob(base64)
|
||||
return Uint8Array.from([...rawData].map(char => char.charCodeAt(0)))
|
||||
}
|
||||
|
||||
const showLocalNotification = (title: string, options?: NotificationOptions) => {
|
||||
if ('Notification' in window && Notification.permission === 'granted') {
|
||||
navigator.serviceWorker.ready.then(reg => {
|
||||
reg.showNotification(title, {
|
||||
icon: '/pwa-192x192.svg',
|
||||
badge: '/favicon.svg',
|
||||
...options as any
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const requestPermission = async (): Promise<boolean> => {
|
||||
if (!('Notification' in window)) return false
|
||||
const permission = await Notification.requestPermission()
|
||||
return permission === 'granted'
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
checkSupport()
|
||||
})
|
||||
|
||||
return {
|
||||
isSupported,
|
||||
subscription,
|
||||
subscribe,
|
||||
unsubscribe,
|
||||
requestPermission,
|
||||
showLocalNotification
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user