/** * @ai-summary Google Maps JavaScript API loader * Handles dynamic loading and singleton pattern */ import { getGoogleMapsApiKey } from '@/core/config/config.types'; let mapsPromise: Promise | null = null; /** * Load Google Maps JavaScript API dynamically * Uses singleton pattern - only loads once * * @returns Promise that resolves when Google Maps is loaded */ export function loadGoogleMaps(): Promise { // Return cached promise if already loading/loaded if (mapsPromise) { return mapsPromise; } // Create loading promise mapsPromise = new Promise((resolve, reject) => { // Check if already loaded in window if ((window as any).google?.maps) { resolve(); return; } // Get API key from runtime config const apiKey = getGoogleMapsApiKey(); if (!apiKey) { reject(new Error('Google Maps API key is not configured')); return; } // Create a global callback for Google Maps to call when ready const callbackName = `gmapsCallback_${Date.now()}`; (window as any)[callbackName] = () => { if ((window as any).google?.maps) { resolve(); } else { reject( new Error('Google Maps loaded but window.google.maps not available') ); } // Clean up callback delete (window as any)[callbackName]; }; // Create script tag with callback // The callback parameter tells Google Maps to call our function when ready // Using async + callback ensures Google Maps initializes asynchronously const script = document.createElement('script'); script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&callback=${callbackName}&libraries=places,marker&loading=async`; script.async = true; script.defer = true; // Load asynchronously without blocking parsing (per Maps best practices) script.onerror = () => { // Reset promise so retry is possible mapsPromise = null; delete (window as any)[callbackName]; reject(new Error('Failed to load Google Maps script')); }; // Add to document document.head.appendChild(script); }); return mapsPromise; } /** * Get Google Maps API (after loading) * * @returns Google Maps object * @throws Error if Google Maps not loaded */ export function getGoogleMapsApi(): typeof google.maps { if (!(window as any).google?.maps) { throw new Error('Google Maps not loaded. Call loadGoogleMaps() first.'); } return (window as any).google.maps; } /** * Reset loader (for testing) */ export function resetMapsLoader(): void { mapsPromise = null; }