๐ Fix duplicate Task bugs
Changes
6 files changed, +58 -16
MODIFY
app/(tabs)/index.tsx
+6 -4
@@ -1,6 +1,6 @@
1
1
import React, { useEffect, useState } from 'react';
2
2
import { StyleSheet, SafeAreaView, Alert } from 'react-native';
3
-import { router } from 'expo-router';
3
+import { router, useFocusEffect } from 'expo-router';
4
4
import { ThemedView } from '@/components/themed-view';
5
5
import { ThemedText } from '@/components/themed-text';
6
6
import { TaskList } from '@/components/task-list';
@@ -12,9 +12,11 @@
12
12
const [tasks, setTasks] = useState<Task[]>([]);
13
13
const [loading, setLoading] = useState(true);
14
14
15
- useEffect(() => {
16
- loadTasks();
17
- }, []);
15
+ useFocusEffect(
16
+ React.useCallback(() => {
17
+ loadTasks();
18
+ }, [])
19
+ );
18
20
19
21
const loadTasks = async () => {
20
22
try {
MODIFY
app/(tabs)/projects.tsx
+6 -4
@@ -1,6 +1,6 @@
1
1
import React, { useEffect, useState } from 'react';
2
2
import { StyleSheet, SafeAreaView, Alert, TouchableOpacity, FlatList, View } from 'react-native';
3
-import { router } from 'expo-router';
3
+import { router, useFocusEffect } from 'expo-router';
4
4
import { ThemedView } from '@/components/themed-view';
5
5
import { ThemedText } from '@/components/themed-text';
6
6
import { FabButton } from '@/components/fab-button';
@@ -12,9 +12,11 @@
12
12
const [taskCounts, setTaskCounts] = useState<Record<string, number>>({});
13
13
const [loading, setLoading] = useState(true);
14
14
15
- useEffect(() => {
16
- loadProjects();
17
- }, []);
15
+ useFocusEffect(
16
+ React.useCallback(() => {
17
+ loadProjects();
18
+ }, [])
19
+ );
18
20
19
21
const loadProjects = async () => {
20
22
try {
MODIFY
app/(tabs)/today.tsx
+9 -4
@@ -1,6 +1,6 @@
1
1
import React, { useEffect, useState } from 'react';
2
2
import { StyleSheet, SafeAreaView, Alert, ScrollView } from 'react-native';
3
-import { router } from 'expo-router';
3
+import { router, useFocusEffect } from 'expo-router';
4
4
import { ThemedView } from '@/components/themed-view';
5
5
import { ThemedText } from '@/components/themed-text';
6
6
import { TaskList } from '@/components/task-list';
@@ -14,9 +14,11 @@
14
14
const [overdueTasks, setOverdueTasks] = useState<Task[]>([]);
15
15
const [loading, setLoading] = useState(true);
16
16
17
- useEffect(() => {
18
- loadTasks();
19
- }, []);
17
+ useFocusEffect(
18
+ React.useCallback(() => {
19
+ loadTasks();
20
+ }, [])
21
+ );
20
22
21
23
const loadTasks = async () => {
22
24
try {
@@ -83,6 +85,7 @@
83
85
tasks={overdueTasks}
84
86
onTaskPress={handleTaskPress}
85
87
onToggleComplete={handleToggleComplete}
88
+ scrollable={false}
86
89
/>
87
90
</ThemedView>
88
91
)}
@@ -94,6 +97,7 @@
94
97
tasks={todayTasks}
95
98
onTaskPress={handleTaskPress}
96
99
onToggleComplete={handleToggleComplete}
100
+ scrollable={false}
97
101
/>
98
102
</ThemedView>
99
103
)}
@@ -105,6 +109,7 @@
105
109
tasks={tomorrowTasks}
106
110
onTaskPress={handleTaskPress}
107
111
onToggleComplete={handleToggleComplete}
112
+ scrollable={false}
108
113
/>
109
114
</ThemedView>
110
115
)}
MODIFY
app/project/[id].tsx
+1 -0
@@ -270,6 +270,7 @@
270
270
onTaskPress={handleTaskPress}
271
271
onToggleComplete={handleToggleComplete}
272
272
emptyMessage="No tasks in this project yet"
273
+ scrollable={false}
273
274
/>
274
275
</ThemedView>
275
276
</>
MODIFY
components/task-list.tsx
+19 -1
@@ -10,9 +10,10 @@
10
10
onTaskPress: (task: Task) => void;
11
11
onToggleComplete: (taskId: string) => void;
12
12
emptyMessage?: string;
13
+ scrollable?: boolean;
13
14
}
14
15
15
-export function TaskList({ tasks, onTaskPress, onToggleComplete, emptyMessage }: TaskListProps) {
16
+export function TaskList({ tasks, onTaskPress, onToggleComplete, emptyMessage, scrollable = true }: TaskListProps) {
16
17
if (tasks.length === 0) {
17
18
return (
18
19
<ThemedView style={styles.emptyContainer}>
@@ -23,6 +24,23 @@
23
24
);
24
25
}
25
26
27
+ if (!scrollable) {
28
+ return (
29
+ <ThemedView style={styles.list}>
30
+ {tasks.map((item, index) => (
31
+ <React.Fragment key={item.id}>
32
+ <TaskItem
33
+ task={item}
34
+ onPress={() => onTaskPress(item)}
35
+ onToggleComplete={() => onToggleComplete(item.id)}
36
+ />
37
+ {index < tasks.length - 1 && <View style={styles.separator} />}
38
+ </React.Fragment>
39
+ ))}
40
+ </ThemedView>
41
+ );
42
+ }
43
+
26
44
return (
27
45
<FlatList
28
46
data={tasks}
MODIFY
services/task.service.ts
+17 -3
@@ -25,10 +25,16 @@
25
25
}
26
26
27
27
/**
28
- * Get inbox tasks
28
+ * Get inbox tasks (only tasks without due date and in inbox status)
29
29
*/
30
30
async getInboxTasks(): Promise<Task[]> {
31
- return this.getTasksByStatus('inbox');
31
+ const tasks = await this.getAllTasks();
32
+ return tasks.filter(task =>
33
+ task.status === 'inbox' &&
34
+ !task.completed &&
35
+ !task.dueDate &&
36
+ !task.projectId
37
+ );
32
38
}
33
39
34
40
/**
@@ -147,13 +153,21 @@
147
153
throw new Error('Task not found');
148
154
}
149
155
156
+ const currentTask = data.tasks[taskIndex];
150
157
const updatedTask = {
151
- ...data.tasks[taskIndex],
158
+ ...currentTask,
152
159
...updates,
153
160
id: taskId,
154
161
updatedAt: Date.now(),
155
162
};
156
163
164
+ // Auto-move out of inbox when task gets processed
165
+ if (currentTask.status === 'inbox') {
166
+ if (updates.dueDate || updates.projectId || (updates.status && updates.status !== 'inbox')) {
167
+ updatedTask.status = updates.status || 'next';
168
+ }
169
+ }
170
+
157
171
data.tasks[taskIndex] = updatedTask;
158
172
await storageService.saveData(data);
159
173