Add Mobile Safari optimizations and fixes to signup page
All checks were successful
Build And Push Image / docker (push) Successful in 3m10s
All checks were successful
Build And Push Image / docker (push) Successful in 3m10s
- Implement device detection and performance optimization flags - Add dynamic CSS classes based on device capabilities - Create mobile safari utility functions and client plugin - Optimize backdrop filters and hardware acceleration for iOS - Fix viewport height issues with mobile Safari fallbacks - Update membership fee config and add IBAN payment details - Prevent horizontal scrolling and improve mobile UX
This commit is contained in:
171
utils/mobile-safari-utils.ts
Normal file
171
utils/mobile-safari-utils.ts
Normal file
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* Mobile Safari Detection and Optimization Utilities
|
||||
* Handles Safari-specific issues and performance optimizations
|
||||
*/
|
||||
|
||||
export interface DeviceInfo {
|
||||
isMobile: boolean;
|
||||
isSafari: boolean;
|
||||
isMobileSafari: boolean;
|
||||
isIOS: boolean;
|
||||
safariVersion?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect device and browser information
|
||||
*/
|
||||
export function getDeviceInfo(): DeviceInfo {
|
||||
if (typeof window === 'undefined') {
|
||||
return {
|
||||
isMobile: false,
|
||||
isSafari: false,
|
||||
isMobileSafari: false,
|
||||
isIOS: false
|
||||
};
|
||||
}
|
||||
|
||||
const userAgent = navigator.userAgent;
|
||||
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent);
|
||||
const isIOS = /iPad|iPhone|iPod/.test(userAgent);
|
||||
const isSafari = /^((?!chrome|android).)*safari/i.test(userAgent);
|
||||
const isMobileSafari = isIOS && isSafari;
|
||||
|
||||
// Extract Safari version if possible
|
||||
let safariVersion: number | undefined;
|
||||
if (isSafari) {
|
||||
const match = userAgent.match(/Version\/(\d+)/);
|
||||
if (match) {
|
||||
safariVersion = parseInt(match[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isMobile,
|
||||
isSafari,
|
||||
isMobileSafari,
|
||||
isIOS,
|
||||
safariVersion
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the device needs performance optimizations
|
||||
*/
|
||||
export function needsPerformanceOptimization(): boolean {
|
||||
const { isMobileSafari, isMobile } = getDeviceInfo();
|
||||
return isMobileSafari || isMobile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if backdrop-filter should be disabled
|
||||
*/
|
||||
export function shouldDisableBackdropFilter(): boolean {
|
||||
const { isMobileSafari, safariVersion } = getDeviceInfo();
|
||||
// Disable backdrop-filter on mobile Safari or older Safari versions
|
||||
return isMobileSafari || Boolean(safariVersion && safariVersion < 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get optimized CSS class names based on device
|
||||
*/
|
||||
export function getOptimizedClasses(): string[] {
|
||||
const classes: string[] = [];
|
||||
const { isMobile, isMobileSafari, isIOS } = getDeviceInfo();
|
||||
|
||||
if (isMobile) classes.push('is-mobile');
|
||||
if (isMobileSafari) classes.push('is-mobile-safari');
|
||||
if (isIOS) classes.push('is-ios');
|
||||
if (needsPerformanceOptimization()) classes.push('performance-mode');
|
||||
|
||||
return classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimized viewport height for mobile Safari
|
||||
*/
|
||||
export function getOptimizedViewportHeight(): string {
|
||||
const { isMobileSafari, safariVersion } = getDeviceInfo();
|
||||
|
||||
if (isMobileSafari) {
|
||||
// Use 100vh for older Safari, -webkit-fill-available for newer
|
||||
return safariVersion && safariVersion >= 15 ? '-webkit-fill-available' : '100vh';
|
||||
}
|
||||
|
||||
// Use dvh for modern browsers, vh as fallback
|
||||
return '100vh';
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply mobile Safari specific fixes
|
||||
*/
|
||||
export function applyMobileSafariFixes(): void {
|
||||
if (typeof window === 'undefined') return;
|
||||
|
||||
const { isMobileSafari } = getDeviceInfo();
|
||||
if (!isMobileSafari) return;
|
||||
|
||||
// Fix viewport height issues
|
||||
const setViewportHeight = () => {
|
||||
const vh = window.innerHeight * 0.01;
|
||||
document.documentElement.style.setProperty('--vh', `${vh}px`);
|
||||
};
|
||||
|
||||
// Set initial value
|
||||
setViewportHeight();
|
||||
|
||||
// Update on resize (debounced)
|
||||
let resizeTimeout: NodeJS.Timeout;
|
||||
window.addEventListener('resize', () => {
|
||||
clearTimeout(resizeTimeout);
|
||||
resizeTimeout = setTimeout(setViewportHeight, 100);
|
||||
});
|
||||
|
||||
// Add performance optimization classes
|
||||
document.documentElement.classList.add(...getOptimizedClasses());
|
||||
}
|
||||
|
||||
/**
|
||||
* Throttle function for performance
|
||||
*/
|
||||
export function throttle<T extends (...args: any[]) => void>(
|
||||
func: T,
|
||||
wait: number
|
||||
): (...args: Parameters<T>) => void {
|
||||
let timeout: NodeJS.Timeout | null = null;
|
||||
let previous = 0;
|
||||
|
||||
return function(this: any, ...args: Parameters<T>) {
|
||||
const now = Date.now();
|
||||
const remaining = wait - (now - previous);
|
||||
|
||||
if (remaining <= 0 || remaining > wait) {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
}
|
||||
previous = now;
|
||||
func.apply(this, args);
|
||||
} else if (!timeout) {
|
||||
timeout = setTimeout(() => {
|
||||
previous = Date.now();
|
||||
timeout = null;
|
||||
func.apply(this, args);
|
||||
}, remaining);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Debounce function for performance
|
||||
*/
|
||||
export function debounce<T extends (...args: any[]) => void>(
|
||||
func: T,
|
||||
wait: number
|
||||
): (...args: Parameters<T>) => void {
|
||||
let timeout: NodeJS.Timeout;
|
||||
|
||||
return function(this: any, ...args: Parameters<T>) {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => func.apply(this, args), wait);
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user