@rivium/push-web 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,272 @@
1
+ /**
2
+ * RiviumPush Service Worker
3
+ * Handles background push notifications with rich features
4
+ *
5
+ * Copy this file to your public directory as 'rivium-push-sw.js'
6
+ */
7
+
8
+ // Cache name for offline support
9
+ const CACHE_NAME = 'rivium-push-v1';
10
+
11
+ // Install event
12
+ self.addEventListener('install', (event) => {
13
+ console.log('[RiviumPush SW] Installing...');
14
+ self.skipWaiting();
15
+ });
16
+
17
+ // Activate event
18
+ self.addEventListener('activate', (event) => {
19
+ console.log('[RiviumPush SW] Activating...');
20
+ event.waitUntil(self.clients.claim());
21
+ });
22
+
23
+ // Push event - received push notification
24
+ self.addEventListener('push', (event) => {
25
+ console.log('[RiviumPush SW] Push received');
26
+
27
+ let data = {
28
+ title: 'New notification',
29
+ body: '',
30
+ icon: '/icon.png',
31
+ badge: '/badge.png',
32
+ data: {},
33
+ };
34
+
35
+ if (event.data) {
36
+ try {
37
+ const payload = event.data.json();
38
+ data = {
39
+ ...data,
40
+ ...payload,
41
+ };
42
+ } catch (e) {
43
+ data.body = event.data.text();
44
+ }
45
+ }
46
+
47
+ // Get localized content if available
48
+ const localizedTitle = getLocalizedContent(data, 'title');
49
+ const localizedBody = getLocalizedContent(data, 'body');
50
+
51
+ const options = {
52
+ body: localizedBody || data.body,
53
+ icon: data.iconUrl || data.icon,
54
+ badge: data.iconUrl || data.icon,
55
+ image: data.imageUrl || data.image,
56
+ tag: data.tag || data.collapseKey || data.threadId || 'rivium-push-notification',
57
+ data: {
58
+ ...data.data,
59
+ deepLink: data.deepLink,
60
+ messageId: data.messageId,
61
+ campaignId: data.campaignId,
62
+ riviumPushPayload: data,
63
+ },
64
+ requireInteraction: true,
65
+ silent: data.sound === 'none',
66
+ actions: [],
67
+ };
68
+
69
+ // Add action buttons (max 2 for web)
70
+ if (data.actions && Array.isArray(data.actions)) {
71
+ options.actions = data.actions.slice(0, 2).map((action) => ({
72
+ action: action.id,
73
+ title: action.title,
74
+ icon: action.icon,
75
+ }));
76
+ }
77
+
78
+ // Show notification with potentially localized title
79
+ const title = localizedTitle || data.title;
80
+
81
+ event.waitUntil(
82
+ self.registration.showNotification(title, options)
83
+ );
84
+ });
85
+
86
+ // Helper function to get localized content
87
+ function getLocalizedContent(data, field) {
88
+ if (!data.localizations || !Array.isArray(data.localizations)) {
89
+ return null;
90
+ }
91
+
92
+ // Get device locale (best effort in service worker context)
93
+ let deviceLocale = 'en';
94
+ try {
95
+ deviceLocale = navigator.language.split('-')[0].toLowerCase();
96
+ } catch (e) {
97
+ // Fallback to 'en'
98
+ }
99
+
100
+ const localized = data.localizations.find((loc) =>
101
+ loc.locale.toLowerCase().startsWith(deviceLocale)
102
+ );
103
+
104
+ return localized ? localized[field] : null;
105
+ }
106
+
107
+ // Notification click event
108
+ self.addEventListener('notificationclick', (event) => {
109
+ console.log('[RiviumPush SW] Notification clicked');
110
+
111
+ const notification = event.notification;
112
+ const action = event.action;
113
+ const data = notification.data || {};
114
+ const riviumPushPayload = data.riviumPushPayload || {};
115
+
116
+ notification.close();
117
+
118
+ // Determine if this is an action button click
119
+ const isActionClick = action && action !== '';
120
+
121
+ // Find action details if action button was clicked
122
+ let actionDetails = null;
123
+ let targetUrl = null;
124
+
125
+ if (isActionClick && riviumPushPayload.actions) {
126
+ // Action button was clicked - find the action's URL
127
+ actionDetails = riviumPushPayload.actions.find((a) => a.id === action);
128
+ if (actionDetails && actionDetails.action) {
129
+ targetUrl = actionDetails.action;
130
+ }
131
+ }
132
+
133
+ // If no URL yet, try deepLink
134
+ if (!targetUrl) {
135
+ targetUrl = data.deepLink || riviumPushPayload.deepLink || data.url || data.click_action;
136
+ }
137
+
138
+ // If still no URL but has actions, use first action's URL as fallback
139
+ if (!targetUrl && riviumPushPayload.actions && riviumPushPayload.actions.length > 0) {
140
+ const firstAction = riviumPushPayload.actions[0];
141
+ if (firstAction && firstAction.action) {
142
+ targetUrl = firstAction.action;
143
+ console.log('[RiviumPush SW] Using first action URL as fallback:', targetUrl);
144
+ }
145
+ }
146
+
147
+ // Default to root if nothing else
148
+ if (!targetUrl) {
149
+ targetUrl = '/';
150
+ }
151
+
152
+ console.log('[RiviumPush SW] Target URL:', targetUrl);
153
+
154
+ event.waitUntil(
155
+ self.clients.matchAll({ type: 'window', includeUncontrolled: true })
156
+ .then((clientList) => {
157
+ // If we have a URL to open, just open it directly
158
+ if (targetUrl && targetUrl !== '/') {
159
+ console.log('[RiviumPush SW] Opening URL:', targetUrl);
160
+ return self.clients.openWindow(targetUrl);
161
+ }
162
+
163
+ // No external URL - check if there's already an open window to focus
164
+ for (const client of clientList) {
165
+ if (client.url.includes(self.location.origin) && 'focus' in client) {
166
+ // Send message to client
167
+ if (isActionClick) {
168
+ client.postMessage({
169
+ type: 'action-clicked',
170
+ actionId: action,
171
+ actionDetails: actionDetails,
172
+ message: {
173
+ title: notification.title,
174
+ body: notification.body,
175
+ data: data,
176
+ deepLink: data.deepLink,
177
+ messageId: data.messageId,
178
+ campaignId: data.campaignId,
179
+ },
180
+ });
181
+ } else {
182
+ client.postMessage({
183
+ type: 'notification-click',
184
+ action: action,
185
+ message: {
186
+ title: notification.title,
187
+ body: notification.body,
188
+ data: data,
189
+ deepLink: data.deepLink,
190
+ messageId: data.messageId,
191
+ campaignId: data.campaignId,
192
+ },
193
+ });
194
+ }
195
+
196
+ return client.focus();
197
+ }
198
+ }
199
+
200
+ // Open new window at root if no existing window
201
+ if (self.clients.openWindow) {
202
+ return self.clients.openWindow('/');
203
+ }
204
+ })
205
+ );
206
+ });
207
+
208
+ // Notification close event
209
+ self.addEventListener('notificationclose', (event) => {
210
+ console.log('[RiviumPush SW] Notification closed');
211
+
212
+ const notification = event.notification;
213
+ const data = notification.data || {};
214
+
215
+ // Send dismissal event to clients
216
+ self.clients.matchAll({ type: 'window', includeUncontrolled: true })
217
+ .then((clientList) => {
218
+ for (const client of clientList) {
219
+ if (client.url.includes(self.location.origin)) {
220
+ client.postMessage({
221
+ type: 'notification-dismissed',
222
+ message: {
223
+ title: notification.title,
224
+ body: notification.body,
225
+ messageId: data.messageId,
226
+ campaignId: data.campaignId,
227
+ },
228
+ });
229
+ }
230
+ }
231
+ });
232
+ });
233
+
234
+ // Message from main app
235
+ self.addEventListener('message', (event) => {
236
+ console.log('[RiviumPush SW] Message received:', event.data);
237
+
238
+ if (event.data.type === 'skip-waiting') {
239
+ self.skipWaiting();
240
+ }
241
+
242
+ if (event.data.type === 'clear-badge') {
243
+ // Handle badge clearing if needed
244
+ }
245
+ });
246
+
247
+ // Background sync (for offline message queue)
248
+ self.addEventListener('sync', (event) => {
249
+ console.log('[RiviumPush SW] Sync event:', event.tag);
250
+
251
+ if (event.tag === 'rivium-push-sync') {
252
+ event.waitUntil(syncMessages());
253
+ }
254
+ });
255
+
256
+ // Sync queued messages
257
+ async function syncMessages() {
258
+ console.log('[RiviumPush SW] Syncing messages...');
259
+ // Implement if you need to sync messages when coming back online
260
+ }
261
+
262
+ // Periodic background sync (if supported)
263
+ self.addEventListener('periodicsync', (event) => {
264
+ if (event.tag === 'rivium-push-periodic-sync') {
265
+ event.waitUntil(doPeriodicSync());
266
+ }
267
+ });
268
+
269
+ async function doPeriodicSync() {
270
+ console.log('[RiviumPush SW] Periodic sync...');
271
+ // Implement periodic sync if needed
272
+ }