Skip to content

feat: network resilience, faster feeds, subscription re-sync, and filter chunking#29

Merged
barrydeen merged 2 commits intomainfrom
feat/network-resilience-improvements
Feb 22, 2026
Merged

feat: network resilience, faster feeds, subscription re-sync, and filter chunking#29
barrydeen merged 2 commits intomainfrom
feat/network-resilience-improvements

Conversation

@barrydeen
Copy link
Copy Markdown
Owner

@barrydeen barrydeen commented Feb 22, 2026

Summary

Network resilience improvements, faster feed updates, subscription re-sync on reconnect, lifecycle management refactor, and filter size chunking for large relay compatibility.

Changes

1. ConnectivityFlow — reactive network monitoring

  • New ConnectivityFlow wraps Android's ConnectivityManager.NetworkCallback as a Kotlin Flow
  • Emits ConnectivityStatus.Active(networkId, isMobile) or ConnectivityStatus.Off
  • 100ms debounce + distinctUntilChanged() prevents reconnection storms during WiFi↔cellular handoffs

2. Faster feed updates (EventRepository.kt)

  • Replaced the 250ms polling loop with a Channel(CONFLATED)-based approach
  • Consumer waits 50ms after last signal before emitting to StateFlow
  • ~5x faster perceived feed updates while still batching effectively

3. Subscription re-sync on relay reconnect (RelayPool.kt)

  • activeSubscriptions map tracks last REQ message per sub ID per relay
  • On relay reconnect, resyncSubscriptions() re-sends all tracked REQs

4. RelayLifecycleManager extraction

  • Extracted connectivity monitoring + pause/resume from FeedViewModel into RelayLifecycleManager
  • Moved lifecycle observer from FeedScreen.kt to Navigation.kt WispNavHost — fires on ALL screens

5. Filter chunking for large author lists

  • Big relays (damus.io, nos.social) reject REQs with "total filter items too large"
  • Author lists now chunked to max 200 per REQ via sendChunkedToAll()
  • Chunk subIds: {base}, {base}:c1, {base}:c2
  • closeOnAllRelays() discovers and closes all chunk variants
  • Applied to: subscribeByAuthors, requestProfiles, requestMissingRelayLists

Testing

  • Verify feed loads normally on cold and warm start
  • Toggle airplane mode → relays should reconnect when network returns
  • Follow 500+ accounts → verify no "filter items too large" errors in relay console
  • Check relay console for chunked subscriptions (e.g., feed, feed:c1, feed:c2)

…iption re-sync

Three targeted improvements to relay connection resilience and feed responsiveness:

1. ConnectivityFlow: wraps Android NetworkCallback as a Flow with 100ms debounce
   and distinctUntilChanged. FeedViewModel observes it to detect WiFi drops,
   cellular handoffs, and network restoration — triggering immediate relay
   reconnection instead of waiting for the next lifecycle event.

2. EventRepository: replace 250ms polling loop with Channel-based emission.
   Uses a conflated channel with 50ms settle window so concurrent relay inserts
   coalesce into a single StateFlow emission. ~5x faster perceived feed updates
   while still batching effectively.

3. RelayPool subscription re-sync: track active REQ messages per relay. When a
   relay reconnects (onOpen fires), automatically re-send all tracked subscriptions
   so data flows immediately. Previously a reconnected relay would sit idle until
   the next manual subscribe call.
…avHost

Move relay lifecycle handling out of FeedViewModel and FeedScreen into a
dedicated RelayLifecycleManager class:

1. RelayLifecycleManager: new class that owns connectivity observation
   (ConnectivityFlow) and app pause/resume logic. Takes an onReconnected
   callback so FeedViewModel can re-subscribe feeds without the manager
   knowing about feed internals.

2. Move lifecycle observer from FeedScreen to WispNavHost: the previous
   placement inside FeedScreen's DisposableEffect meant that pausing/
   resuming the app while on Notifications, DMs, Search, or any other
   screen would not trigger relay reconnection. Now it fires on all screens.

3. Add onAppPause: previously there was no pause handler — health tracker
   sessions were only closed inside reconnectAll()/forceReconnectAll().
   Now onAppPause explicitly closes sessions and marks the pool inactive.

4. FeedViewModel.onAppResume/onAppPause are thin delegates to the
   lifecycle manager, reducing god object surface area.
@barrydeen barrydeen merged commit c3defc7 into main Feb 22, 2026
@barrydeen barrydeen changed the title feat: add network-aware reconnection, faster feed updates, and subscription re-sync feat: network resilience, faster feeds, subscription re-sync, and filter chunking Feb 22, 2026
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