Files
music_library/backlog/tasks/ml-169.10 - Fix-Missing-optimistic-UI-for-delete-operations.md
T
2026-05-20 10:42:35 +01:00

78 lines
3.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: ML-169.10
title: "Fix: Async optimistic UI for index/list delete streams"
status: To Do
assignee: []
created_date: "2026-05-19 11:48"
updated_date: "2026-05-20 09:41"
labels:
- perf
- fix
- ux
dependencies: []
references:
- >-
backlog/documents/doc-27 -
Phase-4-Performance-Audit-Low-Hanging-Fruit-Findings.md
modified_files:
- lib/music_library_web/live_helpers/index_actions.ex
- lib/music_library_web/live/collection_live/index.ex
- lib/music_library_web/live/wishlist_live/index.ex
- lib/music_library_web/live/scrobbled_tracks_live/index.ex
parent_task_id: ML-169
priority: medium
ordinal: 31000
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Add optimistic UI for delete operations only where the item is rendered from a LiveView stream on an index/list page. The item should be removed from the stream immediately, while the database delete runs through LiveView-owned async handling so failures can be reported and rolled back.
## Problem
Index/list delete handlers currently wait for the database delete before updating the UI. `stream_delete` is used after the delete succeeds, so the interaction can feel slower than necessary.
This task is intentionally scoped to stream-backed list views only:
- Collection index and wishlist index record streams via `IndexActions.handle_delete/2`
- Scrobbled tracks index stream
Show-page deletes are out of scope. `CollectionLive.Show` and `WishlistLive.Show` navigate away after delete and do not have a list stream to optimistically update. Their failure and rollback behavior should remain synchronous unless a separate navigation-specific design is created.
## Files
- `lib/music_library_web/live_helpers/index_actions.ex` — remove record from `:records` immediately and start a LiveView async delete operation
- `lib/music_library_web/live/collection_live/index.ex` — handle async record delete success/failure for collection index
- `lib/music_library_web/live/wishlist_live/index.ex` — handle async record delete success/failure for wishlist index
- `lib/music_library_web/live/scrobbled_tracks_live/index.ex` — remove track from `:tracks` immediately and handle async track delete success/failure
## Fix
Use LiveView-owned async handling (`start_async/3` plus `handle_async/3`, or an equivalent pattern that sends results back to the LiveView process). Do not use bare `Task.start/1`, because it cannot update the socket, reinsert the stream item, or show an error toast.
Expected behavior:
1. Fetch the item needed for rollback.
2. Immediately call `stream_delete/3` on the relevant stream.
3. Start an async delete operation owned by the LiveView.
4. On success, leave the stream as-is and optionally broadcast/refresh any cross-view state the existing design requires.
5. On failure or async exit, reinsert the original item into the stream and show a user-facing error toast.
For error text, use the projects existing user-facing error conventions (`ErrorMessages.friendly_message/1` where an error reason is shown).
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Index/list delete operations remove the item from the relevant stream before waiting for the database delete
- [ ] #2 Database delete still executes through LiveView-owned async handling, not bare `Task.start/1`
- [ ] #3 Async delete failure or exit reinserts the original item into the stream and shows an error toast
- [ ] #4 Existing delete confirmation prompts still work correctly
- [ ] #5 Show-page delete handlers are not changed as part of this task
- [ ] #6 Collection and wishlist index delete behavior remains shared through `IndexActions` where practical
<!-- AC:END -->