From 19706fa4a3f44b884a8ff5890b4b391e4338e788 Mon Sep 17 00:00:00 2001 From: le king fu Date: Mon, 30 Mar 2026 19:36:24 -0400 Subject: [PATCH] feat: add inline edit and delete for subtasks (#25) Long-press a subtask to edit its title inline. Tap X to delete with confirmation. Tap still toggles completion. Co-Authored-By: Claude Opus 4.6 (1M context) --- app/task/[id].tsx | 66 ++++++++++++++++++++++++++++++++++++++++++----- src/i18n/en.json | 1 + src/i18n/fr.json | 1 + 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/app/task/[id].tsx b/app/task/[id].tsx index 5e610f1..245900c 100644 --- a/app/task/[id].tsx +++ b/app/task/[id].tsx @@ -76,6 +76,8 @@ export default function TaskDetailScreen() { const [recurrence, setRecurrence] = useState(null); const [subtasks, setSubtasks] = useState([]); const [newSubtask, setNewSubtask] = useState(''); + const [editingSubtaskId, setEditingSubtaskId] = useState(null); + const [editingTitle, setEditingTitle] = useState(''); const [availableTags, setAvailableTags] = useState<{ id: string; name: string; color: string }[]>([]); const [selectedTagIds, setSelectedTagIds] = useState([]); const [lists, setLists] = useState<{ id: string; name: string; color: string | null; icon: string | null; isInbox: boolean }[]>([]); @@ -162,6 +164,38 @@ export default function TaskDetailScreen() { loadSubtasks(); }; + const handleEditSubtask = (sub: SubtaskData) => { + setEditingSubtaskId(sub.id); + setEditingTitle(sub.title); + Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); + }; + + const handleSaveSubtaskEdit = async () => { + if (!editingSubtaskId) return; + const trimmed = editingTitle.trim(); + if (trimmed) { + await updateTask(editingSubtaskId, { title: trimmed }); + loadSubtasks(); + } + setEditingSubtaskId(null); + setEditingTitle(''); + }; + + const handleDeleteSubtask = (subtaskId: string) => { + Alert.alert(t('task.deleteSubtaskConfirm'), '', [ + { text: t('common.cancel'), style: 'cancel' }, + { + text: t('common.delete'), + style: 'destructive', + onPress: async () => { + await deleteTask(subtaskId); + Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium); + loadSubtasks(); + }, + }, + ]); + }; + const handleDateChange = (_: DateTimePickerEvent, date?: Date) => { setShowDatePicker(Platform.OS === 'ios'); if (date) setDueDate(date); @@ -373,7 +407,8 @@ export default function TaskDetailScreen() { {subtasks.map((sub) => ( handleToggleSubtask(sub.id)} + onPress={() => editingSubtaskId === sub.id ? undefined : handleToggleSubtask(sub.id)} + onLongPress={() => handleEditSubtask(sub)} className={`flex-row items-center border-b py-2.5 ${isDark ? 'border-[#3A3A3A]' : 'border-[#E5E7EB]'}`} > {sub.completed && } - + ) : ( + + {sub.title} + + )} + handleDeleteSubtask(sub.id)} + className="ml-2 p-1.5" + hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }} > - {sub.title} - + + ))} diff --git a/src/i18n/en.json b/src/i18n/en.json index b8626be..0790166 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -21,6 +21,7 @@ "completed": "Completed", "newTask": "New task", "deleteConfirm": "Are you sure you want to delete this task?", + "deleteSubtaskConfirm": "Are you sure you want to delete this subtask?", "swipeDelete": "Swipe to delete", "swipeComplete": "Swipe to complete", "dragHandle": "Hold to reorder" diff --git a/src/i18n/fr.json b/src/i18n/fr.json index f029c31..4aa5120 100644 --- a/src/i18n/fr.json +++ b/src/i18n/fr.json @@ -21,6 +21,7 @@ "completed": "Terminée", "newTask": "Nouvelle tâche", "deleteConfirm": "Voulez-vous vraiment supprimer cette tâche ?", + "deleteSubtaskConfirm": "Voulez-vous vraiment supprimer cette sous-tâche ?", "swipeDelete": "Glisser pour supprimer", "swipeComplete": "Glisser pour compléter", "dragHandle": "Maintenir pour réordonner"