import 'dart:async'; import 'dart:convert'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:logger/logger.dart'; import 'package:rxdart/rxdart.dart'; import 'package:seshat/config/constants.dart'; import 'package:seshat/domain/models/owner.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; /// API Client to manages connections to WebSockets class WebsocketClient { /// Storage to access JWT FlutterSecureStorage? _secureStorage; /// Raw channel of data from WebSocket WebSocketChannel? _channel; /// Global WebSocket Stream final BehaviorSubject _baseController = BehaviorSubject(); /// WebSocket Stream dedicated to [Owner] entries final BehaviorSubject _ownersController = BehaviorSubject( sync: true, ); /// Subscription to [_baseController] late final StreamSubscription sub; Logger log = Logger( printer: PrettyPrinter( colors: true, lineLength: 100, methodCount: 0, dateTimeFormat: DateTimeFormat.dateAndTime, ), ); /// Gets a stream of [Owner] Stream get owners => _ownersController.stream; /// Initializes connection to the [_secureStorage] Future _initStore() async { _secureStorage ??= const FlutterSecureStorage(); } /// Connects to the websocket Future connect() async { final url = "wss://$apiBasePath/ws"; log.i("Webocket: $url"); await _initStore(); if (_channel != null) return; _channel = WebSocketChannel.connect(Uri.parse(url)); await _channel!.ready; var token = await _secureStorage!.read(key: "token"); _channel!.sink.add(jsonEncode({"token": "$token"})); _channel!.stream.listen((message) { _baseController.add(message); }); var data = await _baseController.stream.first; var result = jsonDecode(data); if (result["type"] == "auth_success") { sub = _baseController.stream.listen( (message) { final Map data = jsonDecode(message); switch (data["type"]) { case "new_owner": final owner = Owner.fromJSON(data["data"]); _ownersController.add(owner); break; default: } }, onDone: _handleDisconnect, onError: (error) { _handleDisconnect(); }, ); } } /// Disconnects from the websocket void _handleDisconnect() { sub.cancel(); _channel = null; } /// Closes all connections void dispose() { sub.cancel(); _channel?.sink.close(); _ownersController.close(); } }