import AbstractProvider, { EndpointArgument, ParseArgument, ProviderOptions, SearchArgument, SearchResult, } from './provider'; import { Loader, LoaderOptions } from '@googlemaps/js-api-loader'; export interface RequestResult { results: google.maps.GeocoderResult[]; status?: google.maps.GeocoderStatus; } export interface GeocodeError { code: Exclude; endpoint: 'GEOCODER_GEOCODE'; message: string; name: 'MapsRequestError'; stack: string; } export type GoogleProviderOptions = LoaderOptions & ProviderOptions; export default class GoogleProvider extends AbstractProvider< RequestResult, google.maps.GeocoderResult > { loader: Promise | null = null; geocoder: google.maps.Geocoder | null = null; constructor(options: GoogleProviderOptions) { super(options); if (typeof window !== 'undefined') { this.loader = new Loader(options).load().then((google) => { const geocoder = new google.maps.Geocoder(); this.geocoder = geocoder; return geocoder; }); } } endpoint({ query }: EndpointArgument): never { throw new Error('Method not implemented.'); } parse( response: ParseArgument, ): SearchResult[] { return response.data.results.map((r) => { const { lat, lng } = r.geometry.location.toJSON(); const { east, north, south, west } = r.geometry.viewport.toJSON(); return { x: lng, y: lat, label: r.formatted_address, bounds: [ [south, west], [north, east], ], raw: r, }; }); } async search( options: SearchArgument, ): Promise[]> { const geocoder = this.geocoder || (await this.loader); if (!geocoder) { throw new Error( 'GoogleMaps GeoCoder is not loaded. Are you trying to run this server side?', ); } const response = await geocoder .geocode({ address: options.query }, (response) => ({ results: response, })) .catch((e: GeocodeError) => { if (e.code !== 'ZERO_RESULTS') { console.error(`${e.code}: ${e.message}`); } return { results: [] }; }); return this.parse({ data: response }); } }