From 3e19d6917ff245863be43a39a76daa7010ecda6f Mon Sep 17 00:00:00 2001
From: Matthias Nott <mnott@mnsoft.org>
Date: Wed, 01 Apr 2026 18:35:25 +0200
Subject: [PATCH] feat: auto-reconnect on network change (WiFi/cellular/VPN switch)

---
 lib/services/mqtt_service.dart |   35 +++++++++++++++++++++++++++++++++++
 1 files changed, 35 insertions(+), 0 deletions(-)

diff --git a/lib/services/mqtt_service.dart b/lib/services/mqtt_service.dart
index e50adcf..841182d 100644
--- a/lib/services/mqtt_service.dart
+++ b/lib/services/mqtt_service.dart
@@ -5,6 +5,7 @@
 import 'package:crypto/crypto.dart';
 
 import 'package:bonsoir/bonsoir.dart';
+import 'package:connectivity_plus/connectivity_plus.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/widgets.dart';
 import 'package:path_provider/path_provider.dart' as pp;
@@ -50,6 +51,8 @@
   bool _intentionalClose = false;
   String? _clientId;
   String? _lastDiscoveredHost;
+  StreamSubscription? _connectivitySub;
+  List<ConnectivityResult>? _lastConnectivity;
   StreamSubscription? _updatesSub;
 
   // Message deduplication
@@ -103,6 +106,36 @@
 
     _intentionalClose = false;
     _setStatus(ConnectionStatus.connecting);
+
+    // Start listening for network changes (WiFi↔cellular, VPN connect/disconnect)
+    _connectivitySub ??= Connectivity().onConnectivityChanged.listen((results) {
+      if (_lastConnectivity != null && !_intentionalClose) {
+        final changed = results.length != _lastConnectivity!.length ||
+            !results.every((r) => _lastConnectivity!.contains(r));
+        if (changed) {
+          _mqttLog('MQTT: network changed: ${results.map((r) => r.name).join(",")} — forcing reconnect');
+          // Force disconnect and reconnect on new network
+          final client = _client;
+          if (client != null) {
+            _intentionalClose = true;
+            client.autoReconnect = false;
+            try { client.disconnect(); } catch (_) {}
+            _client = null;
+            _updatesSub?.cancel();
+            _updatesSub = null;
+            _intentionalClose = false;
+          }
+          _lastDiscoveredHost = null; // Clear cached discovery — subnet may have changed
+          connectedHost = null;
+          connectedVia = null;
+          _setStatus(ConnectionStatus.reconnecting);
+          Future.delayed(const Duration(milliseconds: 500), () {
+            if (!_intentionalClose) connect();
+          });
+        }
+      }
+      _lastConnectivity = results;
+    });
 
     // Load trusted cert fingerprint for TOFU verification
     if (_trustedFingerprint == null) await _loadTrustedFingerprint();
@@ -724,6 +757,8 @@
     _intentionalClose = true;
     _updatesSub?.cancel();
     _updatesSub = null;
+    _connectivitySub?.cancel();
+    _connectivitySub = null;
 
     try {
       _client?.disconnect();

--
Gitblit v1.3.1