react-pwa-hazhtech 1.0.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/README.md +410 -0
- package/dist/index.d.mts +39 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +3 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +52 -0
package/README.md
ADDED
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
|
|
2
|
+
# react-pwa-hazhtech
|
|
3
|
+
|
|
4
|
+
A React library for Progressive Web App (PWA) functionality with install prompts, status tracking, and standalone mode detection.
|
|
5
|
+
|
|
6
|
+
## Features
|
|
7
|
+
|
|
8
|
+
- 🚀 **PWA Install Prompts**: Easy-to-use install prompt functionality
|
|
9
|
+
- 📱 **Status Tracking**: Track PWA installation and standalone mode status
|
|
10
|
+
- 🎯 **TypeScript Support**: Full TypeScript support with type definitions
|
|
11
|
+
- âš¡ **Lightweight**: Minimal bundle size with tree-shaking support
|
|
12
|
+
- 🔧 **Easy Integration**: Simple React context-based API
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install react-pwa-hazhtech
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
### 1. Wrap your app with PWAProvider
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import React from 'react';
|
|
26
|
+
import { PWAProvider } from 'react-pwa-hazhtech';
|
|
27
|
+
import App from './App';
|
|
28
|
+
|
|
29
|
+
function Root() {
|
|
30
|
+
return (
|
|
31
|
+
<PWAProvider>
|
|
32
|
+
<App />
|
|
33
|
+
</PWAProvider>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default Root;
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 2. Use the PWA hook in your components
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
import React from 'react';
|
|
44
|
+
import { usePWA } from 'react-pwa-hazhtech';
|
|
45
|
+
|
|
46
|
+
function InstallButton() {
|
|
47
|
+
const { isInstallable, isInstalled, isStandalone, promptInstall } = usePWA();
|
|
48
|
+
|
|
49
|
+
const handleInstall = async () => {
|
|
50
|
+
try {
|
|
51
|
+
const result = await promptInstall();
|
|
52
|
+
if (result?.outcome === 'accepted') {
|
|
53
|
+
console.log('PWA installed successfully');
|
|
54
|
+
}
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.error('Installation failed:', error);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
if (isInstalled || isStandalone) {
|
|
61
|
+
return <div>App is already installed</div>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!isInstallable) {
|
|
65
|
+
return <div>App is not installable</div>;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<button onClick={handleInstall}>
|
|
70
|
+
Install App
|
|
71
|
+
</button>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export default InstallButton;
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## API Reference
|
|
79
|
+
|
|
80
|
+
### PWAProvider
|
|
81
|
+
|
|
82
|
+
The `PWAProvider` component provides PWA context to your application.
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
<PWAProvider>
|
|
86
|
+
{/* Your app components */}
|
|
87
|
+
</PWAProvider>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### usePWA Hook
|
|
91
|
+
|
|
92
|
+
The `usePWA` hook provides access to PWA functionality and state.
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
const {
|
|
96
|
+
isInstallable,
|
|
97
|
+
isInstalled,
|
|
98
|
+
isStandalone,
|
|
99
|
+
promptInstall,
|
|
100
|
+
installPromptEvent
|
|
101
|
+
} = usePWA();
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
#### Properties
|
|
105
|
+
|
|
106
|
+
| Property | Type | Description |
|
|
107
|
+
|----------|------|-------------|
|
|
108
|
+
| `isInstallable` | `boolean` | Whether the app can be installed |
|
|
109
|
+
| `isInstalled` | `boolean` | Whether the app has been installed |
|
|
110
|
+
| `isStandalone` | `boolean` | Whether the app is running in standalone mode |
|
|
111
|
+
| `promptInstall` | `() => Promise<{outcome: 'accepted' \| 'dismissed'} \| null>` | Function to trigger the install prompt |
|
|
112
|
+
| `installPromptEvent` | `BeforeInstallPromptEvent \| null` | Raw browser install prompt event |
|
|
113
|
+
|
|
114
|
+
#### Methods
|
|
115
|
+
|
|
116
|
+
##### promptInstall()
|
|
117
|
+
|
|
118
|
+
Triggers the PWA installation prompt.
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
const result = await promptInstall();
|
|
122
|
+
if (result?.outcome === 'accepted') {
|
|
123
|
+
// User accepted the installation
|
|
124
|
+
} else if (result?.outcome === 'dismissed') {
|
|
125
|
+
// User dismissed the installation
|
|
126
|
+
} else {
|
|
127
|
+
// Install prompt not available
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Returns:** `Promise<{outcome: 'accepted' | 'dismissed'} | null>`
|
|
132
|
+
|
|
133
|
+
### Standalone Functions
|
|
134
|
+
|
|
135
|
+
These utility functions can be used without the PWA context:
|
|
136
|
+
|
|
137
|
+
#### triggerPWAInstall()
|
|
138
|
+
|
|
139
|
+
Triggers PWA installation without using the hook context.
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
import { triggerPWAInstall } from 'react-pwa-hazhtech';
|
|
143
|
+
|
|
144
|
+
const handleInstall = async () => {
|
|
145
|
+
const result = await triggerPWAInstall();
|
|
146
|
+
if (result?.outcome === 'accepted') {
|
|
147
|
+
console.log('Installation successful');
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Returns:** `Promise<{outcome: 'accepted' | 'dismissed'} | null>`
|
|
153
|
+
|
|
154
|
+
#### isPWAInstallAvailable()
|
|
155
|
+
|
|
156
|
+
Checks if PWA installation is available.
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
import { isPWAInstallAvailable } from 'react-pwa-hazhtech';
|
|
160
|
+
|
|
161
|
+
if (isPWAInstallAvailable()) {
|
|
162
|
+
// Show install button
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Returns:** `boolean`
|
|
167
|
+
|
|
168
|
+
#### isPWAMode()
|
|
169
|
+
|
|
170
|
+
Checks if the app is running as a PWA (standalone mode).
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
import { isPWAMode } from 'react-pwa-hazhtech';
|
|
174
|
+
|
|
175
|
+
if (isPWAMode()) {
|
|
176
|
+
// App is running as PWA
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Returns:** `boolean`
|
|
181
|
+
|
|
182
|
+
## TypeScript Support
|
|
183
|
+
|
|
184
|
+
This library includes full TypeScript support. The `BeforeInstallPromptEvent` type is automatically available when using the library.
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
import { PWAContextType } from 'react-pwa-hazhtech';
|
|
188
|
+
|
|
189
|
+
// PWAContextType includes all the hook properties and methods
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Advanced Usage
|
|
193
|
+
|
|
194
|
+
### Using the Hook in Components
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
import { usePWA } from 'react-pwa-hazhtech';
|
|
198
|
+
|
|
199
|
+
function PWAStatus() {
|
|
200
|
+
const { isInstalled, isInstallable, isStandalone } = usePWA();
|
|
201
|
+
|
|
202
|
+
return (
|
|
203
|
+
<div>
|
|
204
|
+
<p>Installable: {isInstallable ? 'Yes' : 'No'}</p>
|
|
205
|
+
<p>Installed: {isInstalled ? 'Yes' : 'No'}</p>
|
|
206
|
+
<p>Standalone: {isStandalone ? 'Yes' : 'No'}</p>
|
|
207
|
+
</div>
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Using Standalone Functions
|
|
213
|
+
|
|
214
|
+
For cases where you need to trigger installation outside of the React context:
|
|
215
|
+
|
|
216
|
+
```tsx
|
|
217
|
+
import { triggerPWAInstall, isPWAInstallAvailable } from 'react-pwa-hazhtech';
|
|
218
|
+
|
|
219
|
+
// In any JavaScript function
|
|
220
|
+
const installApp = async () => {
|
|
221
|
+
if (isPWAInstallAvailable()) {
|
|
222
|
+
const result = await triggerPWAInstall();
|
|
223
|
+
if (result?.outcome === 'accepted') {
|
|
224
|
+
console.log('App installed successfully!');
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
// Can be called from anywhere
|
|
230
|
+
document.getElementById('install-btn')?.addEventListener('click', installApp);
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Custom Install Flow
|
|
234
|
+
|
|
235
|
+
```tsx
|
|
236
|
+
import { usePWA } from 'react-pwa-hazhtech';
|
|
237
|
+
|
|
238
|
+
function CustomInstallFlow() {
|
|
239
|
+
const { isInstallable, promptInstall, installPromptEvent } = usePWA();
|
|
240
|
+
const [showPrompt, setShowPrompt] = useState(false);
|
|
241
|
+
|
|
242
|
+
useEffect(() => {
|
|
243
|
+
if (isInstallable && !localStorage.getItem('installPromptShown')) {
|
|
244
|
+
setShowPrompt(true);
|
|
245
|
+
}
|
|
246
|
+
}, [isInstallable]);
|
|
247
|
+
|
|
248
|
+
const handleInstall = async () => {
|
|
249
|
+
try {
|
|
250
|
+
const result = await promptInstall();
|
|
251
|
+
if (result?.outcome === 'accepted') {
|
|
252
|
+
console.log('Installation successful');
|
|
253
|
+
}
|
|
254
|
+
setShowPrompt(false);
|
|
255
|
+
localStorage.setItem('installPromptShown', 'true');
|
|
256
|
+
} catch (error) {
|
|
257
|
+
console.error('Installation error:', error);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const handleDismiss = () => {
|
|
262
|
+
setShowPrompt(false);
|
|
263
|
+
localStorage.setItem('installPromptShown', 'true');
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
if (!showPrompt) return null;
|
|
267
|
+
|
|
268
|
+
return (
|
|
269
|
+
<div className="install-prompt">
|
|
270
|
+
<h3>Install Our App</h3>
|
|
271
|
+
<p>Install our app for the best experience!</p>
|
|
272
|
+
<button onClick={handleInstall}>Install</button>
|
|
273
|
+
<button onClick={handleDismiss}>Maybe Later</button>
|
|
274
|
+
</div>
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## PWA Setup Requirements
|
|
280
|
+
|
|
281
|
+
To use this library effectively, your app needs to meet PWA requirements:
|
|
282
|
+
|
|
283
|
+
### 1. Service Worker
|
|
284
|
+
|
|
285
|
+
Create a `public/sw.js` file:
|
|
286
|
+
|
|
287
|
+
```js
|
|
288
|
+
const CACHE_NAME = 'my-app-v1';
|
|
289
|
+
|
|
290
|
+
self.addEventListener('install', (event) => {
|
|
291
|
+
console.log('[Service Worker] Installed');
|
|
292
|
+
self.skipWaiting();
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
self.addEventListener('activate', (event) => {
|
|
296
|
+
console.log('[Service Worker] Activated');
|
|
297
|
+
event.waitUntil(clients.claim());
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
self.addEventListener('fetch', (event) => {
|
|
301
|
+
event.respondWith(
|
|
302
|
+
caches.match(event.request)
|
|
303
|
+
.then(response => {
|
|
304
|
+
return response || fetch(event.request);
|
|
305
|
+
})
|
|
306
|
+
);
|
|
307
|
+
});
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### 2. Register Service Worker
|
|
311
|
+
|
|
312
|
+
In your main app file:
|
|
313
|
+
|
|
314
|
+
```tsx
|
|
315
|
+
// Register service worker
|
|
316
|
+
if ('serviceWorker' in navigator) {
|
|
317
|
+
window.addEventListener('load', () => {
|
|
318
|
+
navigator.serviceWorker.register('/sw.js')
|
|
319
|
+
.then((registration) => {
|
|
320
|
+
console.log('SW registered: ', registration);
|
|
321
|
+
})
|
|
322
|
+
.catch((registrationError) => {
|
|
323
|
+
console.log('SW registration failed: ', registrationError);
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### 3. Web App Manifest
|
|
330
|
+
|
|
331
|
+
Create a `public/manifest.json` file:
|
|
332
|
+
|
|
333
|
+
```json
|
|
334
|
+
{
|
|
335
|
+
"name": "My PWA App",
|
|
336
|
+
"short_name": "MyApp",
|
|
337
|
+
"description": "My Progressive Web App",
|
|
338
|
+
"start_url": "/",
|
|
339
|
+
"display": "standalone",
|
|
340
|
+
"theme_color": "#000000",
|
|
341
|
+
"background_color": "#ffffff",
|
|
342
|
+
"icons": [
|
|
343
|
+
{
|
|
344
|
+
"src": "icon-192x192.png",
|
|
345
|
+
"sizes": "192x192",
|
|
346
|
+
"type": "image/png"
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
"src": "icon-512x512.png",
|
|
350
|
+
"sizes": "512x512",
|
|
351
|
+
"type": "image/png"
|
|
352
|
+
}
|
|
353
|
+
]
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
Add to your HTML head:
|
|
358
|
+
|
|
359
|
+
```html
|
|
360
|
+
<link rel="manifest" href="/manifest.json">
|
|
361
|
+
<meta name="theme-color" content="#000000">
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
## Browser Support
|
|
365
|
+
|
|
366
|
+
This library supports all modern browsers that implement the PWA standards:
|
|
367
|
+
|
|
368
|
+
- Chrome 68+
|
|
369
|
+
- Edge 79+
|
|
370
|
+
- Safari 14.1+ (iOS 14.5+)
|
|
371
|
+
- Firefox 85+ (Android only)
|
|
372
|
+
|
|
373
|
+
## Contributing
|
|
374
|
+
|
|
375
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
376
|
+
|
|
377
|
+
## License
|
|
378
|
+
|
|
379
|
+
MIT © [HazhTech](https://github.com/hazhtech/react-pwa-hazhtech)
|
|
380
|
+
|
|
381
|
+
## Support
|
|
382
|
+
|
|
383
|
+
If you have any questions or issues, please [open an issue](https://github.com/hazhtech/react-pwa-hazhtech/issues) on GitHub.-worker.js`
|
|
384
|
+
To enable basic service worker behavior in the consuming app, add the following `service-worker.js`:
|
|
385
|
+
|
|
386
|
+
```js
|
|
387
|
+
self.addEventListener('install', (event) => {
|
|
388
|
+
console.log('[Service Worker] Installed');
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
self.addEventListener('activate', (event) => {
|
|
392
|
+
console.log('[Service Worker] Activated');
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
self.addEventListener('fetch', (event) => {
|
|
396
|
+
event.respondWith(fetch(event.request));
|
|
397
|
+
});
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
Register the service worker in your app’s `index.tsx`:
|
|
401
|
+
|
|
402
|
+
```ts
|
|
403
|
+
if ('serviceWorker' in navigator) {
|
|
404
|
+
window.addEventListener('load', () => {
|
|
405
|
+
navigator.serviceWorker.register('/service-worker.js')
|
|
406
|
+
.then(registration => console.log('SW registered:', registration))
|
|
407
|
+
.catch(err => console.error('SW registration failed:', err));
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
```
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface PWAContextProps {
|
|
4
|
+
isInstalled: boolean;
|
|
5
|
+
isInstallable: boolean;
|
|
6
|
+
promptInstall: () => Promise<{
|
|
7
|
+
outcome: 'accepted' | 'dismissed';
|
|
8
|
+
} | null>;
|
|
9
|
+
installPromptEvent: BeforeInstallPromptEvent | null;
|
|
10
|
+
isStandalone: boolean;
|
|
11
|
+
}
|
|
12
|
+
declare const PWAContext: React.Context<PWAContextProps>;
|
|
13
|
+
declare global {
|
|
14
|
+
interface WindowEventMap {
|
|
15
|
+
beforeinstallprompt: BeforeInstallPromptEvent;
|
|
16
|
+
appinstalled: Event;
|
|
17
|
+
}
|
|
18
|
+
interface BeforeInstallPromptEvent extends Event {
|
|
19
|
+
prompt: () => Promise<void>;
|
|
20
|
+
userChoice: Promise<{
|
|
21
|
+
outcome: 'accepted' | 'dismissed';
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
interface PWAProviderProps {
|
|
26
|
+
children: React.ReactNode;
|
|
27
|
+
onInstallPromptAvailable?: (event: BeforeInstallPromptEvent) => void;
|
|
28
|
+
onAppInstalled?: () => void;
|
|
29
|
+
}
|
|
30
|
+
declare const PWAProvider: React.FC<PWAProviderProps>;
|
|
31
|
+
declare const triggerPWAInstall: () => Promise<{
|
|
32
|
+
outcome: "accepted" | "dismissed";
|
|
33
|
+
} | null>;
|
|
34
|
+
declare const isPWAInstallAvailable: () => boolean;
|
|
35
|
+
declare const isPWAMode: () => boolean;
|
|
36
|
+
|
|
37
|
+
declare const usePWA: () => PWAContextProps;
|
|
38
|
+
|
|
39
|
+
export { PWAContext, type PWAContextProps, PWAProvider, isPWAInstallAvailable, isPWAMode, triggerPWAInstall, usePWA };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface PWAContextProps {
|
|
4
|
+
isInstalled: boolean;
|
|
5
|
+
isInstallable: boolean;
|
|
6
|
+
promptInstall: () => Promise<{
|
|
7
|
+
outcome: 'accepted' | 'dismissed';
|
|
8
|
+
} | null>;
|
|
9
|
+
installPromptEvent: BeforeInstallPromptEvent | null;
|
|
10
|
+
isStandalone: boolean;
|
|
11
|
+
}
|
|
12
|
+
declare const PWAContext: React.Context<PWAContextProps>;
|
|
13
|
+
declare global {
|
|
14
|
+
interface WindowEventMap {
|
|
15
|
+
beforeinstallprompt: BeforeInstallPromptEvent;
|
|
16
|
+
appinstalled: Event;
|
|
17
|
+
}
|
|
18
|
+
interface BeforeInstallPromptEvent extends Event {
|
|
19
|
+
prompt: () => Promise<void>;
|
|
20
|
+
userChoice: Promise<{
|
|
21
|
+
outcome: 'accepted' | 'dismissed';
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
interface PWAProviderProps {
|
|
26
|
+
children: React.ReactNode;
|
|
27
|
+
onInstallPromptAvailable?: (event: BeforeInstallPromptEvent) => void;
|
|
28
|
+
onAppInstalled?: () => void;
|
|
29
|
+
}
|
|
30
|
+
declare const PWAProvider: React.FC<PWAProviderProps>;
|
|
31
|
+
declare const triggerPWAInstall: () => Promise<{
|
|
32
|
+
outcome: "accepted" | "dismissed";
|
|
33
|
+
} | null>;
|
|
34
|
+
declare const isPWAInstallAvailable: () => boolean;
|
|
35
|
+
declare const isPWAMode: () => boolean;
|
|
36
|
+
|
|
37
|
+
declare const usePWA: () => PWAContextProps;
|
|
38
|
+
|
|
39
|
+
export { PWAContext, type PWAContextProps, PWAProvider, isPWAInstallAvailable, isPWAMode, triggerPWAInstall, usePWA };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
'use strict';var react=require('react'),jsxRuntime=require('react/jsx-runtime');var i=react.createContext({isInstalled:false,isInstallable:false,promptInstall:async()=>null,installPromptEvent:null,isStandalone:false}),E=({children:d,onInstallPromptAvailable:a,onAppInstalled:l})=>{let[n,r]=react.useState(null),[f,p]=react.useState(false),[P,c]=react.useState(false),[w,v]=react.useState(false);react.useEffect(()=>{let e=()=>{let m=window.matchMedia("(display-mode: standalone)").matches||window.navigator.standalone||document.referrer.includes("android-app://");v(m),p(m);};e();let t=window.matchMedia("(display-mode: standalone)"),o=()=>e();return t.addEventListener?t.addEventListener("change",o):t.addListener(o),()=>{t.removeEventListener?t.removeEventListener("change",o):t.removeListener(o);}},[]),react.useEffect(()=>{let e=o=>{o.preventDefault(),r(o),c(true),a?.(o);},t=()=>{p(true),c(false),r(null),l?.();};return window.addEventListener("beforeinstallprompt",e),window.addEventListener("appinstalled",t),()=>{window.removeEventListener("beforeinstallprompt",e),window.removeEventListener("appinstalled",t);}},[a,l]);let I=react.useCallback(async()=>{if(!n)return console.warn("Install prompt is not available. Make sure the app meets PWA criteria."),null;try{await n.prompt();let e=await n.userChoice;return e.outcome==="accepted"&&(p(!0),c(!1)),r(null),e}catch(e){throw console.error("Error during install prompt:",e),e}},[n]);return jsxRuntime.jsx(i.Provider,{value:{isInstalled:f,isInstallable:P,promptInstall:I,installPromptEvent:n,isStandalone:w},children:d})},W=async()=>typeof window>"u"?(console.warn("triggerPWAInstall: Not in browser environment"),null):window.matchMedia("(display-mode: standalone)").matches||window.navigator.standalone||document.referrer.includes("android-app://")?(console.warn("triggerPWAInstall: App is already installed"),null):new Promise(a=>{let l=async n=>{n.preventDefault();try{await n.prompt();let r=await n.userChoice;a(r);}catch(r){console.error("Error during install prompt:",r),a(null);}};window.deferredPrompt?l(window.deferredPrompt):(window.addEventListener("beforeinstallprompt",l,{once:true}),setTimeout(()=>{window.removeEventListener("beforeinstallprompt",l),console.warn("triggerPWAInstall: Install prompt not available within timeout"),a(null);},5e3));}),b=()=>typeof window>"u"?false:!(window.matchMedia("(display-mode: standalone)").matches||window.navigator.standalone||document.referrer.includes("android-app://"))&&(window.deferredPrompt!==void 0||"serviceWorker"in navigator),y=()=>typeof window>"u"?false:window.matchMedia("(display-mode: standalone)").matches||window.navigator.standalone||document.referrer.includes("android-app://");var C=()=>react.useContext(i);
|
|
2
|
+
exports.PWAContext=i;exports.PWAProvider=E;exports.isPWAInstallAvailable=b;exports.isPWAMode=y;exports.triggerPWAInstall=W;exports.usePWA=C;//# sourceMappingURL=index.js.map
|
|
3
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/PWAProvider.tsx","../src/usePWA.ts"],"names":["PWAContext","createContext","PWAProvider","children","onInstallPromptAvailable","onAppInstalled","deferredPrompt","setDeferredPrompt","useState","isInstalled","setIsInstalled","isInstallable","setIsInstallable","isStandalone","setIsStandalone","useEffect","checkStandalone","standalone","mediaQuery","handleChange","handleBeforeInstall","e","handleInstalled","promptInstall","useCallback","result","error","jsx","triggerPWAInstall","resolve","isPWAInstallAvailable","isPWAMode","usePWA","useContext"],"mappings":"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","file":"index.js","sourcesContent":["import React, { createContext, useEffect, useState, useCallback } from 'react';\n\nexport interface PWAContextProps {\n isInstalled: boolean;\n isInstallable: boolean;\n promptInstall: () => Promise<{ outcome: 'accepted' | 'dismissed' } | null>;\n installPromptEvent: BeforeInstallPromptEvent | null;\n isStandalone: boolean;\n}\n\nexport const PWAContext = createContext<PWAContextProps>({\n isInstalled: false,\n isInstallable: false,\n promptInstall: async () => null,\n installPromptEvent: null,\n isStandalone: false,\n});\n\ndeclare global {\n interface WindowEventMap {\n beforeinstallprompt: BeforeInstallPromptEvent;\n appinstalled: Event;\n }\n \n interface BeforeInstallPromptEvent extends Event {\n prompt: () => Promise<void>;\n userChoice: Promise<{ outcome: 'accepted' | 'dismissed' }>;\n }\n}\n\nexport interface PWAProviderProps {\n children: React.ReactNode;\n onInstallPromptAvailable?: (event: BeforeInstallPromptEvent) => void;\n onAppInstalled?: () => void;\n}\n\nexport const PWAProvider: React.FC<PWAProviderProps> = ({ \n children, \n onInstallPromptAvailable,\n onAppInstalled \n}) => {\n const [deferredPrompt, setDeferredPrompt] = useState<BeforeInstallPromptEvent | null>(null);\n const [isInstalled, setIsInstalled] = useState<boolean>(false);\n const [isInstallable, setIsInstallable] = useState<boolean>(false);\n const [isStandalone, setIsStandalone] = useState<boolean>(false);\n\n // Check if app is running in standalone mode\n useEffect(() => {\n const checkStandalone = () => {\n const standalone = window.matchMedia('(display-mode: standalone)').matches ||\n (window.navigator as any).standalone ||\n document.referrer.includes('android-app://');\n setIsStandalone(standalone);\n setIsInstalled(standalone);\n };\n\n checkStandalone();\n \n // Listen for changes in display mode\n const mediaQuery = window.matchMedia('(display-mode: standalone)');\n const handleChange = () => checkStandalone();\n \n if (mediaQuery.addEventListener) {\n mediaQuery.addEventListener('change', handleChange);\n } else {\n // Fallback for older browsers\n mediaQuery.addListener(handleChange);\n }\n\n return () => {\n if (mediaQuery.removeEventListener) {\n mediaQuery.removeEventListener('change', handleChange);\n } else {\n mediaQuery.removeListener(handleChange);\n }\n };\n }, []);\n\n useEffect(() => {\n const handleBeforeInstall = (e: BeforeInstallPromptEvent) => {\n e.preventDefault();\n setDeferredPrompt(e);\n setIsInstallable(true);\n onInstallPromptAvailable?.(e);\n };\n\n const handleInstalled = () => {\n setIsInstalled(true);\n setIsInstallable(false);\n setDeferredPrompt(null);\n onAppInstalled?.();\n };\n\n window.addEventListener('beforeinstallprompt', handleBeforeInstall);\n window.addEventListener('appinstalled', handleInstalled);\n\n return () => {\n window.removeEventListener('beforeinstallprompt', handleBeforeInstall);\n window.removeEventListener('appinstalled', handleInstalled);\n };\n }, [onInstallPromptAvailable, onAppInstalled]);\n\n const promptInstall = useCallback(async (): Promise<{ outcome: 'accepted' | 'dismissed' } | null> => {\n if (!deferredPrompt) {\n console.warn('Install prompt is not available. Make sure the app meets PWA criteria.');\n return null;\n }\n\n try {\n await deferredPrompt.prompt();\n const result = await deferredPrompt.userChoice;\n \n if (result.outcome === 'accepted') {\n setIsInstalled(true);\n setIsInstallable(false);\n }\n \n setDeferredPrompt(null);\n return result;\n } catch (error) {\n console.error('Error during install prompt:', error);\n throw error;\n }\n }, [deferredPrompt]);\n\n return (\n <PWAContext.Provider value={{ \n isInstalled, \n isInstallable,\n promptInstall, \n installPromptEvent: deferredPrompt,\n isStandalone\n }}>\n {children}\n </PWAContext.Provider>\n );\n};\n\n// Utility function to trigger PWA install without using the hook\nexport const triggerPWAInstall = async (): Promise<{ outcome: 'accepted' | 'dismissed' } | null> => {\n // Check if we're in a browser environment\n if (typeof window === 'undefined') {\n console.warn('triggerPWAInstall: Not in browser environment');\n return null;\n }\n\n // Check if the app is already installed\n const isStandalone = window.matchMedia('(display-mode: standalone)').matches ||\n (window.navigator as any).standalone ||\n document.referrer.includes('android-app://');\n \n if (isStandalone) {\n console.warn('triggerPWAInstall: App is already installed');\n return null;\n }\n\n return new Promise((resolve) => {\n const handleBeforeInstall = async (e: BeforeInstallPromptEvent) => {\n e.preventDefault();\n \n try {\n await e.prompt();\n const result = await e.userChoice;\n resolve(result);\n } catch (error) {\n console.error('Error during install prompt:', error);\n resolve(null);\n }\n };\n\n // Check if there's already a deferred prompt available\n if ((window as any).deferredPrompt) {\n handleBeforeInstall((window as any).deferredPrompt);\n } else {\n // Listen for the beforeinstallprompt event\n window.addEventListener('beforeinstallprompt', handleBeforeInstall, { once: true });\n \n // Timeout after 5 seconds if no install prompt is available\n setTimeout(() => {\n window.removeEventListener('beforeinstallprompt', handleBeforeInstall);\n console.warn('triggerPWAInstall: Install prompt not available within timeout');\n resolve(null);\n }, 5000);\n }\n });\n};\n\n// Utility function to check if PWA install is available\nexport const isPWAInstallAvailable = (): boolean => {\n if (typeof window === 'undefined') return false;\n \n const isStandalone = window.matchMedia('(display-mode: standalone)').matches ||\n (window.navigator as any).standalone ||\n document.referrer.includes('android-app://');\n \n return !isStandalone && (\n (window as any).deferredPrompt !== undefined ||\n 'serviceWorker' in navigator\n );\n};\n\n// Utility function to check if app is running as PWA\nexport const isPWAMode = (): boolean => {\n if (typeof window === 'undefined') return false;\n \n return window.matchMedia('(display-mode: standalone)').matches ||\n (window.navigator as any).standalone ||\n document.referrer.includes('android-app://');\n};","import { useContext } from 'react';\nimport { PWAContext } from './PWAProvider';\n\nexport const usePWA = () => useContext(PWAContext);"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import {createContext,useContext,useState,useEffect,useCallback}from'react';import {jsx}from'react/jsx-runtime';var i=createContext({isInstalled:false,isInstallable:false,promptInstall:async()=>null,installPromptEvent:null,isStandalone:false}),E=({children:d,onInstallPromptAvailable:a,onAppInstalled:l})=>{let[n,r]=useState(null),[f,p]=useState(false),[P,c]=useState(false),[w,v]=useState(false);useEffect(()=>{let e=()=>{let m=window.matchMedia("(display-mode: standalone)").matches||window.navigator.standalone||document.referrer.includes("android-app://");v(m),p(m);};e();let t=window.matchMedia("(display-mode: standalone)"),o=()=>e();return t.addEventListener?t.addEventListener("change",o):t.addListener(o),()=>{t.removeEventListener?t.removeEventListener("change",o):t.removeListener(o);}},[]),useEffect(()=>{let e=o=>{o.preventDefault(),r(o),c(true),a?.(o);},t=()=>{p(true),c(false),r(null),l?.();};return window.addEventListener("beforeinstallprompt",e),window.addEventListener("appinstalled",t),()=>{window.removeEventListener("beforeinstallprompt",e),window.removeEventListener("appinstalled",t);}},[a,l]);let I=useCallback(async()=>{if(!n)return console.warn("Install prompt is not available. Make sure the app meets PWA criteria."),null;try{await n.prompt();let e=await n.userChoice;return e.outcome==="accepted"&&(p(!0),c(!1)),r(null),e}catch(e){throw console.error("Error during install prompt:",e),e}},[n]);return jsx(i.Provider,{value:{isInstalled:f,isInstallable:P,promptInstall:I,installPromptEvent:n,isStandalone:w},children:d})},W=async()=>typeof window>"u"?(console.warn("triggerPWAInstall: Not in browser environment"),null):window.matchMedia("(display-mode: standalone)").matches||window.navigator.standalone||document.referrer.includes("android-app://")?(console.warn("triggerPWAInstall: App is already installed"),null):new Promise(a=>{let l=async n=>{n.preventDefault();try{await n.prompt();let r=await n.userChoice;a(r);}catch(r){console.error("Error during install prompt:",r),a(null);}};window.deferredPrompt?l(window.deferredPrompt):(window.addEventListener("beforeinstallprompt",l,{once:true}),setTimeout(()=>{window.removeEventListener("beforeinstallprompt",l),console.warn("triggerPWAInstall: Install prompt not available within timeout"),a(null);},5e3));}),b=()=>typeof window>"u"?false:!(window.matchMedia("(display-mode: standalone)").matches||window.navigator.standalone||document.referrer.includes("android-app://"))&&(window.deferredPrompt!==void 0||"serviceWorker"in navigator),y=()=>typeof window>"u"?false:window.matchMedia("(display-mode: standalone)").matches||window.navigator.standalone||document.referrer.includes("android-app://");var C=()=>useContext(i);
|
|
2
|
+
export{i as PWAContext,E as PWAProvider,b as isPWAInstallAvailable,y as isPWAMode,W as triggerPWAInstall,C as usePWA};//# sourceMappingURL=index.mjs.map
|
|
3
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/PWAProvider.tsx","../src/usePWA.ts"],"names":["PWAContext","createContext","PWAProvider","children","onInstallPromptAvailable","onAppInstalled","deferredPrompt","setDeferredPrompt","useState","isInstalled","setIsInstalled","isInstallable","setIsInstallable","isStandalone","setIsStandalone","useEffect","checkStandalone","standalone","mediaQuery","handleChange","handleBeforeInstall","e","handleInstalled","promptInstall","useCallback","result","error","jsx","triggerPWAInstall","resolve","isPWAInstallAvailable","isPWAMode","usePWA","useContext"],"mappings":"gHAUaA,IAAAA,CAAAA,CAAaC,aAA+B,CAAA,CACvD,WAAa,CAAA,KAAA,CACb,cAAe,KACf,CAAA,aAAA,CAAe,SAAY,IAAA,CAC3B,kBAAoB,CAAA,IAAA,CACpB,aAAc,KAChB,CAAC,EAoBYC,CAA0C,CAAA,CAAC,CACtD,QAAAC,CAAAA,CAAAA,CACA,wBAAAC,CAAAA,CAAAA,CACA,cAAAC,CAAAA,CACF,IAAM,CACJ,GAAM,CAACC,CAAAA,CAAgBC,CAAiB,CAAA,CAAIC,SAA0C,IAAI,CAAA,CACpF,CAACC,CAAAA,CAAaC,CAAc,CAAA,CAAIF,SAAkB,KAAK,CAAA,CACvD,CAACG,CAAeC,CAAAA,CAAgB,EAAIJ,QAAkB,CAAA,KAAK,CAC3D,CAAA,CAACK,CAAcC,CAAAA,CAAe,EAAIN,QAAkB,CAAA,KAAK,CAG/DO,CAAAA,SAAAA,CAAU,IAAM,CACd,IAAMC,CAAkB,CAAA,IAAM,CAC5B,IAAMC,CAAa,CAAA,MAAA,CAAO,WAAW,4BAA4B,CAAA,CAAE,OAChE,EAAA,MAAA,CAAO,SAAkB,CAAA,UAAA,EAC1B,SAAS,QAAS,CAAA,QAAA,CAAS,gBAAgB,CAAA,CAC7CH,CAAgBG,CAAAA,CAAU,EAC1BP,CAAeO,CAAAA,CAAU,EAC3B,CAAA,CAEAD,CAAgB,EAAA,CAGhB,IAAME,CAAa,CAAA,MAAA,CAAO,UAAW,CAAA,4BAA4B,CAC3DC,CAAAA,CAAAA,CAAe,IAAMH,CAAgB,EAAA,CAE3C,OAAIE,CAAW,CAAA,gBAAA,CACbA,EAAW,gBAAiB,CAAA,QAAA,CAAUC,CAAY,CAAA,CAGlDD,CAAW,CAAA,WAAA,CAAYC,CAAY,CAG9B,CAAA,IAAM,CACPD,CAAAA,CAAW,mBACbA,CAAAA,CAAAA,CAAW,oBAAoB,QAAUC,CAAAA,CAAY,CAErDD,CAAAA,CAAAA,CAAW,cAAeC,CAAAA,CAAY,EAE1C,CACF,CAAA,CAAG,EAAE,CAAA,CAELJ,UAAU,IAAM,CACd,IAAMK,CAAAA,CAAuBC,CAAgC,EAAA,CAC3DA,EAAE,cAAe,EAAA,CACjBd,CAAkBc,CAAAA,CAAC,CACnBT,CAAAA,CAAAA,CAAiB,IAAI,CACrBR,CAAAA,CAAAA,GAA2BiB,CAAC,EAC9B,CAEMC,CAAAA,CAAAA,CAAkB,IAAM,CAC5BZ,CAAAA,CAAe,IAAI,CACnBE,CAAAA,CAAAA,CAAiB,KAAK,CACtBL,CAAAA,CAAAA,CAAkB,IAAI,CAAA,CACtBF,CAAiB,KACnB,EAEA,OAAO,MAAA,CAAA,gBAAA,CAAiB,qBAAuBe,CAAAA,CAAmB,CAClE,CAAA,MAAA,CAAO,iBAAiB,cAAgBE,CAAAA,CAAe,CAEhD,CAAA,IAAM,CACX,MAAA,CAAO,oBAAoB,qBAAuBF,CAAAA,CAAmB,CACrE,CAAA,MAAA,CAAO,mBAAoB,CAAA,cAAA,CAAgBE,CAAe,EAC5D,CACF,CAAG,CAAA,CAAClB,CAA0BC,CAAAA,CAAc,CAAC,CAE7C,CAAA,IAAMkB,CAAgBC,CAAAA,WAAAA,CAAY,SAAmE,CACnG,GAAI,CAAClB,CAAAA,CACH,OAAQ,OAAA,CAAA,IAAA,CAAK,wEAAwE,CAAA,CAC9E,KAGT,GAAI,CACF,MAAMA,CAAe,CAAA,MAAA,GACrB,IAAMmB,CAAAA,CAAS,MAAMnB,CAAAA,CAAe,UAEpC,CAAA,OAAImB,EAAO,OAAY,GAAA,UAAA,GACrBf,CAAe,CAAA,CAAA,CAAI,CACnBE,CAAAA,CAAAA,CAAiB,EAAK,CAGxBL,CAAAA,CAAAA,CAAAA,CAAkB,IAAI,CAAA,CACfkB,CACT,CAAA,MAASC,EAAO,CACd,MAAA,OAAA,CAAQ,MAAM,8BAAgCA,CAAAA,CAAK,EAC7CA,CACR,CACF,CAAG,CAAA,CAACpB,CAAc,CAAC,EAEnB,OACEqB,GAAAA,CAAC3B,CAAW,CAAA,QAAA,CAAX,CAAoB,KAAA,CAAO,CAC1B,WAAAS,CAAAA,CAAAA,CACA,aAAAE,CAAAA,CAAAA,CACA,aAAAY,CAAAA,CAAAA,CACA,mBAAoBjB,CACpB,CAAA,YAAA,CAAAO,CACF,CACG,CAAA,QAAA,CAAAV,EACH,CAEJ,CAAA,CAGayB,CAAoB,CAAA,SAE3B,OAAO,MAAA,CAAW,KACpB,OAAQ,CAAA,IAAA,CAAK,+CAA+C,CAAA,CACrD,IAIY,EAAA,MAAA,CAAO,WAAW,4BAA4B,CAAA,CAAE,OAClE,EAAA,MAAA,CAAO,SAAkB,CAAA,UAAA,EAC1B,SAAS,QAAS,CAAA,QAAA,CAAS,gBAAgB,CAAA,EAG3C,OAAQ,CAAA,IAAA,CAAK,6CAA6C,CACnD,CAAA,IAAA,EAGF,IAAI,OAAA,CAASC,CAAY,EAAA,CAC9B,IAAMT,CAAsB,CAAA,MAAOC,CAAgC,EAAA,CACjEA,CAAE,CAAA,cAAA,GAEF,GAAI,CACF,MAAMA,CAAAA,CAAE,MAAO,EAAA,CACf,IAAMI,CAAS,CAAA,MAAMJ,EAAE,UACvBQ,CAAAA,CAAAA,CAAQJ,CAAM,EAChB,CAAA,MAASC,CAAO,CAAA,CACd,OAAQ,CAAA,KAAA,CAAM,+BAAgCA,CAAK,CAAA,CACnDG,CAAQ,CAAA,IAAI,EACd,CACF,EAGK,MAAe,CAAA,cAAA,CAClBT,CAAqB,CAAA,MAAA,CAAe,cAAc,CAAA,EAGlD,OAAO,gBAAiB,CAAA,qBAAA,CAAuBA,CAAqB,CAAA,CAAE,IAAM,CAAA,IAAK,CAAC,CAGlF,CAAA,UAAA,CAAW,IAAM,CACf,MAAO,CAAA,mBAAA,CAAoB,sBAAuBA,CAAmB,CAAA,CACrE,OAAQ,CAAA,IAAA,CAAK,gEAAgE,CAAA,CAC7ES,EAAQ,IAAI,EACd,CAAG,CAAA,GAAI,CAEX,EAAA,CAAC,EAIUC,CAAwB,CAAA,IAC/B,OAAO,MAAW,CAAA,GAAA,CAAoB,MAMnC,EAJc,MAAA,CAAO,UAAW,CAAA,4BAA4B,CAAE,CAAA,OAAA,EAClE,OAAO,SAAkB,CAAA,UAAA,EAC1B,QAAS,CAAA,QAAA,CAAS,QAAS,CAAA,gBAAgB,KAG1C,MAAe,CAAA,cAAA,GAAmB,MACnC,EAAA,eAAA,GAAmB,SAKVC,CAAAA,CAAAA,CAAAA,CAAY,IACnB,OAAO,MAAA,CAAW,GAAoB,CAAA,KAAA,CAEnC,MAAO,CAAA,UAAA,CAAW,4BAA4B,CAAE,CAAA,OAAA,EACpD,MAAO,CAAA,SAAA,CAAkB,UAC1B,EAAA,QAAA,CAAS,SAAS,QAAS,CAAA,gBAAgB,EC5MlCC,IAAAA,CAAAA,CAAS,IAAMC,UAAAA,CAAWjC,CAAU","file":"index.mjs","sourcesContent":["import React, { createContext, useEffect, useState, useCallback } from 'react';\n\nexport interface PWAContextProps {\n isInstalled: boolean;\n isInstallable: boolean;\n promptInstall: () => Promise<{ outcome: 'accepted' | 'dismissed' } | null>;\n installPromptEvent: BeforeInstallPromptEvent | null;\n isStandalone: boolean;\n}\n\nexport const PWAContext = createContext<PWAContextProps>({\n isInstalled: false,\n isInstallable: false,\n promptInstall: async () => null,\n installPromptEvent: null,\n isStandalone: false,\n});\n\ndeclare global {\n interface WindowEventMap {\n beforeinstallprompt: BeforeInstallPromptEvent;\n appinstalled: Event;\n }\n \n interface BeforeInstallPromptEvent extends Event {\n prompt: () => Promise<void>;\n userChoice: Promise<{ outcome: 'accepted' | 'dismissed' }>;\n }\n}\n\nexport interface PWAProviderProps {\n children: React.ReactNode;\n onInstallPromptAvailable?: (event: BeforeInstallPromptEvent) => void;\n onAppInstalled?: () => void;\n}\n\nexport const PWAProvider: React.FC<PWAProviderProps> = ({ \n children, \n onInstallPromptAvailable,\n onAppInstalled \n}) => {\n const [deferredPrompt, setDeferredPrompt] = useState<BeforeInstallPromptEvent | null>(null);\n const [isInstalled, setIsInstalled] = useState<boolean>(false);\n const [isInstallable, setIsInstallable] = useState<boolean>(false);\n const [isStandalone, setIsStandalone] = useState<boolean>(false);\n\n // Check if app is running in standalone mode\n useEffect(() => {\n const checkStandalone = () => {\n const standalone = window.matchMedia('(display-mode: standalone)').matches ||\n (window.navigator as any).standalone ||\n document.referrer.includes('android-app://');\n setIsStandalone(standalone);\n setIsInstalled(standalone);\n };\n\n checkStandalone();\n \n // Listen for changes in display mode\n const mediaQuery = window.matchMedia('(display-mode: standalone)');\n const handleChange = () => checkStandalone();\n \n if (mediaQuery.addEventListener) {\n mediaQuery.addEventListener('change', handleChange);\n } else {\n // Fallback for older browsers\n mediaQuery.addListener(handleChange);\n }\n\n return () => {\n if (mediaQuery.removeEventListener) {\n mediaQuery.removeEventListener('change', handleChange);\n } else {\n mediaQuery.removeListener(handleChange);\n }\n };\n }, []);\n\n useEffect(() => {\n const handleBeforeInstall = (e: BeforeInstallPromptEvent) => {\n e.preventDefault();\n setDeferredPrompt(e);\n setIsInstallable(true);\n onInstallPromptAvailable?.(e);\n };\n\n const handleInstalled = () => {\n setIsInstalled(true);\n setIsInstallable(false);\n setDeferredPrompt(null);\n onAppInstalled?.();\n };\n\n window.addEventListener('beforeinstallprompt', handleBeforeInstall);\n window.addEventListener('appinstalled', handleInstalled);\n\n return () => {\n window.removeEventListener('beforeinstallprompt', handleBeforeInstall);\n window.removeEventListener('appinstalled', handleInstalled);\n };\n }, [onInstallPromptAvailable, onAppInstalled]);\n\n const promptInstall = useCallback(async (): Promise<{ outcome: 'accepted' | 'dismissed' } | null> => {\n if (!deferredPrompt) {\n console.warn('Install prompt is not available. Make sure the app meets PWA criteria.');\n return null;\n }\n\n try {\n await deferredPrompt.prompt();\n const result = await deferredPrompt.userChoice;\n \n if (result.outcome === 'accepted') {\n setIsInstalled(true);\n setIsInstallable(false);\n }\n \n setDeferredPrompt(null);\n return result;\n } catch (error) {\n console.error('Error during install prompt:', error);\n throw error;\n }\n }, [deferredPrompt]);\n\n return (\n <PWAContext.Provider value={{ \n isInstalled, \n isInstallable,\n promptInstall, \n installPromptEvent: deferredPrompt,\n isStandalone\n }}>\n {children}\n </PWAContext.Provider>\n );\n};\n\n// Utility function to trigger PWA install without using the hook\nexport const triggerPWAInstall = async (): Promise<{ outcome: 'accepted' | 'dismissed' } | null> => {\n // Check if we're in a browser environment\n if (typeof window === 'undefined') {\n console.warn('triggerPWAInstall: Not in browser environment');\n return null;\n }\n\n // Check if the app is already installed\n const isStandalone = window.matchMedia('(display-mode: standalone)').matches ||\n (window.navigator as any).standalone ||\n document.referrer.includes('android-app://');\n \n if (isStandalone) {\n console.warn('triggerPWAInstall: App is already installed');\n return null;\n }\n\n return new Promise((resolve) => {\n const handleBeforeInstall = async (e: BeforeInstallPromptEvent) => {\n e.preventDefault();\n \n try {\n await e.prompt();\n const result = await e.userChoice;\n resolve(result);\n } catch (error) {\n console.error('Error during install prompt:', error);\n resolve(null);\n }\n };\n\n // Check if there's already a deferred prompt available\n if ((window as any).deferredPrompt) {\n handleBeforeInstall((window as any).deferredPrompt);\n } else {\n // Listen for the beforeinstallprompt event\n window.addEventListener('beforeinstallprompt', handleBeforeInstall, { once: true });\n \n // Timeout after 5 seconds if no install prompt is available\n setTimeout(() => {\n window.removeEventListener('beforeinstallprompt', handleBeforeInstall);\n console.warn('triggerPWAInstall: Install prompt not available within timeout');\n resolve(null);\n }, 5000);\n }\n });\n};\n\n// Utility function to check if PWA install is available\nexport const isPWAInstallAvailable = (): boolean => {\n if (typeof window === 'undefined') return false;\n \n const isStandalone = window.matchMedia('(display-mode: standalone)').matches ||\n (window.navigator as any).standalone ||\n document.referrer.includes('android-app://');\n \n return !isStandalone && (\n (window as any).deferredPrompt !== undefined ||\n 'serviceWorker' in navigator\n );\n};\n\n// Utility function to check if app is running as PWA\nexport const isPWAMode = (): boolean => {\n if (typeof window === 'undefined') return false;\n \n return window.matchMedia('(display-mode: standalone)').matches ||\n (window.navigator as any).standalone ||\n document.referrer.includes('android-app://');\n};","import { useContext } from 'react';\nimport { PWAContext } from './PWAProvider';\n\nexport const usePWA = () => useContext(PWAContext);"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-pwa-hazhtech",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A React library for Progressive Web App (PWA) functionality with install prompts and status tracking",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.esm.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsup",
|
|
14
|
+
"dev": "tsup --watch",
|
|
15
|
+
"prepublishOnly": "npm run build",
|
|
16
|
+
"clean": "rm -rf dist"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"react",
|
|
20
|
+
"pwa",
|
|
21
|
+
"progressive-web-app",
|
|
22
|
+
"install",
|
|
23
|
+
"status",
|
|
24
|
+
"hazhtech",
|
|
25
|
+
"beforeinstallprompt",
|
|
26
|
+
"service-worker"
|
|
27
|
+
],
|
|
28
|
+
"author": "HazhTech",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "https://github.com/hazhtech/react-pwa-hazhtech.git"
|
|
33
|
+
},
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/hazhtech/react-pwa-hazhtech/issues"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/hazhtech/react-pwa-hazhtech#readme",
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"react": ">=16.8.0",
|
|
40
|
+
"react-dom": ">=16.8.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/react": "^18.2.0",
|
|
44
|
+
"react": "^18.2.0",
|
|
45
|
+
"react-dom": "^18.2.0",
|
|
46
|
+
"tsup": "^8.0.0",
|
|
47
|
+
"typescript": "^5.0.0"
|
|
48
|
+
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=14.0.0"
|
|
51
|
+
}
|
|
52
|
+
}
|