@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.
- package/LICENSE +21 -0
- package/README.md +276 -0
- package/dist/index.d.ts +547 -0
- package/dist/index.esm.js +2 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.umd.js +2 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/test/__mocks__/pn-protocol.d.ts +54 -0
- package/dist/test/rivium-push.test.d.ts +4 -0
- package/dist/test/setup.d.ts +5 -0
- package/package.json +57 -0
- package/service-worker.js +272 -0
|
@@ -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
|
+
}
|