File size: 3,358 Bytes
1dbc34b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
88
89
90
91
92
93
94
95
96
/**
 * 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;
}