"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 { createSelectedLocation, 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; } 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, country: null, countryCode: null, admin1: null, admin2: null, timezone: null, region: "GLOBAL" as const, })); const selected = createSelectedLocation({ ...place, latitude, longitude }, stations); selectLocation(selected); setMessage(t("location.gpsSelected", { location: selected.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 || 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]); return (
{showPrompt && (

{t("location.gpsPromptTitle")}

{t("location.gpsPromptDescription")}

{!isSecureContext && (

{t("location.gpsSecureContext")}

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

{message}

)}
)}

{t("location.gpsAttribution")}{" "} OpenStreetMap

); }