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 [];
}