handle safari
This commit is contained in:
@@ -57,6 +57,17 @@ type BeforeInstallPromptEvent = Event & {
|
|||||||
userChoice: Promise<{ outcome: 'accepted' | 'dismissed' }>;
|
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) {
|
function SideMenu({ menuState, toggleMenuState, nightModeState, togglenightModeState }: Props) {
|
||||||
const menuStateMemo = useMemo(() => menuState, [menuState]);
|
const menuStateMemo = useMemo(() => menuState, [menuState]);
|
||||||
const closeRef = useRef<HTMLDivElement>(null);
|
const closeRef = useRef<HTMLDivElement>(null);
|
||||||
@@ -65,6 +76,8 @@ function SideMenu({ menuState, toggleMenuState, nightModeState, togglenightModeS
|
|||||||
const { state: shareModal, toggle: toggleShareModal } = useToggle();
|
const { state: shareModal, toggle: toggleShareModal } = useToggle();
|
||||||
const { state: installPwaModal, toggle: toggleInstallPwaModal } = useToggle();
|
const { state: installPwaModal, toggle: toggleInstallPwaModal } = useToggle();
|
||||||
const [deferredPrompt, setDeferredPrompt] = useState<BeforeInstallPromptEvent | null>(null);
|
const [deferredPrompt, setDeferredPrompt] = useState<BeforeInstallPromptEvent | null>(null);
|
||||||
|
const [isIOSDevice, setIsIOSDevice] = useState(false);
|
||||||
|
const [isPWAInstalled, setIsPWAInstalled] = useState(false);
|
||||||
|
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const { name } = params;
|
const { name } = params;
|
||||||
@@ -85,16 +98,23 @@ function SideMenu({ menuState, toggleMenuState, nightModeState, togglenightModeS
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleBeforeInstallPrompt = (e: Event) => {
|
const ios = isIOS();
|
||||||
e.preventDefault();
|
const standalone = isStandalone();
|
||||||
setDeferredPrompt(e as BeforeInstallPromptEvent);
|
setIsIOSDevice(ios);
|
||||||
};
|
setIsPWAInstalled(standalone);
|
||||||
|
|
||||||
window.addEventListener('beforeinstallprompt', handleBeforeInstallPrompt);
|
if (!ios) {
|
||||||
|
const handleBeforeInstallPrompt = (e: Event) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setDeferredPrompt(e as BeforeInstallPromptEvent);
|
||||||
|
};
|
||||||
|
|
||||||
return () => {
|
window.addEventListener('beforeinstallprompt', handleBeforeInstallPrompt);
|
||||||
window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt);
|
|
||||||
};
|
return () => {
|
||||||
|
window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt);
|
||||||
|
};
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleInstallPWA = async () => {
|
const handleInstallPWA = async () => {
|
||||||
@@ -131,6 +151,7 @@ function SideMenu({ menuState, toggleMenuState, nightModeState, togglenightModeS
|
|||||||
.filter(item => {
|
.filter(item => {
|
||||||
if (item.auth && !userIsAuthenticated) return false;
|
if (item.auth && !userIsAuthenticated) return false;
|
||||||
if (item.guestOnly && userIsAuthenticated) return false;
|
if (item.guestOnly && userIsAuthenticated) return false;
|
||||||
|
if (item.href === '?installpwa' && isPWAInstalled) return false;
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
.map(({ icon: Icon, ...item }, index) => {
|
.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} />
|
<DirectInbox className="inline-block ml-2 mb-1.5 stroke-primary dark:stroke-foreground" size={24} />
|
||||||
{tInstallPwaModal('Heading')}
|
{tInstallPwaModal('Heading')}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-xs font-medium mt-6">
|
{isPWAInstalled ? (
|
||||||
{tInstallPwaModal('Description')}
|
<p className="mt-6 text-xs font-medium text-center text-foreground/80 dark:text-neutral-200">
|
||||||
</p>
|
{tInstallPwaModal('AlreadyInstalled')}
|
||||||
|
</p>
|
||||||
<Button onClick={handleInstallPWA} className="text-sm mt-8" disabled={!deferredPrompt}>
|
) : isIOSDevice ? (
|
||||||
<span className="font-light">{tInstallPwaModal('ButtonOk')}</span>
|
<div className="mt-5 rounded-3xl bg-black/5 px-4 py-5 backdrop-blur-sm dark:bg-white/5">
|
||||||
</Button>
|
<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>
|
||||||
|
{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>
|
</Modal>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -50,8 +50,14 @@
|
|||||||
},
|
},
|
||||||
"InstallPwaModal": {
|
"InstallPwaModal": {
|
||||||
"Heading": "نصب اپلیکیشن",
|
"Heading": "نصب اپلیکیشن",
|
||||||
"Description": "برای نصب کلیک کنید.",
|
"Description": "برای نصب اپلیکیشن روی دستگاه خود، از دکمه زیر استفاده کنید.",
|
||||||
"ButtonOk": "نصب کن"
|
"ButtonOk": "نصب کن",
|
||||||
|
"AlreadyInstalled": "اپلیکیشن قبلاً روی دستگاه شما نصب شده است.",
|
||||||
|
"IOSDescription": "برای نصب اپلیکیشن در Safari مراحل زیر را انجام دهید:",
|
||||||
|
"IOSStep1": "از نوار پایین، روی دکمه اشتراکگذاری (Share) بزنید.",
|
||||||
|
"IOSStep2": "در منوی باز شده، گزینه «Add to Home Screen» را انتخاب کنید.",
|
||||||
|
"IOSStep3": "در صفحه بعدی روی دکمه «Add» در گوشه بالا بزنید.",
|
||||||
|
"ChromeFallback": "اگر دکمه نصب را نمیبینید، یا اپلیکیشن قبلاً نصب شده است یا مرورگر هنوز امکان نصب را فعال نکرده است. کمی بعد دوباره تلاش کنید یا از منوی مرورگر گزینه نصب در صفحه اصلی را انتخاب کنید."
|
||||||
},
|
},
|
||||||
"SearchPlaceholder": "جستجو"
|
"SearchPlaceholder": "جستجو"
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user