Five targeted fixes for the biggest idle drains:
- NostrClient keep-alive sweep: the 60s reconnect loop kept ticking for
the whole life of the process even after disconnect() (i.e. the entire
time the app sat in the background). isActive is now a StateFlow and
the loop suspends on it, so an inactive client schedules zero timers.
- Relay WebSocket pings on cellular: every ping on an idle connection
promotes the radio out of its low-power state, per connection. The
mobile-data client now pings every 240s instead of 120s (wifi keeps
120s), staying under common carrier NAT idle timeouts.
- Always-on service watchdog: the 5-min health-check alarm no longer
uses the _WAKEUP variant. Pulling the CPU out of sleep to restart a
service that can't do useful network work on a sleeping device was
pure cost; the alarm now fires as soon as the device is next awake,
and the WorkManager + FCM/UnifiedPush layers still cover doze.
- ScheduledPostWorker: the 15-min periodic worker was enqueued forever
for every user, waking (often cold-starting) the process with nothing
to publish. It is now enqueued exactly while the store holds a PENDING
post, driven by a store-flow observer in AppModules.
- CalendarReminderWorker: same unconditional 15-min periodic schedule,
but worse — LocalCache is memory-only, so a WorkManager wake of a dead
process can never find an RSVP to remind about. The worker is now
scheduled when an ACCEPTED RSVP lands in LocalCache and cancels its
own chain when nothing upcoming remains (or the feature is disabled).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016RJ8EAsdkHx5WHHU2eQJ1P
The notification service no longer creates its own relay subscriptions.
Instead, it relies on the AccountFilterAssembler subscription from the
Compose tree, which covers notifications, gift wraps, metadata, follows,
relay lists, and drafts. This ensures follow/mute list changes are
reflected in notification filtering.
Heavy feed subscriptions (Home, Video, Discovery, ChatroomList) now use
LifecycleAwareKeyDataSourceSubscription which subscribes on ON_START and
unsubscribes on ON_STOP. When the app backgrounds, these feeds pause and
their outbox relays disconnect. Only inbox and DM relays stay connected
via AccountFilterAssembler.
This prevents bandwidth waste on feeds nobody is viewing while the
always-on service keeps relay connections alive.
https://claude.ai/code/session_01LEPfmgGnwjB9a5SDFw5U8t
The service now maintains two independent subscriptions:
- svc:notif: notification events on NIP-65 inbox relays
- svc:giftwrap: NIP-59 gift wraps on NIP-17 DM inbox relays
This ensures DM relays that aren't also notification inbox relays
stay connected when the app backgrounds. Both subscriptions update
reactively when relay lists change.
Also updates PULL_NOTIFICATION.md with detailed "why" explanations
for each resilience layer and documents the DM relay architecture.
https://claude.ai/code/session_01LEPfmgGnwjB9a5SDFw5U8t