@djangocfg/layouts 2.1.45 → 2.1.46

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/layouts",
3
- "version": "2.1.45",
3
+ "version": "2.1.46",
4
4
  "description": "Simple, straightforward layout components for Next.js - import and use with props",
5
5
  "keywords": [
6
6
  "layouts",
@@ -92,9 +92,9 @@
92
92
  "check": "tsc --noEmit"
93
93
  },
94
94
  "peerDependencies": {
95
- "@djangocfg/api": "^2.1.45",
96
- "@djangocfg/centrifugo": "^2.1.45",
97
- "@djangocfg/ui-nextjs": "^2.1.45",
95
+ "@djangocfg/api": "^2.1.46",
96
+ "@djangocfg/centrifugo": "^2.1.46",
97
+ "@djangocfg/ui-nextjs": "^2.1.46",
98
98
  "@hookform/resolvers": "^5.2.0",
99
99
  "consola": "^3.4.2",
100
100
  "lucide-react": "^0.545.0",
@@ -115,7 +115,7 @@
115
115
  "uuid": "^11.1.0"
116
116
  },
117
117
  "devDependencies": {
118
- "@djangocfg/typescript-config": "^2.1.45",
118
+ "@djangocfg/typescript-config": "^2.1.46",
119
119
  "@types/node": "^24.7.2",
120
120
  "@types/react": "^19.1.0",
121
121
  "@types/react-dom": "^19.1.0",
@@ -10,6 +10,7 @@
10
10
  import { useEffect, useState } from 'react';
11
11
 
12
12
  import { pwaLogger } from '../utils/logger';
13
+ import { checkBrowserPushSupport } from '../utils/platform';
13
14
  import { urlBase64ToUint8Array, VapidKeyError } from '../utils/vapid';
14
15
 
15
16
  import type { PushNotificationState, PushNotificationOptions } from '../types';
@@ -25,6 +26,20 @@ export function usePushNotifications(options?: PushNotificationOptions) {
25
26
  useEffect(() => {
26
27
  if (typeof window === 'undefined') return;
27
28
 
29
+ // Check browser-level support first (blocks unsupported browsers like Comet, Opera Mini, etc.)
30
+ const browserSupport = checkBrowserPushSupport();
31
+
32
+ if (!browserSupport.isSupported) {
33
+ pwaLogger.info(`[usePushNotifications] Browser does not support push: ${browserSupport.browserName} - ${browserSupport.reason}`);
34
+ setState((prev) => ({
35
+ ...prev,
36
+ isSupported: false,
37
+ permission: 'denied',
38
+ }));
39
+ return;
40
+ }
41
+
42
+ // Check API-level support
28
43
  const isSupported = 'serviceWorker' in navigator && 'PushManager' in window && 'Notification' in window;
29
44
 
30
45
  setState((prev) => ({
@@ -80,6 +80,16 @@ export {
80
80
  clearAllPushData,
81
81
  } from './utils/localStorage';
82
82
 
83
+ export {
84
+ checkBrowserPushSupport,
85
+ isBrowserPushSupported,
86
+ isStandalone,
87
+ isStandaloneReliable,
88
+ isMobileDevice,
89
+ getDisplayMode,
90
+ type BrowserPushSupport,
91
+ } from './utils/platform';
92
+
83
93
  // Types - Configuration
84
94
  export type { PushNotificationsConfig } from './types';
85
95
 
@@ -5,6 +5,181 @@
5
5
  * Used by hooks to avoid code duplication.
6
6
  */
7
7
 
8
+ // ============================================================================
9
+ // Browser Push Support Detection
10
+ // ============================================================================
11
+
12
+ /**
13
+ * List of browsers/contexts known to NOT support Web Push Notifications
14
+ */
15
+ export interface BrowserPushSupport {
16
+ isSupported: boolean;
17
+ browserName: string;
18
+ reason?: string;
19
+ }
20
+
21
+ /**
22
+ * Check if the current browser supports Web Push Notifications
23
+ *
24
+ * This checks for browsers that are known to NOT support push notifications:
25
+ * - Opera Mini (no service worker support)
26
+ * - Internet Explorer (deprecated, no Push API)
27
+ * - UC Browser (unreliable push support)
28
+ * - In-App browsers (Facebook, Instagram, TikTok, Snapchat, WeChat, etc.)
29
+ * - Generic WebViews
30
+ *
31
+ * Note: Comet (Perplexity) is Chromium-based and DOES support push notifications.
32
+ *
33
+ * @returns Object with isSupported flag, browserName, and optional reason
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * const { isSupported, browserName, reason } = checkBrowserPushSupport();
38
+ * if (!isSupported) {
39
+ * console.log(`Push not supported in ${browserName}: ${reason}`);
40
+ * }
41
+ * ```
42
+ */
43
+ export function checkBrowserPushSupport(): BrowserPushSupport {
44
+ if (typeof window === 'undefined') {
45
+ return { isSupported: false, browserName: 'unknown', reason: 'Server-side rendering' };
46
+ }
47
+
48
+ const ua = window.navigator.userAgent.toLowerCase();
49
+
50
+ // ============================================================================
51
+ // In-App Browsers (WebViews) - typically do NOT support push
52
+ // ============================================================================
53
+
54
+ // Facebook In-App Browser
55
+ if (ua.includes('fban') || ua.includes('fbav') || ua.includes('fb_iab')) {
56
+ return { isSupported: false, browserName: 'Facebook In-App', reason: 'In-app browsers do not support push notifications' };
57
+ }
58
+
59
+ // Instagram In-App Browser
60
+ if (ua.includes('instagram')) {
61
+ return { isSupported: false, browserName: 'Instagram In-App', reason: 'In-app browsers do not support push notifications' };
62
+ }
63
+
64
+ // TikTok In-App Browser
65
+ if (ua.includes('tiktok') || ua.includes('bytedancewebview') || ua.includes('bytelocale')) {
66
+ return { isSupported: false, browserName: 'TikTok In-App', reason: 'In-app browsers do not support push notifications' };
67
+ }
68
+
69
+ // Snapchat In-App Browser
70
+ if (ua.includes('snapchat')) {
71
+ return { isSupported: false, browserName: 'Snapchat In-App', reason: 'In-app browsers do not support push notifications' };
72
+ }
73
+
74
+ // WeChat In-App Browser
75
+ if (ua.includes('micromessenger')) {
76
+ return { isSupported: false, browserName: 'WeChat In-App', reason: 'In-app browsers do not support push notifications' };
77
+ }
78
+
79
+ // Threads In-App Browser (Meta's app - codename Barcelona)
80
+ if (ua.includes('barcelona')) {
81
+ return { isSupported: false, browserName: 'Threads In-App', reason: 'In-app browsers do not support push notifications' };
82
+ }
83
+
84
+ // Pinterest In-App Browser
85
+ if (ua.includes('pinterest')) {
86
+ return { isSupported: false, browserName: 'Pinterest In-App', reason: 'In-app browsers do not support push notifications' };
87
+ }
88
+
89
+ // Telegram In-App Browser
90
+ if (ua.includes('telegram')) {
91
+ return { isSupported: false, browserName: 'Telegram In-App', reason: 'In-app browsers do not support push notifications' };
92
+ }
93
+
94
+ // Line In-App Browser
95
+ if (ua.includes('line/')) {
96
+ return { isSupported: false, browserName: 'Line In-App', reason: 'In-app browsers do not support push notifications' };
97
+ }
98
+
99
+ // KakaoTalk In-App Browser
100
+ if (ua.includes('kakaotalk')) {
101
+ return { isSupported: false, browserName: 'KakaoTalk In-App', reason: 'In-app browsers do not support push notifications' };
102
+ }
103
+
104
+ // Note: Twitter and LinkedIn In-App on Android use Chrome WebView and DO support push
105
+ // We don't block them here
106
+ const isIOSDevice = ua.includes('iphone') || ua.includes('ipad') || ua.includes('ipod');
107
+
108
+ // LinkedIn In-App on iOS - no push
109
+ if (ua.includes('linkedinapp') && isIOSDevice) {
110
+ return { isSupported: false, browserName: 'LinkedIn In-App', reason: 'LinkedIn In-App on iOS does not support push notifications' };
111
+ }
112
+
113
+ // Twitter In-App on iOS - no push
114
+ if (ua.includes('twitter') && isIOSDevice) {
115
+ return { isSupported: false, browserName: 'Twitter In-App', reason: 'Twitter In-App on iOS does not support push notifications' };
116
+ }
117
+
118
+ // ============================================================================
119
+ // Browsers without Push Support
120
+ // ============================================================================
121
+
122
+ // Note: Comet Browser (Perplexity) is Chromium-based and DOES support push
123
+
124
+ // Opera Mini
125
+ if (ua.includes('opera mini') || ua.includes('opios')) {
126
+ return { isSupported: false, browserName: 'Opera Mini', reason: 'Opera Mini does not support service workers' };
127
+ }
128
+
129
+ // Internet Explorer
130
+ if (ua.includes('msie') || ua.includes('trident/')) {
131
+ return { isSupported: false, browserName: 'Internet Explorer', reason: 'Internet Explorer does not support Push API' };
132
+ }
133
+
134
+ // UC Browser (unreliable support)
135
+ if (ua.includes('ucbrowser') || ua.includes('uc browser')) {
136
+ return { isSupported: false, browserName: 'UC Browser', reason: 'UC Browser has unreliable push notification support' };
137
+ }
138
+
139
+ // ============================================================================
140
+ // Generic WebView Detection
141
+ // ============================================================================
142
+
143
+ const isWebView = ua.includes('wv)') ||
144
+ ua.includes('webview') ||
145
+ ua.includes('; wv') ||
146
+ (ua.includes('iphone') && !ua.includes('safari')) ||
147
+ (ua.includes('ipad') && !ua.includes('safari'));
148
+
149
+ if (isWebView) {
150
+ return { isSupported: false, browserName: 'WebView', reason: 'WebViews do not support push notifications' };
151
+ }
152
+
153
+ // ============================================================================
154
+ // Determine Browser Name for supported browsers
155
+ // ============================================================================
156
+
157
+ let browserName = 'unknown';
158
+
159
+ if (ua.includes('comet') || ua.includes('perplexity')) browserName = 'Comet';
160
+ else if (ua.includes('edg/') || ua.includes('edge/')) browserName = 'Edge';
161
+ else if ((window.navigator as any).brave) browserName = 'Brave';
162
+ else if (ua.includes('arc/')) browserName = 'Arc';
163
+ else if (ua.includes('vivaldi')) browserName = 'Vivaldi';
164
+ else if (ua.includes('yabrowser')) browserName = 'Yandex';
165
+ else if (ua.includes('samsungbrowser')) browserName = 'Samsung Internet';
166
+ else if (ua.includes('opr/') || ua.includes('opera')) browserName = 'Opera';
167
+ else if (ua.includes('firefox')) browserName = 'Firefox';
168
+ else if (ua.includes('chrome')) browserName = 'Chrome';
169
+ else if (ua.includes('safari') && ua.includes('version/')) browserName = 'Safari';
170
+
171
+ return { isSupported: true, browserName };
172
+ }
173
+
174
+ /**
175
+ * Quick check if push notifications are supported by the browser
176
+ *
177
+ * @returns true if browser supports push notifications
178
+ */
179
+ export function isBrowserPushSupported(): boolean {
180
+ return checkBrowserPushSupport().isSupported;
181
+ }
182
+
8
183
  /**
9
184
  * Check if running as PWA (standalone mode)
10
185
  *