👔 No longer rapid websocket reconnect (stops after 5 times in a minute)
This commit is contained in:
@@ -52,6 +52,11 @@ class WebSocketService {
|
||||
DateTime? _heartbeatAt;
|
||||
Duration? heartbeatDelay;
|
||||
|
||||
// Reconnection tracking
|
||||
int _reconnectCount = 0;
|
||||
DateTime? _reconnectWindowStart;
|
||||
static const int _maxReconnectsPerMinute = 5;
|
||||
|
||||
Stream<WebSocketPacket> get dataStream => _streamController.stream;
|
||||
Stream<WebSocketState> get statusStream => _statusStreamController.stream;
|
||||
|
||||
@@ -79,8 +84,9 @@ class WebSocketService {
|
||||
_scheduleHeartbeat();
|
||||
_channel!.stream.listen(
|
||||
(data) {
|
||||
final dataStr =
|
||||
data is Uint8List ? utf8.decode(data) : data.toString();
|
||||
final dataStr = data is Uint8List
|
||||
? utf8.decode(data)
|
||||
: data.toString();
|
||||
final packet = WebSocketPacket.fromJson(jsonDecode(dataStr));
|
||||
if (packet.type == 'error.dupe') {
|
||||
_statusStreamController.sink.add(WebSocketState.duplicateDevice());
|
||||
@@ -123,6 +129,34 @@ class WebSocketService {
|
||||
}
|
||||
|
||||
void _scheduleReconnect() {
|
||||
// Check if we've exceeded the reconnect limit
|
||||
final now = DateTime.now();
|
||||
if (_reconnectWindowStart == null ||
|
||||
now.difference(_reconnectWindowStart!).inMinutes >= 1) {
|
||||
// Reset window if it's been more than 1 minute since the window started
|
||||
_reconnectWindowStart = now;
|
||||
_reconnectCount = 0;
|
||||
}
|
||||
|
||||
_reconnectCount++;
|
||||
|
||||
if (_reconnectCount > _maxReconnectsPerMinute) {
|
||||
talker.error(
|
||||
'[WebSocket] Reconnect limit exceeded: $_maxReconnectsPerMinute reconnections in the last minute. Stopping auto-reconnect.',
|
||||
);
|
||||
_statusStreamController.sink.add(WebSocketState.serverDown());
|
||||
return;
|
||||
}
|
||||
|
||||
_reconnectTimer?.cancel();
|
||||
_reconnectTimer = Timer(const Duration(milliseconds: 500), () {
|
||||
_statusStreamController.sink.add(WebSocketState.connecting());
|
||||
connect(_ref);
|
||||
});
|
||||
}
|
||||
|
||||
void manualReconnect() {
|
||||
talker.info('[WebSocket] Manual reconnect triggered by user');
|
||||
_reconnectTimer?.cancel();
|
||||
_reconnectTimer = Timer(const Duration(milliseconds: 500), () {
|
||||
_statusStreamController.sink.add(WebSocketState.connecting());
|
||||
@@ -204,4 +238,9 @@ class WebSocketStateNotifier extends Notifier<WebSocketState> {
|
||||
_reconnectTimer?.cancel();
|
||||
state = const WebSocketState.disconnected();
|
||||
}
|
||||
|
||||
void manualReconnect() {
|
||||
final service = ref.read(websocketProvider);
|
||||
service.manualReconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -530,6 +530,7 @@ class _WebSocketIndicator extends HookConsumerWidget {
|
||||
|
||||
Color indicatorColor;
|
||||
String indicatorText;
|
||||
bool isInteractive = false;
|
||||
|
||||
if (websocketState == WebSocketState.connected()) {
|
||||
indicatorColor = Colors.green;
|
||||
@@ -537,12 +538,16 @@ class _WebSocketIndicator extends HookConsumerWidget {
|
||||
} else if (websocketState == WebSocketState.connecting()) {
|
||||
indicatorColor = Colors.teal;
|
||||
indicatorText = 'connectionReconnecting';
|
||||
} else if (websocketState == WebSocketState.serverDown()) {
|
||||
indicatorColor = Colors.red;
|
||||
indicatorText = 'connectionServerDown';
|
||||
isInteractive = true;
|
||||
} else {
|
||||
indicatorColor = Colors.red;
|
||||
indicatorText = 'connectionDisconnected';
|
||||
}
|
||||
|
||||
return AnimatedPositioned(
|
||||
final widget = AnimatedPositioned(
|
||||
duration: Duration(milliseconds: 1850),
|
||||
top:
|
||||
user.value == null ||
|
||||
@@ -554,15 +559,20 @@ class _WebSocketIndicator extends HookConsumerWidget {
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: indicatorHeight,
|
||||
child: IgnorePointer(
|
||||
child: Material(
|
||||
elevation:
|
||||
user.value == null || websocketState == WebSocketState.connected()
|
||||
? 0
|
||||
: 4,
|
||||
child: AnimatedContainer(
|
||||
duration: Duration(milliseconds: 300),
|
||||
color: indicatorColor,
|
||||
child: Material(
|
||||
elevation:
|
||||
user.value == null || websocketState == WebSocketState.connected()
|
||||
? 0
|
||||
: 4,
|
||||
child: AnimatedContainer(
|
||||
duration: Duration(milliseconds: 300),
|
||||
color: indicatorColor,
|
||||
child: InkWell(
|
||||
onTap: isInteractive
|
||||
? () {
|
||||
ref.read(websocketStateProvider.notifier).manualReconnect();
|
||||
}
|
||||
: null,
|
||||
child: Center(
|
||||
child: Text(
|
||||
indicatorText,
|
||||
@@ -573,5 +583,7 @@ class _WebSocketIndicator extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return isInteractive ? widget : IgnorePointer(child: widget);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user