first release! 0.3.6
This commit is contained in:
97
frontend/src/components/InstallPrompt.jsx
Normal file
97
frontend/src/components/InstallPrompt.jsx
Normal file
@@ -0,0 +1,97 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
function InstallPrompt() {
|
||||
const [deferredPrompt, setDeferredPrompt] = useState(null);
|
||||
const [showPrompt, setShowPrompt] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Check if Safari (which doesn't support beforeinstallprompt)
|
||||
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
||||
if (isSafari) {
|
||||
return; // Don't show this prompt on Safari, use SafariInstallPrompt instead
|
||||
}
|
||||
|
||||
const handler = (e) => {
|
||||
// Prevent the mini-infobar from appearing on mobile
|
||||
e.preventDefault();
|
||||
// Save the event so it can be triggered later
|
||||
setDeferredPrompt(e);
|
||||
// Show our custom install prompt
|
||||
setShowPrompt(true);
|
||||
};
|
||||
|
||||
window.addEventListener('beforeinstallprompt', handler);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('beforeinstallprompt', handler);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleInstall = async () => {
|
||||
if (!deferredPrompt) return;
|
||||
|
||||
// Show the install prompt
|
||||
deferredPrompt.prompt();
|
||||
|
||||
// Wait for the user to respond to the prompt
|
||||
const { outcome } = await deferredPrompt.userChoice;
|
||||
|
||||
console.log(`User response to install prompt: ${outcome}`);
|
||||
|
||||
// Clear the saved prompt
|
||||
setDeferredPrompt(null);
|
||||
setShowPrompt(false);
|
||||
};
|
||||
|
||||
const handleDismiss = () => {
|
||||
setShowPrompt(false);
|
||||
// Remember dismissal for this session
|
||||
sessionStorage.setItem('installPromptDismissed', 'true');
|
||||
};
|
||||
|
||||
// Don't show if already dismissed in this session
|
||||
if (sessionStorage.getItem('installPromptDismissed')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!showPrompt) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed bottom-4 left-4 right-4 sm:left-auto sm:right-4 sm:max-w-md z-50 animate-slideUp">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-2xl border border-gray-200 dark:border-gray-700 p-4">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="flex-shrink-0 text-3xl">
|
||||
📱
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h3 className="font-semibold text-gray-900 dark:text-gray-100 mb-1">
|
||||
Install App
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-3">
|
||||
Install Jackbox Game Picker for quick access and offline support!
|
||||
</p>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={handleInstall}
|
||||
className="flex-1 bg-indigo-600 dark:bg-indigo-700 text-white px-4 py-2 rounded-lg hover:bg-indigo-700 dark:hover:bg-indigo-800 transition font-medium text-sm"
|
||||
>
|
||||
Install
|
||||
</button>
|
||||
<button
|
||||
onClick={handleDismiss}
|
||||
className="px-4 py-2 text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 transition text-sm"
|
||||
>
|
||||
Not now
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default InstallPrompt;
|
||||
|
||||
71
frontend/src/components/SafariInstallPrompt.jsx
Normal file
71
frontend/src/components/SafariInstallPrompt.jsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
function SafariInstallPrompt() {
|
||||
const [showPrompt, setShowPrompt] = useState(false);
|
||||
const [isStandalone, setIsStandalone] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Check if running in standalone mode (already installed)
|
||||
const standalone = window.navigator.standalone || window.matchMedia('(display-mode: standalone)').matches;
|
||||
setIsStandalone(standalone);
|
||||
|
||||
// Check if Safari on iOS or macOS
|
||||
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
||||
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
|
||||
const isMacOS = navigator.platform.includes('Mac') && !isIOS;
|
||||
|
||||
// Show prompt if Safari and not already installed
|
||||
if ((isSafari || isIOS) && !standalone && !sessionStorage.getItem('safariInstallPromptDismissed')) {
|
||||
// Wait a bit before showing to not overwhelm user
|
||||
const timer = setTimeout(() => {
|
||||
setShowPrompt(true);
|
||||
}, 3000);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleDismiss = () => {
|
||||
setShowPrompt(false);
|
||||
sessionStorage.setItem('safariInstallPromptDismissed', 'true');
|
||||
};
|
||||
|
||||
// Don't show if already installed
|
||||
if (isStandalone || !showPrompt) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed bottom-4 left-4 right-4 sm:left-auto sm:right-4 sm:max-w-md z-50 animate-slideUp">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-2xl border border-gray-200 dark:border-gray-700 p-4">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="flex-shrink-0 text-3xl">
|
||||
🍎
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h3 className="font-semibold text-gray-900 dark:text-gray-100 mb-1">
|
||||
Install as App
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-3">
|
||||
Tap the Share button <span className="inline-block w-4 h-4 align-middle">
|
||||
<svg viewBox="0 0 50 50" className="fill-current">
|
||||
<path d="M30.3 13.7L25 8.4l-5.3 5.3-1.4-1.4L25 5.6l6.7 6.7z"/>
|
||||
<path d="M24 7h2v21h-2z"/>
|
||||
<path d="M35 40H15c-1.7 0-3-1.3-3-3V19c0-1.7 1.3-3 3-3h7v2h-7c-.6 0-1 .4-1 1v18c0 .6.4 1 1 1h20c.6 0 1-.4 1-1V19c0-.6-.4-1-1-1h-7v-2h7c1.7 0 3 1.3 3 3v18c0 1.7-1.3 3-3 3z"/>
|
||||
</svg>
|
||||
</span> and select "Add to Home Screen"
|
||||
</p>
|
||||
<button
|
||||
onClick={handleDismiss}
|
||||
className="w-full text-center px-4 py-2 text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 transition text-sm border border-gray-300 dark:border-gray-600 rounded-lg"
|
||||
>
|
||||
Got it
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SafariInstallPrompt;
|
||||
|
||||
Reference in New Issue
Block a user