hyp / apps /ui /src /lib /mobile-detect.ts
Leon4gr45's picture
Upload folder using huggingface_hub
1dbc34b verified
/**
* Mobile Detection Utility
*
* Provides a cached, non-reactive mobile detection for use outside React components.
* Used by service worker registration, query client configuration, and other
* non-component code that needs to know if the device is mobile.
*
* For React components, use the `useIsMobile()` hook from `hooks/use-media-query.ts`
* instead, which responds to viewport changes reactively.
*/
/**
* Cached mobile detection result.
* Evaluated once on module load for consistent behavior across the app lifetime.
* Uses both media query and user agent for reliability:
* - Media query catches small desktop windows
* - User agent catches mobile browsers at any viewport size
* - Touch detection as supplementary signal
*/
export const isMobileDevice: boolean = (() => {
if (typeof window === 'undefined') return false;
// Check viewport width (consistent with useIsMobile hook's 768px breakpoint)
const isSmallViewport = window.matchMedia('(max-width: 768px)').matches;
// Check user agent for mobile devices
const isMobileUA = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
navigator.userAgent
);
// Check for touch-primary device (most mobile devices)
const isTouchPrimary = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
// Consider it mobile if viewport is small OR if it's a mobile UA with touch
return isSmallViewport || (isMobileUA && isTouchPrimary);
})();
/**
* Check if the device has a slow connection.
* Uses the Network Information API when available.
* Falls back to mobile detection as a heuristic.
*/
export function isSlowConnection(): boolean {
if (typeof navigator === 'undefined') return false;
const connection = (
navigator as Navigator & {
connection?: {
effectiveType?: string;
saveData?: boolean;
};
}
).connection;
if (connection) {
// Respect data saver mode
if (connection.saveData) return true;
// 2g and slow-2g are definitely slow
if (connection.effectiveType === '2g' || connection.effectiveType === 'slow-2g') return true;
}
// On mobile without connection info, assume potentially slow
return false;
}
/**
* Detect if the app is running as an installed PWA (standalone mode).
* Checks both the standard display-mode media query and the iOS-specific
* navigator.standalone property for comprehensive detection.
*
* When running as a PWA, the browser chrome is hidden so safe area insets
* can be reduced further to maximize usable screen space.
*/
export const isPwaStandalone: boolean = (() => {
if (typeof window === 'undefined') return false;
// Standard: works on Chrome, Edge, Firefox, and modern Safari
const isStandalone = window.matchMedia('(display-mode: standalone)').matches;
// iOS Safari: navigator.standalone is true when launched from home screen
const isIOSStandalone = (navigator as Navigator & { standalone?: boolean }).standalone === true;
return isStandalone || isIOSStandalone;
})();
/**
* Multiplier for polling intervals on mobile.
* Mobile devices benefit from less frequent polling to save battery and bandwidth.
* Slow connections get an even larger multiplier.
*/
export function getMobilePollingMultiplier(): number {
if (!isMobileDevice) return 1;
if (isSlowConnection()) return 4;
return 2;
}