From 834025e56021df22cbe7e92d734a9addb128543f Mon Sep 17 00:00:00 2001 From: Florian Egger Date: Mon, 18 May 2026 13:14:05 +0200 Subject: [PATCH] Show leave time instead of countdown Replace live countdown with formatted leave time in headers and list items Remove ticking interval and calculateCountdown import from EventHeader Update EventListScreen to derive leave countdown and labels and adjust badge, dot color, status logic and fallbacks Update tests to expect 'Losgehen in' --- apps/mobile/src/__tests__/screens.test.tsx | 2 +- apps/mobile/src/components/EventHeader.tsx | 28 +++---------- apps/mobile/src/screens/EventListScreen.tsx | 44 ++++++++++++++------- 3 files changed, 36 insertions(+), 38 deletions(-) diff --git a/apps/mobile/src/__tests__/screens.test.tsx b/apps/mobile/src/__tests__/screens.test.tsx index f8dcdc3..9a8f923 100644 --- a/apps/mobile/src/__tests__/screens.test.tsx +++ b/apps/mobile/src/__tests__/screens.test.tsx @@ -197,7 +197,7 @@ describe('EventListScreen', () => { expect(getByText('Test Destination')).toBeTruthy(); }); await waitFor(() => { - expect(getByText('Losgehen')).toBeTruthy(); + expect(getByText('Losgehen in')).toBeTruthy(); expect(getByText('S1 -> Wien')).toBeTruthy(); expect(getByText('Inkl. Fußweg zur Station: 10 min')).toBeTruthy(); }); diff --git a/apps/mobile/src/components/EventHeader.tsx b/apps/mobile/src/components/EventHeader.tsx index 01cc925..7afc267 100644 --- a/apps/mobile/src/components/EventHeader.tsx +++ b/apps/mobile/src/components/EventHeader.tsx @@ -1,6 +1,4 @@ -import { useEffect, useState } from 'react'; import { StyleSheet, Text, View } from 'react-native'; -import { calculateCountdown } from '@timetoleave/core'; import type { Event } from '@timetoleave/core'; import type { AppColors } from '../hooks/useColors'; @@ -13,34 +11,20 @@ interface Props { } /** - * Renders the event title, destination, leave countdown, data source, and a + * Renders the event title, destination, leave-by time, data source, and a * three-column grid with leave-by time, arrive-by time, and buffer. */ export function EventHeader({ event, leaveByTime, arrivalBufferMinutes, colors }: Props) { - const [, setTick] = useState(0); const arriveByTime = new Date(event.eventTime.getTime() - arrivalBufferMinutes * 60_000); - const leaveCountdown = leaveByTime ? calculateCountdown(leaveByTime) : null; - - useEffect(() => { - const interval = setInterval(() => setTick((tick) => tick + 1), 30_000); - return () => clearInterval(interval); - }, []); return ( {event.title} {event.destination} - - {leaveCountdown - ? leaveCountdown.label === 'Now' - ? 'Jetzt losgehen' - : `Noch ${leaveCountdown.label} bis Losgehen` - : 'Countdown wird berechnet'} + + {leaveByTime + ? `Losgehen um ${leaveByTime.toLocaleTimeString('de-AT', { hour: '2-digit', minute: '2-digit' })}` + : 'Losgehzeit wird berechnet'} Quelle: {event.source} @@ -72,7 +56,7 @@ const styles = StyleSheet.create({ container: { padding: 20, marginBottom: 12 }, title: { fontSize: 22, fontWeight: '700' }, destination: { fontSize: 16, marginTop: 4 }, - countdown: { fontSize: 18, fontWeight: '700', marginTop: 10 }, + leaveTime: { fontSize: 18, fontWeight: '700', marginTop: 10 }, source: { fontSize: 12, marginTop: 4 }, infoGrid: { flexDirection: 'row', diff --git a/apps/mobile/src/screens/EventListScreen.tsx b/apps/mobile/src/screens/EventListScreen.tsx index 88ed91c..ca1ad56 100644 --- a/apps/mobile/src/screens/EventListScreen.tsx +++ b/apps/mobile/src/screens/EventListScreen.tsx @@ -28,9 +28,9 @@ type ScreenProps = { }; /** - * Home screen showing a scrollable list of upcoming events with countdown - * badges. Pull-to-refresh reloads events from storage. Reloads automatically - * when the screen gains focus (so edits on other screens are reflected). + * Home screen showing a scrollable list of upcoming events with leave-time + * countdowns. Pull-to-refresh reloads events from storage. Reloads + * automatically when the screen gains focus so edits are reflected. */ export function EventListScreen({ navigation }: ScreenProps) { const colors = useColors(); @@ -173,13 +173,18 @@ export function EventListScreen({ navigation }: ScreenProps) { ]); const renderItem = ({ item }: { item: CalendarEvent }) => { - const countdown = calculateCountdown(item.eventTime); const leaveBy = departureInfo.departureTime; + const leaveCountdown = leaveBy ? calculateCountdown(leaveBy) : null; + const leaveCountdownLabel = leaveCountdown?.label === 'Now' ? 'Jetzt' : leaveCountdown?.label; + const leaveByLabel = leaveBy ? formatTime(leaveBy) : null; const trainLabel = selectedJourney?.trains.length ? selectedJourney.trains.join(', ') : 'Zugverbindung wird gesucht'; - - // Derive a simple status — journeys aren't loaded on the list screen for MVP - // so we show countdown-based status instead - const status = countdown.urgent ? 'Bald!' : countdown.label; + const status = leaveCountdown + ? leaveCountdown.label === 'Now' + ? 'Jetzt losgehen' + : `Losgehen in ${leaveCountdown.label}` + : journeysLoading || destStation.loading + ? 'Losgehzeit wird berechnet' + : 'Keine Verbindung'; return ( @@ -190,16 +195,25 @@ export function EventListScreen({ navigation }: ScreenProps) { > - + {item.title} - - {countdown.label} + + {leaveByLabel ?? '--:--'} {item.destination} - Losgehen - - {leaveBy ? formatTime(leaveBy) : journeysLoading || destStation.loading ? '--:--' : 'Keine Verbindung'} + Losgehen in + + {leaveCountdownLabel + ? leaveCountdownLabel + : journeysLoading || destStation.loading + ? '--' + : 'Keine Verbindung'} @@ -233,7 +247,7 @@ export function EventListScreen({ navigation }: ScreenProps) { minute: '2-digit', })} - {status} + {status}