Refactor mobile detection to use unified composable
All checks were successful
Build And Push Image / docker (push) Successful in 2m52s

- Add useMobileDetection composable to centralize device detection logic
- Replace direct utility imports with composable usage across components
- Update MultipleNationalityInput, PhoneInputWrapper, and auth pages
- Simplify mobile-specific styling and behavior handling
- Improve code maintainability by consolidating detection logic
This commit is contained in:
2025-08-09 19:27:15 +02:00
parent d14008efd4
commit 2b2cd5891f
8 changed files with 310 additions and 352 deletions

View File

@@ -153,21 +153,24 @@
</template>
<script setup lang="ts">
import {
getOptimizedClasses,
applyMobileSafariFixes
} from '~/utils/mobile-safari-utils';
import { useMobileDetection } from '~/composables/useMobileDetection';
definePageMeta({
layout: false,
middleware: 'guest'
});
// Use unified mobile detection
const mobileDetection = useMobileDetection();
// Mobile Safari optimization classes
const containerClasses = computed(() => [
'password-setup-page',
...getOptimizedClasses()
].join(' '));
const containerClasses = computed(() => {
const classes = ['password-setup-page'];
if (mobileDetection.isMobile) classes.push('is-mobile');
if (mobileDetection.isMobileSafari) classes.push('is-mobile-safari');
if (mobileDetection.isIOS) classes.push('is-ios');
return classes.join(' ');
});
// Reactive state
const loading = ref(false);
@@ -300,13 +303,8 @@ const setupPassword = async () => {
}
};
// Apply mobile Safari fixes
// Component initialization
onMounted(() => {
// Apply mobile Safari fixes
if (typeof window !== 'undefined') {
applyMobileSafariFixes();
}
console.log('[setup-password] Password setup page loaded for:', email.value);
// Check if we have required parameters

View File

@@ -91,10 +91,7 @@
</template>
<script setup lang="ts">
import {
getOptimizedClasses,
applyMobileSafariFixes
} from '~/utils/mobile-safari-utils';
import { useMobileDetection } from '~/composables/useMobileDetection';
definePageMeta({
layout: false,
@@ -106,11 +103,17 @@ const route = useRoute();
const email = computed(() => route.query.email as string || '');
const partialWarning = computed(() => route.query.warning === 'partial');
// Use unified mobile detection
const mobileDetection = useMobileDetection();
// Mobile Safari optimization classes
const containerClasses = computed(() => [
'verification-success',
...getOptimizedClasses()
].join(' '));
const containerClasses = computed(() => {
const classes = ['verification-success'];
if (mobileDetection.isMobile) classes.push('is-mobile');
if (mobileDetection.isMobileSafari) classes.push('is-mobile-safari');
if (mobileDetection.isIOS) classes.push('is-ios');
return classes.join(' ');
});
// Setup password URL for Keycloak - Fixed URL structure
const setupPasswordUrl = computed(() => {
@@ -144,13 +147,8 @@ const goToPasswordSetup = () => {
});
};
// Apply mobile Safari fixes and track verification
// Track verification
onMounted(() => {
// Apply mobile Safari fixes
if (typeof window !== 'undefined') {
applyMobileSafariFixes();
}
console.log('[verify-success] Email verification completed', {
email: email.value,
partialWarning: partialWarning.value,

View File

@@ -121,21 +121,24 @@
</template>
<script setup lang="ts">
import {
getOptimizedClasses,
applyMobileSafariFixes
} from '~/utils/mobile-safari-utils';
import { useMobileDetection } from '~/composables/useMobileDetection';
definePageMeta({
layout: false,
middleware: 'guest'
});
// Use unified mobile detection
const mobileDetection = useMobileDetection();
// Mobile Safari optimization classes
const containerClasses = computed(() => [
'verification-page',
...getOptimizedClasses()
].join(' '));
const containerClasses = computed(() => {
const classes = ['verification-page'];
if (mobileDetection.isMobile) classes.push('is-mobile');
if (mobileDetection.isMobileSafari) classes.push('is-mobile-safari');
if (mobileDetection.isIOS) classes.push('is-ios');
return classes.join(' ');
});
// Reactive state
const verifying = ref(true);
@@ -202,13 +205,8 @@ const retryVerification = () => {
verifyEmail();
};
// Apply mobile Safari fixes and start verification
// Start verification on mount
onMounted(() => {
// Apply mobile Safari fixes
if (typeof window !== 'undefined') {
applyMobileSafariFixes();
}
console.log('[auth/verify] Starting email verification with token:', token.value?.substring(0, 20) + '...');
// Start verification process

View File

@@ -209,14 +209,7 @@
<script setup lang="ts">
import type { RegistrationFormData, RecaptchaConfig, RegistrationConfig } from '~/utils/types';
import {
getDeviceInfo,
needsPerformanceOptimization,
shouldDisableBackdropFilter,
getOptimizedClasses,
applyMobileSafariFixes,
debounce
} from '~/utils/mobile-safari-utils';
import { useMobileDetection } from '~/composables/useMobileDetection';
// Declare global grecaptcha interface for TypeScript
declare global {
@@ -233,10 +226,8 @@ definePageMeta({
layout: false
});
// Mobile Safari optimization flags
const deviceInfo = ref(getDeviceInfo());
const performanceMode = ref(needsPerformanceOptimization());
const disableBackdropFilter = ref(shouldDisableBackdropFilter());
// Use unified mobile detection
const mobileDetection = useMobileDetection();
// Configs with fallback defaults
const recaptchaConfig = ref<RecaptchaConfig>({ siteKey: '', secretKey: '' });
@@ -288,16 +279,22 @@ useHead({
});
// Dynamic CSS classes based on device
const containerClasses = computed(() => [
'signup-container',
...getOptimizedClasses()
].join(' '));
const containerClasses = computed(() => {
const classes = ['signup-container'];
if (mobileDetection.isMobile) classes.push('is-mobile');
if (mobileDetection.isMobileSafari) classes.push('is-mobile-safari');
if (mobileDetection.isIOS) classes.push('is-ios');
return classes.join(' ');
});
const cardClasses = computed(() => [
'signup-card',
performanceMode.value ? 'performance-optimized' : '',
disableBackdropFilter.value ? 'no-backdrop-filter' : ''
].filter(Boolean).join(' '));
const cardClasses = computed(() => {
const classes = ['signup-card'];
if (mobileDetection.isMobileSafari) {
classes.push('performance-optimized');
classes.push('no-backdrop-filter');
}
return classes.filter(Boolean).join(' ');
});
// Form validation rules
const nameRules = [
@@ -455,34 +452,35 @@ onMounted(async () => {
if (typeof window === 'undefined') return;
try {
// Load configs without complex timeout logic
Promise.all([
// Load reCAPTCHA config
$fetch('/api/recaptcha-config').then((response: any) => {
// Load reCAPTCHA config
$fetch('/api/recaptcha-config')
.then((response: any) => {
if (response?.success && response?.data?.siteKey) {
recaptchaConfig.value.siteKey = response.data.siteKey;
loadRecaptchaScript(response.data.siteKey);
}
}).catch(() => {
})
.catch(() => {
// Silently fail for reCAPTCHA - not critical
}),
// Load registration config
$fetch('/api/registration-config').then((response: any) => {
console.debug('reCAPTCHA config not available');
});
// Load registration config
$fetch('/api/registration-config')
.then((response: any) => {
if (response?.success) {
registrationConfig.value = response.data;
}
}).catch(() => {
})
.catch(() => {
// Use defaults if config fails to load
console.debug('Using default registration config');
registrationConfig.value = {
membershipFee: 150,
iban: 'MC58 1756 9000 0104 0050 1001 860',
accountHolder: 'ASSOCIATION MONACO USA'
};
})
]).catch(() => {
// Global fallback - don't let errors cause page reload
});
});
} catch (error) {
// Prevent any errors from bubbling up and causing reload