Compare commits

...

2 commits

Author SHA1 Message Date
9b1f7e79c9 Merge pull request 'feat: inline edit and delete for subtasks (#25)' (#30) from issue-25-edit-delete-subtasks into master 2026-03-31 00:13:04 +00:00
le king fu
19706fa4a3 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) <noreply@anthropic.com>
2026-03-30 19:36:24 -04:00
3 changed files with 62 additions and 6 deletions

View file

@ -76,6 +76,8 @@ export default function TaskDetailScreen() {
const [recurrence, setRecurrence] = useState<string | null>(null);
const [subtasks, setSubtasks] = useState<SubtaskData[]>([]);
const [newSubtask, setNewSubtask] = useState('');
const [editingSubtaskId, setEditingSubtaskId] = useState<string | null>(null);
const [editingTitle, setEditingTitle] = useState('');
const [availableTags, setAvailableTags] = useState<{ id: string; name: string; color: string }[]>([]);
const [selectedTagIds, setSelectedTagIds] = useState<string[]>([]);
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) => (
<Pressable
key={sub.id}
onPress={() => 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]'}`}
>
<View
@ -385,12 +420,31 @@ export default function TaskDetailScreen() {
>
{sub.completed && <Text className="text-xs text-white" style={{ fontFamily: 'Inter_700Bold' }}></Text>}
</View>
<Text
className={`text-base ${sub.completed ? 'line-through ' + (isDark ? 'text-[#A0A0A0]' : 'text-[#9CA3AF]') : isDark ? 'text-[#F5F5F5]' : 'text-[#1A1A1A]'}`}
style={{ fontFamily: 'Inter_400Regular' }}
{editingSubtaskId === sub.id ? (
<TextInput
value={editingTitle}
onChangeText={setEditingTitle}
onSubmitEditing={handleSaveSubtaskEdit}
onBlur={handleSaveSubtaskEdit}
autoFocus
className={`flex-1 text-base ${isDark ? 'text-[#F5F5F5]' : 'text-[#1A1A1A]'}`}
style={{ fontFamily: 'Inter_400Regular' }}
/>
) : (
<Text
className={`flex-1 text-base ${sub.completed ? 'line-through ' + (isDark ? 'text-[#A0A0A0]' : 'text-[#9CA3AF]') : isDark ? 'text-[#F5F5F5]' : 'text-[#1A1A1A]'}`}
style={{ fontFamily: 'Inter_400Regular' }}
>
{sub.title}
</Text>
)}
<Pressable
onPress={() => handleDeleteSubtask(sub.id)}
className="ml-2 p-1.5"
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
>
{sub.title}
</Text>
<X size={16} color={isDark ? '#A0A0A0' : '#9CA3AF'} />
</Pressable>
</Pressable>
))}

View file

@ -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"

View file

@ -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"