import { lazy, Suspense, useEffect, useState } from 'react'; import { useFetcher } from 'react-router'; import type { LatLngTuple } from 'leaflet'; import useDebouncedValue from '../../../../../hooks/useDebouncedValue'; import type { SimplifiedBrewery } from '../../../breweries.server'; import type { loader as nearbyLoader } from '../../../routes/resources/breweries-nearby'; import type { DistanceUnit } from '../../../utils/distance'; import ChangeLocationButton from './controls/ChangeLocationButton'; import RadiusUnitControls from './controls/RadiusUnitControls'; import NearbyBreweriesHeader from './header/NearbyBreweriesHeader'; import NearbyBreweryList from './list/NearbyBreweryList'; import ClientOnlyMap, { MAP_PLACEHOLDER_CLASS } from './map/ClientOnlyMap'; import type { NearbyMapPin } from './map/NearbyBreweryMap'; const NearbyBreweryMap = lazy(() => import('./map/NearbyBreweryMap')); const DEFAULT_RADIUS_KM = 120; const RADIUS_DEBOUNCE_MS = 300; export interface FeaturedPin { id: string; name: string; latitude: number; longitude: number; } interface FallbackCenter { latitude: number; longitude: number; label: string; } interface NearbyBreweriesSectionProps { featured: FeaturedPin | null; fallbackCenter: FallbackCenter | null; selectedId: string | null; onSelect: (id: string) => void; } const NearbyBreweriesSection = ({ featured, fallbackCenter, selectedId, onSelect, }: NearbyBreweriesSectionProps) => { const [userLocation, setUserLocation] = useState(null); const [locationLabel, setLocationLabel] = useState('your location'); const [geoAttempted, setGeoAttempted] = useState(false); const [radiusKm, setRadiusKm] = useState(DEFAULT_RADIUS_KM); const debouncedRadiusKm = useDebouncedValue(radiusKm, RADIUS_DEBOUNCE_MS); const [unit, setUnit] = useState('km'); const fetcher = useFetcher(); const requestGeolocation = () => { if (!navigator.geolocation) { Promise.resolve().then(() => setGeoAttempted(true)); return; } navigator.geolocation.getCurrentPosition( (pos) => { setUserLocation([pos.coords.latitude, pos.coords.longitude]); setLocationLabel('your location'); setGeoAttempted(true); }, () => setGeoAttempted(true), { enableHighAccuracy: true }, ); }; useEffect(() => { requestGeolocation(); }, []); const center: LatLngTuple | null = userLocation ?? (geoAttempted && fallbackCenter ? [fallbackCenter.latitude, fallbackCenter.longitude] : null); const centerLabel = userLocation ? locationLabel : (fallbackCenter?.label ?? locationLabel); useEffect(() => { if (!center) return; fetcher.load( `/breweries/nearby?latitude=${center[0]}&longitude=${center[1]}&rangeInMetres=${debouncedRadiusKm * 1000}`, ); // eslint-disable-next-line react-hooks/exhaustive-deps }, [center?.[0], center?.[1], debouncedRadiusKm]); const nearby: SimplifiedBrewery[] = fetcher.data?.breweries ?? []; const withDistance = center ? nearby .filter((b) => b.location?.coordinates && b.distanceMetres !== null) .map((b) => ({ brewery: b, distanceMetres: b.distanceMetres as number, })) .sort((a, b) => a.distanceMetres - b.distanceMetres) : []; const pins: NearbyMapPin[] = withDistance.map(({ brewery }) => ({ id: brewery.breweryPostId, name: brewery.breweryName, cityLabel: brewery.location ? `${brewery.location.cityName}, ${brewery.location.stateProvinceCode}` : null, latitude: brewery.location!.coordinates!.latitude, longitude: brewery.location!.coordinates!.longitude, })); if (featured && !pins.some((p) => p.id === featured.id)) { pins.push({ id: featured.id, name: featured.name, cityLabel: null, latitude: featured.latitude, longitude: featured.longitude, }); } return (
setRadiusKm(Number(e.target.value))} onSelectKm={() => setUnit('km')} onSelectMi={() => setUnit('mi')} />
center ? ( }> ) : (
) } />
); }; export default NearbyBreweriesSection;