diff --git a/components/MultipleNationalityInput.vue b/components/MultipleNationalityInput.vue index a7b5fc3..6b801c0 100644 --- a/components/MultipleNationalityInput.vue +++ b/components/MultipleNationalityInput.vue @@ -214,12 +214,8 @@ diff --git a/composables/useMobileDetection.ts b/composables/useMobileDetection.ts new file mode 100644 index 0000000..9979b1f --- /dev/null +++ b/composables/useMobileDetection.ts @@ -0,0 +1,138 @@ +/** + * Unified Mobile Detection Composable + * Provides consistent mobile detection across the app + */ + +export interface MobileDetectionState { + isMobile: boolean; + isSafari: boolean; + isMobileSafari: boolean; + isIOS: boolean; + isAndroid: boolean; + safariVersion?: number; + viewportHeight: number; + isInitialized: boolean; +} + +// Global state to ensure single source of truth +const globalState = reactive({ + isMobile: false, + isSafari: false, + isMobileSafari: false, + isIOS: false, + isAndroid: false, + safariVersion: undefined, + viewportHeight: 0, + isInitialized: false +}); + +// Track if listeners are already set up +let listenersSetup = false; + +export const useMobileDetection = () => { + // Initialize detection on first use + if (!globalState.isInitialized && typeof window !== 'undefined') { + detectDevice(); + globalState.isInitialized = true; + + // Set up listeners only once globally + if (!listenersSetup) { + setupListeners(); + listenersSetup = true; + } + } + + // Return readonly state to prevent external modifications + return readonly(globalState); +}; + +function detectDevice() { + if (typeof window === 'undefined') return; + + const userAgent = navigator.userAgent; + + // Detect mobile + globalState.isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent); + + // Detect iOS + globalState.isIOS = /iPad|iPhone|iPod/.test(userAgent) && !(window as any).MSStream; + + // Detect Android + globalState.isAndroid = /Android/i.test(userAgent); + + // Detect Safari + globalState.isSafari = /^((?!chrome|android).)*safari/i.test(userAgent); + + // Detect Mobile Safari specifically + globalState.isMobileSafari = globalState.isIOS && globalState.isSafari; + + // Extract Safari version + if (globalState.isSafari) { + const match = userAgent.match(/Version\/(\d+)/); + if (match) { + globalState.safariVersion = parseInt(match[1]); + } + } + + // Set initial viewport height + globalState.viewportHeight = window.innerHeight; +} + +function setupListeners() { + if (typeof window === 'undefined') return; + + let resizeTimeout: NodeJS.Timeout; + let lastHeight = window.innerHeight; + + const handleResize = () => { + clearTimeout(resizeTimeout); + + // Debounce and only update if height actually changed + resizeTimeout = setTimeout(() => { + const newHeight = window.innerHeight; + + // Only update if there's a significant change (more than 20px) + // This prevents minor fluctuations from triggering updates + if (Math.abs(newHeight - lastHeight) > 20) { + lastHeight = newHeight; + globalState.viewportHeight = newHeight; + + // Update CSS custom property for mobile Safari + if (globalState.isMobileSafari) { + const vh = newHeight * 0.01; + document.documentElement.style.setProperty('--vh', `${vh}px`); + } + } + }, 150); // Increased debounce time + }; + + // Add resize listener with passive option for better performance + window.addEventListener('resize', handleResize, { passive: true }); + + // Also listen for orientation changes on mobile + if (globalState.isMobile) { + window.addEventListener('orientationchange', () => { + // Wait for orientation change to complete + setTimeout(handleResize, 100); + }); + } + + // Clean up on app unmount (if needed) + if (typeof window !== 'undefined') { + const cleanup = () => { + window.removeEventListener('resize', handleResize); + if (globalState.isMobile) { + window.removeEventListener('orientationchange', handleResize); + } + }; + + // Store cleanup function for manual cleanup if needed + (window as any).__mobileDetectionCleanup = cleanup; + } +} + +// Export helper functions +export const isMobileSafari = () => globalState.isMobileSafari; +export const isMobile = () => globalState.isMobile; +export const isIOS = () => globalState.isIOS; +export const getViewportHeight = () => globalState.viewportHeight; diff --git a/pages/auth/setup-password.vue b/pages/auth/setup-password.vue index 27c7ef8..b893145 100644 --- a/pages/auth/setup-password.vue +++ b/pages/auth/setup-password.vue @@ -153,21 +153,24 @@