block / block/buzz

Mobile: DM auto-mention silently skipped in thread replies when channel list hasn't loaded yet

Open
#6,199 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
32.7k
Forks
4.3k
Avg merge
1d 13h
Merged PRs (30d)
253

Description

## Summary

In the mobile app, a thread reply in a group DM can go out with **zero** `p` tags — same visible symptom as #6027, but a distinct root cause in a different (Flutter) codebase, not the desktop bug recurring.

## Root cause

`SendMessage.call()` (`mobile/lib/features/channels/send_message_provider.dart:62-72`) only adds DM-recipient auto-mentions when a non-null `channel` object is passed in *and* `channel.isDm == true`:

```dart
final dmRecipientPubkeys = channel?.isDm == true
? await _fetchDmRecipientPubkeys(channelId, channel!, authorPubkey)
: null;
final resolvedMentions = dmRecipientPubkeys != null
? messageMentionPubkeys(...)
: explicitMentions; // <-- silent fallback, no DM auto-mention at all
```

`ThreadDetailPage` (`mobile/lib/features/channels/thread_detail_page.dart:527-530`) derives `channel` from a nullable async provider:

```dart
final channelsAsync = ref.watch(channelsProvider);
final channel = channelsAsync.value
?.where((candidate) => candidate.id == channelId)
.firstOrNull;
```

`channelsAsync.value` is `null` whenever the channel list hasn't loaded yet in this session — cold start, a notification/deep-link tap straight into a thread, or any brief loading window. When that happens, `channel` is `null`, `dmRecipientPubkeys` stays `null`, and `SendMessage.call()` silently falls back to `explicitMentions` only. A plain-text reply with no `@name` in it (the common case) then goes out with **no `p` tags at all** — identical failure signature to #6027 (`h` and `e` tags only), but this is Flutter, not the TS union-of-two-sources bug already fixed there.

`ChannelDetailPage` does **not** have this gap — its `channel` is a required, always-non-null constructor field (`channel_detail_page.dart:126,132`), passed in directly by the caller rather than re-derived from a nullable provider. This is why the bug is specifically a **thread-reply** issue, not a top-level-channel-message issue, which matches the live incident it was found from (a silently-dropped thread reply in a group DM, mobile-only, reported working fine from desktop for the same conversation).

## Secondary, lower-severity gap

Even when `channel` is non-null, `_fetchDmRecipientPubkeys()` (`send_message_provider.dart:116-139`) falls back to `channel.participantPubkeys` — a static snapshot parsed once from channel metadata — whenever `_fetchMembers()` throws. Same staleness risk as desktop's `participant_pubkeys` path in #6027: a member added to the DM after that metadata snapshot won't be in the fallback list.

## Suggested fix

Pass the already-known `Channel` object down into `ThreadDetailPage` from its caller (`ChannelDetailPage` already holds a non-null `Channel`) instead of re-deriving it from `channelsProvider` inside the thread page. That removes the null-channel window entirely for the primary path. The metadata-fallback staleness (secondary gap) is the same shape as #6027's open question and can likely share whatever fix lands there.

## Related

- #6027 / PR #6030 — the desktop version of "group DM message goes out with zero p-tags," different root cause (async `member_pubkeys` query racing a static `participant_pubkeys` parse), same visible symptom.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.