Files
2026-05-18 15:01:53 +02:00

129 lines
6.7 KiB
Markdown

# TimeToLeave
TimeToLeave is a departure planner for calendar-driven travel. It imports events with locations, resolves the closest public-transport station, checks live ÖBB HAFAS and Wiener Linien data, and shows when to leave by train or bike.
![TimeToLeave Logo](apps/web/public/timetoleave_logo.png)
![Platform](https://img.shields.io/badge/platform-Web_%26_Mobile-blue) ![Next.js](https://img.shields.io/badge/Next.js-16.2-green) ![React Native](https://img.shields.io/badge/React%20Native-0.81-blue) ![Expo](https://img.shields.io/badge/Expo-54-black) ![TypeScript](https://img.shields.io/badge/TypeScript-5-blue)
## What It Does
1. Imports events from ICS URLs, ICS files, Google Calendar on web, or native device calendars on mobile.
2. Stores events locally in browser `localStorage` or mobile `AsyncStorage`.
3. Uses browser or device geolocation, a saved station, or the default Mödling origin.
4. Geocodes event destinations and resolves nearby stations through HAFAS `LocMatch`.
5. Searches ÖBB HAFAS journeys, enriches train data with the ÖBB GTFS fallback when available, and shows real-time delays and cancellations.
6. Calculates bike and final walking routes through OSRM.
7. Shows a live leave-by countdown and optional browser/mobile notifications.
## Repository Layout
| Path | Purpose |
| --- | --- |
| `apps/web/` | Next.js 16 App Router web UI plus backend proxy routes for HAFAS, geocoding, routing, calendar parsing, Google Calendar, and Wiener Linien. |
| `apps/mobile/` | Expo 54 / React Native 0.81 mobile app with native calendar, location, notification, and local storage integrations. |
| `packages/core/` | Shared types, defaults, HAFAS time parsing, journey parsing/scoring, countdown, formatting, and status utilities. |
| `packages/api-client/` | Shared client for calling the web app's `/api/*` backend routes from web hooks and the mobile app. |
| `docs/` | Architecture, development, API, user, and codebase reference documentation. |
## Prerequisites
- Node.js 20 or newer
- npm 9 or newer
- For mobile native builds: Expo/EAS prerequisites plus Android Studio or Xcode as needed
## Setup
```bash
npm install
cp .env.example .env
```
The web app reads environment variables from the workspace process. For deployment, configure the same values in the hosting environment.
Important variables:
| Variable | Purpose |
| --- | --- |
| `HAFAS_URL` | ÖBB HAFAS endpoint. Defaults to `https://fahrplan.oebb.at/bin/mgate.exe`. |
| `NOMINATIM_URL` and `NOMINATIM_USER_AGENT` | Geocoding endpoint and required user agent. |
| `OSRM_URL` | Routing endpoint used for bike and foot profiles. |
| `WIENER_LINIEN_API_URL` | Wiener Linien live data base URL. |
| `OEBB_GTFS_URL` | Optional ÖBB GTFS ZIP used to enrich HAFAS train metadata. |
| `CORS_ALLOWED_ORIGINS` | Comma-separated origins allowed to call `/api/*`. |
| `DEPLOYMENT_URL` | Public base URL used by Google OAuth redirects. |
| `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, `GOOGLE_REDIRECT_URI` | Required for Google Calendar sync on web. |
| `EXPO_PUBLIC_API_BASE_URL` | Mobile backend URL. Set this for device builds so the app can reach the deployed web backend. |
## Development
| Command | Description |
| --- | --- |
| `npm run dev` | Start the Next.js web app on `http://localhost:3000`. |
| `npm run dev:mobile` | Start the Expo development server. |
| `npm run build` | Build the web app. |
| `npm run start` | Start the built web app. |
| `npm run test` | Run web Vitest and mobile Jest suites. |
| `npm run lint` | Run ESLint across web, mobile, core, and api-client workspaces. |
| `npm run typecheck` | Run TypeScript checks across all workspaces. |
## Web App
Current user-facing routes:
| Route | Description |
| --- | --- |
| `/` | Departure desk. Shows the next upcoming event, leave-by status, transport mode selector, train journeys, bike route, final walk, and nearby Wiener Linien departures. |
| `/calendar` | Calendar import and management view with URL, file, and Google Calendar tabs plus batch destination editing. |
The add/edit event UI is a modal component, not a standalone page route.
## Backend Proxy Routes
All backend routes live under `apps/web/src/app/api/` and are protected by strict CORS plus per-IP rate limiting in `apps/web/src/proxy.ts`.
| Endpoint | Methods | Purpose |
| --- | --- | --- |
| `/api/health` | `GET` | Returns `{ ok, ts, version }`. |
| `/api/hafas` | `GET`, `POST` | Convenience journey search or validated HAFAS relay for `TripSearch` and `LocMatch`. |
| `/api/geocode` | `GET` | Forward geocoding through Nominatim. |
| `/api/bike-route` | `GET` | OSRM bicycle route between two coordinates. |
| `/api/walk-route` | `GET` | OSRM foot route between two coordinates. |
| `/api/calendar` | `GET` | Fetch and parse an allowed remote ICS URL. |
| `/api/calendar/parse` | `POST` | Parse uploaded/raw ICS text. |
| `/api/calendar/google` | `GET` | Fetch Google Calendar events using OAuth cookies. |
| `/api/auth/google` | `GET` | Start Google OAuth. |
| `/api/auth/google/callback` | `GET` | Complete Google OAuth and store token cookie. |
| `/api/auth/google/status` | `GET` | Report Google configuration and connection state. |
| `/api/auth/google/disconnect` | `POST` | Delete the Google token cookie. |
| `/api/wienerlinien/stops` | `GET` | Find nearby Wiener Linien stops. |
| `/api/wienerlinien/monitor` | `GET` | Fetch and flatten live stop departures. |
## Mobile App
The mobile app includes event list, add/edit event, event detail, calendar import, and settings screens. It supports:
- Native calendar sync for the next 30 days.
- Calendar-source selection, including CalDAV/DAVx, Apple, Google, Exchange, subscribed, and local calendars when exposed by the device.
- Saved origin station with current-location lookup.
- Train, bike, walking, and Wiener Linien live sections on event detail.
- Local notifications scheduled from stored event/settings data.
- Dark/light theme toggle.
## Documentation
Start with [docs/README.md](docs/README.md), then use:
- [Architecture](docs/ARCHITECTURE.md)
- [Development Guide](docs/DEVELOPMENT.md)
- [Core & API Client Reference](docs/API_REFERENCE.md)
- [User Guide](docs/USER_GUIDE.md)
- [Codebase Function Guide](docs/CODEBASE_FUNCTION_GUIDE.md)
## Important Implementation Notes
- HAFAS date/time values are Vienna-local strings. Use `parseHafasTime()` and `hafasDateTime()` from `@timetoleave/core`; avoid ad hoc `Date` parsing for HAFAS payloads.
- Remote calendar URLs are restricted to known calendar providers and private/reserved hosts are blocked.
- Mobile devices must use a reachable `EXPO_PUBLIC_API_BASE_URL`; same-origin empty base URLs only work in the web app.
- This repo uses Next.js 16. Before changing Next.js routing, middleware/proxy, or framework conventions, read the relevant guide in `node_modules/next/dist/docs/`.