feat(block2): social settings + offers cubit factory (area, relays)
Persistence + wiring the offers screen needs, per the product decisions: - SocialSettings (keystore-backed, no plaintext): coarse area geohash + configurable relay URLs, both empty by default (no bundled public relays — don't leak metadata to third parties). Registered in DI. - createOffersCubit(): opens a SocialSession on the first configured relay when online, else a null-transport cubit that degrades gracefully. OffersCubit now closes the owning session on dispose. 4 settings tests green; offers/social suites still green.
This commit is contained in:
parent
7fd87e6e62
commit
528b499209
4 changed files with 116 additions and 1 deletions
|
|
@ -4,6 +4,9 @@ import 'package:commons_core/commons_core.dart';
|
|||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../services/social_service.dart';
|
||||
import '../services/social_settings.dart';
|
||||
|
||||
/// State of the offer discovery/publish screen. Transport-agnostic — it holds
|
||||
/// only what the UI shows, never a relay handle.
|
||||
class OffersState extends Equatable {
|
||||
|
|
@ -60,9 +63,15 @@ class OffersState extends Equatable {
|
|||
/// UI stays offline-tolerant (a null transport = the social layer is unavailable
|
||||
/// and the screen degrades gracefully).
|
||||
class OffersCubit extends Cubit<OffersState> {
|
||||
OffersCubit(this._transport) : super(const OffersState());
|
||||
OffersCubit(this._transport, {Future<void> Function()? onDispose})
|
||||
: _onDispose = onDispose,
|
||||
super(const OffersState());
|
||||
|
||||
final OfferTransport? _transport;
|
||||
|
||||
/// Closes the owning [SocialSession]/connection when the cubit is disposed
|
||||
/// (null in tests, where the transport is a fake with nothing to close).
|
||||
final Future<void> Function()? _onDispose;
|
||||
StreamSubscription<Offer>? _sub;
|
||||
|
||||
/// Whether a live transport is available (relay configured and reachable).
|
||||
|
|
@ -113,6 +122,24 @@ class OffersCubit extends Cubit<OffersState> {
|
|||
@override
|
||||
Future<void> close() async {
|
||||
await _sub?.cancel();
|
||||
await _onDispose?.call();
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
|
||||
/// Opens an [OffersCubit] wired to the social layer, or an offline one that
|
||||
/// degrades gracefully. Local-first: when no relay is configured (or connecting
|
||||
/// fails), the cubit is created with a null transport and the screen still opens.
|
||||
Future<OffersCubit> createOffersCubit(
|
||||
SocialService social,
|
||||
SocialSettings settings,
|
||||
) async {
|
||||
final relays = await settings.relayUrls();
|
||||
if (relays.isEmpty) return OffersCubit(null);
|
||||
try {
|
||||
final session = await social.openSession(relays.first);
|
||||
return OffersCubit(session.offers, onDispose: session.close);
|
||||
} catch (_) {
|
||||
return OffersCubit(null); // offline / relay unreachable
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue