- Replace unreliable setTimeout scroll with Keyboard.addListener('keyboardDidShow')
- Track subtask input focus state to scroll only when relevant
- Increase bottom spacer from h-32 to h-80 for more scroll room
- Enlarge expand/collapse button (32px with background) and arrow (fontSize 18)
- Ensure subtasks array is always initialized in widget handler
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
55e02e1b3a
commit
a03085c768
4 changed files with 38 additions and 10 deletions
|
|
@ -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<ScrollView>(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() {
|
|||
/>
|
||||
</View>
|
||||
|
||||
<View className="h-32" />
|
||||
<View className="h-80" />
|
||||
</ScrollView>
|
||||
</KeyboardAvoidingView>
|
||||
</View>
|
||||
|
|
|
|||
|
|
@ -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<ScrollView>(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() {
|
|||
/>
|
||||
</View>
|
||||
|
||||
<View className="h-32" />
|
||||
<View className="h-80" />
|
||||
</ScrollView>
|
||||
</KeyboardAvoidingView>
|
||||
</View>
|
||||
|
|
|
|||
|
|
@ -251,12 +251,13 @@ function TaskItemRow({
|
|||
{hasSubtasks ? (
|
||||
<FlexWidget
|
||||
style={{
|
||||
width: 28,
|
||||
height: 28,
|
||||
borderRadius: 14,
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: 16,
|
||||
backgroundColor: isDark ? '#2A2A2A' as ColorProp : '#F0E8DC' as ColorProp,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginLeft: 4,
|
||||
marginLeft: 6,
|
||||
}}
|
||||
clickAction="TOGGLE_EXPAND"
|
||||
clickActionData={{ taskId: task.id }}
|
||||
|
|
@ -264,7 +265,7 @@ function TaskItemRow({
|
|||
<TextWidget
|
||||
text={isExpanded ? '▾' : '▸'}
|
||||
style={{
|
||||
fontSize: 14,
|
||||
fontSize: 18,
|
||||
fontFamily: FONT_SEMIBOLD,
|
||||
color: TODAY_COLOR,
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,10 @@ async function getWidgetTasks(): Promise<WidgetTask[]> {
|
|||
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 [];
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue