Skip to content

fix(app): hide pending-delete conversations from refresh result#8053

Merged
kodjima33 merged 2 commits into
mainfrom
rex/fix-delete-conversation-undo-race
Jun 20, 2026
Merged

fix(app): hide pending-delete conversations from refresh result#8053
kodjima33 merged 2 commits into
mainfrom
rex/fix-delete-conversation-undo-race

Conversation

@mdmohsin7

@mdmohsin7 mdmohsin7 commented Jun 20, 2026

Copy link
Copy Markdown
Member

Summary

Users reported that deleting a conversation in the app and then pulling-to-refresh "a few seconds later" makes the conversation reappear, as if the delete failed.

The backend isn't failing. Verified via GCP HTTPS LB access logs: 331/331 DELETE /v1/conversations/* requests in the last 24h returned 204 — every delete that reaches the server succeeds.

The actual bug is a client-side race: swipe-to-delete in deleteConversationLocally removes the conversation from the local list and schedules a Future.delayed(3s, sendDelete) for the undo window. A pull-to-refresh inside that 3-second window re-fetches the server list, which still has the conversation (DELETE hasn't fired yet), and fetchConversations() overwrites conversations with the fresh server list without filtering anything pending-delete — so the just-deleted conversation reappears. After the 3s timer fires, the DELETE actually goes out and succeeds, but the user has already seen "delete didn't work".

Fix

Add a tiny _filterPendingDeletes() helper and apply it to every list assignment that comes from the server:

  • Fresh fetch result (fetchConversations).
  • Paginated fetch result (getMoreConversationsFromServer).
  • Cached fallback when the fetch fails or returns empty.

The conversation stays hidden until either (a) the undo timer fires and the server DELETE lands, at which point it disappears for real, or (b) the user taps Undo, at which point undoDeletedConversation removes it from memoriesToDelete and the next refresh shows it normally.

12 lines changed (1 helper + 4 call sites), no public API change, no backend change.

Test plan

  • Swipe-to-delete a conversation → pull-to-refresh within 3s → conversation does not reappear.
  • Swipe-to-delete → wait 5s → pull-to-refresh → conversation stays gone (DELETE has fired, server confirms 204).
  • Swipe-to-delete → tap Undo within 3s → pull-to-refresh → conversation reappears (correct).
  • Detail-page confirm-delete → pull-to-refresh quickly → conversation does not flicker back. (Detail path goes via fire-and-forget too, but with no undo timer; this PR doesn't change that path, but it's a much narrower race.)
  • Conversation list still renders correctly on cold start when no deletes are pending.

🤖 Generated with Claude Code

Review in cubic

Swipe-to-delete queues a 3-second undo timer before the DELETE request
fires. A pull-to-refresh inside that window re-fetches the server list,
which still contains the conversation, so it reappears — users read this
as "delete didn't work". Confirmed via GCP LB logs that the eventual
DELETE succeeds (204 on every call in the last 24h).

Filter `memoriesToDelete` IDs out of any list we adopt from the server
(fresh fetch, paginated fetch, cached fallback) so the conversation stays
hidden until the actual DELETE lands.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread app/lib/providers/conversation_provider.dart
Cubic flagged that `_filterPendingDeletes` can leave `conversations.length`
non-multiple-of-50, blocking `getMoreConversationsFromServer` (which gates
on `length % 50 == 0`) until the pending delete resolves. The offset would
also be wrong by the pending-delete count, skipping items.

Compute a server-equivalent length (local + pending-delete count) and use
that for both the gate and the offset. The pending deletes still exist
server-side until the 3-second undo timer fires, so this matches reality.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="app/lib/providers/conversation_provider.dart">

<violation number="1" location="app/lib/providers/conversation_provider.dart:773">
P2: Pending-delete filtering stops too early because IDs are cleared before DELETE completion. On slow network, pull-to-refresh can still reintroduce a just-deleted conversation.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

// or in-flight HTTP). Without this, a pull-to-refresh during that window
// re-surfaces the just-deleted conversation, which users read as "delete didn't work".
List<ServerConversation> _filterPendingDeletes(List<ServerConversation> items) {
if (memoriesToDelete.isEmpty) return items;

@cubic-dev-ai cubic-dev-ai Bot Jun 20, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Pending-delete filtering stops too early because IDs are cleared before DELETE completion. On slow network, pull-to-refresh can still reintroduce a just-deleted conversation.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/lib/providers/conversation_provider.dart, line 773:

<comment>Pending-delete filtering stops too early because IDs are cleared before DELETE completion. On slow network, pull-to-refresh can still reintroduce a just-deleted conversation.</comment>

<file context>
@@ -761,6 +766,14 @@ class ConversationProvider extends ChangeNotifier {
+  // or in-flight HTTP). Without this, a pull-to-refresh during that window
+  // re-surfaces the just-deleted conversation, which users read as "delete didn't work".
+  List<ServerConversation> _filterPendingDeletes(List<ServerConversation> items) {
+    if (memoriesToDelete.isEmpty) return items;
+    return items.where((c) => !memoriesToDelete.containsKey(c.id)).toList();
+  }
</file context>
Fix with cubic

@kodjima33 kodjima33 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mobile bug fix conf 4/5: filter pending-delete conversations from refresh/load-more so they don't re-surface during the 3s undo window

@kodjima33 kodjima33 merged commit d261850 into main Jun 20, 2026
3 checks passed
@kodjima33 kodjima33 deleted the rex/fix-delete-conversation-undo-race branch June 20, 2026 14:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants