From 6f1880574952fd2bf975d53ce29955d30a566233 Mon Sep 17 00:00:00 2001 From: Tom Brow Date: Tue, 27 Oct 2026 13:36:43 -0700 Subject: [PATCH] fix(mobile): treat muted Solo chats like muted channels in the push lease Muted Solo chats stay out of the lease, as muted channels always have, so they push only on mention. This removes the separate muted budget, past which some muted Solo chats were silent and others notified on mention. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Tom Brow --- .../push/push_subscription_provider.dart | 18 +++++------- .../push/push_subscription_provider_test.dart | 29 +++++-------------- 2 files changed, 15 insertions(+), 32 deletions(-) diff --git a/mobile/lib/shared/push/push_subscription_provider.dart b/mobile/lib/shared/push/push_subscription_provider.dart index c94953e218..0d592f5737 100644 --- a/mobile/lib/shared/push/push_subscription_provider.dart +++ b/mobile/lib/shared/push/push_subscription_provider.dart @@ -139,22 +139,18 @@ desiredBuzzPushSubscriptions({ for (final channel in joined) if (channel.isDm) channel.id, ]; + // Muted Solo chats stay out, like muted channels, so they push only on + // mention. final ordered = [ for (final channel in joined) - if (!channel.isDm && channel.isPersonalChat(pubkey, legacySoloChannelIds)) + if (!channel.isDm && + !isMuted(channel.id) && + channel.isPersonalChat(pubkey, legacySoloChannelIds)) channel, ]..sort(_mostRecentFirst); - - var unmutedRoom = + final room = buzzPushMaxChannelWideChannels - dms.where((id) => !isMuted(id)).length; - var mutedRoom = buzzPushMaxMutedChannels - dms.where(isMuted).length; - final solos = []; - for (final solo in ordered) { - final soloMuted = isMuted(solo.id); - if ((soloMuted ? mutedRoom : unmutedRoom) <= 0) continue; - solos.add(solo.id); - soloMuted ? mutedRoom-- : unmutedRoom--; - } + final solos = [for (final solo in ordered.take(room < 0 ? 0 : room)) solo.id]; return (dms: dms, solos: solos, omitted: ordered.length - solos.length); } diff --git a/mobile/test/shared/push/push_subscription_provider_test.dart b/mobile/test/shared/push/push_subscription_provider_test.dart index 92b0367f78..e709e7a772 100644 --- a/mobile/test/shared/push/push_subscription_provider_test.dart +++ b/mobile/test/shared/push/push_subscription_provider_test.dart @@ -87,8 +87,10 @@ void main() { mutedChannelIds: const [mutedSoloID], )?.subscriptions; + // A muted Solo chat is left out, like a muted channel, so only a + // mention pushes it. expect(subscriptions, hasLength(2)); - expect(subscriptions!.first.ignore.last.hTags, [mutedSoloID]); + expect(subscriptions!.first.ignore, hasLength(1)); expect(subscriptions.last.filter.kinds, buzzPushChannelKinds); expect(subscriptions.last.filter.hTags, [dmID, soloID, legacySoloID]); }); @@ -132,21 +134,14 @@ void main() { ); }); - for (final (limit, descriptor, soloCount, mutedSolos) in [ + for (final (limit, descriptor, soloCount) in [ ( 'plaintext', - _descriptor(contentLength: 65536, plaintextLength: 32768), + _descriptor(contentLength: 65536, plaintextLength: 16384), 400, - 100, ), - ( - 'content', - _descriptor(contentLength: 24000, plaintextLength: 32768), - 400, - 100, - ), - ('muted channel count', _descriptor(), 400, 400), - ('NIP-44 plaintext', _descriptor(), 600, 200), + ('content', _descriptor(contentLength: 24000, plaintextLength: 32768), 400), + ('NIP-44 plaintext', _descriptor(endpointLength: 40000), 700), ]) { test('keeps the newest Solo chats within the $limit limit', () async { final keys = nostr.Keys.generate(); @@ -156,24 +151,16 @@ void main() { for (var n = dmCount; n < dmCount + soloCount; n++) _solo(n, keys.public), ]; - // Muted chats are the newest, so every lease filter ignores them. - final muted = [ - for (var n = channels.length - mutedSolos; n < channels.length; n++) - _id(n), - ]; - final desired = desiredBuzzPushSubscriptions( community: _community(keys), descriptor: descriptor, channels: channels, - mutedChannelIds: muted, + mutedChannelIds: const [], )!; final kept = { for (final subscription in desired.subscriptions.skip(1)) ...?subscription.filter.hTags, - for (final ignore in desired.subscriptions.first.ignore.skip(1)) - ...?ignore.hTags, }; final keptSolos = soloCount - desired.omittedSoloChats; expect(keptSolos, inInclusiveRange(1, soloCount - 1));