diff --git a/locales/rn/de.json b/locales/rn/de.json index 488b521f..d59235be 100644 --- a/locales/rn/de.json +++ b/locales/rn/de.json @@ -128,6 +128,7 @@ "add_placeholder": "Aufgabe hinzufügen…", "add_time": "Uhrzeit hinzufügen", "clear_due_date": "Fälligkeitsdatum entfernen", + "delete_confirm": "Diese Aufgabe löschen?", "delete_error": "Aufgabe konnte nicht gelöscht werden", "not_found": "Diese Aufgabe ist nicht mehr verfügbar.", "save_error": "Aufgabe konnte nicht gespeichert werden", diff --git a/locales/rn/en.json b/locales/rn/en.json index abb546ec..1297a77e 100644 --- a/locales/rn/en.json +++ b/locales/rn/en.json @@ -131,6 +131,7 @@ "add_placeholder": "Add a task…", "add_time": "Add time", "clear_due_date": "Clear due date", + "delete_confirm": "Delete this task?", "delete_error": "Failed to delete task", "not_found": "This task is no longer available.", "save_error": "Failed to save task", diff --git a/src/components/calendar/ICalSubscriptionSheet.tsx b/src/components/calendar/ICalSubscriptionSheet.tsx index 73a11250..486785fc 100644 --- a/src/components/calendar/ICalSubscriptionSheet.tsx +++ b/src/components/calendar/ICalSubscriptionSheet.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { - View, Text, StyleSheet, ScrollView, Pressable, Modal, Animated, Easing, TextInput, ActivityIndicator, + View, Text, StyleSheet, ScrollView, Pressable, Modal, Animated, Easing, TextInput, ActivityIndicator, Alert, } from 'react-native'; import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'; import { X, Plus, RefreshCw, Trash2, Rss, AlertTriangle } from 'lucide-react-native'; @@ -279,7 +279,24 @@ export function ICalSubscriptionSheet({ visible, onClose }: Props) { )} { void removeSubscription(sub.id); }} + // Unsubscribing also deletes the calendar and its events. + onPress={() => Alert.alert( + t('calendar.subscription.unsubscribe', 'Unsubscribe'), + t('calendar.subscription.confirm_delete', 'Unsubscribe from "{name}"? The calendar and all its events will be removed.', { name: sub.name }), + [ + { text: t('common.cancel', 'Cancel'), style: 'cancel' }, + { + text: t('calendar.subscription.unsubscribe', 'Unsubscribe'), + style: 'destructive', + onPress: () => { + removeSubscription(sub.id).catch((err: unknown) => Alert.alert( + t('calendar.subscription.delete_error', 'Failed to remove subscription'), + err instanceof Error ? err.message : undefined, + )); + }, + }, + ], + )} hitSlop={6} style={styles.subBtn} accessibilityRole="button" diff --git a/src/screens/CalendarScreen.tsx b/src/screens/CalendarScreen.tsx index 52f53bf9..982070fe 100644 --- a/src/screens/CalendarScreen.tsx +++ b/src/screens/CalendarScreen.tsx @@ -634,15 +634,32 @@ export default function CalendarScreen() { [t], ); + // A single event is gone for good once deleted; ask first, as the + // recurrence-scope dialog already does for a series. + const confirmDeleteEvent = React.useCallback((event: CalendarEvent) => { + Alert.alert( + t('calendar.detail.delete_confirm', 'Delete this event?'), + event.title || undefined, + [ + { text: t('common.cancel', 'Cancel'), style: 'cancel' }, + { + text: t('calendar.recurrence_scope.delete', 'Delete'), + style: 'destructive', + onPress: () => { deleteEvent(event.id).catch(reportError); }, + }, + ], + ); + }, [deleteEvent, reportError, t]); + const handleDeleteFromDetail = React.useCallback((event: CalendarEvent) => { if (isReadOnlyEvent(event)) { setDetailEvent(null); return; } setDetailEvent(null); if (isRecurringSeriesMember(event)) { setPendingAction({ kind: 'delete', event }); } else { - deleteEvent(event.id).catch(reportError); + confirmDeleteEvent(event); } - }, [deleteEvent, isReadOnlyEvent, reportError]); + }, [confirmDeleteEvent, isReadOnlyEvent]); // "This and following": end the master at the occurrence and hand back the // master plus its untouched rules so the caller can start a new series (or @@ -815,9 +832,9 @@ export default function CalendarScreen() { setPendingAction({ kind: 'delete', event }); return; } - await deleteEvent(event.id).catch(reportError); + confirmDeleteEvent(event); }, - [deleteEvent, reportError], + [confirmDeleteEvent], ); const onRefresh = React.useCallback(() => refresh(), [refresh]); @@ -838,12 +855,16 @@ export default function CalendarScreen() { const handleDeleteTask = React.useCallback( (id: string) => { - deleteTask(id).catch((err: unknown) => { + const run = () => deleteTask(id).catch((err: unknown) => { Alert.alert( t('calendar.tasks.delete_error', 'Failed to delete task'), err instanceof Error ? err.message : undefined, ); }); + Alert.alert(t('calendar.tasks.delete_confirm', 'Delete this task?'), undefined, [ + { text: t('common.cancel', 'Cancel'), style: 'cancel' }, + { text: t('calendar.tasks.delete', 'Delete'), style: 'destructive', onPress: () => { void run(); } }, + ]); }, [deleteTask, t], );