handle safari

This commit is contained in:
hamid zarghami
2025-12-15 16:58:31 +03:30
parent 2a07c722c9
commit b383e54c88
2 changed files with 83 additions and 17 deletions
+63 -3
View File
@@ -57,6 +57,17 @@ type BeforeInstallPromptEvent = Event & {
userChoice: Promise<{ outcome: 'accepted' | 'dismissed' }>;
};
const isIOS = () => {
if (typeof window === 'undefined') return false;
return /iPad|iPhone|iPod/.test(navigator.userAgent) ||
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
};
const isStandalone = () => {
if (typeof window === 'undefined') return false;
return ('standalone' in window.navigator) && (window.navigator as { standalone?: boolean }).standalone === true;
};
function SideMenu({ menuState, toggleMenuState, nightModeState, togglenightModeState }: Props) {
const menuStateMemo = useMemo(() => menuState, [menuState]);
const closeRef = useRef<HTMLDivElement>(null);
@@ -65,6 +76,8 @@ function SideMenu({ menuState, toggleMenuState, nightModeState, togglenightModeS
const { state: shareModal, toggle: toggleShareModal } = useToggle();
const { state: installPwaModal, toggle: toggleInstallPwaModal } = useToggle();
const [deferredPrompt, setDeferredPrompt] = useState<BeforeInstallPromptEvent | null>(null);
const [isIOSDevice, setIsIOSDevice] = useState(false);
const [isPWAInstalled, setIsPWAInstalled] = useState(false);
const params = useParams();
const { name } = params;
@@ -85,6 +98,12 @@ function SideMenu({ menuState, toggleMenuState, nightModeState, togglenightModeS
};
useEffect(() => {
const ios = isIOS();
const standalone = isStandalone();
setIsIOSDevice(ios);
setIsPWAInstalled(standalone);
if (!ios) {
const handleBeforeInstallPrompt = (e: Event) => {
e.preventDefault();
setDeferredPrompt(e as BeforeInstallPromptEvent);
@@ -95,6 +114,7 @@ function SideMenu({ menuState, toggleMenuState, nightModeState, togglenightModeS
return () => {
window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt);
};
}
}, []);
const handleInstallPWA = async () => {
@@ -131,6 +151,7 @@ function SideMenu({ menuState, toggleMenuState, nightModeState, togglenightModeS
.filter(item => {
if (item.auth && !userIsAuthenticated) return false;
if (item.guestOnly && userIsAuthenticated) return false;
if (item.href === '?installpwa' && isPWAInstalled) return false;
return true;
})
.map(({ icon: Icon, ...item }, index) => {
@@ -293,13 +314,52 @@ function SideMenu({ menuState, toggleMenuState, nightModeState, togglenightModeS
<DirectInbox className="inline-block ml-2 mb-1.5 stroke-primary dark:stroke-foreground" size={24} />
{tInstallPwaModal('Heading')}
</h2>
<p className="text-xs font-medium mt-6">
{isPWAInstalled ? (
<p className="mt-6 text-xs font-medium text-center text-foreground/80 dark:text-neutral-200">
{tInstallPwaModal('AlreadyInstalled')}
</p>
) : isIOSDevice ? (
<div className="mt-5 rounded-3xl bg-black/5 px-4 py-5 backdrop-blur-sm dark:bg-white/5">
<p className="text-xs font-medium leading-relaxed text-center text-foreground/80 dark:text-neutral-200">
{tInstallPwaModal('IOSDescription')}
</p>
<ol className="mt-5 space-y-3 text-xs font-medium rtl:text-right">
<li className="flex items-start gap-2">
<span className="mt-0.5 flex h-5 w-5 items-center justify-center rounded-full bg-primary/10 text-[11px] font-bold text-primary">
۱
</span>
<span>{tInstallPwaModal('IOSStep1')}</span>
</li>
<li className="flex items-start gap-2">
<span className="mt-0.5 flex h-5 w-5 items-center justify-center rounded-full bg-primary/10 text-[11px] font-bold text-primary">
۲
</span>
<span>{tInstallPwaModal('IOSStep2')}</span>
</li>
<li className="flex items-start gap-2">
<span className="mt-0.5 flex h-5 w-5 items-center justify-center rounded-full bg-primary/10 text-[11px] font-bold text-primary">
۳
</span>
<span>{tInstallPwaModal('IOSStep3')}</span>
</li>
</ol>
</div>
) : (
<>
<p className="mt-6 text-xs font-medium leading-relaxed text-center text-foreground/80 dark:text-neutral-200">
{tInstallPwaModal('Description')}
</p>
<Button onClick={handleInstallPWA} className="text-sm mt-8" disabled={!deferredPrompt}>
{deferredPrompt ? (
<Button onClick={handleInstallPWA} className="mt-8 text-sm">
<span className="font-light">{tInstallPwaModal('ButtonOk')}</span>
</Button>
) : (
<p className="mt-4 text-[11px] font-medium leading-relaxed text-center text-foreground/60 dark:text-neutral-300">
{tInstallPwaModal('ChromeFallback')}
</p>
)}
</>
)}
</Modal>
</>
+8 -2
View File
@@ -50,8 +50,14 @@
},
"InstallPwaModal": {
"Heading": "نصب اپلیکیشن",
"Description": "برای نصب کلیک کنید.",
"ButtonOk": "نصب کن"
"Description": "برای نصب اپلیکیشن روی دستگاه خود، از دکمه زیر استفاده کنید.",
"ButtonOk": "نصب کن",
"AlreadyInstalled": "اپلیکیشن قبلاً روی دستگاه شما نصب شده است.",
"IOSDescription": "برای نصب اپلیکیشن در Safari مراحل زیر را انجام دهید:",
"IOSStep1": "از نوار پایین، روی دکمه اشتراک‌گذاری (Share) بزنید.",
"IOSStep2": "در منوی باز شده، گزینه «Add to Home Screen» را انتخاب کنید.",
"IOSStep3": "در صفحه بعدی روی دکمه «Add» در گوشه بالا بزنید.",
"ChromeFallback": "اگر دکمه نصب را نمی‌بینید، یا اپلیکیشن قبلاً نصب شده است یا مرورگر هنوز امکان نصب را فعال نکرده است. کمی بعد دوباره تلاش کنید یا از منوی مرورگر گزینه نصب در صفحه اصلی را انتخاب کنید."
},
"SearchPlaceholder": "جستجو"
}