@djangocfg/layouts 2.1.37 → 2.1.38
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/README.md +204 -18
- package/package.json +5 -5
- package/src/components/errors/index.ts +9 -0
- package/src/components/errors/types.ts +38 -0
- package/src/layouts/AppLayout/AppLayout.tsx +33 -45
- package/src/layouts/AppLayout/BaseApp.tsx +104 -33
- package/src/layouts/AuthLayout/AuthContext.tsx +7 -1
- package/src/layouts/AuthLayout/OAuthProviders.tsx +1 -10
- package/src/layouts/AuthLayout/OTPForm.tsx +1 -0
- package/src/layouts/PrivateLayout/PrivateLayout.tsx +1 -1
- package/src/layouts/PublicLayout/PublicLayout.tsx +1 -1
- package/src/layouts/PublicLayout/components/PublicMobileDrawer.tsx +1 -1
- package/src/layouts/PublicLayout/components/PublicNavigation.tsx +1 -1
- package/src/layouts/_components/UserMenu.tsx +1 -1
- package/src/layouts/index.ts +1 -1
- package/src/layouts/types/index.ts +47 -0
- package/src/layouts/types/layout.types.ts +61 -0
- package/src/layouts/types/providers.types.ts +65 -0
- package/src/layouts/types/ui.types.ts +103 -0
- package/src/snippets/Analytics/index.ts +1 -0
- package/src/snippets/Analytics/types.ts +10 -0
- package/src/snippets/PWAInstall/@docs/README.md +92 -0
- package/src/snippets/PWAInstall/README.md +185 -0
- package/src/snippets/{PWA → PWAInstall}/components/A2HSHint.tsx +85 -84
- package/src/snippets/PWAInstall/components/DesktopGuide.tsx +229 -0
- package/src/snippets/PWAInstall/context/InstallContext.tsx +102 -0
- package/src/snippets/{PWA → PWAInstall}/hooks/useInstallPrompt.ts +3 -0
- package/src/snippets/{PWA → PWAInstall}/index.ts +12 -31
- package/src/snippets/{PWA → PWAInstall}/types/components.ts +0 -6
- package/src/snippets/PWAInstall/types/config.ts +22 -0
- package/src/snippets/{PWA → PWAInstall}/types/index.ts +4 -4
- package/src/snippets/{PWA → PWAInstall}/utils/localStorage.ts +1 -23
- package/src/snippets/PushNotifications/@docs/README.md +191 -0
- package/src/snippets/PushNotifications/@docs/guides/django-integration.md +648 -0
- package/src/snippets/PushNotifications/@docs/guides/service-worker.md +467 -0
- package/src/snippets/PushNotifications/@docs/guides/vapid-setup.md +352 -0
- package/src/snippets/PushNotifications/README.md +328 -0
- package/src/snippets/{PWA → PushNotifications}/config.ts +2 -2
- package/src/snippets/PushNotifications/context/DjangoPushContext.tsx +190 -0
- package/src/snippets/{PWA → PushNotifications}/hooks/useDjangoPush.ts +63 -81
- package/src/snippets/{PWA → PushNotifications}/hooks/usePushNotifications.ts +12 -8
- package/src/snippets/PushNotifications/index.ts +87 -0
- package/src/snippets/PushNotifications/types/config.ts +28 -0
- package/src/snippets/PushNotifications/types/index.ts +9 -0
- package/src/snippets/PushNotifications/utils/localStorage.ts +60 -0
- package/src/snippets/PushNotifications/utils/logger.ts +149 -0
- package/src/snippets/PushNotifications/utils/platform.ts +151 -0
- package/src/snippets/index.ts +37 -12
- package/src/layouts/shared/index.ts +0 -21
- package/src/layouts/shared/types.ts +0 -247
- package/src/snippets/PWA/@refactoring/ARCHITECTURE_ANALYSIS.md +0 -1179
- package/src/snippets/PWA/@refactoring/EXECUTIVE_SUMMARY.md +0 -271
- package/src/snippets/PWA/@refactoring/README.md +0 -204
- package/src/snippets/PWA/@refactoring/REFACTORING_PROPOSALS.md +0 -1109
- package/src/snippets/PWA/@refactoring2/COMPARISON-WITH-NEXTJS.md +0 -718
- package/src/snippets/PWA/@refactoring2/P1-FIXES-COMPLETED.md +0 -188
- package/src/snippets/PWA/@refactoring2/POST-P0-ANALYSIS.md +0 -362
- package/src/snippets/PWA/@refactoring2/README.md +0 -85
- package/src/snippets/PWA/@refactoring2/RECOMMENDATIONS.md +0 -1321
- package/src/snippets/PWA/@refactoring2/REMAINING-ISSUES.md +0 -557
- package/src/snippets/PWA/README.md +0 -387
- package/src/snippets/PWA/context/DjangoPushContext.tsx +0 -105
- package/src/snippets/PWA/context/InstallContext.tsx +0 -118
- package/src/snippets/PWA/context/PushContext.tsx +0 -156
- /package/src/layouts/{shared → types}/README.md +0 -0
- /package/src/snippets/{PWA/@docs/research.md → PWAInstall/@docs/research/ios-android-install-flows.md} +0 -0
- /package/src/snippets/{PWA → PWAInstall}/components/IOSGuide.tsx +0 -0
- /package/src/snippets/{PWA → PWAInstall}/components/IOSGuideDrawer.tsx +0 -0
- /package/src/snippets/{PWA → PWAInstall}/components/IOSGuideModal.tsx +0 -0
- /package/src/snippets/{PWA → PWAInstall}/hooks/useIsPWA.ts +0 -0
- /package/src/snippets/{PWA → PWAInstall}/types/install.ts +0 -0
- /package/src/snippets/{PWA → PWAInstall}/types/platform.ts +0 -0
- /package/src/snippets/{PWA → PWAInstall}/utils/logger.ts +0 -0
- /package/src/snippets/{PWA → PWAInstall}/utils/platform.ts +0 -0
- /package/src/snippets/{PWA → PushNotifications}/components/PushPrompt.tsx +0 -0
- /package/src/snippets/{PWA → PushNotifications}/types/push.ts +0 -0
- /package/src/snippets/{PWA → PushNotifications}/utils/vapid.ts +0 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Desktop Installation Guide Modal
|
|
5
|
+
*
|
|
6
|
+
* Visual step-by-step guide for installing PWA on desktop browsers
|
|
7
|
+
* Uses platform detection from InstallContext
|
|
8
|
+
* Supports: Chrome, Edge, Brave, Arc, Vivaldi, Opera, Yandex, Firefox, Safari
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import React, { useMemo } from 'react';
|
|
12
|
+
import { Monitor, Check, ArrowDownToLine, Menu, Search, Plus } from 'lucide-react';
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
Dialog,
|
|
16
|
+
DialogContent,
|
|
17
|
+
DialogDescription,
|
|
18
|
+
DialogFooter,
|
|
19
|
+
DialogHeader,
|
|
20
|
+
DialogTitle,
|
|
21
|
+
Button,
|
|
22
|
+
Card,
|
|
23
|
+
CardContent,
|
|
24
|
+
} from '@djangocfg/ui-nextjs';
|
|
25
|
+
|
|
26
|
+
import type { IOSGuideModalProps, InstallStep } from '../types';
|
|
27
|
+
import { useInstall } from '../context/InstallContext';
|
|
28
|
+
|
|
29
|
+
type BrowserCategory = 'chromium' | 'firefox' | 'safari' | 'unknown';
|
|
30
|
+
|
|
31
|
+
function getBrowserCategory(browser: {
|
|
32
|
+
isChromium: boolean;
|
|
33
|
+
isFirefox: boolean;
|
|
34
|
+
isSafari: boolean;
|
|
35
|
+
}): BrowserCategory {
|
|
36
|
+
if (browser.isChromium) return 'chromium';
|
|
37
|
+
if (browser.isFirefox) return 'firefox';
|
|
38
|
+
if (browser.isSafari) return 'safari';
|
|
39
|
+
return 'unknown';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function getBrowserSteps(category: BrowserCategory, browserName: string): InstallStep[] {
|
|
43
|
+
switch (category) {
|
|
44
|
+
case 'chromium':
|
|
45
|
+
// Chrome, Edge, Brave, Arc, Vivaldi, Opera, Yandex, etc.
|
|
46
|
+
return [
|
|
47
|
+
{
|
|
48
|
+
number: 1,
|
|
49
|
+
title: 'Find Install Icon',
|
|
50
|
+
icon: ArrowDownToLine,
|
|
51
|
+
description: 'Look for install icon in address bar (right side)',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
number: 2,
|
|
55
|
+
title: 'Click Install',
|
|
56
|
+
icon: Plus,
|
|
57
|
+
description: 'Click the icon and select "Install"',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
number: 3,
|
|
61
|
+
title: 'Confirm',
|
|
62
|
+
icon: Check,
|
|
63
|
+
description: 'Click "Install" in the popup dialog',
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
case 'firefox':
|
|
68
|
+
return [
|
|
69
|
+
{
|
|
70
|
+
number: 1,
|
|
71
|
+
title: 'Open Menu',
|
|
72
|
+
icon: Menu,
|
|
73
|
+
description: 'Click the menu button (three lines)',
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
number: 2,
|
|
77
|
+
title: 'Find Install Option',
|
|
78
|
+
icon: Search,
|
|
79
|
+
description: 'Look for "Install" or "Add to Home Screen"',
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
number: 3,
|
|
83
|
+
title: 'Confirm',
|
|
84
|
+
icon: Check,
|
|
85
|
+
description: 'Follow the installation prompts',
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
case 'safari':
|
|
90
|
+
return [
|
|
91
|
+
{
|
|
92
|
+
number: 1,
|
|
93
|
+
title: 'Limited Support',
|
|
94
|
+
icon: Monitor,
|
|
95
|
+
description: 'Safari on macOS has limited PWA support',
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
number: 2,
|
|
99
|
+
title: 'Use Chromium Browser',
|
|
100
|
+
icon: ArrowDownToLine,
|
|
101
|
+
description: 'Consider using Chrome, Edge, or Brave for full PWA experience',
|
|
102
|
+
},
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
default:
|
|
106
|
+
return [
|
|
107
|
+
{
|
|
108
|
+
number: 1,
|
|
109
|
+
title: 'Check Address Bar',
|
|
110
|
+
icon: ArrowDownToLine,
|
|
111
|
+
description: 'Look for an install or download icon',
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
number: 2,
|
|
115
|
+
title: 'Or Use Menu',
|
|
116
|
+
icon: Menu,
|
|
117
|
+
description: 'Check browser menu for install option',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
number: 3,
|
|
121
|
+
title: 'Confirm',
|
|
122
|
+
icon: Check,
|
|
123
|
+
description: 'Follow the installation prompts',
|
|
124
|
+
},
|
|
125
|
+
];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function StepCard({ step }: { step: InstallStep }) {
|
|
130
|
+
return (
|
|
131
|
+
<Card className="border border-border">
|
|
132
|
+
<CardContent className="p-4">
|
|
133
|
+
<div className="flex items-start gap-3">
|
|
134
|
+
<div
|
|
135
|
+
className="flex items-center justify-center rounded-full bg-primary text-primary-foreground flex-shrink-0"
|
|
136
|
+
style={{ width: '32px', height: '32px' }}
|
|
137
|
+
>
|
|
138
|
+
<span className="text-sm font-semibold">{step.number}</span>
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
<div className="flex-1 min-w-0">
|
|
142
|
+
<div className="flex items-center gap-2 mb-1">
|
|
143
|
+
<step.icon className="w-5 h-5 text-primary" />
|
|
144
|
+
<h3 className="font-semibold text-foreground">{step.title}</h3>
|
|
145
|
+
</div>
|
|
146
|
+
<p className="text-sm text-muted-foreground">{step.description}</p>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
</CardContent>
|
|
150
|
+
</Card>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function DesktopGuide({ onDismiss, open = true }: IOSGuideModalProps) {
|
|
155
|
+
const {
|
|
156
|
+
browserName,
|
|
157
|
+
isChromium,
|
|
158
|
+
isFirefox,
|
|
159
|
+
isSafari,
|
|
160
|
+
isEdge,
|
|
161
|
+
isBrave,
|
|
162
|
+
isArc,
|
|
163
|
+
isVivaldi,
|
|
164
|
+
isOpera,
|
|
165
|
+
isYandex,
|
|
166
|
+
} = useInstall();
|
|
167
|
+
|
|
168
|
+
const category = useMemo(
|
|
169
|
+
() => getBrowserCategory({ isChromium, isFirefox, isSafari }),
|
|
170
|
+
[isChromium, isFirefox, isSafari]
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
const steps = useMemo(() => getBrowserSteps(category, browserName), [category, browserName]);
|
|
174
|
+
|
|
175
|
+
// Get specific browser display name with emoji
|
|
176
|
+
const displayName = useMemo(() => {
|
|
177
|
+
if (isEdge) return 'Edge';
|
|
178
|
+
if (isBrave) return 'Brave';
|
|
179
|
+
if (isArc) return 'Arc';
|
|
180
|
+
if (isVivaldi) return 'Vivaldi';
|
|
181
|
+
if (isOpera) return 'Opera';
|
|
182
|
+
if (isYandex) return 'Yandex Browser';
|
|
183
|
+
return browserName;
|
|
184
|
+
}, [browserName, isEdge, isBrave, isArc, isVivaldi, isOpera, isYandex]);
|
|
185
|
+
|
|
186
|
+
return (
|
|
187
|
+
<Dialog open={open} onOpenChange={(isOpen) => !isOpen && onDismiss()}>
|
|
188
|
+
<DialogContent className="sm:max-w-md">
|
|
189
|
+
<DialogHeader className="text-left">
|
|
190
|
+
<DialogTitle className="flex items-center gap-2">
|
|
191
|
+
<Monitor className="w-5 h-5 text-primary" />
|
|
192
|
+
Install App on Desktop
|
|
193
|
+
</DialogTitle>
|
|
194
|
+
<DialogDescription className="text-left">
|
|
195
|
+
{isSafari
|
|
196
|
+
? 'Safari on macOS has limited PWA support. For the best experience, use Chrome, Edge, or Brave.'
|
|
197
|
+
: `Install this app on ${displayName} for quick access from your desktop`
|
|
198
|
+
}
|
|
199
|
+
</DialogDescription>
|
|
200
|
+
</DialogHeader>
|
|
201
|
+
|
|
202
|
+
<div className="space-y-3 py-4">
|
|
203
|
+
{steps.map((step) => (
|
|
204
|
+
<StepCard key={step.number} step={step} />
|
|
205
|
+
))}
|
|
206
|
+
</div>
|
|
207
|
+
|
|
208
|
+
{category === 'chromium' && (
|
|
209
|
+
<div className="p-3 bg-muted/30 rounded-lg text-xs text-muted-foreground">
|
|
210
|
+
💡 Tip: You can also right-click the page and look for "Install" option in {displayName}
|
|
211
|
+
</div>
|
|
212
|
+
)}
|
|
213
|
+
|
|
214
|
+
{category === 'firefox' && (
|
|
215
|
+
<div className="p-3 bg-amber-500/10 rounded-lg text-xs text-amber-700 dark:text-amber-400">
|
|
216
|
+
ℹ️ Note: Firefox has limited PWA support. Some features may not work as expected.
|
|
217
|
+
</div>
|
|
218
|
+
)}
|
|
219
|
+
|
|
220
|
+
<DialogFooter>
|
|
221
|
+
<Button onClick={onDismiss} variant="default" className="w-full">
|
|
222
|
+
<Check className="w-4 h-4 mr-2" />
|
|
223
|
+
Got It
|
|
224
|
+
</Button>
|
|
225
|
+
</DialogFooter>
|
|
226
|
+
</DialogContent>
|
|
227
|
+
</Dialog>
|
|
228
|
+
);
|
|
229
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PWA Install Context
|
|
5
|
+
*
|
|
6
|
+
* Minimal global state for PWA installation
|
|
7
|
+
* No tracking, no metrics, no engagement — just install state
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import React, { createContext, useContext, ReactNode } from 'react';
|
|
11
|
+
|
|
12
|
+
import type { InstallOutcome } from '../types';
|
|
13
|
+
import { useInstallPrompt } from '../hooks/useInstallPrompt';
|
|
14
|
+
|
|
15
|
+
export interface PwaContextValue {
|
|
16
|
+
// Platform
|
|
17
|
+
isIOS: boolean;
|
|
18
|
+
isAndroid: boolean;
|
|
19
|
+
isDesktop: boolean;
|
|
20
|
+
|
|
21
|
+
// Browsers
|
|
22
|
+
isSafari: boolean;
|
|
23
|
+
isChrome: boolean;
|
|
24
|
+
isFirefox: boolean;
|
|
25
|
+
isEdge: boolean;
|
|
26
|
+
isOpera: boolean;
|
|
27
|
+
isBrave: boolean;
|
|
28
|
+
isArc: boolean;
|
|
29
|
+
isVivaldi: boolean;
|
|
30
|
+
isYandex: boolean;
|
|
31
|
+
isSamsungBrowser: boolean;
|
|
32
|
+
isUCBrowser: boolean;
|
|
33
|
+
isChromium: boolean; // Any Chromium-based browser
|
|
34
|
+
browserName: string;
|
|
35
|
+
|
|
36
|
+
// State
|
|
37
|
+
isInstalled: boolean;
|
|
38
|
+
canPrompt: boolean;
|
|
39
|
+
|
|
40
|
+
// Actions
|
|
41
|
+
install: () => Promise<InstallOutcome>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const PwaContext = createContext<PwaContextValue | undefined>(undefined);
|
|
45
|
+
|
|
46
|
+
export interface PwaConfig {
|
|
47
|
+
enabled?: boolean;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function PwaProvider({ children, ...config }: PwaConfig & { children: ReactNode }) {
|
|
51
|
+
// If not enabled, acts as a simple pass-through
|
|
52
|
+
if (config.enabled === false) {
|
|
53
|
+
return <>{children}</>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const prompt = useInstallPrompt();
|
|
57
|
+
|
|
58
|
+
const value: PwaContextValue = {
|
|
59
|
+
// Platform
|
|
60
|
+
isIOS: prompt.isIOS,
|
|
61
|
+
isAndroid: prompt.isAndroid,
|
|
62
|
+
isDesktop: !prompt.isIOS && !prompt.isAndroid,
|
|
63
|
+
|
|
64
|
+
// Browsers (from useBrowserDetect)
|
|
65
|
+
isSafari: prompt.browser.isSafari && !prompt.browser.isChromium, // Real Safari only
|
|
66
|
+
isChrome: prompt.browser.isChrome,
|
|
67
|
+
isFirefox: prompt.browser.isFirefox,
|
|
68
|
+
isEdge: prompt.browser.isEdge,
|
|
69
|
+
isOpera: prompt.browser.isOpera,
|
|
70
|
+
isBrave: prompt.browser.isBrave,
|
|
71
|
+
isArc: prompt.browser.isArc,
|
|
72
|
+
isVivaldi: prompt.browser.isVivaldi,
|
|
73
|
+
isYandex: prompt.browser.isYandex,
|
|
74
|
+
isSamsungBrowser: prompt.browser.isSamsungBrowser,
|
|
75
|
+
isUCBrowser: prompt.browser.isUCBrowser,
|
|
76
|
+
isChromium: prompt.browser.isChromium,
|
|
77
|
+
browserName: prompt.browser.browserName,
|
|
78
|
+
|
|
79
|
+
// State
|
|
80
|
+
isInstalled: prompt.isInstalled,
|
|
81
|
+
canPrompt: prompt.canPrompt,
|
|
82
|
+
|
|
83
|
+
// Actions
|
|
84
|
+
install: prompt.promptInstall,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
return <PwaContext.Provider value={value}>{children}</PwaContext.Provider>;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Use install context
|
|
92
|
+
* Must be used within <PwaProvider>
|
|
93
|
+
*/
|
|
94
|
+
export function useInstall(): PwaContextValue {
|
|
95
|
+
const context = useContext(PwaContext);
|
|
96
|
+
|
|
97
|
+
if (context === undefined) {
|
|
98
|
+
throw new Error('useInstall must be used within <PwaProvider>');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return context;
|
|
102
|
+
}
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* PWA Install
|
|
2
|
+
* PWA Install Snippet
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Simplified PWA installation for web apps
|
|
5
|
+
* Handles Add to Home Screen (A2HS) for iOS Safari and Android Chrome
|
|
6
6
|
*
|
|
7
7
|
* @example Basic usage
|
|
8
8
|
* ```tsx
|
|
9
|
-
* import {
|
|
10
|
-
* import { DEFAULT_VAPID_PUBLIC_KEY } from './config';
|
|
9
|
+
* import { PwaProvider, A2HSHint } from '@/snippets/PWAInstall';
|
|
11
10
|
*
|
|
12
11
|
* export default function Layout({ children }) {
|
|
13
12
|
* return (
|
|
14
|
-
* <
|
|
13
|
+
* <PwaProvider>
|
|
15
14
|
* {children}
|
|
16
15
|
* <A2HSHint resetAfterDays={3} />
|
|
17
|
-
* </
|
|
16
|
+
* </PwaProvider>
|
|
18
17
|
* );
|
|
19
18
|
* }
|
|
20
19
|
* ```
|
|
@@ -22,19 +21,12 @@
|
|
|
22
21
|
|
|
23
22
|
// Main API
|
|
24
23
|
export { PwaProvider, useInstall } from './context/InstallContext';
|
|
25
|
-
export { PushProvider, usePush } from './context/PushContext';
|
|
26
|
-
export type { PushMessage, PushContextValue } from './context/PushContext';
|
|
27
|
-
|
|
28
|
-
// Django Integration
|
|
29
|
-
export { DjangoPushProvider, useDjangoPushContext } from './context/DjangoPushContext';
|
|
30
24
|
export { A2HSHint } from './components/A2HSHint';
|
|
31
|
-
export {
|
|
32
|
-
export {
|
|
25
|
+
export { IOSGuide } from './components/IOSGuide';
|
|
26
|
+
export { DesktopGuide } from './components/DesktopGuide';
|
|
33
27
|
|
|
34
28
|
// Hooks
|
|
35
29
|
export { useIsPWA, clearIsPWACache, type UseIsPWAOptions } from './hooks/useIsPWA';
|
|
36
|
-
export { usePushNotifications } from './hooks/usePushNotifications';
|
|
37
|
-
export { useDjangoPush } from './hooks/useDjangoPush';
|
|
38
30
|
|
|
39
31
|
// Utilities
|
|
40
32
|
export {
|
|
@@ -53,24 +45,16 @@ export {
|
|
|
53
45
|
isPWADebugEnabled,
|
|
54
46
|
} from './utils/logger';
|
|
55
47
|
|
|
56
|
-
export {
|
|
57
|
-
urlBase64ToUint8Array,
|
|
58
|
-
isValidVapidKey,
|
|
59
|
-
getVapidKeyInfo,
|
|
60
|
-
safeUrlBase64ToUint8Array,
|
|
61
|
-
VapidKeyError,
|
|
62
|
-
type VapidKeyErrorCode,
|
|
63
|
-
} from './utils/vapid';
|
|
64
|
-
|
|
65
48
|
export {
|
|
66
49
|
STORAGE_KEYS,
|
|
67
50
|
markA2HSDismissed,
|
|
68
|
-
markPushDismissed,
|
|
69
51
|
isA2HSDismissedRecently,
|
|
70
|
-
|
|
71
|
-
clearAllPWAData,
|
|
52
|
+
clearAllPWAInstallData,
|
|
72
53
|
} from './utils/localStorage';
|
|
73
54
|
|
|
55
|
+
// Types - Configuration
|
|
56
|
+
export type { PwaInstallConfig } from './types';
|
|
57
|
+
|
|
74
58
|
// Types - Platform
|
|
75
59
|
export type { PlatformInfo } from './types';
|
|
76
60
|
|
|
@@ -82,9 +66,6 @@ export type {
|
|
|
82
66
|
IOSGuideState,
|
|
83
67
|
} from './types';
|
|
84
68
|
|
|
85
|
-
// Types - Push Notifications
|
|
86
|
-
export type { PushNotificationState, PushNotificationOptions } from './types';
|
|
87
|
-
|
|
88
69
|
// Types - Components
|
|
89
70
|
export type {
|
|
90
71
|
InstallContextType,
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
import type { PlatformInfo } from './platform';
|
|
6
6
|
import type { InstallOutcome } from './install';
|
|
7
|
-
import type { PushNotificationState } from './push';
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
* Install context type
|
|
@@ -22,11 +21,6 @@ export interface InstallContextType {
|
|
|
22
21
|
// Actions
|
|
23
22
|
promptInstall: () => Promise<InstallOutcome>;
|
|
24
23
|
dismissIOSGuide: () => void;
|
|
25
|
-
|
|
26
|
-
// Push Notifications
|
|
27
|
-
pushState: PushNotificationState;
|
|
28
|
-
subscribeToPush: () => Promise<boolean>;
|
|
29
|
-
unsubscribeFromPush: () => Promise<boolean>;
|
|
30
24
|
}
|
|
31
25
|
|
|
32
26
|
/**
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PWA Install Configuration Types
|
|
3
|
+
*
|
|
4
|
+
* Configuration for Progressive Web App installation features
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface PwaInstallConfig {
|
|
8
|
+
/** Enable PWA installation features */
|
|
9
|
+
enabled?: boolean;
|
|
10
|
+
|
|
11
|
+
/** Show A2HS (Add to Home Screen) hint (enabled by default when enabled is true) */
|
|
12
|
+
showInstallHint?: boolean;
|
|
13
|
+
|
|
14
|
+
/** Number of days before re-showing dismissed hint (null = never show again) */
|
|
15
|
+
resetAfterDays?: number | null;
|
|
16
|
+
|
|
17
|
+
/** Delay before showing hint (ms) */
|
|
18
|
+
delayMs?: number;
|
|
19
|
+
|
|
20
|
+
/** App logo URL to display in hint */
|
|
21
|
+
logo?: string;
|
|
22
|
+
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* PWA Types - Central Export
|
|
2
|
+
* PWA Install Types - Central Export
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
// Configuration
|
|
6
|
+
export type { PwaInstallConfig } from './config';
|
|
7
|
+
|
|
5
8
|
// Platform
|
|
6
9
|
export type { PlatformInfo } from './platform';
|
|
7
10
|
|
|
@@ -13,9 +16,6 @@ export type {
|
|
|
13
16
|
IOSGuideState,
|
|
14
17
|
} from './install';
|
|
15
18
|
|
|
16
|
-
// Push Notifications
|
|
17
|
-
export type { PushNotificationState, PushNotificationOptions } from './push';
|
|
18
|
-
|
|
19
19
|
// Components
|
|
20
20
|
export type {
|
|
21
21
|
InstallContextType,
|
|
@@ -11,7 +11,6 @@ export const STORAGE_KEYS = {
|
|
|
11
11
|
IOS_GUIDE_DISMISSED: 'pwa_ios_guide_dismissed_at',
|
|
12
12
|
APP_INSTALLED: 'pwa_app_installed',
|
|
13
13
|
A2HS_DISMISSED: 'pwa_a2hs_dismissed_at',
|
|
14
|
-
PUSH_DISMISSED: 'pwa_push_dismissed_at',
|
|
15
14
|
} as const;
|
|
16
15
|
|
|
17
16
|
/**
|
|
@@ -149,26 +148,6 @@ export function markA2HSDismissed(): void {
|
|
|
149
148
|
}
|
|
150
149
|
}
|
|
151
150
|
|
|
152
|
-
/**
|
|
153
|
-
* Check if push prompt was dismissed recently
|
|
154
|
-
* @param resetDays Number of days before re-showing (default: 7)
|
|
155
|
-
*/
|
|
156
|
-
export function isPushDismissedRecently(resetDays: number = 7): boolean {
|
|
157
|
-
return isDismissedRecentlyHelper(resetDays, STORAGE_KEYS.PUSH_DISMISSED);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Mark push prompt as dismissed
|
|
162
|
-
*/
|
|
163
|
-
export function markPushDismissed(): void {
|
|
164
|
-
if (typeof window === 'undefined') return;
|
|
165
|
-
try {
|
|
166
|
-
localStorage.setItem(STORAGE_KEYS.PUSH_DISMISSED, Date.now().toString());
|
|
167
|
-
} catch {
|
|
168
|
-
// Fail silently
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
151
|
/**
|
|
173
152
|
* Helper: Check if a key was dismissed recently
|
|
174
153
|
* @internal
|
|
@@ -189,14 +168,13 @@ function isDismissedRecentlyHelper(resetDays: number, key: string): boolean {
|
|
|
189
168
|
/**
|
|
190
169
|
* Clear all PWA install data
|
|
191
170
|
*/
|
|
192
|
-
export function
|
|
171
|
+
export function clearAllPWAInstallData(): void {
|
|
193
172
|
if (typeof window === 'undefined') return;
|
|
194
173
|
|
|
195
174
|
try {
|
|
196
175
|
localStorage.removeItem(STORAGE_KEYS.IOS_GUIDE_DISMISSED);
|
|
197
176
|
localStorage.removeItem(STORAGE_KEYS.APP_INSTALLED);
|
|
198
177
|
localStorage.removeItem(STORAGE_KEYS.A2HS_DISMISSED);
|
|
199
|
-
localStorage.removeItem(STORAGE_KEYS.PUSH_DISMISSED);
|
|
200
178
|
} catch {
|
|
201
179
|
// Fail silently
|
|
202
180
|
}
|