forked from zaidmukaddam/scira
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathInstallPrompt.tsx
139 lines (125 loc) · 4.41 KB
/
InstallPrompt.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import { useEffect, useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { X, Share, Plus, Download } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Card } from '@/components/ui/card';
export function InstallPrompt() {
const [showPrompt, setShowPrompt] = useState(false);
const [platform, setPlatform] = useState<'ios' | 'android' | 'chrome' | 'other'>('other');
const [deferredPrompt, setDeferredPrompt] = useState<any>(null);
useEffect(() => {
const isDismissed = localStorage.getItem('installPromptDismissed');
if (isDismissed) return;
// Detect platform
const userAgent = navigator.userAgent.toLowerCase();
const isIOSDevice = /ipad|iphone|ipod/.test(userAgent) && !(window as any).MSStream;
const isAndroid = /android/.test(userAgent);
const isChrome = /chrome/.test(userAgent) && /google inc/.test(navigator.vendor.toLowerCase());
if (isIOSDevice) setPlatform('ios');
else if (isAndroid) setPlatform('android');
else if (isChrome) setPlatform('chrome');
// Don't show if already installed
if (window.matchMedia('(display-mode: standalone)').matches) return;
// Handle PWA install prompt
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault();
setDeferredPrompt(e);
setShowPrompt(true);
});
// Show prompt for iOS after delay
if (isIOSDevice) {
setTimeout(() => setShowPrompt(true), 2000);
}
}, []);
const handleDismiss = () => {
setShowPrompt(false);
localStorage.setItem('installPromptDismissed', 'true');
};
const handleInstall = async () => {
if (!deferredPrompt) return;
try {
await deferredPrompt.prompt();
const choiceResult = await deferredPrompt.userChoice;
if (choiceResult.outcome === 'accepted') {
setShowPrompt(false);
setDeferredPrompt(null);
}
} catch (error) {
console.error('Install prompt error:', error);
}
};
const getInstructions = () => {
switch (platform) {
case 'ios':
return (
<p className="text-neutral-600 dark:text-neutral-400">
Tap <Share className="inline h-4 w-4 mx-1" /> and then{" "}
<span className="whitespace-nowrap">
“Add to Home Screen” <Plus className="inline h-4 w-4 ml-1" />
</span>
</p>
);
case 'android':
return (
<p className="text-neutral-600 dark:text-neutral-400">
Tap the menu <span className="font-bold">⋮</span> and select “Install app”
</p>
);
default:
return (
<p className="text-neutral-600 dark:text-neutral-400">
Install our app for a better experience <Download className="inline h-4 w-4 ml-1" />
</p>
);
}
};
return (
<AnimatePresence>
{showPrompt && (
<motion.div
initial={{ opacity: 0, y: 50 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 50 }}
className="fixed top-4 left-4 right-4 z-[100] md:left-auto md:right-4 md:w-96"
>
<Card className="p-4 bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800 shadow-lg">
<div className="flex items-start justify-between">
<div className="space-y-2">
<h3 className="font-medium text-xl text-neutral-900 dark:text-neutral-100">
Install Scira
</h3>
{getInstructions()}
</div>
<Button
variant="ghost"
size="icon"
className="h-6 w-6"
onClick={handleDismiss}
>
<X className="h-4 w-4" />
<span className="sr-only">Dismiss</span>
</Button>
</div>
{platform !== 'ios' && (
<div className="mt-4 flex justify-end gap-2">
<Button
variant="secondary"
size="sm"
onClick={handleDismiss}
>
Maybe later
</Button>
<Button
size="sm"
onClick={handleInstall}
>
Install
</Button>
</div>
)}
</Card>
</motion.div>
)}
</AnimatePresence>
);
}