All files / src/components/pwa IosBanner.tsx

91.66% Statements 22/24
90% Branches 9/10
100% Functions 6/6
100% Lines 17/17

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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      2x     4x       2x       15x 15x     4x   15x   4x   3x       32x   32x 15x     32x   3x 2x 2x     3x                                                                                          
import { useState, useEffect } from "react";
import { Share, X } from "lucide-react";
 
const IOS_DISMISSED_KEY = "ios_install_dismissed";
 
function safeGetItem(key: string): string | null {
	try { return localStorage.getItem(key); } catch { return null; }
}
 
function safeSetItem(key: string, value: string): void {
	try { localStorage.setItem(key, value); } catch { /* storage unavailable */ }
}
 
function shouldShowIosBanner(): boolean {
	const isIos = /iPhone|iPad/.test(navigator.userAgent);
	if (!isIos) return false;
 
	const isStandalone =
		window.matchMedia("(display-mode: standalone)").matches ||
		(navigator as Navigator & { standalone?: boolean }).standalone === true;
	Iif (isStandalone) return false;
 
	if (safeGetItem(IOS_DISMISSED_KEY)) return false;
 
	return true;
}
 
export function IosBanner() {
	const [visible, setVisible] = useState(false);
 
	useEffect(() => {
		setVisible(shouldShowIosBanner());
	}, []);
 
	if (!visible) return null;
 
	const handleDismiss = () => {
		safeSetItem(IOS_DISMISSED_KEY, "true");
		setVisible(false);
	};
 
	return (
		<div className="mb-4 rounded-lg border border-blue-200 bg-blue-50 p-2 sm:p-4 dark:border-blue-800 dark:bg-blue-950">
			{/* Mobile slim view */}
			<div className="flex items-center gap-2 sm:hidden">
				<Share className="h-4 w-4 flex-shrink-0 text-blue-600" />
				<p className="flex-1 min-w-0 text-xs font-medium text-foreground-default truncate">
					Tap ↑ Share → Add to Home Screen
				</p>
				<button
					type="button"
					aria-label="Dismiss iOS install banner"
					onClick={handleDismiss}
					className="flex-shrink-0 p-1 text-foreground-muted hover:text-foreground-default rounded"
				>
					<X className="h-4 w-4" />
				</button>
			</div>
			{/* Desktop expanded view */}
			<div className="hidden sm:flex items-start gap-3">
				<Share className="mt-0.5 h-5 w-5 flex-shrink-0 text-blue-600" />
				<div className="flex-1 min-w-0">
					<p className="text-sm font-medium text-foreground-default">
						Install this app to your home screen
					</p>
					<p className="mt-0.5 text-xs text-foreground-muted">
						Tap the share icon{" "}
						<span aria-hidden="true">
							<Share className="inline h-3 w-3" />
						</span>{" "}
						and choose &ldquo;Add to Home Screen&rdquo; to receive push
						notifications.
					</p>
				</div>
				<button
					type="button"
					aria-label="Dismiss iOS install banner"
					onClick={handleDismiss}
					className="flex-shrink-0 p-1 text-foreground-muted hover:text-foreground-default rounded"
				>
					<X className="h-4 w-4" />
				</button>
			</div>
		</div>
	);
}