Support multiple API base URLs with fallback
Support Multiple API Base URLs With Fallback Make ApiClient accept multiple base URLs and try the next when responses indicate unavailability (408, 429, 502, 503, 504 or any >=500) or on network errors. Add fetchApi helper and selection logic, and use a Tailscale dev server as a fallback in the mobile service. Also update EventHeader to show a live leave-by countdown.
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
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';
|
||||
|
||||
@@ -11,25 +13,34 @@ interface Props {
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the event title, destination, formatted date/time, data source,
|
||||
* and a three-column grid with leave-by time, arrive-by time, and buffer.
|
||||
* Renders the event title, destination, leave countdown, 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.time, { color: colors.accent }]}>
|
||||
{event.eventTime.toLocaleString('de-AT', {
|
||||
weekday: 'long',
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})}
|
||||
<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>
|
||||
<Text style={[styles.source, { color: colors.subtext }]}>Quelle: {event.source}</Text>
|
||||
|
||||
@@ -61,7 +72,7 @@ const styles = StyleSheet.create({
|
||||
container: { padding: 20, marginBottom: 12 },
|
||||
title: { fontSize: 22, fontWeight: '700' },
|
||||
destination: { fontSize: 16, marginTop: 4 },
|
||||
time: { fontSize: 14, marginTop: 8 },
|
||||
countdown: { fontSize: 18, fontWeight: '700', marginTop: 10 },
|
||||
source: { fontSize: 12, marginTop: 4 },
|
||||
infoGrid: {
|
||||
flexDirection: 'row',
|
||||
|
||||
@@ -2,7 +2,10 @@ import { ApiClient } from '@timetoleave/api-client';
|
||||
|
||||
/**
|
||||
* Thin wrapper around the shared `ApiClient` class.
|
||||
* Reads the base URL from the Expo environment variable `EXPO_PUBLIC_API_BASE_URL`.
|
||||
* Reads the primary base URL from `EXPO_PUBLIC_API_BASE_URL` and falls back
|
||||
* to the Tailscale dev server when the primary server is unreachable.
|
||||
*/
|
||||
const baseUrl = process.env.EXPO_PUBLIC_API_BASE_URL ?? '';
|
||||
export const api = new ApiClient(baseUrl);
|
||||
const fallbackBaseUrl = 'http://100.103.83.12:3030';
|
||||
|
||||
export const api = new ApiClient([baseUrl, fallbackBaseUrl]);
|
||||
|
||||
Reference in New Issue
Block a user