diff --git a/app/task/[id].tsx b/app/task/[id].tsx index 9cffc2e..d1e3b0c 100644 --- a/app/task/[id].tsx +++ b/app/task/[id].tsx @@ -9,6 +9,7 @@ import { Alert, Platform, KeyboardAvoidingView, + Keyboard, } from 'react-native'; import { useRouter, useLocalSearchParams } from 'expo-router'; import { @@ -83,6 +84,16 @@ export default function TaskDetailScreen() { const [saving, setSaving] = useState(false); const scrollRef = useRef(null); const subtaskInputY = useRef(0); + const subtaskFocused = useRef(false); + + useEffect(() => { + const sub = Keyboard.addListener('keyboardDidShow', () => { + if (subtaskFocused.current) { + scrollRef.current?.scrollTo({ y: subtaskInputY.current - 80, animated: true }); + } + }); + return () => sub.remove(); + }, []); useEffect(() => { if (!isValidUUID(id)) { @@ -405,7 +416,8 @@ export default function TaskDetailScreen() { value={newSubtask} onChangeText={setNewSubtask} onSubmitEditing={handleAddSubtask} - onFocus={() => setTimeout(() => scrollRef.current?.scrollTo({ y: subtaskInputY.current - 80, animated: true }), 300)} + onFocus={() => { subtaskFocused.current = true; }} + onBlur={() => { subtaskFocused.current = false; }} placeholder={t('task.addSubtask')} placeholderTextColor={isDark ? '#A0A0A0' : '#6B6B6B'} className={`ml-2 flex-1 text-base ${isDark ? 'text-[#F5F5F5]' : 'text-[#1A1A1A]'}`} @@ -413,7 +425,7 @@ export default function TaskDetailScreen() { /> - + diff --git a/app/task/new.tsx b/app/task/new.tsx index 80774c2..938839b 100644 --- a/app/task/new.tsx +++ b/app/task/new.tsx @@ -8,6 +8,7 @@ import { useColorScheme, Platform, KeyboardAvoidingView, + Keyboard, } from 'react-native'; import { useRouter, useLocalSearchParams } from 'expo-router'; import { @@ -61,6 +62,16 @@ export default function NewTaskScreen() { const [newSubtask, setNewSubtask] = useState(''); const scrollRef = useRef(null); const subtaskInputY = useRef(0); + const subtaskFocused = useRef(false); + + useEffect(() => { + const sub = Keyboard.addListener('keyboardDidShow', () => { + if (subtaskFocused.current) { + scrollRef.current?.scrollTo({ y: subtaskInputY.current - 80, animated: true }); + } + }); + return () => sub.remove(); + }, []); useEffect(() => { getAllLists().then(setLists); @@ -375,7 +386,8 @@ export default function NewTaskScreen() { value={newSubtask} onChangeText={setNewSubtask} onSubmitEditing={handleAddPendingSubtask} - onFocus={() => setTimeout(() => scrollRef.current?.scrollTo({ y: subtaskInputY.current - 80, animated: true }), 300)} + onFocus={() => { subtaskFocused.current = true; }} + onBlur={() => { subtaskFocused.current = false; }} placeholder={t('task.addSubtask')} placeholderTextColor={isDark ? '#A0A0A0' : '#6B6B6B'} className={`ml-2 flex-1 text-base ${isDark ? 'text-[#F5F5F5]' : 'text-[#1A1A1A]'}`} @@ -383,7 +395,7 @@ export default function NewTaskScreen() { /> - + diff --git a/src/widgets/TaskListWidget.tsx b/src/widgets/TaskListWidget.tsx index 8939f5d..2341e3b 100644 --- a/src/widgets/TaskListWidget.tsx +++ b/src/widgets/TaskListWidget.tsx @@ -251,12 +251,13 @@ function TaskItemRow({ {hasSubtasks ? ( { if (!data) return []; const parsed: unknown = JSON.parse(data); if (!Array.isArray(parsed)) return []; - return parsed.filter(isWidgetTask); + return parsed.filter(isWidgetTask).map((t) => ({ + ...t, + subtasks: Array.isArray(t.subtasks) ? t.subtasks : [], + })); } catch { return []; }