import { useState } from 'react'; import { ActivityIndicator, ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, View, } from 'react-native'; import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; import type { RouteProp } from '@react-navigation/native'; import { api } from '../services/api'; import { fetchNativeEvents } from '../services/calendar'; import { addEvent, loadEvents } from '../store/eventStore'; import type { Event as CalendarEvent } from '@timetoleave/core'; import type { RootStack } from '../types/navigation'; import { useColors } from '../hooks/useColors'; type ScreenProps = { navigation: NativeStackNavigationProp; route: RouteProp; }; /** * Screen for importing events from either an ICS calendar URL or the device's * native calendar. Deduplicates against already-imported events by ID. */ export function CalendarImportScreen({ navigation }: ScreenProps) { const colors = useColors(); const [url, setUrl] = useState(''); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [count, setCount] = useState(null); const handleImport = async () => { if (!url.trim()) { setError('Bitte ICS-URL eingeben'); return; } setLoading(true); setError(null); setCount(null); try { const [events, existing] = await Promise.all([ api.fetchCalendar(url.trim()), loadEvents(), ]); const existingIds = new Set(existing.map((e) => e.id)); let added = 0; for (const evt of events) { if (!existingIds.has(evt.id)) { const localEvent: CalendarEvent = { id: evt.id, title: evt.title, destination: evt.destination, eventTime: new Date(evt.eventTime), source: `calendar:${url.trim().slice(0, 40)}`, }; await addEvent(localEvent); added++; } } setCount(added); } catch (err) { setError(err instanceof Error ? err.message : 'Import fehlgeschlagen'); } finally { setLoading(false); } }; const handleSyncNative = async () => { setLoading(true); setError(null); setCount(null); try { // Fetch events from the next 30 days const now = new Date(); const thirtyDaysLater = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000); const nativeEvents = await fetchNativeEvents(now, thirtyDaysLater); // Load existing events to avoid duplicates const existing = await loadEvents(); const existingIds = new Set(existing.map((e) => e.id)); let added = 0; for (const evt of nativeEvents) { if (!existingIds.has(evt.id)) { await addEvent(evt); added++; } } setCount(added); } catch (err) { setError(err instanceof Error ? err.message : 'Sync fehlgeschlagen'); } finally { setLoading(false); } }; return ( Kalender-Import Importiere Termine über eine ICS-URL oder sync mit dem Geräte-Kalender. ICS-URL Import {loading ? ( ) : ( ICS Importieren )} Geräte-Kalender Sync Hole Termine der nächsten 30 Tage aus den Kalendern auf deinem Gerät. 📅 Kalender Sync {error && ( {error} )} {count !== null && ( ✓ {count} Termin(e) erfolgreich importiert! )} navigation.goBack()} > ← Zurück ); } const styles = StyleSheet.create({ container: { flex: 1 }, content: { padding: 20 }, heading: { fontSize: 22, fontWeight: '700', marginBottom: 4 }, description: { fontSize: 14, marginBottom: 20, lineHeight: 20 }, section: { marginBottom: 24 }, sectionTitle: { fontSize: 16, fontWeight: '600', marginBottom: 8 }, sectionDesc: { fontSize: 13, marginBottom: 12, lineHeight: 18 }, input: { borderRadius: 10, paddingHorizontal: 14, paddingVertical: 12, fontSize: 16, borderWidth: 1, marginBottom: 12, }, errorBanner: { borderRadius: 8, padding: 12, marginBottom: 12 }, errorText: { color: '#fff', fontSize: 14 }, successBanner: { borderRadius: 8, padding: 12, marginBottom: 12 }, successText: { color: '#fff', fontSize: 14 }, importBtn: { backgroundColor: '#8B5CF6', paddingVertical: 14, borderRadius: 12, alignItems: 'center', }, importBtnDisabled: { opacity: 0.6 }, importBtnText: { color: '#fff', fontSize: 16, fontWeight: '600' }, backBtn: { paddingVertical: 10, alignItems: 'center', }, backBtnText: { fontSize: 15 }, });