From 8cd9d2bd700c83525f09d7327093af9efa2b553d Mon Sep 17 00:00:00 2001 From: Lalit Sharma Date: Sun, 22 Feb 2026 18:17:52 +0000 Subject: [PATCH] Bump mobile to 1.1.13 and improve photo map detail visibility --- CHANGELOG.md | 12 +++++ apps/mobile/app.config.ts | 4 ++ apps/mobile/package.json | 2 +- apps/mobile/src/screens/TimerScreen.tsx | 61 ++++++++++++++++++---- apps/mobile/src/utils/previewGeometry.ts | 20 +++++-- apps/mobile/tests/preview-geometry.test.ts | 35 +++++++++++++ 6 files changed, 119 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5965785..81d29a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.1.13] — 2026-02-22 + +### Fixed +- Corrected preview contact interpolation so C1/C2/C3/C4 remain geometrically consistent even when the moon track has a non-zero closest-approach offset. +- Improved satellite/hybrid map readability by reducing eclipse overlay opacity in photo map modes and forcing map remount when switching map type to refresh tile detail loading. + +### Changed +- Bumped `apps/mobile` version to `1.1.13`. + +### Tests +- Added a regression test that verifies C1/C4 tangency remains exact for partial eclipse preview geometry with non-zero closest approach and directional travel. + ## [1.1.12] — 2026-02-22 ### Changed diff --git a/apps/mobile/app.config.ts b/apps/mobile/app.config.ts index d13a463..49cc471 100644 --- a/apps/mobile/app.config.ts +++ b/apps/mobile/app.config.ts @@ -14,6 +14,10 @@ if (typeof packageVersion !== "string" || !/^\d+\.\d+\.\d+$/.test(packageVersion expo.version = packageVersion; expo.runtimeVersion = packageVersion; +expo.extra = { + ...expo.extra, + googleMapsAndroidApiKeyConfigured: Boolean(googleMapsAndroidApiKey), +}; if (!googleMapsAndroidApiKey) { console.warn( diff --git a/apps/mobile/package.json b/apps/mobile/package.json index e094b47..dd7d51a 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -1,6 +1,6 @@ { "name": "@eclipse-timer/mobile", - "version": "1.1.12", + "version": "1.1.13", "private": true, "main": "index.js", "scripts": { diff --git a/apps/mobile/src/screens/TimerScreen.tsx b/apps/mobile/src/screens/TimerScreen.tsx index 3309a48..f80eb5f 100644 --- a/apps/mobile/src/screens/TimerScreen.tsx +++ b/apps/mobile/src/screens/TimerScreen.tsx @@ -89,6 +89,12 @@ function normalizeLongitudeDeg(lonDeg: number) { return (((lonDeg % 360) + 540) % 360) - 180; } +function mapTypeLabel(mapType: TimerState["mapType"]) { + if (mapType === "satellite") return "Satellite"; + if (mapType === "hybrid") return "Hybrid"; + return "Standard"; +} + function destinationPoint( latDeg: number, lonDeg: number, @@ -182,6 +188,13 @@ export default function TimerScreen({ : activeKindCode === "H" ? "Central Path" : "Totality Path"; + const isPhotoMapMode = timer.mapType !== "standard"; + const visibleOverlayColor = isPhotoMapMode ? "rgba(79, 195, 247, 0.12)" : VISIBLE_PATH_COLOR; + const activeCentralOverlayColor = isPhotoMapMode + ? activeKindCode === "A" + ? "rgba(255, 167, 38, 0.16)" + : "rgba(255, 82, 82, 0.16)" + : centralOverlayColor; const favoriteAtCurrentPin = useMemo( () => favoriteLocations.find((location) => @@ -242,6 +255,7 @@ export default function TimerScreen({ timer.result, ]); const hasDirectionsData = contactDirectionOverlays.length > 0; + const mapTypeText = mapTypeLabel(timer.mapType); const closeAddFavoriteModal = () => { setIsAddFavoriteModalOpen(false); @@ -341,19 +355,24 @@ export default function TimerScreen({ {timer.showVisibleOverlay ? timer.overlayVisiblePolygons.map((coordinates, idx) => ( @@ -364,7 +383,7 @@ export default function TimerScreen({ @@ -438,15 +457,18 @@ export default function TimerScreen({ - - {timer.mapType === "standard" - ? "Standard" - : timer.mapType === "satellite" - ? "Satellite" - : "Hybrid"} - + {mapTypeText} + {timer.mapType !== "standard" ? ( + + + If satellite/hybrid tiles look old or blank on Android, rebuild with + GOOGLE_MAPS_ANDROID_API_KEY in apps/mobile/.env.local. + + + ) : null} + - + Eclipse Visible {timer.showVisibleOverlay ? "On" : "Off"} @@ -484,7 +506,9 @@ export default function TimerScreen({ } > - + {centralLegendLabel} {timer.showCentralOverlay ? "On" : "Off"} @@ -948,6 +972,21 @@ const styles = StyleSheet.create({ fontWeight: "700", fontSize: 12, }, + mapModeHint: { + position: "absolute", + top: 52, + right: 10, + maxWidth: 230, + borderRadius: 10, + paddingHorizontal: 10, + paddingVertical: 6, + backgroundColor: "rgba(0,0,0,0.72)", + }, + mapModeHintText: { + color: "#d2d2d2", + fontSize: 11, + lineHeight: 14, + }, mapGpsBtn: { position: "absolute", top: 10, diff --git a/apps/mobile/src/utils/previewGeometry.ts b/apps/mobile/src/utils/previewGeometry.ts index 7758368..bf056d5 100644 --- a/apps/mobile/src/utils/previewGeometry.ts +++ b/apps/mobile/src/utils/previewGeometry.ts @@ -128,9 +128,18 @@ function buildMotionAnchors( contacts: PreviewMotionContacts, sunRadius: number, moonRadius: number, + moonClosestOffset: number, ): Array<{ progress: number; offsetX: number }> { - const externalTouchOffset = sunRadius + moonRadius; - const internalTouchOffset = Math.abs(sunRadius - moonRadius); + const axisDistanceForTouchOffset = (touchOffset: number) => { + const radialSq = touchOffset * touchOffset; + const closestSq = moonClosestOffset * moonClosestOffset; + const axisSq = radialSq - closestSq; + if (!Number.isFinite(axisSq) || axisSq <= 0) return 0; + return Math.sqrt(axisSq); + }; + + const externalTouchOffset = axisDistanceForTouchOffset(sunRadius + moonRadius); + const internalTouchOffset = axisDistanceForTouchOffset(Math.abs(sunRadius - moonRadius)); const anchors: Array<{ progress: number; offsetX: number }> = [ { progress: 0, offsetX: -externalTouchOffset }, @@ -195,7 +204,12 @@ export function calculatePreviewMoonGeometry(params: { sunRadius, ); - const anchors = buildMotionAnchors(params.contacts ?? {}, sunRadius, moonRadius); + const anchors = buildMotionAnchors( + params.contacts ?? {}, + sunRadius, + moonRadius, + moonClosestOffset, + ); const axisOffset = interpolateOffsetX(params.progress, anchors); const travelVector = params.travelVector ?? { x: 1, y: 0 }; const moonOffsetX = axisOffset * travelVector.x - moonClosestOffset * travelVector.y; diff --git a/apps/mobile/tests/preview-geometry.test.ts b/apps/mobile/tests/preview-geometry.test.ts index e96bf97..1bdba6e 100644 --- a/apps/mobile/tests/preview-geometry.test.ts +++ b/apps/mobile/tests/preview-geometry.test.ts @@ -95,4 +95,39 @@ describe("preview moon geometry", () => { ); expect(describePreviewTravelDirection({ x: -0.7, y: 0.1 })).toBe("right to left"); }); + + it("keeps C1 and C4 as tangency even with non-zero closest approach", () => { + const travelVector = determinePreviewTravelVector({ + c1BearingDeg: 230, + c4BearingDeg: 30, + }); + + const c1Geometry = calculatePreviewMoonGeometry({ + progress: 0, + kindAtLocation: "partial", + magnitude: 0.72, + contacts: { c1: 0, max: 0.5, c4: 1 }, + travelVector, + }); + const c4Geometry = calculatePreviewMoonGeometry({ + progress: 1, + kindAtLocation: "partial", + magnitude: 0.72, + contacts: { c1: 0, max: 0.5, c4: 1 }, + travelVector, + }); + + const stageCenter = 150; + const c1Distance = Math.hypot( + c1Geometry.moonCenterX - stageCenter, + c1Geometry.moonCenterY - stageCenter, + ); + const c4Distance = Math.hypot( + c4Geometry.moonCenterX - stageCenter, + c4Geometry.moonCenterY - stageCenter, + ); + + expect(c1Distance).toBeCloseTo(PREVIEW_SUN_RADIUS + c1Geometry.moonRadius, 6); + expect(c4Distance).toBeCloseTo(PREVIEW_SUN_RADIUS + c4Geometry.moonRadius, 6); + }); });