Refactor mobile detection to use built-in Nuxt device module
Some checks failed
Build And Push Image / docker (push) Failing after 2m27s
Some checks failed
Build And Push Image / docker (push) Failing after 2m27s
Replace custom useMobileDetection composable with Nuxt's useDevice(), removing reactive mobile detection in favor of static detection to prevent reload loops and simplify viewport handling
This commit is contained in:
@@ -1,174 +0,0 @@
|
||||
/**
|
||||
* 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<MobileDetectionState>({
|
||||
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;
|
||||
let initialHeight = window.innerHeight;
|
||||
let keyboardOpen = false;
|
||||
|
||||
const handleResize = () => {
|
||||
// Don't handle resize if we're in the middle of a transition
|
||||
if (document.hidden) return;
|
||||
|
||||
clearTimeout(resizeTimeout);
|
||||
|
||||
// Debounce with longer delay for mobile
|
||||
const debounceDelay = globalState.isMobile ? 300 : 150;
|
||||
|
||||
resizeTimeout = setTimeout(() => {
|
||||
const newHeight = window.innerHeight;
|
||||
const heightDiff = newHeight - lastHeight;
|
||||
const absoluteDiff = Math.abs(heightDiff);
|
||||
|
||||
// Detect keyboard open/close on mobile
|
||||
if (globalState.isMobile) {
|
||||
// Keyboard likely opened (viewport got smaller)
|
||||
if (heightDiff < -100 && newHeight < initialHeight * 0.75) {
|
||||
keyboardOpen = true;
|
||||
// Don't trigger viewport updates for keyboard changes
|
||||
return;
|
||||
}
|
||||
|
||||
// Keyboard likely closed (viewport got bigger)
|
||||
if (heightDiff > 100 && keyboardOpen) {
|
||||
keyboardOpen = false;
|
||||
// Don't trigger viewport updates for keyboard changes
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Only update for significant non-keyboard changes (more than 50px)
|
||||
// or orientation changes (height differs significantly from initial)
|
||||
const isOrientationChange = absoluteDiff > initialHeight * 0.3;
|
||||
const isSignificantChange = absoluteDiff > 50;
|
||||
|
||||
if (isOrientationChange || (isSignificantChange && !keyboardOpen)) {
|
||||
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`);
|
||||
}
|
||||
|
||||
// Update initial height after orientation change
|
||||
if (isOrientationChange) {
|
||||
initialHeight = newHeight;
|
||||
}
|
||||
}
|
||||
}, debounceDelay);
|
||||
};
|
||||
|
||||
// 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', () => {
|
||||
// Reset keyboard state on orientation change
|
||||
keyboardOpen = false;
|
||||
// Wait for orientation change to complete
|
||||
setTimeout(handleResize, 200);
|
||||
});
|
||||
}
|
||||
|
||||
// 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;
|
||||
Reference in New Issue
Block a user