"use client"; import { useCallback, useEffect, useRef, useState } from "react"; import { ExternalLink, LoaderCircle, LocateFixed, MapPinned, ShieldAlert, X } from "lucide-react"; import { Button } from "@/components/ui/button"; import { useI18n } from "@/lib/i18n"; import { fetchReverseLocation } from "@/lib/location-api"; import { findNearestSynopStation, type LocatedSynopStation } from "@/lib/location-utils"; import { useWeatherStore } from "@/lib/store"; const GPS_PROMPT_SEEN_KEY = "wtr:gps-prompt-seen"; function roundCoordinate(value: number) { return Number(value.toFixed(3)); } export function CurrentLocationControl({ stations }: { stations: LocatedSynopStation[] }) { const { language, t } = useI18n(); const selectLocation = useWeatherStore((state) => state.selectLocation); const [showPrompt, setShowPrompt] = useState(false); const [isLocating, setIsLocating] = useState(false); const [message, setMessage] = useState(null); const [isSecureContext, setIsSecureContext] = useState(true); const autoLocated = useRef(false); const dismissPrompt = useCallback(() => { window.localStorage.setItem(GPS_PROMPT_SEEN_KEY, "true"); setShowPrompt(false); }, []); const locate = useCallback(() => { dismissPrompt(); setMessage(null); if (!window.isSecureContext) { setMessage(t("location.gpsSecureContext")); return; } if (!navigator.geolocation) { setMessage(t("location.gpsUnavailable")); return; } if (!stations.length) { setMessage(t("location.gpsStationsPending")); return; } setIsLocating(true); navigator.geolocation.getCurrentPosition( async ({ coords }) => { const latitude = roundCoordinate(coords.latitude); const longitude = roundCoordinate(coords.longitude); try { const place = await fetchReverseLocation(latitude, longitude, language).catch(() => ({ name: t("location.gpsFallbackName"), province: null, district: null, })); const nearest = findNearestSynopStation({ ...place, latitude, longitude }, stations); if (!nearest) { setMessage(t("location.gpsStationsPending")); return; } selectLocation(nearest); setMessage(t("location.gpsSelected", { location: nearest.name })); } catch { setMessage(t("location.gpsPositionUnavailable")); } finally { setIsLocating(false); } }, (error) => { setIsLocating(false); setMessage(error.code === error.PERMISSION_DENIED ? t("location.gpsDenied") : error.code === error.TIMEOUT ? t("location.gpsTimeout") : t("location.gpsPositionUnavailable")); }, { enableHighAccuracy: true, maximumAge: 5 * 60 * 1000, timeout: 12_000 }, ); }, [dismissPrompt, language, selectLocation, stations, t]); useEffect(() => { const animationFrame = window.requestAnimationFrame(() => { setIsSecureContext(window.isSecureContext); if (window.localStorage.getItem(GPS_PROMPT_SEEN_KEY) !== "true") setShowPrompt(true); }); return () => window.cancelAnimationFrame(animationFrame); }, []); useEffect(() => { if (!window.isSecureContext || !stations.length || autoLocated.current || !navigator.permissions?.query) return; navigator.permissions.query({ name: "geolocation" }).then((permission) => { if (permission.state !== "granted" || autoLocated.current) return; autoLocated.current = true; locate(); }).catch(() => undefined); }, [locate, stations.length]); return (
{showPrompt && (

{t("location.gpsPromptTitle")}

{t("location.gpsPromptDescription")}

{!isSecureContext &&

{t("location.gpsSecureContext")}

}
)} {!showPrompt && (
{message &&

{message}

}
)}

{t("location.gpsAttribution")} OpenStreetMap

); }