refactor(social): one shared relay connection per identity
Every feature (offers, messaging, trust, profile, the inbox listener) used to open its OWN RelayPool via social.openSession — several sockets to the same relays, more battery, and reconnection logic living only in the inbox listener. Add SocialConnection: ONE shared session per identity, lazily connected and reused by all. It watches connectivity — dropping the session when the network goes and reconnecting when it returns, announcing each change on a sessions stream so the inbox listener re-subscribes automatically. Callers get the session via connection.session() and never close it (the connection owns its lifecycle); recreated/disposed on an identity switch. - InboxService now consumes the shared connection (subscribes on its sessions stream) instead of owning its own socket + connectivity code. - createOffersCubit + the chat/market/profile screens use the shared connection; dead createMessagesCubit/createTrustCubit factories removed. - DI registers SocialConnection per identity; Bootstrap starts it (after the inbox subscribes); switchSocialAccount disposes + recreates it. Tests: SocialConnection lifecycle (reuse, concurrent connect, offline drop + reconnect, unreachable retry) with a fake opener + online stream; inbox test updated. nostr added as a dev_dependency for the channel fake.
This commit is contained in:
parent
e2b88b4f26
commit
bb4ee2fd89
19 changed files with 431 additions and 280 deletions
|
|
@ -37,6 +37,7 @@ import '../services/offer_outbox.dart';
|
|||
import '../services/profile_cache.dart';
|
||||
import '../services/profile_store.dart';
|
||||
import '../services/social_account_store.dart';
|
||||
import '../services/social_connection.dart';
|
||||
import '../services/social_service.dart';
|
||||
import '../services/social_settings.dart';
|
||||
import '../services/trust_referents.dart';
|
||||
|
|
@ -199,19 +200,25 @@ Future<void> configureDependencies() async {
|
|||
// Optional: absent only if the derivation above failed — then the app runs
|
||||
// inventory-only, by design (market/chat/profile hidden).
|
||||
if (socialService != null) {
|
||||
// ONE shared relay connection per identity, reused by every feature.
|
||||
final connection = SocialConnection(
|
||||
social: socialService,
|
||||
settings: getIt<SocialSettings>(),
|
||||
);
|
||||
getIt
|
||||
..registerSingleton<SocialService>(socialService)
|
||||
..registerSingleton<SocialConnection>(connection)
|
||||
// Tracks unread private messages so the UI can badge them.
|
||||
..registerSingleton<UnreadService>(
|
||||
UnreadService(getIt<MessageStore>(), secretStore),
|
||||
)
|
||||
// App-wide inbox listener so private messages arrive (and land in the
|
||||
// inbox list) even when the specific chat isn't open. Started in `main`.
|
||||
// It also updates unread counts and fires notifications.
|
||||
// App-wide inbox listener over the shared connection, so private messages
|
||||
// arrive (and land in the inbox list) even when the chat isn't open.
|
||||
// Started in `Bootstrap`; it also updates unread and fires notifications.
|
||||
..registerSingleton<InboxService>(
|
||||
InboxService(
|
||||
social: socialService,
|
||||
settings: getIt<SocialSettings>(),
|
||||
connection: connection,
|
||||
selfPubkey: socialService.publicKeyHex,
|
||||
store: getIt<MessageStore>(),
|
||||
profileCache: getIt<ProfileCache>(),
|
||||
unread: getIt<UnreadService>(),
|
||||
|
|
@ -238,11 +245,18 @@ Future<void> switchSocialAccount(int account) async {
|
|||
final scope = socialAccountScope(account);
|
||||
final rootSeedHex = await getIt<SecureKeyStore>().rootSeedHex();
|
||||
|
||||
// Tear down the old identity's live listener/sessions before replacing.
|
||||
// Tear down the old identity's listener + shared connection before replacing.
|
||||
if (getIt.isRegistered<InboxService>()) {
|
||||
await getIt<InboxService>().stop();
|
||||
await getIt.unregister<InboxService>();
|
||||
}
|
||||
if (getIt.isRegistered<SocialConnection>()) {
|
||||
await getIt<SocialConnection>().dispose();
|
||||
await getIt.unregister<SocialConnection>();
|
||||
}
|
||||
if (getIt.isRegistered<UnreadService>()) {
|
||||
await getIt.unregister<UnreadService>();
|
||||
}
|
||||
if (getIt.isRegistered<SocialService>()) {
|
||||
await getIt.unregister<SocialService>();
|
||||
}
|
||||
|
|
@ -257,20 +271,29 @@ Future<void> switchSocialAccount(int account) async {
|
|||
..registerSingleton<ProfileStore>(
|
||||
ProfileStore(secretStore, accountScope: scope))
|
||||
..registerSingleton<ProfileCache>(
|
||||
ProfileCache(secretStore, accountScope: scope));
|
||||
ProfileCache(secretStore, accountScope: scope))
|
||||
..registerSingleton<UnreadService>(
|
||||
UnreadService(getIt<MessageStore>(), secretStore));
|
||||
|
||||
final social =
|
||||
await SocialService.fromRootSeedHex(rootSeedHex, account: account);
|
||||
final connection =
|
||||
SocialConnection(social: social, settings: getIt<SocialSettings>());
|
||||
final inbox = InboxService(
|
||||
social: social,
|
||||
settings: getIt<SocialSettings>(),
|
||||
connection: connection,
|
||||
selfPubkey: social.publicKeyHex,
|
||||
store: getIt<MessageStore>(),
|
||||
profileCache: getIt<ProfileCache>(),
|
||||
unread: getIt<UnreadService>(),
|
||||
notifications: getIt<NotificationService>(),
|
||||
);
|
||||
getIt
|
||||
..registerSingleton<SocialService>(social)
|
||||
..registerSingleton<SocialConnection>(connection)
|
||||
..registerSingleton<InboxService>(inbox);
|
||||
unawaited(inbox.start());
|
||||
// Subscribe the listener BEFORE the connection starts connecting.
|
||||
inbox.start();
|
||||
connection.start();
|
||||
}
|
||||
|
||||
Future<Directory> _backupsDir() async {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue