import 'dart:async'; import 'package:electrum_adapter/electrum_adapter.dart'; import '../wallets/crypto_currency/crypto_currency.dart'; class ClientManager { ClientManager._(); static final ClientManager sharedInstance = ClientManager._(); final Map<String, ElectrumClient> _map = {}; final Map<String, int> _heights = {}; final Map<String, StreamSubscription<BlockHeader>> _subscriptions = {}; final Map<String, Completer<int>> _heightCompleters = {}; String _keyHelper(CryptoCurrency cryptoCurrency) { return "${cryptoCurrency.runtimeType}_${cryptoCurrency.network.name}"; } final Finalizer<ClientManager> _finalizer = Finalizer((manager) async { await manager._kill(); }); ElectrumClient? getClient({ required CryptoCurrency cryptoCurrency, }) => _map[_keyHelper(cryptoCurrency)]; void addClient( ElectrumClient client, { required CryptoCurrency cryptoCurrency, }) { final key = _keyHelper(cryptoCurrency); if (_map[key] != null) { throw Exception("ElectrumX Client for $key already exists."); } else { _map[key] = client; } _heightCompleters[key] = Completer<int>(); _subscriptions[key] = client.subscribeHeaders().listen((event) { _heights[key] = event.height; if (!_heightCompleters[key]!.isCompleted) { _heightCompleters[key]!.complete(event.height); } }); } Future<int> getChainHeightFor(CryptoCurrency cryptoCurrency) async { final key = _keyHelper(cryptoCurrency); if (_map[key] == null) { throw Exception( "No managed ElectrumClient for $key found.", ); } if (_heightCompleters[key] == null) { throw Exception( "No managed _heightCompleters for $key found.", ); } return _heights[key] ?? await _heightCompleters[key]!.future; } Future<ElectrumClient?> remove({ required CryptoCurrency cryptoCurrency, }) async { final key = _keyHelper(cryptoCurrency); await _subscriptions[key]?.cancel(); _subscriptions.remove(key); _heights.remove(key); _heightCompleters.remove(key); return _map.remove(key); } Future<void> closeAll() async { await _kill(); _finalizer.detach(this); } Future<void> _kill() async { for (final sub in _subscriptions.values) { await sub.cancel(); } for (final client in _map.values) { await client.close(); } _heightCompleters.clear(); _heights.clear(); _subscriptions.clear(); _map.clear(); } }