Skip to content

fix(chat): fetch the feed again on foreground when a load ran across a suspension - #972

Merged
bmc08gt merged 1 commit into
mainfrom
fix/chat-feed-foreground-rerun
Oct 5, 2026
Merged

bmc08gt merged 1 commit into
mainfrom
fix/chat-feed-foreground-rerun

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator

When the app is backgrounded while a feed load is in flight, the next foreground joins that load and gets the feed as the server had it before suspension. A message that arrived while the app was away doesn't show in the chat list until the next refresh trigger.

loadFeed() joins a load already in flight on purpose: start(), the launch foreground hook and a reconnect all converge on it, and dedupesFeedLoads asserts one fetch per feed. That stays. What changes:

  • AppDelegate's .background case calls a new ConversationController.handleBackground(), which marks an in-flight load as interrupted. shutDownForBackground() closes the database but doesn't stop the controller, so the load really can outlive the suspension.
  • handleForeground() waits out an interrupted load, then starts a fresh one instead of joining. A launch with no background in between still joins.
  • The owner of a load clears feedLoadTask only if it still holds its own task, so it can't drop the load the foreground started.
  • MockConversations.getDmChatFeed reads feed when the call arrives instead of after feedDelay, the way a server answers with the state at request time. Without that the new test passes with or without the fix.

New test foregroundAfterBackgroundMidLoadRefetchesFeed fails with handleBackground() stubbed out and passes with the fix. ConversationControllerTests and ChatListLaunchSyncTests pass (98 tests), including dedupesFeedLoads.

refetchAfterReconnect() has the same shape: a reconnect during an in-flight load joins it. This PR doesn't change that.

…a suspension

loadFeed() joins a load already in flight, so start(), the launch foreground
hook and a reconnect make one fetch. When the app was backgrounded while
that load's requests were out, the foreground joined it too, and the feed
stayed as the server had it before the app went away until the next trigger.

The background hook now marks an in-flight load as interrupted. The next
handleForeground() waits that load out and starts a fresh one instead of
joining. A launch with no background in between still joins, so
dedupesFeedLoads is unchanged. The owner of a load clears feedLoadTask only
if it still holds its own task, so it can't drop the one the foreground
started.

MockConversations now reads the DM feed when the call arrives rather than
after feedDelay, as a server would; the new test needs that to hold a stale
answer in flight.
@bmc08gt bmc08gt self-assigned this Oct 5, 2026
@bmc08gt
bmc08gt merged commit 016f77c into main Oct 5, 2026
2 of 3 checks passed
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.

1 participant