android: optimistic UI for task complete and skip#308
Conversation
Tasks now disappear from the active list immediately when completed or skipped, instead of waiting for server sync. The implementation adds PENDING_COMPLETE and PENDING_SKIP local states that the Room DAO query filters out, mirroring the existing PENDING_DELETE pattern. The SyncCoordinator now parses the server response (which was already returned but ignored) to handle recurring tasks correctly: if the server returns a task with a next due date, it is upserted as SYNCED; otherwise the task is deleted from Room. On sync failure, the local state reverts to PENDING_UPDATE so the task reappears with a pending indicator rather than vanishing permanently. Also fixes an edge case where remapId (after CREATE sync) could overwrite a subsequent PENDING_COMPLETE/PENDING_SKIP state, and removes the unused pendingSyncTaskIds/TaskWithSyncState dead code. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR updates the Android client’s local/offline sync flow to support optimistic UI when completing or skipping tasks: tasks are removed from the active list immediately, then later either finalized (delete/upsert) when the server confirms or reverted back into view on failure.
Changes:
- Added
PENDING_COMPLETE/PENDING_SKIPlocal states and filtered them out from the active task observation query. - Updated outbox sync handling for COMPLETE/SKIP/UNCOMPLETE to use the server-returned
TaskResponseand to delete vs upsert based on recurrence. - Removed unused task sync state plumbing from the ViewModel/Repository.
Show a summary per file
| File | Description |
|---|---|
| android/app/src/main/java/com/dkhalife/tasks/viewmodel/TaskListViewModel.kt | Removes unused pending-sync task state flow and related imports. |
| android/app/src/main/java/com/dkhalife/tasks/repo/TaskRepository.kt | Adds operation-specific local state for enqueueing COMPLETE/SKIP and removes unused sync-state wrapper model. |
| android/app/src/main/java/com/dkhalife/tasks/data/sync/SyncCoordinator.kt | Applies server-returned task updates on COMPLETE/SKIP/UNCOMPLETE and reverts local state on HTTP failure. |
| android/app/src/main/java/com/dkhalife/tasks/data/db/LocalState.kt | Introduces PENDING_COMPLETE and PENDING_SKIP. |
| android/app/src/main/java/com/dkhalife/tasks/data/db/dao/TaskDao.kt | Filters out pending-complete/skip tasks from observeTasks() and preserves non-create states during ID remap. |
| android/app/src/main/java/com/dkhalife/tasks/api/TaskWizardApi.kt | Aligns task action endpoints to return Response<TaskResponse> (matching backend JSON shape). |
Copilot's findings
- Files reviewed: 6/6 changed files
- Comments generated: 1
Only preserve PENDING_CREATE for coalescing operations (UPDATE) that fold into the CREATE payload. COMPLETE and SKIP now override to their specific states so the task disappears optimistically even in the create-then-complete/skip flow. The CREATE handler uses getTaskById() which is unfiltered, so it still builds the payload correctly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Addressed in 42e7a1a — good catch. The fix now only preserves PENDING_CREATE for coalescing operations (i.e., when localState == PENDING_UPDATE), which covers UPDATE and UNCOMPLETE. COMPLETE/SKIP now override to their specific states so the task disappears optimistically even in the create-then-complete/skip flow. The CREATE handler uses getTaskById() (no state filter), so it still finds the task to build the payload. After CREATE succeeds, remapId() preserves the PENDING_COMPLETE/PENDING_SKIP state across the ID remap. |
Summary
Tasks now disappear from the active list immediately when completed or skipped, instead of waiting for server sync.
Changes
Edge Cases Handled