Technical Documentation
A cross-platform React Native app for exploring astronomical events, photography light windows, and halachic Zmanim by date and location
The web calendar works. But a calendar you open in a browser is different from one you tap into instantly, wherever you are, whenever you look up at the sky. The app exists because those are two different experiences. The web version is something you sit down with. The native app is something you reach for in a moment of curiosity — standing outside at night, wondering what phase the moon is in, wanting to know right now. That immediacy changes the whole feel of it.
This cross-platform mobile app adapts the calculation engine and interaction model from bachtogauss.com/calendar into a native iOS and Android experience. It is designed as a practical celestial calendar: select a day, change location, review sun and moon data, inspect moon phases and eclipses on the calendar grid, and toggle the supporting systems that matter to you.
The most interesting engineering work is in separating astronomical calculations, halachic calculations, moon phase data, eclipse data, and UI state into clear independent modules — then running them in parallel while preserving partial results if one source fails. The web calendar was the reference implementation; this project is the native re-architecture.
The app coordinates multiple independent calculation domains (astronomical rise/set events, photography light windows, moon phases, eclipse detection, and halachic Zmanim), location input paths, timezone formatting, user settings, custom iconography, and native mobile concerns behind one interface. Each isolated domain is run in parallel with fault-tolerant merging so that a failure in one domain does not degrade the others.
The app is organized around calculation services, UI components, and a small global state store. Calculation domains are kept strictly independent — astronomical and halachic data are fetched in parallel and merged only after each path completes.
src/ components/ Reusable UI: calendar grid, legend, location input, cards components/icons/ Bottom tab icons components/symbols/ Moon phase and eclipse SVG symbols lib/ Library access wrappers navigation/ React Navigation configuration screens/ Calendar, Legend, and Settings screens services/ Location, geocoding, moon data, and merged time services store/ Zustand calendar and settings store styles/ Shared theme, colors, typography, spacing types/ Shared TypeScript interfaces utils/ Astronomy, halachic, eclipse, moon phase, and time helpers
The pipeline is designed so that each domain operates independently and the UI degrades gracefully when any
value is unavailable — returning
null
for that value rather than blocking the whole view.
CalendarScreen
reacts to changes in selected date, current month, location, and hemisphere
moonDataService
calculates moon phases, eclipses, age, and illumination for the visible month in a single pass
timeService.fetchAllTimes()
fetches astronomy and halachic data in parallel
GeoLocation
and
Zmanim
MergedTimes
object rendered by
SunMoonBar
and
ZmanimCard
| CalendarScreen.tsx | Main composition layer: calendar, pager, moon data loading, time loading |
| CalendarGrid.tsx | Fixed-height month grid, date navigation, phase and eclipse markers |
| timeService.ts | Parallel orchestration of astronomy and halachic calculations with fault-tolerant fallbacks |
| moonDataService.ts | Unified moon phase, eclipse, illumination, and age pipeline |
| astronomyUtils.ts | Sunrise, sunset, moonrise, moonset, blue hour, golden hour, illumination, moon direction |
| halachicUtils.ts | Hebcal-backed Zmanim with per-value error isolation |
| eclipseUtils.ts | Solar and lunar eclipse searches and symbol mapping |
| geocodingService.ts | Location parsing, search ranking, geocoding, reverse geocoding, coordinate formatting |
| LocationField.tsx | Debounced manual search, GPS flow, dropdown selection, location status display |
| calendarStore.ts | Centralized date, location, display preference, and feature-toggle state via Zustand |
| Mobile framework | React Native 0.81 |
| Language | TypeScript |
| UI foundation | React Native Paper, custom styles, custom fonts |
| Navigation | React Navigation bottom tabs |
| State management | Zustand |
| Date/time handling | Luxon |
| Astronomy calculations | astronomy-engine |
| Halachic calculations | @hebcal/core |
| Location | @react-native-community/geolocation |
| Geocoding | OpenStreetMap Nominatim |
| Testing | Jest, React Test Renderer, standalone calculation scripts |
Astronomy and halachic data are fetched separately and merged only after each path completes. A failure in one domain does not erase the other domain's usable results. Individual Zmanim values are each wrapped in per-value error isolation — a single bad calculation does not blank the entire Zmanim card.
Moon phases, current moon age, current illumination, and eclipse markers are all calculated through
moonDataService
in a single pass. This ensures the calendar grid and detail cards always agree on the selected month and
date — no possibility of the grid showing a different phase than the dashboard.
The calendar grid uses explicit percentage widths and a fixed six-row structure to avoid platform-specific flexbox inconsistencies and month-to-month height changes. Northern and southern hemisphere orientation is handled explicitly for first and last quarter moon symbols, which appear mirrored depending on hemisphere.
Zustand stores only app-level state shared across screens: selected date, visible month, location, loading and error state, hemisphere, time format, feature toggles, and Zmanim system. Component-local state stays local.
A
web_reference/
directory preserves source material from the original browser implementation. The React Native code keeps
the calculation intent while replacing DOM-oriented patterns with native navigation, gesture paging, React
Native Paper controls, and platform font registration.
The repository includes both React Native test coverage and calculation-focused Node scripts that can be run independently of the full app build:
App.test.tsx
— verifies the app tree renders
test_calculations.js
— checks real astronomy and halachic calculations for the default location
test_edge_cases.js
— runs calculations across representative cities and seasonal dates including high-latitude cases
test_simple_celestial.js
— validates basic astronomy-engine API usage for moon phase and eclipse lookup