Skip to content

fix(chat): sync the group feed on every trigger that refreshes the list - #1475

Merged
bmc08gt merged 1 commit into
code/cashfrom
fix/group-feed-refresh
Sep 16, 2026
Merged

bmc08gt merged 1 commit into
code/cashfrom
fix/group-feed-refresh

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator

The conversation list is two feeds behind one surface, and only one of them was reachable after login. refreshFeed delegated to FeedSyncDelegate, which fetches CONTACT_DM and TIP_DM. A group's row comes from GroupFeedDelegate, and its syncGroupFeed had exactly one caller: onUserLoggedIn. A chat push, a foreground resume, a network reconnect, the heartbeat's stream recovery, and both payment delegates refreshed the DM half alone.

Why it mostly did not show

A push at a named chat plans two actions, and only one of them is the feed refresh. The other is ApplyMessage or LoadMessages, neither of which branches on chat type, so a group the device already has reorders and re-previews exactly like a DM.

Both of those only ever issue UPDATE chat_metadata SET … WHERE chat_id_hex = :hex, which is a silent no-op when there is no row, and nothing else on the push path inserts one. A group joined from another device, or joined while this one was backgrounded, stayed out of the list until the next login — with a notification posted for a chat the list did not contain.

The event stream has the same hole from the other side: it emits SyncFeedRequested precisely when a message arrives for a chat metadataDataSource has no row for, which routed to the DM-only sync.

The change

syncFeeds pairs the two fetches, and all six trigger sites go through it rather than either delegate directly. The delegates keep separate jobs, so the fetches overlap rather than queue, and a group-feed failure is traced and dropped inside GroupFeedDelegate — it cannot take the DM list down the way a contact-feed failure does.

Group and DM sync stay in separate delegates. GroupFeedDelegate documents why: the group feed pages where the DM feed does not, and their failure modes differ. Pairing them at the coordinator is the orchestration this class already owns.

getChat(chatId) would be a more targeted fix for the missing-row case, and is deliberately not used here. The group feed is requested descending with a limit of 100, so a group that just pushed you a message is at the head of the page this fetches; a targeted call would only add something for a group with 100 more-recently-active groups ahead of it.

Tests

Three cases in GroupChatRoutingTest: refreshFeed reaches the group delegate, a foreground resume reaches it, and refreshFeed still fetches both DM feeds. The last one exists because refreshFeed now overrides the FeedOperations delegation, so the half that used to be the only one running needs asserting too.

The file's tests now tear the coordinator down in a finally. The heartbeat the login hook starts is a while (true) on the test scheduler, so a test that fails before reaching its teardown line hangs the run instead of reporting the failure — which is how the first run of these tests against the unfixed source behaved.

Related

Separate from #1474, which fixes who a group push says it came from. This one fixes whether the chat it names is in the list at all.

The conversation list is two feeds behind one surface, and only one of them
was reachable after login. `refreshFeed` delegated to `FeedSyncDelegate`,
which fetches `CONTACT_DM` and `TIP_DM`; a group's row comes from
`GroupFeedDelegate`, whose `syncGroupFeed` had exactly one caller,
`onUserLoggedIn`. A chat push, a foreground resume, a network reconnect, the
heartbeat's stream recovery, and both payment delegates refreshed the DM half
alone.

The chat-id-targeted half of a push plan hid most of this. `ApplyMessage` and
`LoadMessages` do not branch on chat type, so a group the device already has
reorders and re-previews normally. Both only ever `UPDATE chat_metadata ...
WHERE chat_id_hex = :hex`, though, which is a no-op with no row, so a group
joined from another device or joined while this one was backgrounded stayed
out of the list until the next login.

`syncFeeds` pairs the two fetches and every trigger goes through it, including
the event stream's signal that a message arrived for a chat with no local row.
The group feed is requested descending with a limit of 100, so a group that
just pushed you a message is at the head of the page that fetches.

The routing tests now tear the coordinator down in a `finally`. The heartbeat
the login hook starts is a `while (true)` on the test scheduler, so a test that
fails before its teardown line hangs the run instead of reporting.
@bmc08gt bmc08gt self-assigned this Sep 16, 2026
@github-actions github-actions Bot added the type: fix Bug fix label Sep 16, 2026
@bmc08gt
bmc08gt merged commit 3d6c1b0 into code/cash Sep 16, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: fix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant