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:
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user