block / block/buzz

Mobile: agent presence shows offline after relay reconnect — no snapshot backstop, unlike desktop

Open
#6,200 1 comment 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

On mobile, agents (and other users) can show as "offline" in the app even while genuinely online and visible from desktop, for extended stretches after any relay reconnect. This is a confirmed asymmetry between the mobile and desktop presence implementations, not a hypothesis.

## Root cause

`PresenceCacheNotifier` (`mobile/lib/features/profile/presence_cache_provider.dart`) is subscribe-only:

```dart
if (sessionState.status == SessionStatus.connected) {
_subscribePresenceUpdates();
}
return {};
```

`_subscribePresenceUpdates()` opens a live `kind:20001` subscription with `limit: 0` — no snapshot of current state, only future events. `track()` (line 40-46) is explicitly a no-op for the actual fetch:

```dart
/// Currently a no-op for the actual fetch — we rely on live kind:20001
/// events. The tracked set is still used to filter incoming events so the
/// cache doesn't grow unbounded.
void track(List pubkeys) { ... }
// TODO(presence): once the relay supports a `presence:true` filter
// extension, issue a one-shot fetch here for the latest known state per
// pubkey. Until then, presence is "online whenever they publish".
```

Every time `sessionState` transitions and `build()` re-fires — which happens on every relay reconnect — the presence map resets to `{}` and stays empty until a tracked pubkey happens to publish a fresh presence event during that session. iOS backgrounds/suspends the WebSocket aggressively (app backgrounding, network changes, lock screen), so mobile reconnects far more often than desktop ever does, and each reconnect blanks presence until something happens to re-publish.

Desktop has a real backstop for exactly this gap. `desktop/src/features/presence/lib/presence.ts:40-50`:

```ts
// get_presence omits offline/unknown pubkeys, so a live online event often
// targets a pubkey absent from the lookup — merge it in rather than dropping it.
export function mergePresenceUpdate(...)
```

Desktop calls a `get_presence` snapshot query and merges it with the live `kind:20001` stream, so a reconnect doesn't wipe known state. Mobile has no equivalent call anywhere in `presence_cache_provider.dart` — this was apparently deferred pending a relay-side `presence:true` filter extension per the TODO, not an oversight introduced by a recent regression.

## Suggested fix

Short term: add a one-shot presence snapshot fetch (mirroring desktop's `get_presence` call) in `_subscribePresenceUpdates()`, issued once per successful (re)connect, merged into `state` the same way desktop merges live updates into its query cache. Long term: swap to the relay-side `presence:true` filter extension mentioned in the TODO once it exists, to avoid a separate REST/query round trip.

## Repro

Force a mobile relay reconnect (background the app for >30s, then foreground it, or toggle airplane mode briefly) while an agent that isn't actively posting is a member of a visible channel/DM. The agent will show offline on mobile until it next publishes, while desktop (open at the same time, same relay) shows it online throughout.

Contributor guide

Open the contributing guide

Research direction

Start in mobile/lib/features/profile/presence_cache_provider.dart, especially PresenceCacheNotifier, _subscribePresenceUpdates(), and track(); then compare the snapshot and merge behavior in desktop/src/features/presence/lib/presence.ts. Reproduce by forcing a relay reconnect while observing an inactive agent from mobile and desktop. Done means mobile presence remains available after reconnect instead of waiting for a new kind:20001 event.

Written by the indexing model from the issue text.

Assessment

Tech stack
dart, typescript
Domain
mobile
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.