added task size management features and fixed trash recover feature

This commit is contained in:
2026-02-04 18:05:24 -06:00
parent 54a0da9896
commit fc3f1066e1
8 changed files with 343 additions and 73 deletions
+41 -15
View File
@@ -32,6 +32,7 @@ export interface Task {
dayOfMonth?: number; // For monthly (1-31)
lastUncompleted?: string; // ISO date of last automatic reset
};
size?: number; // 0-10, task complexity/size
}
export const checkRecurringTasks = () => {
@@ -280,6 +281,11 @@ export const getCombinedScore = (task: Task): number => {
});
}
// Size adjustment: (size - 5) * -0.4
// Small tasks (0-4) get a slight boost, large tasks (6-10) get a slight penalty
const sizeScore = ((task.size ?? 5) - 5) * -0.4;
baseScore += sizeScore;
return baseScore;
};
@@ -303,7 +309,8 @@ const mapRecordToTask = (r: any): Task => {
deletedAt: r.deletedAt ? new Date(r.deletedAt).getTime() : undefined,
created: r.created,
updated: r.updated,
recurrence: r.recurrence
recurrence: r.recurrence,
size: (r.size === null || r.size === undefined) ? undefined : r.size
};
};
@@ -362,9 +369,24 @@ export const subscribeToRealtime = async () => {
return;
}
// Normal Task
const updatedTask = mapRecordToTask(e.record);
setStore("tasks", t => t.id === e.record.id, updatedTask);
// Normal Task - only apply if incoming record is newer or equal
const existingTask = store.tasks.find(t => t.id === e.record.id);
if (existingTask) {
const incomingUpdated = new Date(e.record.updated).getTime();
const localUpdated = new Date(existingTask.updated).getTime();
// Only apply update if incoming is newer (or we don't have a timestamp)
if (!existingTask.updated || incomingUpdated >= localUpdated) {
const updatedTask = mapRecordToTask(e.record);
setStore("tasks", t => t.id === e.record.id, updatedTask);
} else {
console.log("Skipping stale realtime update for task:", e.record.id);
}
} else {
// Task doesn't exist locally, add it
const updatedTask = mapRecordToTask(e.record);
setStore("tasks", t => [updatedTask, ...t]);
}
}
if (e.action === 'delete') {
@@ -496,7 +518,7 @@ export const initStore = async () => {
// -- Actions --
export const addTask = async (title: string, options?: { dueDate?: Date | string, priority?: number, tags?: string[], content?: string }) => {
export const addTask = async (title: string, options?: { dueDate?: Date | string, priority?: number, tags?: string[], content?: string, size?: number }) => {
if (!pb.authStore.isValid) return;
// Default to ~24h from now if no date provided
@@ -509,6 +531,7 @@ export const addTask = async (title: string, options?: { dueDate?: Date | string
const priority = options?.priority ?? 5;
const tags = options?.tags ?? ["work"];
const content = options?.content ?? "";
const size = options?.size ?? 5;
const tempId = "temp-" + Date.now();
const newTask: Task = {
@@ -520,6 +543,7 @@ export const addTask = async (title: string, options?: { dueDate?: Date | string
completed: false,
tags,
content,
size,
created: new Date().toISOString(),
updated: new Date().toISOString()
};
@@ -536,7 +560,8 @@ export const addTask = async (title: string, options?: { dueDate?: Date | string
priority,
completed: false,
tags,
content
content,
size
});
// Replace temp task with real record (merging server fields like id, created, updated)
@@ -572,7 +597,8 @@ export const copyTask = async (id: string) => {
priority: original.priority,
dueDate: original.dueDate,
tags: [...(original.tags || [])],
content: original.content
content: original.content,
size: original.size
});
toast.success("Task duplicated");
@@ -581,8 +607,12 @@ export const copyTask = async (id: string) => {
export const updateTask = async (id: string, updates: Partial<Task>) => {
if (!pb.authStore.isValid) return;
// Optimistic
setStore("tasks", (t) => t.id === id, updates);
// Optimistic update with timestamp to prevent stale realtime events
const optimisticUpdates = {
...updates,
updated: new Date().toISOString()
};
setStore("tasks", (t) => t.id === id, optimisticUpdates);
try {
// Map Task fields to PB fields if needed (dates are ISO strings, so should matches)
@@ -674,12 +704,8 @@ export const removeTask = (id: string) => {
};
export const restoreTask = (id: string) => {
// This requires passing undefined or null to updateTask
// createStore handles undefined ok.
setStore("tasks", (t) => t.id === id, "deletedAt", undefined);
// For PB, we need to send null explicitly for date clear?
// updateTask handles this check.
// updateTask handles both optimistic update and PB sync
// Pass undefined for deletedAt, updateTask will convert to null for PB
updateTask(id, { deletedAt: undefined });
};