From 1794a4b67877b14aa8e7ae9c41537e7c516171a1 Mon Sep 17 00:00:00 2001 From: Kewonit <108450560+kewonit@users.noreply.github.com> Date: Sat, 14 Feb 2026 20:40:21 +0530 Subject: [PATCH] feat: enhance flight tracker with GitHub stars display and improve reset view functionality; update trail history sampling rate --- src/components/flight-tracker.tsx | 77 +++++++++++++++++++++++- src/components/map/camera-controller.tsx | 6 +- src/components/map/flight-layers.tsx | 19 ++++-- src/hooks/use-trail-history.ts | 2 +- 4 files changed, 92 insertions(+), 12 deletions(-) diff --git a/src/components/flight-tracker.tsx b/src/components/flight-tracker.tsx index 61c96e1..0341b7f 100644 --- a/src/components/flight-tracker.tsx +++ b/src/components/flight-tracker.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState, useCallback, useSyncExternalStore } from "react"; +import { useState, useCallback, useEffect, useSyncExternalStore } from "react"; import { ErrorBoundary } from "@/components/error-boundary"; import { Map } from "@/components/map/map"; import { CameraController } from "@/components/map/camera-controller"; @@ -18,11 +18,14 @@ import { CITIES, type City } from "@/lib/cities"; import { findByIata, airportToCity } from "@/lib/airports"; import type { FlightState } from "@/lib/opensky"; import type { PickingInfo } from "@deck.gl/core"; +import { Github, Star } from "lucide-react"; const DEFAULT_CITY_ID = "mia"; const STYLE_STORAGE_KEY = "aeris:mapStyle"; const DEFAULT_CITY = CITIES.find((c) => c.id === DEFAULT_CITY_ID) ?? CITIES[0]; +const GITHUB_REPO_URL = "https://github.com/kewonit/aeris"; +const GITHUB_REPO_API = "https://api.github.com/repos/kewonit/aeris"; const subscribeNoop = () => () => {}; @@ -121,6 +124,29 @@ function FlightTrackerInner() { const trails = useTrailHistory(flights); const [hoveredFlight, setHoveredFlight] = useState(null); const [cursorPos, setCursorPos] = useState({ x: 0, y: 0 }); + const [repoStars, setRepoStars] = useState(null); + + useEffect(() => { + let mounted = true; + + async function loadRepoStars() { + try { + const res = await fetch(GITHUB_REPO_API, { cache: "no-store" }); + if (!res.ok) return; + const data = (await res.json()) as { stargazers_count?: number }; + if (mounted && typeof data.stargazers_count === "number") { + setRepoStars(data.stargazers_count); + } + } catch { + /* silent fallback */ + } + } + + loadRepoStars(); + return () => { + mounted = false; + }; + }, []); const handleHover = useCallback((info: PickingInfo | null) => { if (info?.object) { @@ -143,8 +169,12 @@ function FlightTrackerInner() { }, []); const handleResetView = useCallback(() => { - window.dispatchEvent(new CustomEvent("aeris:reset-view")); - }, []); + window.dispatchEvent( + new CustomEvent("aeris:reset-view", { + detail: { center: activeCity.coordinates }, + }), + ); + }, [activeCity.coordinates]); return (
@@ -175,6 +205,41 @@ function FlightTrackerInner() {
+ + + {repoStars != null && ( + + + + {formatStarCount(repoStars)} + + + )} + ); } + +function formatStarCount(value: number): string { + if (value >= 1_000_000) return `${(value / 1_000_000).toFixed(1)}m`; + if (value >= 1_000) return `${(value / 1_000).toFixed(1)}k`; + return `${value}`; +} diff --git a/src/components/map/camera-controller.tsx b/src/components/map/camera-controller.tsx index bd65465..eec0619 100644 --- a/src/components/map/camera-controller.tsx +++ b/src/components/map/camera-controller.tsx @@ -44,9 +44,11 @@ export function CameraController({ city }: { city: City }) { }); }; - const onResetView = () => { + const onResetView = (event: Event) => { + const customEvent = event as CustomEvent<{ center?: [number, number] }>; + const center = customEvent.detail?.center ?? city.coordinates; map.flyTo({ - center: city.coordinates, + center, zoom: DEFAULT_ZOOM, pitch: DEFAULT_PITCH, bearing: DEFAULT_BEARING, diff --git a/src/components/map/flight-layers.tsx b/src/components/map/flight-layers.tsx index 1ae3ed0..d12d7be 100644 --- a/src/components/map/flight-layers.tsx +++ b/src/components/map/flight-layers.tsx @@ -14,6 +14,7 @@ const TELEPORT_THRESHOLD = 0.3; // degrees const TRAIL_BELOW_AIRCRAFT_METERS = 20; const STARTUP_TRAIL_POLLS = 3; const STARTUP_TRAIL_STEP_SEC = 12; +const TRACK_DAMPING = 0.18; function buildStartupFallbackTrail(f: FlightState): [number, number][] { if (f.longitude == null || f.latitude == null) return []; @@ -164,11 +165,16 @@ export function FlightLayers({ const next = new Map(); for (const f of flights) { if (f.longitude != null && f.latitude != null) { + const prev = newPrev.get(f.icao24); + const rawTrack = f.trueTrack ?? 0; next.set(f.icao24, { lng: f.longitude, lat: f.latitude, alt: f.baroAltitude ?? 0, - track: f.trueTrack ?? 0, + track: + prev != null + ? lerpAngle(prev.track, rawTrack, TRACK_DAMPING) + : rawTrack, }); } } @@ -228,7 +234,7 @@ export function FlightLayers({ const elapsed = performance.now() - dataTimestampRef.current; const rawT = elapsed / ANIM_DURATION_MS; const tPos = Math.min(rawT, 1); - const tAngle = smoothStep(tPos); + const tAngle = smoothStep(smoothStep(smoothStep(tPos))); const currentFlights = flightsRef.current; const currentTrails = trailsRef.current; @@ -402,11 +408,12 @@ export function FlightLayers({ : defaultColor; return Array.from({ length: len }, (_, i) => { const tVal = len > 1 ? i / (len - 1) : 1; + const fade = Math.pow(tVal, 2.4); return [ - base[0], - base[1], - base[2], - Math.round(70 + tVal * 130), + Math.min(255, base[0] + 22), + Math.min(255, base[1] + 22), + Math.min(255, base[2] + 22), + Math.round(20 + fade * 200), ]; }) as [number, number, number, number][]; }, diff --git a/src/hooks/use-trail-history.ts b/src/hooks/use-trail-history.ts index 8b9f432..49f60fa 100644 --- a/src/hooks/use-trail-history.ts +++ b/src/hooks/use-trail-history.ts @@ -13,7 +13,7 @@ export type TrailEntry = { const MAX_POINTS = 40; const JUMP_THRESHOLD_DEG = 0.3; -export const SAMPLES_PER_SEGMENT = 8; +export const SAMPLES_PER_SEGMENT = 16; const HISTORICAL_BOOTSTRAP_POLLS = 3; const HISTORICAL_BOOTSTRAP_STEP_SEC = 12; const BOOTSTRAP_UPDATES = 3;