From 08890895def2c8138bc955615c1299e1914ac408 Mon Sep 17 00:00:00 2001 From: kolaente Date: Sat, 27 Jun 2026 10:32:28 +0200 Subject: [PATCH] fix(task): don't drop the list-view done save during the check animation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Marking a task done via the list-view checkbox deferred the entire update — including the network request — by 300ms to play the check animation. If the page was torn down within that window (a refresh, tab close, or leaving the app), the request was never sent and the save was silently lost. For repeating tasks this is especially confusing: the due date never advances, yet the checkbox un-checks itself anyway, so the failure looks identical to success. Fire the request immediately with the intended done value snapshotted, and defer only the animation-coupled follow-up (result swap, pop sound, toast). The optimistic v-model state already drives the check animation during the 300ms, so nothing visual is lost. --- .../tasks/partials/SingleTaskInProject.vue | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/tasks/partials/SingleTaskInProject.vue b/frontend/src/components/tasks/partials/SingleTaskInProject.vue index 57a115b85..1137f7126 100644 --- a/frontend/src/components/tasks/partials/SingleTaskInProject.vue +++ b/frontend/src/components/tasks/partials/SingleTaskInProject.vue @@ -326,9 +326,17 @@ const isOverdue = computed(() => ( let oldTask async function markAsDone(checked: boolean, wasReverted: boolean = false) { - const updateFunc = async () => { - oldTask = {...task.value} - const newTask = await taskStore.update(task.value) + oldTask = {...task.value} + + // Fire the request immediately and with the intended done value snapshotted, so a re-render or + // teardown during the animation delay can neither drop the save nor make it send a stale state. + const updatePromise = taskStore.update({ + ...task.value, + done: checked, + }) + + const finish = async () => { + const newTask = await updatePromise task.value = newTask updateDueDate() @@ -354,9 +362,9 @@ async function markAsDone(checked: boolean, wasReverted: boolean = false) { } if (checked) { - setTimeout(updateFunc, 300) // Delay it to show the animation when marking a task as done + setTimeout(finish, 300) // Delay only the follow-up to show the animation when marking a task as done } else { - await updateFunc() // Don't delay it when un-marking it as it doesn't have an animation the other way around + await finish() // Don't delay it when un-marking it as it doesn't have an animation the other way around } }