diff --git a/package-lock.json b/package-lock.json index 9a251cc..ec7618f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "my-weekly-todo-list", - "version": "1.103.1", + "version": "1.103.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "my-weekly-todo-list", - "version": "1.103.1", + "version": "1.103.2", "license": "MIT", "dependencies": { "@auth/prisma-adapter": "^2.11.1", diff --git a/package.json b/package.json index a6b5532..e3733ed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "my-weekly-todo-list", - "version": "1.103.1", + "version": "1.103.2", "description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view", "main": "index.js", "scripts": { diff --git a/src/app/api/tasks/sync/route.ts b/src/app/api/tasks/sync/route.ts index c9116c3..c7504a3 100644 --- a/src/app/api/tasks/sync/route.ts +++ b/src/app/api/tasks/sync/route.ts @@ -144,11 +144,8 @@ export async function GET(req: NextRequest) { continue; } - const remoteUpdated = new Date(remote.updated); - const localUpdated = localTask.lastSyncedAt || localTask.updatedAt; - if (remoteUpdated <= localUpdated) continue; - - const updateData: any = { lastSyncedAt: new Date() }; + // Field-by-field reconciliation (see Outlook block for rationale). + const updateData: any = {}; const remoteCompleted = remote.status === 'completed'; if (remoteCompleted !== localTask.completed) { @@ -173,17 +170,13 @@ export async function GET(req: NextRequest) { updateData.parentTaskId = null; } - if (Object.keys(updateData).length > 1) { + if (Object.keys(updateData).length > 0) { + updateData.lastSyncedAt = new Date(); await prisma.task.update({ where: { id: localTask.id }, data: updateData }); updated++; - } else { - await prisma.task.update({ - where: { id: localTask.id }, - data: { lastSyncedAt: new Date() } - }); } } @@ -295,11 +288,13 @@ export async function GET(req: NextRequest) { continue; } - const remoteUpdated = new Date(remote.lastModifiedDateTime); - const localUpdated = localTask.lastSyncedAt || localTask.updatedAt; - if (remoteUpdated <= localUpdated) continue; - - const updateData: any = { lastSyncedAt: new Date() }; + // Field-by-field reconciliation: always check each field and update if it + // differs from remote. Local-side mutations are pushed eagerly via + // /api/tasks PATCH, so a remote-side change is the authoritative source + // when fields disagree at pull time. (A timestamp gate here was previously + // too strict — bumping lastSyncedAt on no-op pulls hid genuine remote + // changes such as the importance "star" being toggled in Outlook.) + const updateData: any = {}; const remoteCompleted = isMsTodoTaskCompleted(remote.status); if (remoteCompleted !== localTask.completed) { @@ -332,17 +327,13 @@ export async function GET(req: NextRequest) { updateData.scheduledDate = remoteDue; } - if (Object.keys(updateData).length > 1) { + if (Object.keys(updateData).length > 0) { + updateData.lastSyncedAt = new Date(); await prisma.task.update({ where: { id: localTask.id }, data: updateData }); updated++; - } else { - await prisma.task.update({ - where: { id: localTask.id }, - data: { lastSyncedAt: new Date() } - }); } } diff --git a/src/components/WeeklyView.tsx b/src/components/WeeklyView.tsx index 5bae63b..f00429e 100644 --- a/src/components/WeeklyView.tsx +++ b/src/components/WeeklyView.tsx @@ -2002,28 +2002,29 @@ export default function WeeklyView() { } }, []); - // Periodic pull-sync from external task providers (every 15 minutes) + // Pull-sync from external task providers: once on mount + every 15 minutes. + // The eager mount call makes remote-side changes (e.g. Outlook To-Do star) + // visible immediately on next page load instead of after a 15-minute wait. useEffect(() => { if (!session) return; - const interval = setInterval( - async () => { - try { - const res = await fetch("/api/tasks/sync"); - if (res.ok) { - const data = await res.json(); - if (data.updated > 0 || data.deleted > 0 || data.created > 0) { - console.log( - `[SYNC] Pulled ${data.updated} updates, ${data.deleted} deletions, ${data.created || 0} new tasks`, - ); - fetchTasks(); - } + const runPullSync = async () => { + try { + const res = await fetch("/api/tasks/sync"); + if (res.ok) { + const data = await res.json(); + if (data.updated > 0 || data.deleted > 0 || data.created > 0) { + console.log( + `[SYNC] Pulled ${data.updated} updates, ${data.deleted} deletions, ${data.created || 0} new tasks`, + ); + fetchTasks(); } - } catch (e) { - console.error("[SYNC] Task sync error:", e); } - }, - 15 * 60 * 1000, - ); + } catch (e) { + console.error("[SYNC] Task sync error:", e); + } + }; + runPullSync(); + const interval = setInterval(runPullSync, 15 * 60 * 1000); return () => clearInterval(interval); }, [session]);