web-manager 4.2.0 → 4.3.1

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/dist/index.js CHANGED
@@ -133,9 +133,9 @@ class Manager {
133
133
  // Set up auth event listeners (uses event delegation, no need to wait for DOM)
134
134
  this._auth.setupEventListeners();
135
135
 
136
- // Set up push notification auto-request if enabled
137
- if (this.config.pushNotifications?.enabled && this.config.pushNotifications?.config?.autoRequest > 0) {
138
- this._setupNotificationAutoRequest();
136
+ // Set up push notifications
137
+ if (this.config.pushNotifications?.enabled) {
138
+ this._notifications.initialize(this.config.pushNotifications.config);
139
139
  }
140
140
 
141
141
  // Initialize Chatsy chat widget if enabled
@@ -568,29 +568,6 @@ class Manager {
568
568
  }, this.config.refreshNewVersion.config.interval);
569
569
  }
570
570
 
571
- _setupNotificationAutoRequest() {
572
- // Quit if document is not available
573
- if (typeof document === 'undefined') {
574
- return;
575
- }
576
-
577
- // Set up click listener to request notification permissions
578
- const handleClick = () => {
579
- // Remove listener after first click
580
- document.removeEventListener('click', handleClick);
581
-
582
- // Set timeout to request notifications
583
- setTimeout(() => {
584
- console.log('Auto-requesting notification permissions...');
585
- this._notifications.subscribe().catch(err => {
586
- console.error('Notification subscription failed:', err.message);
587
- });
588
- }, this.config.pushNotifications.config.autoRequest);
589
- };
590
-
591
- // Wait for user click
592
- document.addEventListener('click', handleClick);
593
- }
594
571
 
595
572
  // async _loadPolyfillsIfNeeded() {
596
573
  // // Check if polyfills are needed by testing for ES6 features
@@ -4,6 +4,58 @@ class Notifications {
4
4
  this._requestInProgress = false;
5
5
  }
6
6
 
7
+ initialize(config) {
8
+ this._vapidKey = this.manager.config?.firebase?.messaging?.config?.vapidKey || null;
9
+
10
+ const storage = this.manager.storage();
11
+ const stored = storage.get('notifications');
12
+ const permission = typeof Notification !== 'undefined' ? Notification.permission : 'default';
13
+
14
+ console.log('[WM:push] Page load check:', { storedSubscribed: stored?.subscribed, storedToken: stored?.token?.slice(-8), permission });
15
+
16
+ // If localStorage says subscribed but browser permission disagrees, clear it
17
+ if (stored?.subscribed && permission !== 'granted') {
18
+ console.log('[WM:push] Clearing stale subscription — permission is', permission);
19
+ storage.set('notifications', { subscribed: false, token: null });
20
+ }
21
+
22
+ // Arm auto-request if not currently subscribed (including just-cleared)
23
+ const autoRequest = config?.autoRequest;
24
+ if ((!stored?.subscribed || permission !== 'granted') && autoRequest > 0) {
25
+ console.log('[WM:push] Arming auto-request (delay:', autoRequest + 'ms)');
26
+ this._setupAutoRequest(autoRequest);
27
+ }
28
+
29
+ // Listen for foreground messages (tab is focused)
30
+ if (permission === 'granted') {
31
+ console.log('[WM:push] Setting up foreground listener...', { supported: this.isSupported(), hasMessaging: !!this.manager.firebaseMessaging });
32
+ this.onMessage((payload) => {
33
+ console.log('[WM:push] Foreground message received:', payload);
34
+ }).then(unsub => {
35
+ console.log('[WM:push] Foreground listener registered:', typeof unsub === 'function' ? 'OK' : 'FAILED (got empty fn)');
36
+ });
37
+ }
38
+ }
39
+
40
+ _setupAutoRequest(delay) {
41
+ if (typeof document === 'undefined') {
42
+ return;
43
+ }
44
+
45
+ const handleClick = () => {
46
+ document.removeEventListener('click', handleClick);
47
+
48
+ setTimeout(() => {
49
+ console.log('[WM:push] Auto-requesting notification permissions...');
50
+ this.subscribe().catch(err => {
51
+ console.error('[WM:push] Auto-subscription failed:', err.message);
52
+ });
53
+ }, delay);
54
+ };
55
+
56
+ document.addEventListener('click', handleClick);
57
+ }
58
+
7
59
  // Check if notifications are supported
8
60
  isSupported() {
9
61
  return 'Notification' in window &&
@@ -62,10 +114,9 @@ class Notifications {
62
114
 
63
115
  // Get FCM token
64
116
  const { getToken } = await import('firebase/messaging');
65
- const token = await getToken(messaging, {
66
- serviceWorkerRegistration: swRegistration,
67
- vapidKey: options.vapidKey
68
- });
117
+ const tokenOptions = { serviceWorkerRegistration: swRegistration };
118
+ if (this._vapidKey) { tokenOptions.vapidKey = this._vapidKey; }
119
+ const token = await getToken(messaging, tokenOptions);
69
120
 
70
121
  if (!token) {
71
122
  throw new Error('Failed to get FCM token');
@@ -148,9 +199,9 @@ class Notifications {
148
199
  const { getToken } = await import('firebase/messaging');
149
200
  const swRegistration = this.manager.state.serviceWorker;
150
201
 
151
- return await getToken(messaging, {
152
- serviceWorkerRegistration: swRegistration
153
- });
202
+ const tokenOptions = { serviceWorkerRegistration: swRegistration };
203
+ if (this._vapidKey) { tokenOptions.vapidKey = this._vapidKey; }
204
+ return await getToken(messaging, tokenOptions);
154
205
  } catch (error) {
155
206
  console.error('Get token error:', error);
156
207
  return null;
@@ -222,31 +273,54 @@ class Notifications {
222
273
  }
223
274
  }
224
275
 
225
- // Sync subscription when auth state changes
276
+ // Sync subscription when auth state changes or on page load.
277
+ // Re-fetches the current FCM token — if it changed (browser rotated it,
278
+ // service worker was re-registered, etc.), saves the new token to Firestore
279
+ // and updates localStorage. Clears the subscribed state if the token is gone.
226
280
  async syncSubscription() {
227
281
  try {
228
- // Check if we have a stored notification token
229
282
  const storage = this.manager.storage();
230
283
  const storedNotification = storage.get('notifications');
231
284
 
232
- if (!storedNotification?.token) {
285
+ const permission = typeof Notification !== 'undefined' ? Notification.permission : 'default';
286
+
287
+ console.log('[WM:push:sync] Starting sync:', { storedSubscribed: storedNotification?.subscribed, storedToken: storedNotification?.token?.slice(-8), permission });
288
+
289
+ if (permission !== 'granted') {
290
+ if (storedNotification?.subscribed) {
291
+ console.log('[WM:push:sync] Permission not granted — clearing localStorage');
292
+ storage.set('notifications', { subscribed: false, token: null });
293
+ } else {
294
+ console.log('[WM:push:sync] Permission not granted and not subscribed — nothing to do');
295
+ }
233
296
  return false;
234
297
  }
235
298
 
236
- // Update the subscription in Firestore with current auth state
237
- await this._saveSubscription(storedNotification.token);
299
+ // Permission is granted check if there's a live token (covers localStorage cleared, new browser, etc.)
300
+ const currentToken = await this.getToken();
301
+
302
+ if (!currentToken) {
303
+ console.log('[WM:push:sync] Token fetch returned null — clearing localStorage');
304
+ storage.set('notifications', { subscribed: false, token: null });
305
+ return false;
306
+ }
307
+
308
+ console.log('[WM:push:sync] Token valid:', currentToken.slice(-8), storedNotification?.token ? (storedNotification.token.slice(-8) === currentToken.slice(-8) ? '(unchanged)' : '(CHANGED from ' + storedNotification.token.slice(-8) + ')') : '(recovered — localStorage was empty)');
309
+
310
+ await this._saveSubscription(currentToken);
238
311
 
239
- // Update local storage with current user ID
240
312
  const user = this.manager.auth().getUser();
241
313
  storage.set('notifications', {
242
- ...storedNotification,
314
+ subscribed: true,
315
+ token: currentToken,
243
316
  uid: user?.uid || null,
244
- timestamp: new Date().toISOString()
317
+ timestamp: new Date().toISOString(),
245
318
  });
246
319
 
320
+ console.log('[WM:push:sync] Sync complete — subscribed');
247
321
  return true;
248
322
  } catch (error) {
249
- console.error('Sync subscription error:', error);
323
+ console.error('[WM:push:sync] Sync error:', error);
250
324
  return false;
251
325
  }
252
326
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "web-manager",
3
- "version": "4.2.0",
3
+ "version": "4.3.1",
4
4
  "description": "Easily access important variables such as the query string, current domain, and current page in a single object.",
5
5
  "main": "dist/index.js",
6
6
  "module": "src/index.js",
@@ -43,14 +43,14 @@
43
43
  "@sentry/browser": "Resolved by using OVERRIDES in web-manager (lighthouse is the issue"
44
44
  },
45
45
  "dependencies": {
46
- "@sentry/browser": "^10.52.0",
46
+ "@sentry/browser": "^10.54.0",
47
47
  "chatsy": "^2.0.14",
48
48
  "firebase": "^12.13.0",
49
49
  "itwcw-package-analytics": "^1.0.8",
50
50
  "lodash": "^4.18.1"
51
51
  },
52
52
  "devDependencies": {
53
- "mocha": "^11.7.5",
53
+ "mocha": "^11.7.6",
54
54
  "prepare-package": "^2.1.0"
55
55
  }
56
56
  }