129 lines
6.7 KiB
Markdown
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.
|
|
|
|

|
|
|
|
    
|
|
|
|
## 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/`.
|