added Recurrence feature
This commit is contained in:
@@ -26,8 +26,85 @@ export interface Task {
|
||||
deletedAt?: number; // Timestamp of soft delete
|
||||
created: string; // ISO string from PB
|
||||
updated: string; // ISO string from PB
|
||||
recurrence?: {
|
||||
type: 'daily' | 'weekly' | 'monthly';
|
||||
days?: number[]; // For weekly (0-6, 0=Sunday)
|
||||
dayOfMonth?: number; // For monthly (1-31)
|
||||
lastUncompleted?: string; // ISO date of last automatic reset
|
||||
};
|
||||
}
|
||||
|
||||
export const checkRecurringTasks = () => {
|
||||
const tasks = store.tasks;
|
||||
const nowObj = new Date();
|
||||
const todayStr = nowObj.toISOString().split('T')[0];
|
||||
|
||||
tasks.forEach(task => {
|
||||
if (!task.completed || !task.recurrence) return;
|
||||
|
||||
// If deleted, we don't recurse?
|
||||
if (task.deletedAt) return;
|
||||
|
||||
const { type, days, dayOfMonth, lastUncompleted } = task.recurrence;
|
||||
let shouldReset = false;
|
||||
|
||||
// Check if we already reset it today/this cycle
|
||||
if (lastUncompleted) {
|
||||
const lastDate = new Date(lastUncompleted).toISOString().split('T')[0];
|
||||
if (lastDate === todayStr) return; // Already reset today
|
||||
}
|
||||
|
||||
// Also check 'updated' date. If it was marked completed TODAY, don't reset immediately.
|
||||
// We only reset if the completion happened BEFORE the current cycle trigger.
|
||||
// E.g. Completed yesterday, today is a new day -> Reset.
|
||||
const updatedDate = new Date(task.updated).toISOString().split('T')[0];
|
||||
if (updatedDate === todayStr) {
|
||||
// If manual loop: user completes it, we shouldn't immediately uncomplete it.
|
||||
// But what if it's supposed to be uncompleted today?
|
||||
// "Tasks that 'uncomplete' at given intervals" implies:
|
||||
// If I complete it today, it stays completed until the NEXT interval.
|
||||
// So we only reset if it was completed BEFORE today (for daily).
|
||||
if (type === 'daily') return;
|
||||
}
|
||||
|
||||
if (type === 'daily') {
|
||||
// If completed before today, reset.
|
||||
if (updatedDate < todayStr) {
|
||||
shouldReset = true;
|
||||
}
|
||||
}
|
||||
else if (type === 'weekly') {
|
||||
// If today is one of the recurrence days
|
||||
const currentDay = nowObj.getDay();
|
||||
if (days?.includes(currentDay)) {
|
||||
// Reset if it was completed BEFORE today.
|
||||
if (updatedDate < todayStr) {
|
||||
shouldReset = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type === 'monthly') {
|
||||
const currentDate = nowObj.getDate();
|
||||
if (dayOfMonth === currentDate) {
|
||||
if (updatedDate < todayStr) {
|
||||
shouldReset = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldReset) {
|
||||
console.log(`Resetting recurring task: ${task.title}`);
|
||||
updateTask(task.id, {
|
||||
completed: false,
|
||||
recurrence: {
|
||||
...task.recurrence,
|
||||
lastUncompleted: new Date().toISOString()
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export interface TaskTemplate {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -309,6 +386,9 @@ export const initStore = async () => {
|
||||
setStore("tasks", reconcile(allTasks));
|
||||
setStore("templates", loadedTemplates);
|
||||
|
||||
// Check recurring tasks after load
|
||||
checkRecurringTasks();
|
||||
|
||||
// 4. Reconstructive Migration: Verify all tags in tasks have definitions
|
||||
const foundTagNames = new Set<string>();
|
||||
allTasks.forEach(t => t.tags?.forEach(tag => foundTagNames.add(tag)));
|
||||
|
||||
Reference in New Issue
Block a user