Files
motovaultpro/frontend/src/features/stations/utils/maps-loader.ts
Eric Gullickson bb4a356b9e Google Maps Bug
2025-11-08 12:17:29 -06:00

94 lines
2.6 KiB
TypeScript

/**
* @ai-summary Google Maps JavaScript API loader
* Handles dynamic loading and singleton pattern
*/
import { getGoogleMapsApiKey } from '@/core/config/config.types';
let mapsPromise: Promise<void> | 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<void> {
// 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;
}