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
|
|
@ -25,19 +25,36 @@ class SocialConnection {
|
|||
required SocialSettings settings,
|
||||
SessionOpener? open,
|
||||
Stream<bool>? online,
|
||||
List<Duration>? retrySchedule,
|
||||
}) : _settings = settings,
|
||||
_open = open ?? social.openSession,
|
||||
_online = online;
|
||||
_online = online,
|
||||
_retrySchedule = retrySchedule ?? _defaultRetrySchedule;
|
||||
|
||||
/// Backoff for self-retries after a failed attempt while started and not
|
||||
/// knowingly offline (the fresh-install case: online the whole time, first
|
||||
/// connect fails, so no connectivity change ever retriggers a connect).
|
||||
static const _defaultRetrySchedule = [
|
||||
Duration(seconds: 5),
|
||||
Duration(seconds: 15),
|
||||
Duration(seconds: 45),
|
||||
Duration(seconds: 90),
|
||||
];
|
||||
|
||||
final SocialSettings _settings;
|
||||
final SessionOpener _open;
|
||||
final Stream<bool>? _online;
|
||||
final List<Duration> _retrySchedule;
|
||||
|
||||
final _sessions = StreamController<SocialSession?>.broadcast();
|
||||
SocialSession? _current;
|
||||
Future<SocialSession?>? _pending;
|
||||
StreamSubscription<bool>? _onlineSub;
|
||||
bool _disposed = false;
|
||||
bool _started = false;
|
||||
bool _knownOffline = false;
|
||||
Timer? _retryTimer;
|
||||
int _retryIndex = 0;
|
||||
|
||||
/// Emits the live session on each (re)connect, and null when it drops.
|
||||
Stream<SocialSession?> get sessions => _sessions.stream;
|
||||
|
|
@ -48,10 +65,14 @@ class SocialConnection {
|
|||
/// Begins watching connectivity (reconnect on regain, drop when offline) and
|
||||
/// attempts an initial connect. Idempotent-ish; call once at startup.
|
||||
void start() {
|
||||
_started = true;
|
||||
_onlineSub = (_online ?? _connectivityOnline()).listen((isOnline) {
|
||||
_knownOffline = !isOnline;
|
||||
if (!isOnline) {
|
||||
_cancelRetry();
|
||||
_drop();
|
||||
} else if (_current == null) {
|
||||
_retryIndex = 0; // fresh network — start the backoff over
|
||||
unawaited(session());
|
||||
}
|
||||
});
|
||||
|
|
@ -75,15 +96,40 @@ class SocialConnection {
|
|||
return null;
|
||||
}
|
||||
_current = s;
|
||||
_retryIndex = 0;
|
||||
_cancelRetry();
|
||||
_sessions.add(s);
|
||||
return s;
|
||||
} catch (_) {
|
||||
return null; // unreachable — a later connectivity change retries
|
||||
_scheduleRetry(); // unreachable — retry with backoff (see below)
|
||||
return null;
|
||||
} finally {
|
||||
_pending = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// After a failed attempt, retries by itself while started and not knowingly
|
||||
/// offline — a connectivity change may never come if the device was online
|
||||
/// all along. Un-started connections (one-shot [session] callers, tests)
|
||||
/// never leave a timer behind.
|
||||
void _scheduleRetry() {
|
||||
if (!_started || _disposed || _knownOffline || _current != null) return;
|
||||
_cancelRetry();
|
||||
final i = _retryIndex < _retrySchedule.length
|
||||
? _retryIndex
|
||||
: _retrySchedule.length - 1;
|
||||
_retryIndex++;
|
||||
_retryTimer = Timer(_retrySchedule[i], () {
|
||||
if (_disposed || _current != null) return;
|
||||
unawaited(session());
|
||||
});
|
||||
}
|
||||
|
||||
void _cancelRetry() {
|
||||
_retryTimer?.cancel();
|
||||
_retryTimer = null;
|
||||
}
|
||||
|
||||
void _drop() {
|
||||
final s = _current;
|
||||
_current = null;
|
||||
|
|
@ -95,6 +141,7 @@ class SocialConnection {
|
|||
|
||||
Future<void> dispose() async {
|
||||
_disposed = true;
|
||||
_cancelRetry();
|
||||
await _onlineSub?.cancel();
|
||||
_onlineSub = null;
|
||||
_drop();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue