Update city radius values and add Miami; refine map styles and improve OpenSky code readability

This commit is contained in:
Kewonit
2026-02-14 16:39:17 +05:30
parent bea74cc70f
commit 0f8012361f
13 changed files with 4647 additions and 196 deletions

View File

@ -0,0 +1,111 @@
"use client";
import { useEffect, useRef } from "react";
import { useMap } from "./map";
import { useSettings } from "@/hooks/use-settings";
import type { City } from "@/lib/cities";
const IDLE_TIMEOUT_MS = 5_000;
export function CameraController({ city }: { city: City }) {
const { map, isLoaded } = useMap();
const { settings } = useSettings();
const prevCityRef = useRef<string | null>(null);
const idleTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const orbitFrameRef = useRef<number | null>(null);
const isInteractingRef = useRef(false);
useEffect(() => {
if (!map || !isLoaded || !city) return;
if (city.id === prevCityRef.current) return;
prevCityRef.current = city.id;
map.flyTo({
center: city.coordinates,
zoom: 9.2,
pitch: 49,
bearing: 27.4,
duration: 2800,
essential: true,
});
}, [map, isLoaded, city]);
useEffect(() => {
if (!map || !isLoaded || !city || !settings.autoOrbit) {
if (orbitFrameRef.current) cancelAnimationFrame(orbitFrameRef.current);
if (idleTimerRef.current) clearTimeout(idleTimerRef.current);
return;
}
const prefersReducedMotion =
window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false;
if (prefersReducedMotion) return;
const directionMultiplier =
settings.orbitDirection === "clockwise" ? 1 : -1;
const speed = settings.orbitSpeed * directionMultiplier;
function startOrbit() {
if (!map || isInteractingRef.current) return;
function tick() {
if (!map || isInteractingRef.current) return;
const bearing = map.getBearing() + speed;
map.setBearing(bearing % 360);
orbitFrameRef.current = requestAnimationFrame(tick);
}
orbitFrameRef.current = requestAnimationFrame(tick);
}
function stopOrbit() {
if (orbitFrameRef.current) {
cancelAnimationFrame(orbitFrameRef.current);
orbitFrameRef.current = null;
}
}
function resetIdleTimer() {
isInteractingRef.current = true;
stopOrbit();
if (idleTimerRef.current) clearTimeout(idleTimerRef.current);
idleTimerRef.current = setTimeout(() => {
isInteractingRef.current = false;
startOrbit();
}, IDLE_TIMEOUT_MS);
}
const events = ["mousedown", "wheel", "touchstart"] as const;
const container = map.getContainer();
events.forEach((e) =>
container.addEventListener(e, resetIdleTimer, { passive: true }),
);
const onMoveStart = () => {
if (isInteractingRef.current) stopOrbit();
};
map.on("movestart", onMoveStart);
idleTimerRef.current = setTimeout(() => {
isInteractingRef.current = false;
startOrbit();
}, IDLE_TIMEOUT_MS);
return () => {
stopOrbit();
if (idleTimerRef.current) clearTimeout(idleTimerRef.current);
events.forEach((e) => container.removeEventListener(e, resetIdleTimer));
map.off("movestart", onMoveStart);
};
}, [
map,
isLoaded,
city,
settings.autoOrbit,
settings.orbitSpeed,
settings.orbitDirection,
]);
return null;
}