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'
This commit is contained in:
2026-05-18 13:14:05 +02:00
parent b3edf2c47b
commit 834025e560
3 changed files with 36 additions and 38 deletions
+1 -1
View File
@@ -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();
});
+6 -22
View File
@@ -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 (
<View style={[styles.container, { backgroundColor: colors.card }]}>
<Text style={[styles.title, { color: colors.text }]}>{event.title}</Text>
<Text style={[styles.destination, { color: colors.subtext }]}>{event.destination}</Text>
<Text
style={[
styles.countdown,
{ color: leaveCountdown?.urgent ? colors.delete : colors.text },
]}
>
{leaveCountdown
? leaveCountdown.label === 'Now'
? 'Jetzt losgehen'
: `Noch ${leaveCountdown.label} bis Losgehen`
: 'Countdown wird berechnet'}
<Text style={[styles.leaveTime, { color: leaveByTime ? colors.accent : colors.subtext }]}>
{leaveByTime
? `Losgehen um ${leaveByTime.toLocaleTimeString('de-AT', { hour: '2-digit', minute: '2-digit' })}`
: 'Losgehzeit wird berechnet'}
</Text>
<Text style={[styles.source, { color: colors.subtext }]}>Quelle: {event.source}</Text>
@@ -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',
+29 -15
View File
@@ -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 (
<View style={styles.cardWrapper}>
@@ -190,16 +195,25 @@ export function EventListScreen({ navigation }: ScreenProps) {
>
<View style={[styles.card, { backgroundColor: colors.card }]}>
<View style={styles.dotRow}>
<View style={[styles.dot, { backgroundColor: countdown.urgent ? colors.delete : '#34C759' }]} />
<View style={[styles.dot, { backgroundColor: leaveCountdown?.urgent ? colors.delete : '#34C759' }]} />
<Text style={[styles.title, { color: colors.text }]}>{item.title}</Text>
<Text style={[styles.badge, { color: countdown.urgent ? colors.delete : colors.accent }]}>
{countdown.label}
<Text style={[styles.badge, { color: leaveByLabel ? colors.accent : colors.subtext }]}>
{leaveByLabel ?? '--:--'}
</Text>
</View>
<Text style={[styles.subtitle, { color: colors.subtext }]}>{item.destination}</Text>
<Text style={[styles.leaveLabel, { color: colors.subtext }]}>Losgehen</Text>
<Text style={[styles.leaveTime, { color: leaveBy ? colors.accent : colors.subtext }]}>
{leaveBy ? formatTime(leaveBy) : journeysLoading || destStation.loading ? '--:--' : 'Keine Verbindung'}
<Text style={[styles.leaveLabel, { color: colors.subtext }]}>Losgehen in</Text>
<Text
style={[
styles.leaveTime,
{ color: leaveCountdownLabel ? colors.accent : colors.subtext },
]}
>
{leaveCountdownLabel
? leaveCountdownLabel
: journeysLoading || destStation.loading
? '--'
: 'Keine Verbindung'}
</Text>
<View style={[styles.trainBox, { borderColor: colors.border }]}>
<Text style={[styles.trainTitle, { color: colors.text }]} numberOfLines={2}>
@@ -233,7 +247,7 @@ export function EventListScreen({ navigation }: ScreenProps) {
minute: '2-digit',
})}
</Text>
<Text style={[styles.status, { color: countdown.urgent ? colors.delete : '#34C759' }]}>{status}</Text>
<Text style={[styles.status, { color: leaveCountdown?.urgent ? colors.delete : '#34C759' }]}>{status}</Text>
</View>
</TouchableOpacity>