Switch mobile app to dark theme and fix calendar filtering
Switch mobile app to dark theme and fix calendar filtering - Default to dark theme in mobile app, matching web app style - Filter native calendar events by location to exclude empty ones - Add batch edit panel for destinations on web calendar - Add edit support to AddEventModal - Update notification trigger input type export
This commit is contained in:
@@ -29,6 +29,7 @@ export function SettingsScreen({ navigation: _navigation }: ScreenProps) {
|
||||
const [query, setQuery] = useState('');
|
||||
const [results, setResults] = useState<Station[]>([]);
|
||||
const [searching, setSearching] = useState(false);
|
||||
const [searchError, setSearchError] = useState<string | null>(null);
|
||||
const [notifSettings, setNotifSettings] = useState<ReminderSettings>({
|
||||
bufferMinutes: 30,
|
||||
enabled: true,
|
||||
@@ -41,11 +42,11 @@ export function SettingsScreen({ navigation: _navigation }: ScreenProps) {
|
||||
const searchTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
const colors = dark ? {
|
||||
background: '#1c1c1e',
|
||||
card: '#2c2c2e',
|
||||
text: '#f2f2f2',
|
||||
subtext: '#aeaeb2',
|
||||
accent: '#0a84ff',
|
||||
background: '#090816',
|
||||
card: '#17112A',
|
||||
text: '#F4F1EA',
|
||||
subtext: 'rgba(244,241,234,0.5)',
|
||||
accent: '#8B5CF6',
|
||||
border: '#38383a',
|
||||
success: '#30d158',
|
||||
} : {
|
||||
@@ -53,7 +54,7 @@ export function SettingsScreen({ navigation: _navigation }: ScreenProps) {
|
||||
card: '#ffffff',
|
||||
text: '#1c1c1e',
|
||||
subtext: '#8e8e93',
|
||||
accent: '#007AFF',
|
||||
accent: '#B23CFF',
|
||||
border: '#e5e5ea',
|
||||
success: '#34C759',
|
||||
};
|
||||
@@ -72,14 +73,18 @@ export function SettingsScreen({ navigation: _navigation }: ScreenProps) {
|
||||
const searchStation = useCallback(async (q: string) => {
|
||||
if (q.trim().length < 2) {
|
||||
setResults([]);
|
||||
setSearchError(null);
|
||||
return;
|
||||
}
|
||||
setSearching(true);
|
||||
setSearchError(null);
|
||||
try {
|
||||
const stations = await api.searchStation(q.trim());
|
||||
setResults(stations);
|
||||
if (stations.length === 0) setSearchError('Keine Stationen gefunden.');
|
||||
} catch {
|
||||
setResults([]);
|
||||
setSearchError('API nicht erreichbar. Läuft der Server auf deinem Gerät? Überprüfe EXPO_PUBLIC_API_BASE_URL in .env.');
|
||||
} finally {
|
||||
setSearching(false);
|
||||
}
|
||||
@@ -87,6 +92,7 @@ export function SettingsScreen({ navigation: _navigation }: ScreenProps) {
|
||||
|
||||
const onQueryChange = (text: string) => {
|
||||
setQuery(text);
|
||||
setSearchError(null);
|
||||
// Proper debounce using useRef — no `any`
|
||||
if (searchTimerRef.current) clearTimeout(searchTimerRef.current);
|
||||
searchTimerRef.current = setTimeout(() => searchStation(text), 400);
|
||||
@@ -121,8 +127,10 @@ export function SettingsScreen({ navigation: _navigation }: ScreenProps) {
|
||||
|
||||
// Try the WienerLinien nearby-stops proxy first
|
||||
let stops: Awaited<ReturnType<typeof api.findNearbyStops>> | null = null;
|
||||
let apiReachable = false;
|
||||
try {
|
||||
stops = await api.findNearbyStops(userLat, userLng, 2000);
|
||||
apiReachable = true;
|
||||
} catch {
|
||||
// API unavailable, will fall back to HAFAS LocMatch below
|
||||
}
|
||||
@@ -146,15 +154,28 @@ export function SettingsScreen({ navigation: _navigation }: ScreenProps) {
|
||||
}
|
||||
|
||||
// Fallback: use HAFAS LocMatch directly (same pattern as the web app)
|
||||
const nearestStation = await api.findNearestStationByCoords(userLat, userLng);
|
||||
if (!nearestStation) {
|
||||
Alert.alert('Keine Station gefunden', 'Kein ÖPNV-Halt in der Nähe gefunden.');
|
||||
try {
|
||||
const nearestStation = await api.findNearestStationByCoords(userLat, userLng);
|
||||
if (!nearestStation) {
|
||||
Alert.alert('Keine Station gefunden', 'Kein ÖPNV-Halt in der Nähe gefunden.');
|
||||
return;
|
||||
}
|
||||
await selectStation(nearestStation);
|
||||
return;
|
||||
} catch {
|
||||
// API not reachable
|
||||
}
|
||||
|
||||
await selectStation(nearestStation);
|
||||
if (!apiReachable) {
|
||||
Alert.alert(
|
||||
'API nicht erreichbar',
|
||||
'Der Server konnte nicht erreicht werden. Stelle sicher, dass EXPO_PUBLIC_API_BASE_URL in der .env-Datei auf die LAN-IP deines Entwicklungsrechners zeigt (z. B. http://192.168.1.x:3000) und nicht auf localhost.'
|
||||
);
|
||||
} else {
|
||||
Alert.alert('Keine Station gefunden', 'Kein ÖPNV-Halt in der Nähe gefunden.');
|
||||
}
|
||||
} catch (_err) {
|
||||
Alert.alert('Fehler', 'Standort konnte nicht ermittelt werden.');
|
||||
Alert.alert('Fehler', 'Standortermittlung fehlgeschlagen. Bitte überprüfe die Berechtigungen in den Systemeinstellungen.');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -232,6 +253,9 @@ export function SettingsScreen({ navigation: _navigation }: ScreenProps) {
|
||||
accessibilityLabel="Station suchen"
|
||||
/>
|
||||
{searching && <ActivityIndicator style={{ marginVertical: 8 }} color={colors.accent} />}
|
||||
{searchError && (
|
||||
<Text style={[styles.errorText, { color: '#ff453a' }]}>{searchError}</Text>
|
||||
)}
|
||||
{origin && (
|
||||
<Text style={[styles.currentStation, { color: colors.success }]}>Aktuell: {origin.name}</Text>
|
||||
)}
|
||||
@@ -344,6 +368,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
locBtnText: { fontSize: 15, fontWeight: '500' },
|
||||
locStatus: { fontSize: 12, marginTop: 6 },
|
||||
errorText: { fontSize: 13, marginTop: 6 },
|
||||
advancedToggle: {
|
||||
marginTop: 12,
|
||||
marginBottom: 12,
|
||||
|
||||
Reference in New Issue
Block a user