From c58a4dce2d95c311b87e6d56a2ca84e4629cbf76 Mon Sep 17 00:00:00 2001 From: le king fu Date: Sat, 21 Feb 2026 20:55:29 -0500 Subject: [PATCH] feat: add list selector to task detail screen Allow viewing and changing a task's list from the task detail page. Uses the same chip-style selector with list icons and colors as the new task screen. Co-Authored-By: Claude Opus 4.6 --- app/task/[id].tsx | 60 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/app/task/[id].tsx b/app/task/[id].tsx index 2a3360a..bafe8a2 100644 --- a/app/task/[id].tsx +++ b/app/task/[id].tsx @@ -10,7 +10,13 @@ import { Platform, } from 'react-native'; import { useRouter, useLocalSearchParams } from 'expo-router'; -import { ArrowLeft, Plus, Trash2, Calendar, X, Repeat, Download } from 'lucide-react-native'; +import { + ArrowLeft, Plus, Trash2, Calendar, X, Repeat, Download, + List, ShoppingCart, Briefcase, Home, Heart, Star, BookOpen, + GraduationCap, Dumbbell, Utensils, Plane, Music, Code, Wrench, + Gift, Camera, Palette, Dog, Leaf, Zap, +} from 'lucide-react-native'; +import type { LucideIcon } from 'lucide-react-native'; import { useTranslation } from 'react-i18next'; import * as Haptics from 'expo-haptics'; import DateTimePicker, { DateTimePickerEvent } from '@react-native-community/datetimepicker'; @@ -29,6 +35,13 @@ import { toggleComplete, } from '@/src/db/repository/tasks'; import { getAllTags, getTagsForTask, setTagsForTask } from '@/src/db/repository/tags'; +import { getAllLists } from '@/src/db/repository/lists'; + +const ICON_MAP: Record = { + List, ShoppingCart, Briefcase, Home, Heart, Star, BookOpen, + GraduationCap, Dumbbell, Utensils, Plane, Music, Code, Wrench, + Gift, Camera, Palette, Dog, Leaf, Zap, +}; import TagChip from '@/src/components/task/TagChip'; import { exportAndShareICS } from '@/src/services/icsExport'; @@ -64,6 +77,8 @@ export default function TaskDetailScreen() { const [newSubtask, setNewSubtask] = 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 }[]>([]); + const [selectedListId, setSelectedListId] = useState(''); useEffect(() => { if (!isValidUUID(id)) { @@ -73,6 +88,7 @@ export default function TaskDetailScreen() { loadTask(); loadSubtasks(); getAllTags().then(setAvailableTags); + getAllLists().then(setLists); }, [id]); const loadTask = async () => { @@ -84,6 +100,7 @@ export default function TaskDetailScreen() { setPriority(result.priority); setDueDate(result.dueDate ? new Date(result.dueDate) : null); setRecurrence(result.recurrence ?? null); + setSelectedListId(result.listId); const taskTags = await getTagsForTask(id!); setSelectedTagIds(taskTags.map((t) => t.id)); @@ -102,6 +119,7 @@ export default function TaskDetailScreen() { priority, dueDate, recurrence, + listId: selectedListId, }); await setTagsForTask(task.id, selectedTagIds); router.back(); @@ -298,6 +316,46 @@ export default function TaskDetailScreen() { )} + {/* List selector */} + {lists.length > 1 && ( + <> + + {t('nav.lists')} + + + {lists.map((list) => { + const isSelected = selectedListId === list.id; + const chipColor = isSelected ? colors.bleu.DEFAULT : isDark ? '#A0A0A0' : '#6B6B6B'; + const IconComp = list.icon && ICON_MAP[list.icon] ? ICON_MAP[list.icon] : null; + return ( + setSelectedListId(list.id)} + className={`mb-2 mr-2 flex-row items-center rounded-full border px-3 py-1.5 ${ + isSelected ? 'border-bleu bg-bleu/10' : isDark ? 'border-[#3A3A3A]' : 'border-[#E5E7EB]' + }`} + > + {IconComp ? ( + + ) : list.color ? ( + + ) : null} + + {list.isInbox ? t('list.inbox') : list.name} + + + ); + })} + + + )} + {/* Subtasks */} {t('task.subtasks')}