feat(core): A.6.1 — use OS connectivity signals to drive reconnect
Extends the A.6 supervisor with an OS-level connectivity hint so a returning network triggers a redial immediately instead of waiting out the current backoff slot (up to 60 s). The watchdog remains the authoritative loss detector — the OS signal is advisory. * `chanora_core::NetworkState` (Unknown / Online / Offline) is owned by `ChanoraSession` via a `tokio::sync::watch::Sender`. `set_network_state()` / `network_state()` are the public accessors. * The supervisor's watch-phase `select!` gains a `network_rx` branch: Offline pre-charges watchdog misses (capped at `MAX_MISSES - 1`) so the next probe failure trips immediately; Online clears stale misses. This shrinks UI-banner latency on a Wi-Fi drop from ~15 s to ~5 s. * The reconnect-loop's backoff sleep races against Online: a transition cuts the sleep short and resets the attempt counter so future losses start at the smallest backoff window again. * `chanora_bridge` adds `BridgeNetworkState` (mirror enum) and a sync `set_network_state(state)` function. On platforms with no signal wired the supervisor stays at Unknown and falls back to pure watchdog/backoff — no behavioural regression vs A.6. * Flutter adds `connectivity_plus ^6.1.0` and wires `_wireConnectivity()` in `main()`: seeds with `checkConnectivity()` then forwards every `onConnectivityChanged` to the bridge, mapping any non-`none` transport to Online. Verified on Moto G Stylus 5G (Android 14): `svc wifi disable && svc data disable` for ~40 s — reconnect banner appeared promptly because the watchdog was pre-charged. After `svc wifi enable && svc data enable` the supervisor woke from its 15 s backoff slot and reconnected within seconds; the channel tree re-rendered without user action.
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'l10n/generated/app_localizations.dart';
|
||||
@@ -20,9 +21,42 @@ import 'src/rust/frb_generated.dart';
|
||||
Future<void> main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await RustLib.init();
|
||||
// Push the OS-reported connectivity state into the core supervisor.
|
||||
// The supervisor uses this to short-circuit reconnect backoff when
|
||||
// the network comes back, and to pre-charge the loss watchdog when
|
||||
// the OS already knows we're offline (A.6.1).
|
||||
unawaited(_wireConnectivity());
|
||||
runApp(const ChanoraApp());
|
||||
}
|
||||
|
||||
/// Map `connectivity_plus`' list-of-results to our coarse tri-state.
|
||||
/// We consider the device "Online" if any of the reported transports
|
||||
/// is non-`none`. This is intentionally permissive — the supervisor's
|
||||
/// watchdog still verifies reachability against the actual server.
|
||||
rust.BridgeNetworkState _mapConnectivity(List<ConnectivityResult> results) {
|
||||
if (results.isEmpty) return rust.BridgeNetworkState.unknown;
|
||||
final allNone = results.every((r) => r == ConnectivityResult.none);
|
||||
if (allNone) return rust.BridgeNetworkState.offline;
|
||||
return rust.BridgeNetworkState.online;
|
||||
}
|
||||
|
||||
Future<void> _wireConnectivity() async {
|
||||
final connectivity = Connectivity();
|
||||
// Seed with the current value so the supervisor has a real reading
|
||||
// before the first transition.
|
||||
try {
|
||||
final initial = await connectivity.checkConnectivity();
|
||||
rust.setNetworkState(state: _mapConnectivity(initial));
|
||||
} catch (_) {
|
||||
// Best effort; if the plugin isn't available on this platform
|
||||
// we stay at Unknown and the supervisor falls back to its
|
||||
// watchdog-only behaviour.
|
||||
}
|
||||
connectivity.onConnectivityChanged.listen((results) {
|
||||
rust.setNetworkState(state: _mapConnectivity(results));
|
||||
});
|
||||
}
|
||||
|
||||
class ChanoraApp extends StatelessWidget {
|
||||
const ChanoraApp({super.key});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user