fegger 44aaa32296 Add edit icon button to event list item
Remove emoji icons from journey display and replace with FontAwesome icons
Add FontAwesome icon imports and styles for new icon usage
Update calendar service to use FontAwesome icons instead of emojis
Remove redundant emoji field from CalendarSourceInfo type
Adjust journey scoring weights to prioritize fewer changes
Remove emoji icons and simplify event metadata display
2026-05-19 00:06:53 +02:00
2026-05-18 15:01:53 +02:00
2026-05-18 15:01:53 +02:00
2026-05-18 15:01:53 +02:00
2026-05-18 15:01:53 +02:00
2026-05-14 18:53:41 +02:00
2026-05-18 15:01:53 +02:00
2026-05-18 15:01:53 +02:00
2026-05-18 15:01:53 +02:00
2026-05-10 21:19:39 +02:00

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

Platform Next.js React Native Expo TypeScript

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

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, then use:

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/.
S
Description
No description provided
Readme 35 MiB
Languages
TypeScript 96.5%
JavaScript 1.1%
Kotlin 0.7%
CSS 0.7%
Ruby 0.4%
Other 0.6%