diff --git a/mobile/lib/shared/relay/relay_provider.dart b/mobile/lib/shared/relay/relay_provider.dart index 73643ff020..7e3f0d00a2 100644 --- a/mobile/lib/shared/relay/relay_provider.dart +++ b/mobile/lib/shared/relay/relay_provider.dart @@ -82,12 +82,16 @@ class Env { class RelayConfigNotifier extends Notifier { @override RelayConfig build() { - // Watch the active community so that when it changes (community switch), - // the config rebuilds, triggering the full provider cascade. - final activeAsync = ref.watch(activeCommunityProvider); - final active = activeAsync.value; - if (active != null) { - return RelayConfig(baseUrl: active.relayUrl, nsec: active.nsec); + // Push lease bookkeeping updates the community while using its live + // session. Only connection-context changes may restart that session. + final context = ref.watch( + activeCommunityProvider.select((value) { + final active = value.value; + return (id: active?.id, relayUrl: active?.relayUrl, nsec: active?.nsec); + }), + ); + if (context.relayUrl != null) { + return RelayConfig(baseUrl: context.relayUrl!, nsec: context.nsec); } // Fallback to compile-time env config (dev mode). diff --git a/mobile/test/shared/community/push_relay_config_regression_test.dart b/mobile/test/shared/community/push_relay_config_regression_test.dart new file mode 100644 index 0000000000..53a9fb564e --- /dev/null +++ b/mobile/test/shared/community/push_relay_config_regression_test.dart @@ -0,0 +1,82 @@ +import 'package:buzz/shared/community/community.dart'; +import 'package:buzz/shared/community/community_provider.dart'; +import 'package:buzz/shared/community/community_storage.dart'; +import 'package:buzz/shared/relay/relay_provider.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +import 'community_storage_test.dart'; + +void main() { + test( + 'reserving a push lease does not notify relay config listeners', + () async { + final storage = CommunityStorage(secure: FakeSecureStorage()); + final community = Community.create( + name: 'Tom Town', + relayUrl: 'https://relay.example.com', + ).copyWith(pushNotificationsEnabled: true); + await storage.save(community); + await storage.saveActiveId(community.id); + final container = ProviderContainer( + overrides: [ + communityStorageProvider.overrideWithValue(storage), + communitySnapshotWriterProvider.overrideWithValue((_) async {}), + ], + ); + addTearDown(container.dispose); + await container.read(activeCommunityProvider.future); + final changes = []; + container.listen(relayConfigProvider, (_, next) => changes.add(next)); + await container + .read(communityListProvider.notifier) + .reservePushLeaseGeneration(community.id); + await container.read(activeCommunityProvider.future); + await container.pump(); + expect( + changes, + isEmpty, + reason: 'Push metadata must not reset the connected relay session', + ); + }, + ); + for (final change in ['community', 'relay', 'identity']) { + test('$change change notifies relay config listeners', () async { + final storage = CommunityStorage(secure: FakeSecureStorage()); + final original = Community.create( + name: 'Original', + relayUrl: 'https://relay.example.com', + nsec: 'first', + ); + await storage.save(original); + await storage.saveActiveId(original.id); + final container = ProviderContainer( + overrides: [ + communityStorageProvider.overrideWithValue(storage), + communitySnapshotWriterProvider.overrideWithValue((_) async {}), + ], + ); + addTearDown(container.dispose); + await container.read(activeCommunityProvider.future); + final changes = []; + container.listen(relayConfigProvider, (_, next) => changes.add(next)); + final updated = switch (change) { + 'community' => Community.create( + name: 'Other', + relayUrl: original.relayUrl, + nsec: original.nsec, + ), + 'relay' => original.copyWith(relayUrl: 'https://other.example.com'), + _ => original.copyWith(nsec: 'second'), + }; + await storage.save(updated); + await storage.saveActiveId(updated.id); + container.invalidate(communityListProvider); + await container.read(activeCommunityProvider.future); + await container.pump(); + expect(changes, hasLength(1)); + expect(changes.single.baseUrl, updated.relayUrl); + expect(changes.single.nsec, updated.nsec); + }); + } +}