PageSourceSearch

https://www.postpone.app/sw.js

js postpone.app collected 2026-10-02 10:35:07 UTC 3,260 bytes, 120 lines download raw bytes

1// Service Worker for Web Push Notifications
2
3const APP_URL = self.location.origin
4
5// Handle push notifications
6self.addEventListener('push', (event) => {
7  const handlePush = async () => {
8    if (!event.data) {
9      console.warn('Push event received but no data')
10      return
11    }
12
13    let data
14    try {
15      data = event.data.json()
16    } catch (e) {
17      console.error('Failed to parse push data:', e)
18      return
19    }
20
21    const title = data.title || 'Postpone'
22    const options = {
23      body: data.body || 'You have a new notification',
24      icon: data.icon || '/img/logo/logomark.svg',
25      badge: '/img/logo/logomark.svg',
26      tag: data.tag || 'postpone-notification',
27      data: data.data || {},
28      requireInteraction: true
29    }
30
31    await self.registration.showNotification(title, options)
32  }
33
34  event.waitUntil(handlePush())
35})
36
37// Handle notification clicks
38self.addEventListener('notificationclick', (event) => {
39  event.notification.close()
40
41  const notificationData = event.notification.data || {}
42
43  // Use URL from payload if provided, otherwise build from data
44  let targetUrl = notificationData.url || null
45
46  if (!targetUrl) {
47    targetUrl = APP_URL + '/overdue'
48
49    // Build URL based on notification type
50    if (notificationData.type === 'post.manual_publish') {
51      const platform = (notificationData.platform || '').toLowerCase()
52      const submissionId = notificationData.submissionId
53      const postId = notificationData.postId
54
55      if (platform && (submissionId || postId)) {
56        targetUrl = `${APP_URL}/overdue?platform=${platform}`
57        if (submissionId) {
58          targetUrl += `&submissionId=${submissionId}`
59        }
60        if (postId) {
61          targetUrl += `&postId=${postId}`
62        }
63      }
64    }
65  } else if (!targetUrl.startsWith('http')) {
66    // Handle relative URLs
67    targetUrl = APP_URL + targetUrl
68  }
69
70  const handleClick = async () => {
71    // Try to focus an existing window first
72    const windowClients = await clients.matchAll({
73      type: 'window',
74      includeUncontrolled: true
75    })
76
77    for (const client of windowClients) {
78      if (client.url.startsWith(APP_URL) && 'focus' in client) {
79        await client.focus()
80        if ('navigate' in client) {
81          await client.navigate(targetUrl)
82        }
83        return
84      }
85    }
86
87    // No existing window found, open a new one
88    await clients.openWindow(targetUrl)
89  }
90
91  event.waitUntil(handleClick())
92})
93
94// Handle push subscription change (e.g., when browser refreshes the subscription)
95self.addEventListener('pushsubscriptionchange', (event) => {
96  event.waitUntil(
97    (async () => {
98      // Notify all open clients about the subscription change
99      const allClients = await clients.matchAll({ type: 'window' })
100      for (const client of allClients) {
101        client.postMessage({
102          type: 'PUSH_SUBSCRIPTION_CHANGED',
103          oldEndpoint: event.oldSubscription
104            ? event.oldSubscription.endpoint
105            : null
106        })
107      }
108    })()
109  )
110})
111
112// Service worker installation
113self.addEventListener('install', (event) => {
114  self.skipWaiting()
115})
116
117// Service worker activation
118self.addEventListener('activate', (event) => {
119  event.waitUntil(clients.claim())
120})

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.