From aa608be1e4c1e182111f53f3a868b11f2f4b9c7a Mon Sep 17 00:00:00 2001 From: otsmr Date: Tue, 21 Jan 2025 08:41:27 +0100 Subject: [PATCH] websocket automatic reconnection --- devtools_options.yaml | 3 ++ lib/src/providers/api_provider.dart | 52 ++++++++++++++++++++------ lib/src/utils.dart | 5 ++- lib/src/views/camera_preview_view.dart | 8 ++-- lib/src/views/profile_view.dart | 7 +++- 5 files changed, 57 insertions(+), 18 deletions(-) create mode 100644 devtools_options.yaml diff --git a/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 0000000..fa0b357 --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/lib/src/providers/api_provider.dart b/lib/src/providers/api_provider.dart index dcb39a7..17cd447 100644 --- a/lib/src/providers/api_provider.dart +++ b/lib/src/providers/api_provider.dart @@ -12,6 +12,7 @@ import 'package:twonly/src/proto/api/error.pb.dart'; import 'package:twonly/src/proto/api/server_to_client.pb.dart' as server; import 'package:twonly/src/signal/signal_helper.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:web_socket_channel/io.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; class Result { @@ -31,19 +32,19 @@ class ApiProvider { final String apiUrl; final String? backupApiUrl; final log = Logger("connect::ApiProvider"); - final HashMap> _callbacks = HashMap(); + Function(bool)? _connectionStateCallback; final HashMap messagesV0 = HashMap(); - WebSocketChannel? _channel; + IOWebSocketChannel? _channel; Future _connectTo(String apiUrl) async { try { - var channel = WebSocketChannel.connect( + var channel = IOWebSocketChannel.connect( Uri.parse(apiUrl), ); _channel = channel; - _channel!.stream.listen(_onData); + _channel!.stream.listen(_onData, onDone: _onDone, onError: _onError); await _channel!.ready; log.info("Websocket is connected!"); print("Websocket is connected!"); @@ -56,9 +57,14 @@ class ApiProvider { Future connect(Function(bool)? callBack) async { print("Trying to connect to the backend $apiUrl!"); + if (callBack != null) { + _connectionStateCallback = callBack; + } if (_channel != null && _channel!.closeCode != null) { + print("is connected"); return true; } + log.info("Trying to connect to the backend $apiUrl!"); if (await _connectTo(apiUrl)) { if (callBack != null) callBack(true); @@ -76,6 +82,37 @@ class ApiProvider { bool get isConnected => _channel != null && _channel!.closeCode != null; + void _onDone() { + if (_connectionStateCallback != null) { + _connectionStateCallback!(false); + } + _channel = null; + tryToReconnect(5); + } + + void _onError(dynamic e) { + if (_connectionStateCallback != null) { + _connectionStateCallback!(false); + } + _channel = null; + tryToReconnect(5); + } + + void tryToReconnect(int delay) { + Future.delayed(Duration(seconds: delay)).then( + (value) async { + if (!await connect(_connectionStateCallback)) { + if (delay > 60 * 5) { + delay = 60 * 5; + } else { + delay = delay * 2; + } + tryToReconnect(delay); + } + }, + ); + } + void _onData(dynamic msgBuffer) { try { final msg = server.ServerToClient.fromBuffer(msgBuffer); @@ -89,13 +126,6 @@ class ApiProvider { } } - void addNotifier(String messageType, Function callBackFunction) { - if (!_callbacks.containsKey(messageType)) { - _callbacks[messageType] = []; - } - _callbacks[messageType]!.add(callBackFunction); - } - // TODO: There must be a smarter move to do that :/ Future _waitForResponse(Int64 seq) async { final startTime = DateTime.now(); diff --git a/lib/src/utils.dart b/lib/src/utils.dart index f7af799..3052389 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -25,14 +25,17 @@ Future isUserCreated() async { Future getUser() async { final storage = getSecureStorage(); String? userJson = await storage.read(key: "user_data"); + print(userJson); if (userJson == null) { return null; } try { final userMap = jsonDecode(userJson) as Map; final user = UserData.fromJson(userMap); + print(user); return user; - } catch (_) { + } catch (e) { + print(e); return null; } } diff --git a/lib/src/views/camera_preview_view.dart b/lib/src/views/camera_preview_view.dart index 900f711..ef96c8b 100644 --- a/lib/src/views/camera_preview_view.dart +++ b/lib/src/views/camera_preview_view.dart @@ -43,10 +43,10 @@ class CameraPreviewViewState extends State { final size = Size(50, 300); - _controller.initialize().then((_) { - _controller.value = _controller.value.copyWith(previewSize: size); - setState(() {}); - }); + // _controller.initialize().then((_) { + // _controller.value = _controller.value.copyWith(previewSize: size); + // setState(() {}); + // }); // Next, initialize the controller. This returns a Future. _initializeControllerFuture = _controller.initialize(); diff --git a/lib/src/views/profile_view.dart b/lib/src/views/profile_view.dart index 2892f69..46c6277 100644 --- a/lib/src/views/profile_view.dart +++ b/lib/src/views/profile_view.dart @@ -1,3 +1,5 @@ +import 'package:twonly/src/model/user_data_json.dart'; + import '../settings/settings_controller.dart'; import '../settings/settings_view.dart'; import '../utils.dart'; @@ -14,13 +16,14 @@ class ProfileView extends StatefulWidget { } class _ProfileViewState extends State { + final Future _userData = getUser(); + @override Widget build(BuildContext context) { - // var user = await getUser(); return Scaffold( appBar: AppBar( title: FutureBuilder( - future: getUser(), + future: _userData, builder: (context, snap) { if (snap.hasData) { return Text("Hello ${snap.data!.username}!");