fix(market): recover by itself when the shared connection comes up
Fresh installs could sit on 'can't reach the servers' forever: the offers cubit captured the transport once at build time, the shared connection only retried on a connectivity CHANGE, and a silently-filtered relay could stall the pool for minutes. - SocialConnection: retry with backoff after a failed attempt while started and not knowingly offline (injectable schedule for tests) - OffersCubit: follow connection.sessions, re-attach the transport and re-run the last discovery on (re)connect; announce drops via connectionEpoch - market _init: record the wanted area on the cubit even while offline - NostrOfferTransport.discoverPage: sort a copy (channel lists may be unmodifiable)
This commit is contained in:
parent
981cf10048
commit
8a7dc8d3dc
6 changed files with 273 additions and 9 deletions
|
|
@ -10,6 +10,7 @@ import '../services/offer_mapper.dart';
|
|||
import '../services/offer_outbox.dart';
|
||||
import '../services/offer_thumbnail.dart';
|
||||
import '../services/social_connection.dart';
|
||||
import '../services/social_service.dart';
|
||||
|
||||
/// State of the offer discovery/publish screen. Transport-agnostic — it holds
|
||||
/// only what the UI shows, never a relay handle.
|
||||
|
|
@ -28,6 +29,7 @@ class OffersState extends Equatable {
|
|||
this.nextPageCursor,
|
||||
this.publishing = false,
|
||||
this.hasSearched = false,
|
||||
this.connectionEpoch = 0,
|
||||
this.error,
|
||||
});
|
||||
|
||||
|
|
@ -80,6 +82,11 @@ class OffersState extends Equatable {
|
|||
/// from "searched, found nothing".
|
||||
final bool hasSearched;
|
||||
|
||||
/// Bumped whenever the underlying transport comes or goes, so the UI rebuilds
|
||||
/// and re-reads [OffersCubit.isOnline] — states differing only here are
|
||||
/// otherwise Equatable-equal and bloc would skip the emit.
|
||||
final int connectionEpoch;
|
||||
|
||||
/// Last error, in human terms for the UI (null when fine).
|
||||
final String? error;
|
||||
|
||||
|
|
@ -136,6 +143,7 @@ class OffersState extends Equatable {
|
|||
int? Function()? nextPageCursor,
|
||||
bool? publishing,
|
||||
bool? hasSearched,
|
||||
int? connectionEpoch,
|
||||
String? Function()? error,
|
||||
}) {
|
||||
return OffersState(
|
||||
|
|
@ -153,6 +161,7 @@ class OffersState extends Equatable {
|
|||
nextPageCursor != null ? nextPageCursor() : this.nextPageCursor,
|
||||
publishing: publishing ?? this.publishing,
|
||||
hasSearched: hasSearched ?? this.hasSearched,
|
||||
connectionEpoch: connectionEpoch ?? this.connectionEpoch,
|
||||
error: error != null ? error() : this.error,
|
||||
);
|
||||
}
|
||||
|
|
@ -172,6 +181,7 @@ class OffersState extends Equatable {
|
|||
nextPageCursor,
|
||||
publishing,
|
||||
hasSearched,
|
||||
connectionEpoch,
|
||||
error,
|
||||
];
|
||||
}
|
||||
|
|
@ -183,15 +193,40 @@ class OffersState extends Equatable {
|
|||
class OffersCubit extends Cubit<OffersState> {
|
||||
OffersCubit(
|
||||
this._transport, {
|
||||
SocialConnection? connection,
|
||||
Future<Uint8List?> Function(String varietyId)? coverPhoto,
|
||||
String? Function(Uint8List bytes)? thumbnail,
|
||||
Future<void> Function()? onDispose,
|
||||
}) : _coverPhoto = coverPhoto,
|
||||
_thumbnail = thumbnail,
|
||||
_onDispose = onDispose,
|
||||
super(const OffersState());
|
||||
super(const OffersState()) {
|
||||
// Fresh-install fix: the transport captured at build time may be null while
|
||||
// the shared connection is still coming up (or dropped). Follow the
|
||||
// connection so the market recovers by itself instead of staying offline
|
||||
// until a manual retry.
|
||||
_connSub = connection?.sessions.listen(_onSession);
|
||||
}
|
||||
|
||||
final OfferTransport? _transport;
|
||||
OfferTransport? _transport;
|
||||
StreamSubscription<SocialSession?>? _connSub;
|
||||
|
||||
/// The last area prefix asked of [discover]; re-run when the connection
|
||||
/// (re)appears so results show without the user tapping anything.
|
||||
String? _lastPrefix;
|
||||
|
||||
void _onSession(SocialSession? session) {
|
||||
if (isClosed) return;
|
||||
final transport = session?.offers;
|
||||
if (identical(transport, _transport)) return;
|
||||
_transport = transport;
|
||||
final prefix = _lastPrefix;
|
||||
if (transport != null && prefix != null) {
|
||||
unawaited(discover(prefix)); // emits fresh states as results arrive
|
||||
} else {
|
||||
emit(state.copyWith(connectionEpoch: state.connectionEpoch + 1));
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetches a variety's cover photo bytes; null in tests or when no inventory
|
||||
/// repo is wired.
|
||||
|
|
@ -225,6 +260,7 @@ class OffersCubit extends Cubit<OffersState> {
|
|||
/// from now on — older results arrive via [loadNextPage], not by draining the
|
||||
/// whole area into memory.
|
||||
Future<void> discover(String geohashPrefix) async {
|
||||
_lastPrefix = geohashPrefix;
|
||||
final transport = _transport;
|
||||
if (transport == null) {
|
||||
emit(state.copyWith(error: () => 'offline', hasSearched: true));
|
||||
|
|
@ -467,6 +503,7 @@ class OffersCubit extends Cubit<OffersState> {
|
|||
@override
|
||||
Future<void> close() async {
|
||||
_searchTimeout?.cancel();
|
||||
await _connSub?.cancel();
|
||||
await _sub?.cancel();
|
||||
await _onDispose?.call();
|
||||
return super.close();
|
||||
|
|
@ -484,6 +521,7 @@ Future<OffersCubit> createOffersCubit(
|
|||
final session = await connection.session();
|
||||
return OffersCubit(
|
||||
session?.offers,
|
||||
connection: connection, // keeps following (re)connects — see _onSession
|
||||
coverPhoto: repository?.coverPhotoFor,
|
||||
thumbnail: offerThumbnailDataUri,
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue