Fix mobile Safari reload loop by persisting config cache in window object
All checks were successful
Build And Push Image / docker (push) Successful in 2m56s

- Store config cache in window.__configCache instead of module-level variable to maintain persistence across Vue reactivity cycles
- Fix cardClasses ref to store computed value instead of function
- Add client plugin for config cache initialization
- Add documentation for mobile Safari reload loop fix
This commit is contained in:
2025-08-10 16:09:15 +02:00
parent 0774e16fb2
commit 86977ca92a
4 changed files with 318 additions and 20 deletions

View File

@@ -6,7 +6,7 @@
import type { RecaptchaConfig, RegistrationConfig } from './types';
// Global cache storage
// Global cache storage - use window object to persist across Vue reactivity cycles
interface ConfigCache {
recaptcha: RecaptchaConfig | null;
registration: RegistrationConfig | null;
@@ -16,14 +16,32 @@ interface ConfigCache {
registrationError: string | null;
}
let globalConfigCache: ConfigCache = {
recaptcha: null,
registration: null,
recaptchaLoading: false,
registrationLoading: false,
recaptchaError: null,
registrationError: null
};
// Use window object for true persistence across component lifecycle
function getGlobalCache(): ConfigCache {
if (typeof window === 'undefined') {
return {
recaptcha: null,
registration: null,
recaptchaLoading: false,
registrationLoading: false,
recaptchaError: null,
registrationError: null
};
}
if (!(window as any).__configCache) {
(window as any).__configCache = {
recaptcha: null,
registration: null,
recaptchaLoading: false,
registrationLoading: false,
recaptchaError: null,
registrationError: null
};
}
return (window as any).__configCache;
}
// Circuit breaker to prevent rapid successive calls
const CIRCUIT_BREAKER_THRESHOLD = 5; // Max calls in time window
@@ -55,8 +73,11 @@ function shouldBlockCall(apiName: string): boolean {
* Get reCAPTCHA configuration with caching and circuit breaker
*/
export async function getCachedRecaptchaConfig(): Promise<RecaptchaConfig> {
const globalConfigCache = getGlobalCache();
// Return cached result if available
if (globalConfigCache.recaptcha) {
console.log('[config-cache] Returning cached reCAPTCHA config');
return globalConfigCache.recaptcha;
}
@@ -125,8 +146,11 @@ export async function getCachedRecaptchaConfig(): Promise<RecaptchaConfig> {
* Get registration configuration with caching and circuit breaker
*/
export async function getCachedRegistrationConfig(): Promise<RegistrationConfig> {
const globalConfigCache = getGlobalCache();
// Return cached result if available
if (globalConfigCache.registration) {
console.log('[config-cache] Returning cached registration config');
return globalConfigCache.registration;
}
@@ -244,14 +268,17 @@ export async function loadAllConfigs(): Promise<{
*/
export function clearConfigCache(): void {
console.log('[config-cache] Clearing all cached configurations');
globalConfigCache = {
recaptcha: null,
registration: null,
recaptchaLoading: false,
registrationLoading: false,
recaptchaError: null,
registrationError: null
};
if (typeof window !== 'undefined' && (window as any).__configCache) {
(window as any).__configCache = {
recaptcha: null,
registration: null,
recaptchaLoading: false,
registrationLoading: false,
recaptchaError: null,
registrationError: null
};
}
// Clear call history for circuit breaker
Object.keys(callHistory).forEach(key => {
@@ -263,19 +290,21 @@ export function clearConfigCache(): void {
* Get cache status for debugging
*/
export function getConfigCacheStatus(): ConfigCache {
return { ...globalConfigCache };
return { ...getGlobalCache() };
}
/**
* Force reload specific config (bypasses cache)
*/
export async function reloadRecaptchaConfig(): Promise<RecaptchaConfig> {
const globalConfigCache = getGlobalCache();
globalConfigCache.recaptcha = null;
globalConfigCache.recaptchaError = null;
return getCachedRecaptchaConfig();
}
export async function reloadRegistrationConfig(): Promise<RegistrationConfig> {
const globalConfigCache = getGlobalCache();
globalConfigCache.registration = null;
globalConfigCache.registrationError = null;
return getCachedRegistrationConfig();