From 7343ba4b1feaa997fd80bd426d88e415a5cd9841 Mon Sep 17 00:00:00 2001 From: Morteza Date: Tue, 7 Jan 2025 21:39:49 +0530 Subject: [PATCH] refactor: Major codebase cleanup (#415) --- devtools_options.yaml | 4 + lib/_model/wallet.dart | 5 + lib/_pkg/boltz/swap.dart | 6 +- lib/_pkg/deep_link.dart | 13 +- lib/_pkg/migrations/migration0_1to0_2.dart | 8 +- lib/_pkg/migrations/migration0_2to0_3.dart | 2 +- lib/_pkg/payjoin/manager.dart | 13 +- lib/_pkg/storage/storage.dart | 1 + lib/_pkg/wallet/address.dart | 6 +- lib/_pkg/wallet/balance.dart | 6 +- lib/_pkg/wallet/bdk/create.dart | 6 +- lib/_pkg/wallet/bdk/sensitive_create.dart | 6 +- lib/_pkg/wallet/create.dart | 8 +- lib/_pkg/wallet/lwk/transaction.dart | 6 +- lib/_pkg/wallet/network.dart | 6 +- lib/_pkg/wallet/repository/network.dart | 43 -- lib/_pkg/wallet/sync.dart | 14 +- lib/_pkg/wallet/transaction.dart | 14 +- lib/_repository/app_wallets_repository.dart | 214 +++++++ lib/_repository/currency_repository.dart | 0 lib/_repository/network_repository.dart | 298 +++++++++ lib/_repository/wallet/internal_network.dart | 56 ++ .../wallet/internal_wallets.dart} | 2 +- .../wallet/sensitive_wallet_storage.dart} | 0 .../wallet/wallet_storage.dart} | 0 lib/_repository/wallet_service.dart | 391 ++++++++++++ lib/_ui/app_bar.dart | 3 +- lib/_ui/bottom_wallet_actions.dart | 22 +- .../molecules/address/address_display.dart | 66 -- lib/_ui/molecules/address/address_input.dart | 58 -- lib/_ui/molecules/fee_rate/fee_rate.dart | 68 --- .../fee_rate/fee_rate_picker_widget.dart | 173 ------ lib/address/bloc/address_cubit.dart | 99 ++- lib/address/pop_up.dart | 12 +- lib/auth/page.dart | 5 +- lib/create/bloc/create_cubit.dart | 99 +-- lib/create/page.dart | 18 +- lib/currency/bloc/currency_cubit.dart | 19 +- lib/currency/conversion.dart | 18 +- lib/home/bloc/home_bloc.dart | 107 ++++ lib/home/bloc/home_cubit.dart | 207 ------- lib/home/bloc/home_event.dart | 30 + lib/home/bloc/home_state.dart | 299 +++++---- lib/home/deep_linking.dart | 13 +- lib/home/home_page.dart | 234 +++---- lib/home/listeners.dart | 184 +++--- lib/home/transactions.dart | 64 +- lib/import/bloc/import_cubit.dart | 24 +- .../hardware_import_cubit.dart | 8 +- lib/import/hardware_page.dart | 6 +- lib/import/listeners.dart | 5 +- lib/import/page.dart | 10 +- lib/import/wallet_type_selection.dart | 50 +- lib/locator.dart | 90 +-- lib/main.dart | 109 ++-- lib/network/bloc/event.dart | 86 +++ lib/network/bloc/network_bloc.dart | 439 +++++++++++++ lib/network/bloc/network_cubit.dart | 458 -------------- lib/network/bloc/state.dart | 78 +-- lib/network/listeners.dart | 4 +- lib/network/popup.dart | 114 ++-- lib/network_fees/bloc/networkfees_cubit.dart | 26 +- lib/network_fees/popup.dart | 5 +- lib/receive/bloc/receive_cubit.dart | 73 +-- lib/receive/bloc/state.dart | 35 +- lib/receive/listeners.dart | 30 +- lib/receive/receive_page.dart | 260 +++++--- lib/routes.dart | 195 +++--- lib/send/advanced.dart | 9 +- lib/send/bloc/send_cubit.dart | 578 +++++++----------- lib/send/bloc/send_state.dart | 97 ++- lib/send/listeners.dart | 28 +- lib/send/psbt.dart | 4 +- lib/send/send_page.dart | 253 +++++--- lib/settings/bitcoin_settings_page.dart | 22 +- lib/settings/bloc/broadcasttx_cubit.dart | 53 +- lib/settings/broadcast.dart | 20 +- lib/settings/core_wallet_settings_page.dart | 68 ++- lib/swap/create_swap_bloc/swap_cubit.dart | 64 +- lib/swap/listeners.dart | 4 +- lib/swap/receive.dart | 31 +- lib/swap/send.dart | 15 +- lib/swap/swap_confirmation.dart | 19 +- .../swap_history_bloc/swap_history_cubit.dart | 99 +-- lib/swap/swap_history_page.dart | 8 +- lib/swap/swap_page.dart | 69 ++- lib/swap/swap_page_progress.dart | 4 +- lib/swap/swap_page_progress_page.dart | 12 +- .../ui_swapwidget}/bb_form_field.dart | 0 .../ui_swapwidget}/currency_input_widget.dart | 2 +- .../ui_swapwidget}/swap_widget.dart | 4 +- .../ui_swapwidget}/wallet_card.dart | 0 .../ui_swapwidget}/wallet_dropdown.dart | 2 +- lib/swap/watcher_bloc/watchtxs_bloc.dart | 251 +++----- lib/testground.dart | 66 +- lib/transaction/bloc/transaction_cubit.dart | 193 ++---- lib/transaction/bump_fees.dart | 60 +- lib/transaction/transaction_page.dart | 171 ++---- lib/wallet/bloc/event.dart | 70 ++- lib/wallet/bloc/state.dart | 43 +- lib/wallet/bloc/wallet_bloc.dart | 452 ++------------ lib/wallet/details.dart | 22 +- lib/wallet/wallet_card.dart | 24 +- lib/wallet/wallet_page.dart | 46 +- lib/wallet/wallet_txs.dart | 33 +- lib/wallet_settings/accounting.dart | 59 +- lib/wallet_settings/addresses.dart | 2 +- lib/wallet_settings/backup.dart | 40 +- lib/wallet_settings/bloc/state.dart | 3 +- .../bloc/wallet_settings_cubit.dart | 223 +++---- lib/wallet_settings/descriptors.dart | 4 +- lib/wallet_settings/listeners.dart | 48 +- lib/wallet_settings/test_backup.dart | 30 +- lib/wallet_settings/wallet_settings_page.dart | 102 ++-- pubspec.lock | 16 + pubspec.yaml | 2 + 116 files changed, 4278 insertions(+), 4178 deletions(-) create mode 100644 devtools_options.yaml delete mode 100644 lib/_pkg/wallet/repository/network.dart create mode 100644 lib/_repository/app_wallets_repository.dart create mode 100644 lib/_repository/currency_repository.dart create mode 100644 lib/_repository/network_repository.dart create mode 100644 lib/_repository/wallet/internal_network.dart rename lib/{_pkg/wallet/repository/wallets.dart => _repository/wallet/internal_wallets.dart} (98%) rename lib/{_pkg/wallet/repository/sensitive_storage.dart => _repository/wallet/sensitive_wallet_storage.dart} (100%) rename lib/{_pkg/wallet/repository/storage.dart => _repository/wallet/wallet_storage.dart} (100%) create mode 100644 lib/_repository/wallet_service.dart delete mode 100644 lib/_ui/molecules/address/address_display.dart delete mode 100644 lib/_ui/molecules/address/address_input.dart delete mode 100644 lib/_ui/molecules/fee_rate/fee_rate.dart delete mode 100644 lib/_ui/molecules/fee_rate/fee_rate_picker_widget.dart create mode 100644 lib/home/bloc/home_bloc.dart delete mode 100644 lib/home/bloc/home_cubit.dart create mode 100644 lib/home/bloc/home_event.dart create mode 100644 lib/network/bloc/event.dart create mode 100644 lib/network/bloc/network_bloc.dart delete mode 100644 lib/network/bloc/network_cubit.dart rename lib/{_ui/atoms => swap/ui_swapwidget}/bb_form_field.dart (100%) rename lib/{_ui/molecules => swap/ui_swapwidget}/currency_input_widget.dart (99%) rename lib/{_ui/organisms => swap/ui_swapwidget}/swap_widget.dart (98%) rename lib/{_ui/molecules/wallet => swap/ui_swapwidget}/wallet_card.dart (100%) rename lib/{_ui/molecules/wallet => swap/ui_swapwidget}/wallet_dropdown.dart (97%) diff --git a/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 00000000..2bc8e05f --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1,4 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: + - provider: true \ No newline at end of file diff --git a/lib/_model/wallet.dart b/lib/_model/wallet.dart index 5f89aee4..a9a2537a 100644 --- a/lib/_model/wallet.dart +++ b/lib/_model/wallet.dart @@ -63,6 +63,7 @@ class Wallet with _$Wallet { @Default(0) int subKeyIndex, // List? labelTags, // List? bip329Labels, + Address? firstAddress, }) = _Wallet; const Wallet._(); @@ -515,6 +516,10 @@ class Wallet with _$Wallet { List swapsToProcess() { return swaps.where((swap) => swap.proceesTx() && !swap.failed()).toList(); } + + int balanceSats() => balance ?? 0; + + String balanceStr() => ((balance ?? 0) / 100000000).toStringAsFixed(8); } @freezed diff --git a/lib/_pkg/boltz/swap.dart b/lib/_pkg/boltz/swap.dart index cdffb87d..7d1983bf 100644 --- a/lib/_pkg/boltz/swap.dart +++ b/lib/_pkg/boltz/swap.dart @@ -8,7 +8,7 @@ import 'package:bb_mobile/_pkg/consts/configs.dart'; import 'package:bb_mobile/_pkg/error.dart'; import 'package:bb_mobile/_pkg/storage/secure_storage.dart'; import 'package:bb_mobile/_pkg/storage/storage.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/network.dart'; +import 'package:bb_mobile/_repository/wallet/internal_network.dart'; import 'package:boltz_dart/boltz_dart.dart'; import 'package:dio/dio.dart'; @@ -16,14 +16,14 @@ class SwapBoltz { SwapBoltz({ required SecureStorage secureStorage, required Dio dio, - required NetworkRepository networkRepository, + required InternalNetworkRepository networkRepository, }) : _secureStorage = secureStorage, _networkRepository = networkRepository, _dio = dio; final SecureStorage _secureStorage; final Dio _dio; - final NetworkRepository _networkRepository; + final InternalNetworkRepository _networkRepository; Future<(Invoice?, Err?)> decodeInvoice({ required String invoice, diff --git a/lib/_pkg/deep_link.dart b/lib/_pkg/deep_link.dart index e9d704c0..0fa0249c 100644 --- a/lib/_pkg/deep_link.dart +++ b/lib/_pkg/deep_link.dart @@ -3,8 +3,9 @@ import 'dart:async'; import 'package:bb_mobile/_model/wallet.dart'; import 'package:bb_mobile/_pkg/error.dart'; import 'package:bb_mobile/_pkg/wallet/bip21.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/home/bloc/home_bloc.dart'; +import 'package:bb_mobile/network/bloc/event.dart'; +import 'package:bb_mobile/network/bloc/network_bloc.dart'; import 'package:flutter/material.dart'; // import 'package:uni_links/uni_links.dart'; @@ -39,8 +40,8 @@ class DeepLink { Future handleUri({ required String link, // required SettingsCubit settingsCubit, - required HomeCubit homeCubit, - required NetworkCubit networkCubit, + required HomeBloc homeCubit, + required NetworkBloc networkCubit, required BuildContext context, }) async { try { @@ -50,8 +51,8 @@ class DeepLink { final address = bip21Obj.address; final isTestnet = isTestnetAddress(address); if (isTestnet == null) return Err('Invalid address'); - final currentIsTestnet = networkCubit.state.testnet; - if (currentIsTestnet != isTestnet) networkCubit.toggleTestnet(); + final currentIsTestnet = networkCubit.state.networkData.testnet; + if (currentIsTestnet != isTestnet) networkCubit.add(ToggleTestnet()); await Future.delayed(const Duration(milliseconds: 200)); final wallet = homeCubit.state.getFirstWithSpendableAndBalance( isTestnet ? BBNetwork.Testnet : BBNetwork.Mainnet, diff --git a/lib/_pkg/migrations/migration0_1to0_2.dart b/lib/_pkg/migrations/migration0_1to0_2.dart index 2645d8bc..eb902703 100644 --- a/lib/_pkg/migrations/migration0_1to0_2.dart +++ b/lib/_pkg/migrations/migration0_1to0_2.dart @@ -15,9 +15,9 @@ import 'package:bb_mobile/_pkg/wallet/bdk/sensitive_create.dart'; import 'package:bb_mobile/_pkg/wallet/create.dart'; import 'package:bb_mobile/_pkg/wallet/lwk/create.dart'; import 'package:bb_mobile/_pkg/wallet/lwk/sensitive_create.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/sensitive_storage.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/storage.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/wallets.dart'; +import 'package:bb_mobile/_repository/wallet/internal_wallets.dart'; +import 'package:bb_mobile/_repository/wallet/sensitive_wallet_storage.dart'; +import 'package:bb_mobile/_repository/wallet/wallet_storage.dart'; // int mainWalletIndex = 0; // int testWalletIndex = 0; @@ -331,7 +331,7 @@ Future> createLiquidWallet( Seed? liquidTestnetSeed, HiveStorage hiveStorage, ) async { - final WalletsRepository walletRep = WalletsRepository(); + final InternalWalletsRepository walletRep = InternalWalletsRepository(); final BDKCreate bdkCreate = BDKCreate(walletsRepository: walletRep); final BDKSensitiveCreate bdkSensitiveCreate = BDKSensitiveCreate(walletsRepository: walletRep, bdkCreate: bdkCreate); diff --git a/lib/_pkg/migrations/migration0_2to0_3.dart b/lib/_pkg/migrations/migration0_2to0_3.dart index a7b98776..e3e1d357 100644 --- a/lib/_pkg/migrations/migration0_2to0_3.dart +++ b/lib/_pkg/migrations/migration0_2to0_3.dart @@ -5,7 +5,7 @@ import 'package:bb_mobile/_model/wallet.dart'; import 'package:bb_mobile/_pkg/storage/hive.dart'; import 'package:bb_mobile/_pkg/storage/secure_storage.dart'; import 'package:bb_mobile/_pkg/storage/storage.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/sensitive_storage.dart'; +import 'package:bb_mobile/_repository/wallet/sensitive_wallet_storage.dart'; import 'package:boltz_dart/boltz_dart.dart'; Future doMigration0_2to0_3( diff --git a/lib/_pkg/payjoin/manager.dart b/lib/_pkg/payjoin/manager.dart index 6e755dc9..0cb3581a 100644 --- a/lib/_pkg/payjoin/manager.dart +++ b/lib/_pkg/payjoin/manager.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'dart:isolate'; import 'dart:math'; -import 'dart:typed_data'; import 'package:bb_mobile/_model/wallet.dart'; import 'package:bb_mobile/_pkg/error.dart'; @@ -218,7 +217,7 @@ class PayjoinManager { SendPort? mainToIsolateSendPort; receivePort.listen((message) async { - print('Receiver isolate: $message'); + // print('Receiver isolate: $message'); if (message is Map) { try { switch (message['type']) { @@ -325,10 +324,12 @@ class PayjoinManager { if (senderErr != null) throw senderErr; final filteredReceivers = receiverSessions - .where((session) => - session.walletId == wallet.id && - session.status != PayjoinSessionStatus.success && - session.status != PayjoinSessionStatus.unrecoverable) + .where( + (session) => + session.walletId == wallet.id && + session.status != PayjoinSessionStatus.success && + session.status != PayjoinSessionStatus.unrecoverable, + ) .toList(); final filteredSenders = senderSessions.where((session) { return session.walletId == wallet.id && diff --git a/lib/_pkg/storage/storage.dart b/lib/_pkg/storage/storage.dart index 5e638ce6..845794de 100644 --- a/lib/_pkg/storage/storage.dart +++ b/lib/_pkg/storage/storage.dart @@ -13,6 +13,7 @@ class StorageKeys { static const wallets = 'wallets'; static const settings = 'settings'; static const network = 'network'; + static const networkReposity = 'networkReposity'; static const networkFees = 'networkFees'; static const currency = 'currency'; static const lighting = 'lighting'; diff --git a/lib/_pkg/wallet/address.dart b/lib/_pkg/wallet/address.dart index f06055f8..1391e391 100644 --- a/lib/_pkg/wallet/address.dart +++ b/lib/_pkg/wallet/address.dart @@ -4,18 +4,18 @@ import 'package:bb_mobile/_pkg/error.dart'; import 'package:bb_mobile/_pkg/wallet/_interface.dart'; import 'package:bb_mobile/_pkg/wallet/bdk/address.dart'; import 'package:bb_mobile/_pkg/wallet/lwk/address.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/wallets.dart'; +import 'package:bb_mobile/_repository/wallet/internal_wallets.dart'; class WalletAddress implements IWalletAddress { WalletAddress({ - required WalletsRepository walletsRepository, + required InternalWalletsRepository walletsRepository, required BDKAddress bdkAddress, required LWKAddress lwkAddress, }) : _walletsRepository = walletsRepository, _bdkAddress = bdkAddress, _lwkAddress = lwkAddress; - final WalletsRepository _walletsRepository; + final InternalWalletsRepository _walletsRepository; final BDKAddress _bdkAddress; final LWKAddress _lwkAddress; diff --git a/lib/_pkg/wallet/balance.dart b/lib/_pkg/wallet/balance.dart index 5c782338..9ba99e36 100644 --- a/lib/_pkg/wallet/balance.dart +++ b/lib/_pkg/wallet/balance.dart @@ -3,18 +3,18 @@ import 'package:bb_mobile/_pkg/error.dart'; import 'package:bb_mobile/_pkg/wallet/_interface.dart'; import 'package:bb_mobile/_pkg/wallet/bdk/balance.dart'; import 'package:bb_mobile/_pkg/wallet/lwk/balance.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/wallets.dart'; +import 'package:bb_mobile/_repository/wallet/internal_wallets.dart'; class WalletBalance implements IWalletBalance { WalletBalance({ - required WalletsRepository walletsRepository, + required InternalWalletsRepository walletsRepository, required BDKBalance bdkBalance, required LWKBalance lwkBalance, }) : _walletsRepository = walletsRepository, _bdkBalance = bdkBalance, _lwkBalance = lwkBalance; - final WalletsRepository _walletsRepository; + final InternalWalletsRepository _walletsRepository; final BDKBalance _bdkBalance; final LWKBalance _lwkBalance; diff --git a/lib/_pkg/wallet/bdk/create.dart b/lib/_pkg/wallet/bdk/create.dart index dd676e8c..48d7d86f 100644 --- a/lib/_pkg/wallet/bdk/create.dart +++ b/lib/_pkg/wallet/bdk/create.dart @@ -2,16 +2,16 @@ import 'package:bb_mobile/_model/address.dart'; import 'package:bb_mobile/_model/cold_card.dart'; import 'package:bb_mobile/_model/wallet.dart'; import 'package:bb_mobile/_pkg/error.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/wallets.dart'; import 'package:bb_mobile/_pkg/wallet/utils.dart'; +import 'package:bb_mobile/_repository/wallet/internal_wallets.dart'; import 'package:bdk_flutter/bdk_flutter.dart' as bdk; import 'package:path_provider/path_provider.dart'; class BDKCreate { - BDKCreate({required WalletsRepository walletsRepository}) + BDKCreate({required InternalWalletsRepository walletsRepository}) : _walletsRepository = walletsRepository; - final WalletsRepository _walletsRepository; + final InternalWalletsRepository _walletsRepository; Future<(bdk.Wallet?, Err?)> loadPublicBdkWallet( Wallet wallet, diff --git a/lib/_pkg/wallet/bdk/sensitive_create.dart b/lib/_pkg/wallet/bdk/sensitive_create.dart index 30e9fb7c..a79156df 100644 --- a/lib/_pkg/wallet/bdk/sensitive_create.dart +++ b/lib/_pkg/wallet/bdk/sensitive_create.dart @@ -4,19 +4,19 @@ import 'package:bb_mobile/_model/wallet.dart'; import 'package:bb_mobile/_pkg/error.dart'; import 'package:bb_mobile/_pkg/wallet/bdk/create.dart'; import 'package:bb_mobile/_pkg/wallet/create.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/wallets.dart'; import 'package:bb_mobile/_pkg/wallet/utils.dart'; +import 'package:bb_mobile/_repository/wallet/internal_wallets.dart'; import 'package:bdk_flutter/bdk_flutter.dart' as bdk; import 'package:path_provider/path_provider.dart'; class BDKSensitiveCreate { BDKSensitiveCreate({ - required WalletsRepository walletsRepository, + required InternalWalletsRepository walletsRepository, required BDKCreate bdkCreate, }) : _walletsRepository = walletsRepository, _bdkCreate = bdkCreate; - final WalletsRepository _walletsRepository; + final InternalWalletsRepository _walletsRepository; final BDKCreate _bdkCreate; Future<(List?, Err?)> createMnemonic() async { diff --git a/lib/_pkg/wallet/create.dart b/lib/_pkg/wallet/create.dart index 0e68633d..52fe8bcc 100644 --- a/lib/_pkg/wallet/create.dart +++ b/lib/_pkg/wallet/create.dart @@ -3,12 +3,12 @@ import 'package:bb_mobile/_pkg/error.dart'; import 'package:bb_mobile/_pkg/wallet/_interface.dart'; import 'package:bb_mobile/_pkg/wallet/bdk/create.dart'; import 'package:bb_mobile/_pkg/wallet/lwk/create.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/storage.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/wallets.dart'; +import 'package:bb_mobile/_repository/wallet/internal_wallets.dart'; +import 'package:bb_mobile/_repository/wallet/wallet_storage.dart'; class WalletCreate implements IWalletCreate { WalletCreate({ - required WalletsRepository walletsRepository, + required InternalWalletsRepository walletsRepository, required LWKCreate lwkCreate, required BDKCreate bdkCreate, required WalletsStorageRepository walletsStorageRepository, @@ -17,7 +17,7 @@ class WalletCreate implements IWalletCreate { _bdkCreate = bdkCreate, _walletsStorageRepository = walletsStorageRepository; - final WalletsRepository _walletsRepository; + final InternalWalletsRepository _walletsRepository; final WalletsStorageRepository _walletsStorageRepository; final LWKCreate _lwkCreate; diff --git a/lib/_pkg/wallet/lwk/transaction.dart b/lib/_pkg/wallet/lwk/transaction.dart index 30ef72cf..0783d08b 100644 --- a/lib/_pkg/wallet/lwk/transaction.dart +++ b/lib/_pkg/wallet/lwk/transaction.dart @@ -7,18 +7,18 @@ import 'package:bb_mobile/_model/transaction.dart'; import 'package:bb_mobile/_model/wallet.dart'; import 'package:bb_mobile/_pkg/boltz/swap.dart'; import 'package:bb_mobile/_pkg/error.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/network.dart'; +import 'package:bb_mobile/_repository/wallet/internal_network.dart'; import 'package:convert/convert.dart'; import 'package:lwk_dart/lwk_dart.dart' as lwk; class LWKTransactions { LWKTransactions({ - required NetworkRepository networkRepository, + required InternalNetworkRepository networkRepository, required SwapBoltz swapBoltz, }) : _networkRepository = networkRepository, _swapBoltz = swapBoltz; - final NetworkRepository _networkRepository; + final InternalNetworkRepository _networkRepository; final SwapBoltz _swapBoltz; Transaction addOutputAddresses(Address newAddress, Transaction tx) { diff --git a/lib/_pkg/wallet/network.dart b/lib/_pkg/wallet/network.dart index af645350..f9a14126 100644 --- a/lib/_pkg/wallet/network.dart +++ b/lib/_pkg/wallet/network.dart @@ -3,17 +3,17 @@ import 'dart:async'; import 'package:bb_mobile/_pkg/error.dart'; import 'package:bb_mobile/_pkg/wallet/_interface.dart'; import 'package:bb_mobile/_pkg/wallet/bdk/network.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/network.dart'; +import 'package:bb_mobile/_repository/wallet/internal_network.dart'; class WalletNetwork implements IWalletNetwork { WalletNetwork({ - required NetworkRepository networkRepository, + required InternalNetworkRepository networkRepository, required BDKNetwork bdkNetwork, // required Logger logger, }) : _networkRepository = networkRepository, _bdkNetwork = bdkNetwork; - final NetworkRepository _networkRepository; + final InternalNetworkRepository _networkRepository; final BDKNetwork _bdkNetwork; // final Logger _logger; diff --git a/lib/_pkg/wallet/repository/network.dart b/lib/_pkg/wallet/repository/network.dart deleted file mode 100644 index aed40a52..00000000 --- a/lib/_pkg/wallet/repository/network.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:bb_mobile/_pkg/error.dart'; -import 'package:bdk_flutter/bdk_flutter.dart' as bdk; - -class NetworkRepository { - bdk.Blockchain? _bdkBlockchain; - String? _bitcoinUrl; - String? _liquidUrl; - // bool _isTestnet = false; - - (bdk.Blockchain?, Err?) get bdkBlockchain => - _bdkBlockchain != null ? (_bdkBlockchain, null) : (null, Err('Network not setup')); - - (String?, Err?) get bitcoinUrl => - _bitcoinUrl != null ? (_bitcoinUrl, null) : (null, Err('Network not setup')); - - Err? setBitcoinUrl(String url) { - _bitcoinUrl = url; - return null; - } - - (String?, Err?) get liquidUrl => - _liquidUrl != null ? (_liquidUrl, null) : (null, Err('Network not setup')); - - Err? setLiquidUrl(String url) { - _liquidUrl = url; - return null; - } - - Err? setBdkBlockchain(bdk.Blockchain blockchain) { - _bdkBlockchain = blockchain; - return null; - } - - // Err? setTestnet(bool isTestnet) { - // _isTestnet = isTestnet; - // return null; - // } - - // bool get isTestnet => _isTestnet; - - Err? checkNetworks() => - (_bdkBlockchain == null || _liquidUrl == null) ? Err('Network not setup') : null; -} diff --git a/lib/_pkg/wallet/sync.dart b/lib/_pkg/wallet/sync.dart index 46a3ef82..1f1e5492 100644 --- a/lib/_pkg/wallet/sync.dart +++ b/lib/_pkg/wallet/sync.dart @@ -5,15 +5,15 @@ import 'package:bb_mobile/_pkg/error.dart'; import 'package:bb_mobile/_pkg/wallet/_interface.dart'; import 'package:bb_mobile/_pkg/wallet/bdk/sync.dart'; import 'package:bb_mobile/_pkg/wallet/lwk/sync.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/network.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/storage.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/wallets.dart'; +import 'package:bb_mobile/_repository/wallet/internal_network.dart'; +import 'package:bb_mobile/_repository/wallet/internal_wallets.dart'; +import 'package:bb_mobile/_repository/wallet/wallet_storage.dart'; import 'package:bb_mobile/locator.dart'; class WalletSync implements IWalletSync { WalletSync({ - required WalletsRepository walletsRepository, - required NetworkRepository networkRepository, + required InternalWalletsRepository walletsRepository, + required InternalNetworkRepository networkRepository, required BDKSync bdkSync, required LWKSync lwkSync, }) : _walletsRepository = walletsRepository, @@ -21,8 +21,8 @@ class WalletSync implements IWalletSync { _bdkSync = bdkSync, _lwkSync = lwkSync; - final WalletsRepository _walletsRepository; - final NetworkRepository _networkRepository; + final InternalWalletsRepository _walletsRepository; + final InternalNetworkRepository _networkRepository; final BDKSync _bdkSync; final LWKSync _lwkSync; diff --git a/lib/_pkg/wallet/transaction.dart b/lib/_pkg/wallet/transaction.dart index 8de103be..6b31cb53 100644 --- a/lib/_pkg/wallet/transaction.dart +++ b/lib/_pkg/wallet/transaction.dart @@ -13,19 +13,19 @@ import 'package:bb_mobile/_pkg/wallet/bdk/transaction.dart'; import 'package:bb_mobile/_pkg/wallet/bdk/utxo.dart'; import 'package:bb_mobile/_pkg/wallet/lwk/address.dart'; import 'package:bb_mobile/_pkg/wallet/lwk/transaction.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/network.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/sensitive_storage.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/wallets.dart'; import 'package:bb_mobile/_pkg/wallet/update.dart'; +import 'package:bb_mobile/_repository/wallet/internal_network.dart'; +import 'package:bb_mobile/_repository/wallet/internal_wallets.dart'; +import 'package:bb_mobile/_repository/wallet/sensitive_wallet_storage.dart'; import 'package:bdk_flutter/bdk_flutter.dart' as bdk; class WalletTx implements IWalletTransactions { WalletTx({ - required WalletsRepository walletsRepository, + required InternalWalletsRepository walletsRepository, required WalletSensitiveStorageRepository walletSensitiveStorageRepository, required WalletAddress walletAddress, required WalletUpdate walletUpdate, - required NetworkRepository networkRepository, + required InternalNetworkRepository networkRepository, required BDKTransactions bdkTransactions, required LWKTransactions lwkTransactions, required BDKAddress bdkAddress, @@ -44,8 +44,8 @@ class WalletTx implements IWalletTransactions { _bdkAddress = bdkAddress, _lwkAddress = lwkAddress; - final WalletsRepository _walletsRepository; - final NetworkRepository _networkRepository; + final InternalWalletsRepository _walletsRepository; + final InternalNetworkRepository _networkRepository; final WalletSensitiveStorageRepository _walletSensitiveStorageRepository; final WalletAddress _walletAddress; diff --git a/lib/_repository/app_wallets_repository.dart b/lib/_repository/app_wallets_repository.dart new file mode 100644 index 00000000..25cacb6a --- /dev/null +++ b/lib/_repository/app_wallets_repository.dart @@ -0,0 +1,214 @@ +import 'package:bb_mobile/_model/swap.dart'; +import 'package:bb_mobile/_model/transaction.dart'; +import 'package:bb_mobile/_model/wallet.dart'; +import 'package:bb_mobile/_repository/wallet/wallet_storage.dart'; +import 'package:bb_mobile/_repository/wallet_service.dart'; + +class AppWalletsRepository { + AppWalletsRepository({ + required WalletsStorageRepository walletsStorageRepository, + }) : _walletsStorageRepository = walletsStorageRepository; + final WalletsStorageRepository _walletsStorageRepository; + + final List _walletServices = []; + + Future getWalletsFromStorage() async { + final (wallets, err) = await _walletsStorageRepository.readAllWallets(); + if (err != null && err.toString() != 'No Key') { + return; + } + + if (wallets == null) { + return; + } + + _walletServices.clear(); + _walletServices.addAll( + wallets + .map((_) => createWalletService(wallet: _, fromStorage: true)) + .toList(), + ); + } + + List get allWallets => _walletServices.map((_) => _.wallet).toList(); + + Wallet? getWalletById(String id) { + final idx = _walletServices.indexWhere((_) => _.wallet.id == id); + if (idx == -1) return null; + return _walletServices[idx].wallet; + } + + WalletService? getWalletServiceById(String id) { + final idx = _walletServices.indexWhere((_) => _.wallet.id == id); + if (idx == -1) return null; + return _walletServices[idx]; + } + + void deleteWallet(String id) { + _walletServices.removeWhere((_) => _.wallet.id == id); + } + + List walletServiceFromNetwork(BBNetwork network) => + _walletServices.where((_) => _.wallet.network == network).toList(); + + Future loadAllInNetwork(BBNetwork network) async { + final ws = walletServiceFromNetwork(network); + await Future.wait( + ws.map((w) => w.loadWallet()), + ); + } + + Future syncAllInNetwork(BBNetwork network) async { + final ws = walletServiceFromNetwork(network); + for (final w in ws) { + w.syncWallet(); + } + } + + bool get hasWallets => _walletServices.isNotEmpty; + bool get hasMainWallets => _walletServices.any((_) => _.wallet.mainWallet); + List walletsFromNetwork(BBNetwork network) => _walletServices + .map((_) => _.wallet) + .where((_) => _.network == network) + .toList(); + + Wallet getColdCardWallet(BBNetwork network) { + return walletsFromNetwork(network) + .where( + (_) => _.network == network && _.type == BBWalletType.coldcard, + ) + .first; + } + + Wallet? getMainInstantWallet(BBNetwork network) { + final wallets = walletsFromNetwork(network); + final idx = wallets.indexWhere( + (_) => _.isInstant() && _.mainWallet, + ); + if (idx == -1) return null; + return wallets[idx]; + } + + Wallet? getMainSecureWallet(BBNetwork network) { + final wallets = walletsFromNetwork(network); + final idx = wallets.indexWhere( + (_) => _.isSecure() && _.mainWallet, + ); + if (idx == -1) return null; + return wallets[idx]; + } + + WalletService? getMainSecureWalletService(BBNetwork network) { + final wallet = getMainSecureWallet(network); + if (wallet == null) return null; + return getWalletServiceById(wallet.id); + } + + WalletService? getMainInstantWalletService(BBNetwork network) { + final wallet = getMainInstantWallet(network); + if (wallet == null) return null; + return getWalletServiceById(wallet.id); + } + + List getMainWalletServices(bool isTestnet) { + final network = isTestnet ? BBNetwork.Testnet : BBNetwork.Mainnet; + final instantwallet = getMainInstantWalletService(network); + final securewallet = getMainSecureWalletService(network); + return [ + if (instantwallet != null) instantwallet, + if (securewallet != null) securewallet, + ]; + } + + List getMainWallets(bool isTestnet) { + final network = isTestnet ? BBNetwork.Testnet : BBNetwork.Mainnet; + final instantwallet = getMainInstantWallet(network); + final securewallet = getMainSecureWallet(network); + return [ + if (instantwallet != null) instantwallet, + if (securewallet != null) securewallet, + ]; + } + + Transaction? getTxFromSwap(SwapTx swap) { + final isLiq = swap.isLiquid(); + final network = swap.network; + final wallet = + !isLiq ? getMainSecureWallet(network) : getMainInstantWallet(network); + if (wallet == null) return null; + final idx = wallet.transactions.indexWhere((t) => t.swapTx?.id == swap.id); + if (idx == -1) return null; + return wallet.transactions[idx]; + } + + WalletService? findWalletServiceWithSameFngr(Wallet wallet) { + for (final ws in _walletServices) { + final w = ws.wallet; + if (w.id == wallet.id) continue; + if (w.sourceFingerprint == wallet.sourceFingerprint) return ws; + } + return null; + } + + List walletsWithEnoughBalance( + int sats, + BBNetwork network, { + bool onlyMain = false, + bool onlyBitcoin = false, + bool onlyLiquid = false, + }) { + final wallets = walletsFromNetwork(network).where( + (_) { + final wallet = _; + if (onlyMain && !wallet.mainWallet) return false; + if (onlyBitcoin && !wallet.isBitcoin()) return false; + if (onlyLiquid && !wallet.isLiquid()) return false; + return true; + }, + ).toList(); + + final List walletsWithEnoughBalance = []; + + for (final walletBloc in wallets) { + final enoughBalance = (walletBloc.balance ?? 0) >= sats; + if (enoughBalance) walletsWithEnoughBalance.add(walletBloc); + } + return walletsWithEnoughBalance.isEmpty + ? wallets + : walletsWithEnoughBalance; + } + + List walletNotMainFromNetwork(BBNetwork network) { + final wallets = walletsFromNetwork(network) + .where( + (_) => _.network == network && !_.mainWallet, + ) + .toList() + .reversed + .toList(); + + return wallets; + } + + Wallet? getWalletFromTx(Transaction tx) { + if (allWallets.isEmpty) return null; + + for (final wallet in allWallets) { + if (wallet.transactions.indexWhere((t) => t.txid == tx.txid) != -1) { + return wallet; + } + } + + return null; + } + + List walletFromNetworkExcludeWatchOnly(BBNetwork network) { + final blocs = allWallets + .where( + (_) => _.network == network && _.watchOnly() == false, + ) + .toList(); + + return blocs; + } +} diff --git a/lib/_repository/currency_repository.dart b/lib/_repository/currency_repository.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/_repository/network_repository.dart b/lib/_repository/network_repository.dart new file mode 100644 index 00000000..c10dcc34 --- /dev/null +++ b/lib/_repository/network_repository.dart @@ -0,0 +1,298 @@ +import 'package:bb_mobile/_model/network.dart'; +import 'package:bb_mobile/_model/wallet.dart'; +import 'package:bb_mobile/_pkg/consts/configs.dart'; +import 'package:bb_mobile/_pkg/error.dart'; +import 'package:bb_mobile/_pkg/storage/hive.dart'; +import 'package:bb_mobile/_pkg/wallet/network.dart'; +import 'package:bdk_flutter/bdk_flutter.dart' as bdk; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:rxdart/rxdart.dart'; + +part 'network_repository.freezed.dart'; +part 'network_repository.g.dart'; + +@freezed +class NetworkRepoData with _$NetworkRepoData { + const factory NetworkRepoData({ + @Default(false) bool testnet, + @Default([]) List networks, + @Default(ElectrumTypes.bullbitcoin) ElectrumTypes selectedNetwork, + @Default([]) List liquidNetworks, + @Default(LiquidElectrumTypes.blockstream) + LiquidElectrumTypes selectedLiquidNetwork, + ElectrumTypes? tempNetwork, + ElectrumNetwork? tempNetworkDetails, + LiquidElectrumTypes? tempLiquidNetwork, + LiquidElectrumNetwork? tempLiquidNetworkDetails, + @Default(false) bool networkConnected, + }) = _NetworkRepoData; + const NetworkRepoData._(); + + factory NetworkRepoData.fromJson(Map json) => + _NetworkRepoData.fromJson(json); +} + +class NetworkRepository { + NetworkRepository({ + required WalletNetwork walletNetwork, + required HiveStorage hiveStorage, + }) : _walletNetwork = walletNetwork, + _hiveStorage = hiveStorage; + + final _data = + BehaviorSubject.seeded(const NetworkRepoData()); + + Stream get dataStream => _data.asBroadcastStream(); + NetworkRepoData get data => _data.value; + + final WalletNetwork _walletNetwork; + final HiveStorage _hiveStorage; + + void dispose() { + _data.close(); + } + + // Future init() async { + // final (result, err) = + // await _hiveStorage.getValue(StorageKeys.networkReposity); + // if (err != null) return err; + + // _data.add( + // NetworkRepoData.fromJson( + // jsonDecode(result!) as Map, + // ), + // ); + + // return null; + // } + + // Future save() async { + // await _hiveStorage.saveValue( + // key: StorageKeys.networkReposity, + // value: jsonEncode(_data.value.toJson()), + // ); + // } + + Future setupBlockchain({bool? isLiquid, bool? isTestnetLocal}) async { + final isTestnet = isTestnetLocal ?? _data.value.testnet; + + _data.add(_data.value.copyWith(networkConnected: false)); + + if (isLiquid == null || !isLiquid) { + final selectedNetwork = getNetwork; + if (selectedNetwork == null) return Err('Network not setup'); + + final errBitcoin = await _walletNetwork.createBlockChain( + isTestnet: isTestnet, + stopGap: selectedNetwork.stopGap, + timeout: selectedNetwork.timeout, + retry: selectedNetwork.retry, + url: isTestnet ? selectedNetwork.testnet : selectedNetwork.mainnet, + validateDomain: selectedNetwork.validateDomain, + ); + if (errBitcoin != null) return errBitcoin; + + _data.add(_data.value.copyWith(networkConnected: true)); + return null; + } + + if (isLiquid) { + final selectedLiqNetwork = getLiquidNetwork; + if (selectedLiqNetwork == null) return Err('Liquid Network not setup'); + + final errLiquid = await _walletNetwork.createBlockChain( + url: + isTestnet ? selectedLiqNetwork.testnet : selectedLiqNetwork.mainnet, + isTestnet: isTestnet, + ); + if (errLiquid != null) return errLiquid; + + _data.add(_data.value.copyWith(networkConnected: true)); + return null; + } + return null; + } + + Future loadNetworks() async { + if (_data.value.networks.isNotEmpty) { + final selectedNetwork = _data.value.networks + .firstWhere((_) => _.type == _data.value.selectedNetwork); + + _data.add( + _data.value.copyWith( + tempNetworkDetails: selectedNetwork, + tempNetwork: selectedNetwork.type, + selectedNetwork: selectedNetwork.type, + ), + ); + + await setupBlockchain(isLiquid: false); + } else { + final newNetworks = [ + const ElectrumNetwork.defaultElectrum(), + const ElectrumNetwork.bullbitcoin(), + const ElectrumNetwork.custom( + mainnet: 'ssl://$bbelectrumMain', + testnet: 'ssl://$openelectrumTest', + ), + ]; + + final selectedNetwork = newNetworks[2]; + + _data.add( + _data.value.copyWith( + networks: newNetworks, + tempNetworkDetails: selectedNetwork, + tempNetwork: selectedNetwork.type, + selectedNetwork: selectedNetwork.type, + ), + ); + + await setupBlockchain(isLiquid: false); + } + + if (_data.value.liquidNetworks.isNotEmpty) { + var selectedNetwork = _data.value.liquidNetworks + .firstWhere((_) => _.type == _data.value.selectedLiquidNetwork); + final updatedLiqNetworks = _data.value.liquidNetworks.toList(); + if (_data.value.liquidNetworks.length == 2) { + updatedLiqNetworks.insert(1, const LiquidElectrumNetwork.bullbitcoin()); + selectedNetwork = updatedLiqNetworks[1]; + } + + _data.add( + _data.value.copyWith( + tempLiquidNetworkDetails: selectedNetwork, + tempLiquidNetwork: selectedNetwork.type, + selectedLiquidNetwork: selectedNetwork.type, + liquidNetworks: updatedLiqNetworks, + ), + ); + + await setupBlockchain(isLiquid: true); + } else { + final newLiqNetworks = [ + const LiquidElectrumNetwork.blockstream(), + const LiquidElectrumNetwork.bullbitcoin(), + const LiquidElectrumNetwork.custom( + mainnet: liquidElectrumUrl, + testnet: liquidElectrumTestUrl, + ), + ]; + final selectedLiqNetwork = newLiqNetworks[1]; + + _data.add( + _data.value.copyWith( + liquidNetworks: newLiqNetworks, + tempLiquidNetworkDetails: selectedLiqNetwork, + tempLiquidNetwork: selectedLiqNetwork.type, + selectedLiquidNetwork: selectedLiqNetwork.type, + ), + ); + + await setupBlockchain(isLiquid: true); + } + } + + bool get testnet => _data.value.testnet; + + ElectrumNetwork? get getNetwork { + if (_data.value.networks.isEmpty) return null; + return _data.value.networks + .firstWhere((_) => _.type == _data.value.selectedNetwork); + } + + LiquidElectrumNetwork? get getLiquidNetwork { + if (_data.value.liquidNetworks.isEmpty) return null; + return _data.value.liquidNetworks + .firstWhere((_) => _.type == _data.value.selectedLiquidNetwork); + } + + BBNetwork get getBBNetwork => + _data.value.testnet ? BBNetwork.Testnet : BBNetwork.Mainnet; + bdk.Network get getBdkNetwork => + _data.value.testnet ? bdk.Network.testnet : bdk.Network.bitcoin; + + String get getLiquidNetworkUrl { + final network = getLiquidNetwork; + if (network == null) return ''; + return network.getNetworkUrl(_data.value.testnet, split: false); + } + + String get getNetworkUrl { + final network = getNetwork; + if (network == null) return ''; + return network.getNetworkUrl(_data.value.testnet); + } + + double get pickLiquidFees { + if (testnet) { + switch (_data.value.selectedLiquidNetwork) { + case LiquidElectrumTypes.custom: + case LiquidElectrumTypes.blockstream: + return 0.1; + case LiquidElectrumTypes.bullbitcoin: + return 0.1; + } + } + switch (_data.value.selectedLiquidNetwork) { + case LiquidElectrumTypes.custom: + case LiquidElectrumTypes.blockstream: + return 0.1; + case LiquidElectrumTypes.bullbitcoin: + return 0.01; + } + } + + void setNetworkData({ + // bool? testnet, + List? networks, + List? liquidNetworks, + ElectrumNetwork? tempNetworkDetails, + LiquidElectrumNetwork? tempLiquidNetworkDetails, + ElectrumTypes? tempNetwork, + LiquidElectrumTypes? tempLiquidNetwork, + ElectrumTypes? selectedNetwork, + LiquidElectrumTypes? selectedLiquidNetwork, + bool? networkConnected, + }) { + _data.add( + _data.value.copyWith( + // testnet: testnet ?? _data.value.testnet, + networkConnected: networkConnected ?? _data.value.networkConnected, + networks: networks ?? _data.value.networks, + liquidNetworks: liquidNetworks ?? _data.value.liquidNetworks, + tempNetworkDetails: + tempNetworkDetails ?? _data.value.tempNetworkDetails, + tempLiquidNetworkDetails: + tempLiquidNetworkDetails ?? _data.value.tempLiquidNetworkDetails, + tempNetwork: tempNetwork ?? _data.value.tempNetwork, + tempLiquidNetwork: tempLiquidNetwork ?? _data.value.tempLiquidNetwork, + selectedNetwork: selectedNetwork ?? _data.value.selectedNetwork, + selectedLiquidNetwork: + selectedLiquidNetwork ?? _data.value.selectedLiquidNetwork, + ), + ); + } + + void setAllData(NetworkRepoData _) => _data.add(_); + + Future changeTestnet(bool testnet) async { + _data.add(_data.value.copyWith(testnet: testnet)); + await setupBlockchain(isTestnetLocal: testnet); + await setupBlockchain(isTestnetLocal: testnet, isLiquid: true); + } + + void resetNetworkData({ + bool tempNetwork = false, + bool tempLiquidNetwork = false, + }) { + _data.add( + _data.value.copyWith( + tempNetwork: tempNetwork ? null : _data.value.tempNetwork, + tempLiquidNetwork: + tempLiquidNetwork ? null : _data.value.tempLiquidNetwork, + ), + ); + } +} diff --git a/lib/_repository/wallet/internal_network.dart b/lib/_repository/wallet/internal_network.dart new file mode 100644 index 00000000..4a410e02 --- /dev/null +++ b/lib/_repository/wallet/internal_network.dart @@ -0,0 +1,56 @@ +import 'package:bb_mobile/_pkg/error.dart'; +import 'package:bdk_flutter/bdk_flutter.dart' as bdk; + +class InternalNetworkRepository { + bdk.Blockchain? _bdkBlockchain; + String? _bitcoinUrl; + String? _liquidUrl; + // bool _isTestnet = false; + + (bdk.Blockchain?, Err?) get bdkBlockchain => _bdkBlockchain != null + ? (_bdkBlockchain, null) + : (null, Err('Network not setup')); + + (String?, Err?) get bitcoinUrl => _bitcoinUrl != null + ? (_bitcoinUrl, null) + : (null, Err('Network not setup')); + + Err? setBitcoinUrl(String url) { + _bitcoinUrl = url; + return null; + } + + (String?, Err?) get liquidUrl => _liquidUrl != null + ? (_liquidUrl, null) + : (null, Err('Network not setup')); + + Err? setLiquidUrl(String url) { + _liquidUrl = url; + return null; + } + + Err? setBdkBlockchain(bdk.Blockchain blockchain) { + _bdkBlockchain = blockchain; + return null; + } + + // Err? setTestnet(bool isTestnet) { + // _isTestnet = isTestnet; + // return null; + // } + + // bool get isTestnet => _isTestnet; + + Err? checkNetworks() => (_bdkBlockchain == null || _liquidUrl == null) + ? Err('Network not setup') + : null; + + Err? checkNetworks2(bool isLiquid) => + isLiquid ? checkLiquidNetwork() : checkBitcoinNetwork(); + + Err? checkBitcoinNetwork() => + (_bitcoinUrl == null) ? Err('Network not setup') : null; + + Err? checkLiquidNetwork() => + (_liquidUrl == null) ? Err('Network not setup') : null; +} diff --git a/lib/_pkg/wallet/repository/wallets.dart b/lib/_repository/wallet/internal_wallets.dart similarity index 98% rename from lib/_pkg/wallet/repository/wallets.dart rename to lib/_repository/wallet/internal_wallets.dart index 452db030..f757370d 100644 --- a/lib/_pkg/wallet/repository/wallets.dart +++ b/lib/_repository/wallet/internal_wallets.dart @@ -3,7 +3,7 @@ import 'package:bb_mobile/_pkg/error.dart'; import 'package:bdk_flutter/bdk_flutter.dart' as bdk; import 'package:lwk_dart/lwk_dart.dart' as lwk; -class WalletsRepository { +class InternalWalletsRepository { final Set<({String id, bdk.Wallet wallet})> _bdkWallets = {}; final Set<({String id, lwk.Wallet wallet})> _lwkWallets = {}; diff --git a/lib/_pkg/wallet/repository/sensitive_storage.dart b/lib/_repository/wallet/sensitive_wallet_storage.dart similarity index 100% rename from lib/_pkg/wallet/repository/sensitive_storage.dart rename to lib/_repository/wallet/sensitive_wallet_storage.dart diff --git a/lib/_pkg/wallet/repository/storage.dart b/lib/_repository/wallet/wallet_storage.dart similarity index 100% rename from lib/_pkg/wallet/repository/storage.dart rename to lib/_repository/wallet/wallet_storage.dart diff --git a/lib/_repository/wallet_service.dart b/lib/_repository/wallet_service.dart new file mode 100644 index 00000000..50a4a149 --- /dev/null +++ b/lib/_repository/wallet_service.dart @@ -0,0 +1,391 @@ +import 'dart:async'; + +import 'package:bb_mobile/_model/address.dart'; +import 'package:bb_mobile/_model/wallet.dart'; +import 'package:bb_mobile/_pkg/error.dart'; +import 'package:bb_mobile/_pkg/logger.dart'; +import 'package:bb_mobile/_pkg/wallet/address.dart'; +import 'package:bb_mobile/_pkg/wallet/balance.dart'; +import 'package:bb_mobile/_pkg/wallet/create.dart'; +import 'package:bb_mobile/_pkg/wallet/sync.dart'; +import 'package:bb_mobile/_pkg/wallet/transaction.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; +import 'package:bb_mobile/_repository/wallet/internal_network.dart'; +import 'package:bb_mobile/_repository/wallet/wallet_storage.dart'; +import 'package:bb_mobile/locator.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:rxdart/rxdart.dart'; + +part 'wallet_service.freezed.dart'; +part 'wallet_service.g.dart'; + +@freezed +class WalletServiceData with _$WalletServiceData { + const factory WalletServiceData({ + required Wallet wallet, + @Default(3) int loadingAttemptsLeft, + @Default(false) bool errLoading, + @Default(0) int syncErrCount, + @Default(false) bool syncing, + }) = _WalletServiceData; + const WalletServiceData._(); + + factory WalletServiceData.fromJson(Map json) => + _WalletServiceData.fromJson(json); +} + +class WalletService { + WalletService({ + required Wallet wallet, + required WalletsStorageRepository walletsStorageRepository, + required InternalNetworkRepository internalNetworkRepository, + required WalletSync walletSync, + required WalletBalance walletBalance, + required WalletAddress walletAddress, + required WalletCreate walletCreate, + required WalletTx walletTransaction, + required NetworkRepository networkRepository, + bool fromStorage = false, + }) : _walletsStorageRepository = walletsStorageRepository, + _internalNetworkRepository = internalNetworkRepository, + _walletSync = walletSync, + _walletBalance = walletBalance, + _walletAddress = walletAddress, + _walletCreate = walletCreate, + _walletTransactionn = walletTransaction, + _networkRepository = networkRepository, + _fromStorage = fromStorage, + _data = BehaviorSubject.seeded( + WalletServiceData(wallet: wallet), + ); + + // Lock lock = Lock(reentrant: true); + + final BehaviorSubject _data; + + Stream get dataStream => _data.asBroadcastStream(); + // Stream get walletStream => _data.stream.map((e) => e.wallet); + + // WalletServiceData get data => _data.value; + + final bool _fromStorage; + + final WalletsStorageRepository _walletsStorageRepository; + final InternalNetworkRepository _internalNetworkRepository; + + final WalletSync _walletSync; + final WalletBalance _walletBalance; + final WalletAddress _walletAddress; + final WalletCreate _walletCreate; + final WalletTx _walletTransactionn; + final NetworkRepository _networkRepository; + + Wallet get wallet => _data.value.wallet; + + void dispose() { + _data.close(); + } + + Future loadWallet({bool syncAfter = false}) async { + _data.add(_data.value.copyWith(errLoading: false)); + final (w, err) = await _walletCreate.loadPublicWallet( + saveDir: _data.value.wallet.getWalletStorageString(), + wallet: _data.value.wallet, + network: _data.value.wallet.network, + ); + if (err != null) { + _data.add(_data.value.copyWith(errLoading: true)); + return err; + } + + _data.add( + _data.value.copyWith( + wallet: w!, + loadingAttemptsLeft: 3, + ), + ); + + if (syncAfter) await syncWallet(); + + return null; + } + + Future syncWallet() async { + if (_data.value.errLoading && _data.value.loadingAttemptsLeft > 0) { + _data.add( + _data.value.copyWith( + loadingAttemptsLeft: _data.value.loadingAttemptsLeft - 1, + ), + ); + final errLoad = await loadWallet(); + if (errLoad != null) return errLoad; + } + + final isLiq = _data.value.wallet.isLiquid(); + + final errNetwork = _internalNetworkRepository.checkNetworks2(isLiq); + if (errNetwork != null) { + await _networkRepository.loadNetworks(); + final errBC = await _networkRepository.setupBlockchain(isLiquid: isLiq); + if (errBC != null) return errBC; + final errNetwork2 = _internalNetworkRepository.checkNetworks2(isLiq); + if (errNetwork2 != null) return errNetwork2; + } + + final liqTxt = isLiq ? 'Instant' : 'Secure'; + locator().log( + 'Start $liqTxt Wallet Sync for ${_data.value.wallet.id}', + printToConsole: true, + ); + + _data.add(_data.value.copyWith(syncing: true)); + final err = await _walletSync.syncWallet(_data.value.wallet); + _data.add(_data.value.copyWith(syncing: false)); + locator().log( + 'End $liqTxt Wallet Sync for ${_data.value.wallet.id} - err: $err', + printToConsole: true, + ); + + if (err != null) { + if (err.message.toLowerCase().contains('panic') && + _data.value.syncErrCount < 5) { + await _networkRepository.loadNetworks(); + final errBC2 = + await _networkRepository.setupBlockchain(isLiquid: isLiq); + + _data.add( + _data.value.copyWith(syncErrCount: _data.value.syncErrCount + 1), + ); + if (errBC2 != null) return errBC2; + final err2 = await _walletSync.syncWallet(_data.value.wallet); + if (err2 != null) return err2; + } + + locator().log(err.toString()); + } + + _data.add(_data.value.copyWith(syncErrCount: 0)); + + scheduleMicrotask(() async { + // await Future.wait([ + // if (!_fromStorage) await getFirstAddress(); + await getBalance(); + await listTransactions(); + // ]); + }); + + return null; + } + + Future updateWallet( + Wallet wallet, { + bool saveToStorage = true, + required List updateTypes, + bool syncAfter = false, + int delaySync = 0, + }) async { + // return lock.synchronized(() async { + if (!saveToStorage) { + _data.add(_data.value.copyWith(wallet: wallet)); + return; + } + + if (updateTypes.contains(UpdateWalletTypes.load)) { + final err = await _walletsStorageRepository.updateWallet( + _data.value.wallet, + ); + if (err != null) locator().log(err.toString()); + + _data.add(_data.value.copyWith(wallet: wallet)); + return; + } + + var (storageWallet, errr) = await _walletsStorageRepository.readWallet( + walletHashId: _data.value.wallet.getWalletStorageString(), + ); + if (errr != null) locator().log(errr.toString()); + if (storageWallet == null) return; + + for (final eventType in updateTypes) { + switch (eventType) { + case UpdateWalletTypes.load: + break; + case UpdateWalletTypes.balance: + if (wallet.balance != null) { + storageWallet = storageWallet!.copyWith( + balance: wallet.balance, + fullBalance: wallet.fullBalance, + ); + } + case UpdateWalletTypes.transactions: + if (wallet.transactions.isNotEmpty) { + storageWallet = storageWallet!.copyWith( + transactions: wallet.transactions, + ); + } + + if (wallet.unsignedTxs.isNotEmpty) { + storageWallet = storageWallet!.copyWith( + unsignedTxs: wallet.unsignedTxs, + ); + } + + case UpdateWalletTypes.swaps: + storageWallet = storageWallet!.copyWith( + swaps: wallet.swaps, + revKeyIndex: wallet.revKeyIndex, + subKeyIndex: wallet.subKeyIndex, + ); + + case UpdateWalletTypes.addresses: + if (wallet.myAddressBook.isNotEmpty) { + storageWallet = storageWallet!.copyWith( + myAddressBook: wallet.myAddressBook, + ); + } + + if (wallet.externalAddressBook != null && + wallet.externalAddressBook!.isNotEmpty) { + storageWallet = storageWallet!.copyWith( + externalAddressBook: wallet.externalAddressBook, + ); + } + + if (wallet.lastGeneratedAddress != null) { + storageWallet = storageWallet!.copyWith( + lastGeneratedAddress: wallet.lastGeneratedAddress, + ); + } + + case UpdateWalletTypes.utxos: + storageWallet = storageWallet!.copyWith(utxos: wallet.utxos); + + case UpdateWalletTypes.settings: + if (wallet.backupTested != storageWallet!.backupTested) { + storageWallet = storageWallet.copyWith( + backupTested: wallet.backupTested, + ); + } + + if (wallet.name != storageWallet.name) { + storageWallet = storageWallet.copyWith( + name: wallet.name, + ); + } + + if (wallet.lastBackupTested != null && + wallet.lastBackupTested != storageWallet.lastBackupTested) { + storageWallet = storageWallet.copyWith( + lastBackupTested: wallet.lastBackupTested, + ); + } + } + + final err = await _walletsStorageRepository.updateWallet( + storageWallet!, + ); + if (err != null) { + locator().log(err.toString(), printToConsole: true); + } + + _data.add(_data.value.copyWith(wallet: storageWallet)); + await Future.delayed(Duration(milliseconds: delaySync)); + if (syncAfter) syncWallet(); + } + // }); + } + + Future getBalance() async { + final (w, err) = await _walletBalance.getBalance(_data.value.wallet); + if (err != null) return err; + + _data.add(_data.value.copyWith(wallet: w!.$1)); + await updateWallet( + wallet, + saveToStorage: _fromStorage, + updateTypes: [UpdateWalletTypes.balance], + ); + return null; + } + + Future listTransactions() async { + final (w, err) = + await _walletTransactionn.getTransactions(_data.value.wallet); + if (err != null) return err; + + _data.add(_data.value.copyWith(wallet: w!)); + await updateWallet( + wallet, + saveToStorage: _fromStorage, + updateTypes: [ + UpdateWalletTypes.transactions, + UpdateWalletTypes.addresses, + UpdateWalletTypes.utxos, + ], + ); + return null; + } + + Future getFirstAddress() async { + final (address, err) = + await _walletAddress.peekIndex(wallet: _data.value.wallet, idx: 0); + + if (err != null) return err; + + _data.add( + _data.value.copyWith( + wallet: _data.value.wallet.copyWith( + firstAddress: Address( + address: address!, + index: 0, + kind: AddressKind.deposit, + state: AddressStatus.unused, + ), + ), + ), + ); + return null; + } + + void killSync() { + _walletSync.cancelSync(); + } +} + +WalletService createWalletService({ + required Wallet wallet, + bool fromStorage = false, +}) { + final walletsStorageRepository = locator(); + final internalNetworkkRepository = locator(); + + final walletSync = locator(); + final walletBalance = locator(); + final walletAddress = locator(); + final walletCreate = locator(); + final walletTransaction = locator(); + final networkRepository = locator(); + + return WalletService( + wallet: wallet, + walletsStorageRepository: walletsStorageRepository, + internalNetworkRepository: internalNetworkkRepository, + walletSync: walletSync, + walletBalance: walletBalance, + walletAddress: walletAddress, + walletCreate: walletCreate, + walletTransaction: walletTransaction, + networkRepository: networkRepository, + fromStorage: fromStorage, + ); +} + +enum UpdateWalletTypes { + load, + balance, + transactions, + swaps, + addresses, + settings, + utxos +} diff --git a/lib/_ui/app_bar.dart b/lib/_ui/app_bar.dart index de2f6d02..8e7613d8 100644 --- a/lib/_ui/app_bar.dart +++ b/lib/_ui/app_bar.dart @@ -32,7 +32,8 @@ class BBAppBar extends StatelessWidget { ).animate(delay: 100.ms).fadeIn(), ), ], - const Spacer(), + // const Spacer(), + const Gap(16), Padding( padding: const EdgeInsets.only(bottom: 8), child: BBText.titleLarge( diff --git a/lib/_ui/bottom_wallet_actions.dart b/lib/_ui/bottom_wallet_actions.dart index 90d1ff34..6ef0c4c8 100644 --- a/lib/_ui/bottom_wallet_actions.dart +++ b/lib/_ui/bottom_wallet_actions.dart @@ -1,21 +1,21 @@ +import 'package:bb_mobile/_model/wallet.dart'; import 'package:bb_mobile/_pkg/consts/keys.dart'; import 'package:bb_mobile/_ui/components/button.dart'; import 'package:bb_mobile/settings/bloc/lighting_cubit.dart'; import 'package:bb_mobile/styles.dart'; -import 'package:bb_mobile/wallet/bloc/wallet_bloc.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; class WalletActionButtons extends StatelessWidget { - const WalletActionButtons({super.key, this.walletBloc}); + const WalletActionButtons({super.key, this.wallet}); - final WalletBloc? walletBloc; + final Wallet? wallet; @override Widget build(BuildContext context) { - // final hasWallets = context.select((HomeCubit x) => x.state.hasWallets()); + // final hasWallets = context.select((HomeBloc x) => x.state.hasWallets()); // if (!hasWallets) return const SizedBox.shrink(); @@ -27,15 +27,15 @@ class WalletActionButtons extends StatelessWidget { (Lighting x) => x.state.currentTheme(context) == ThemeMode.dark, ); - final shouldShowSwap = (walletBloc!.state.wallet?.isMain() != true || - walletBloc!.state.wallet?.isLiquid() == true) && - walletBloc!.state.wallet?.watchOnly() == false; + final shouldShowSwap = + (wallet?.isMain() != true || wallet?.isLiquid() == true) && + wallet?.watchOnly() == false; final leftImage = darkMode ? 'assets/images/swap_icon_white.png' : 'assets/images/swap_icon.png'; - final canShowSend = walletBloc!.state.wallet?.watchOnly() == false; + final canShowSend = wallet?.watchOnly() == false; return Container( padding: const EdgeInsets.only( @@ -72,7 +72,7 @@ class WalletActionButtons extends StatelessWidget { filled: true, onPressed: () async { context.push( - '/swap-page?fromWalletId=${walletBloc!.state.wallet?.id}', + '/swap-page?fromWalletId=${wallet?.id}', ); }, label: 'Swap', @@ -89,7 +89,7 @@ class WalletActionButtons extends StatelessWidget { buttonKey: UIKeys.homeReceiveButton, filled: true, onPressed: () async { - context.push('/receive', extra: walletBloc); + context.push('/receive', extra: wallet?.id); }, label: 'Receive', ), @@ -103,7 +103,7 @@ class WalletActionButtons extends StatelessWidget { onPressed: () async { context.push( '/send', - extra: walletBloc?.state.wallet!.id, + extra: wallet?.id, ); }, label: 'Send', diff --git a/lib/_ui/molecules/address/address_display.dart b/lib/_ui/molecules/address/address_display.dart deleted file mode 100644 index 158ad56f..00000000 --- a/lib/_ui/molecules/address/address_display.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:bb_mobile/_pkg/clipboard.dart'; -import 'package:flutter/material.dart'; -import 'package:qr_flutter/qr_flutter.dart'; - -class AddressDisplay extends StatelessWidget { - const AddressDisplay({ - super.key, - required this.address, - }); - - final String address; - - void _onCopy(BuildContext context, String address) { - BBClipboard.copy(address); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Copied address'), - ), - ); - } - - List _buildQRAndCopyText(BuildContext context, String address) { - if (address.isEmpty) { - return const [ - Center( - child: Padding( - padding: EdgeInsets.all(8.0), - child: Text('No address specified'), - ), - ), - ]; - } - - return [ - Center( - child: QrImageView( - data: address, - size: 200.0, - ), - ), - const SizedBox( - height: 10, - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(address), - const SizedBox( - width: 10, - ), - IconButton( - icon: const Icon(Icons.copy), - onPressed: () => _onCopy(context, address), - ), - ], - ), - ]; - } - - @override - Widget build(BuildContext context) { - return Column( - children: _buildQRAndCopyText(context, address), - ); - } -} diff --git a/lib/_ui/molecules/address/address_input.dart b/lib/_ui/molecules/address/address_input.dart deleted file mode 100644 index cf2d7c3b..00000000 --- a/lib/_ui/molecules/address/address_input.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'package:bb_mobile/_pkg/clipboard.dart'; -import 'package:bb_mobile/_ui/atoms/bb_form_field.dart'; -import 'package:flutter/material.dart'; - -class AddressInput extends StatelessWidget { - const AddressInput({ - super.key, - required this.addressController, - this.label = 'Address', - this.disabled = false, - this.decoration, - this.errorMsg = '', - this.showPaste = true, - this.showScan = true, - }); - - final TextEditingController addressController; - final String label; - final bool disabled; - final InputDecoration? decoration; - final String errorMsg; - - final bool showPaste; - final bool showScan; - - Future _onPaste() async { - final String? text = await BBClipboard.paste(); - - addressController.text = text ?? ''; - } - - Future _onScan() async {} - - @override - Widget build(BuildContext context) { - return BBFormField( - label: label, - editingController: addressController, - disabled: disabled, - errorMsg: errorMsg, - suffix: Row( - mainAxisSize: MainAxisSize.min, - children: [ - if (showPaste) - IconButton( - icon: const Icon(Icons.paste), - onPressed: _onPaste, - ), - if (showScan) - IconButton( - icon: const Icon(Icons.qr_code), - onPressed: _onScan, - ), - ], - ), - ); - } -} diff --git a/lib/_ui/molecules/fee_rate/fee_rate.dart b/lib/_ui/molecules/fee_rate/fee_rate.dart deleted file mode 100644 index b5fd5dd7..00000000 --- a/lib/_ui/molecules/fee_rate/fee_rate.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'package:bb_mobile/_pkg/fee_rate/models/fee_rate.dart'; -import 'package:bb_mobile/_ui/molecules/fee_rate/fee_rate_picker_widget.dart'; -import 'package:flutter/material.dart'; - -class FeeRateSelector extends StatelessWidget { - const FeeRateSelector({ - super.key, - required this.label, - required this.selectedFeeRate, - required this.feeRate, - required this.currentFeeRate, - required this.onDefaultFeeRateChange, - }); - - final String label; - - final FeeRateType selectedFeeRate; - final int feeRate; - final FeeRate currentFeeRate; - - final Function({ - required int updatedDefaultFeeRate, - required FeeRateType selectedFeeRate, - }) onDefaultFeeRateChange; - - void _onTap(BuildContext context) { - showModalBottomSheet( - context: context, - isScrollControlled: true, - builder: (BuildContext context) { - return SingleChildScrollView( - child: Container( - padding: EdgeInsets.only( - bottom: MediaQuery.of(context).viewInsets.bottom, - ), - child: Wrap( - children: [ - FeeRatePicker( - currentFeeRate: currentFeeRate, - feeRate: feeRate, - onDefaultFeeRateChange: onDefaultFeeRateChange, - selectedFeeRate: selectedFeeRate, - ), - ], - ), - ), - ); - }, - ); - } - - @override - Widget build(BuildContext context) { - return ListTile( - title: Text(label), - trailing: SizedBox( - width: 100, - child: Row( - children: [ - Text('$feeRate sats/vB'), - const Icon(Icons.chevron_right), - ], - ), - ), - onTap: () => _onTap(context), - ); - } -} diff --git a/lib/_ui/molecules/fee_rate/fee_rate_picker_widget.dart b/lib/_ui/molecules/fee_rate/fee_rate_picker_widget.dart deleted file mode 100644 index 7926c78c..00000000 --- a/lib/_ui/molecules/fee_rate/fee_rate_picker_widget.dart +++ /dev/null @@ -1,173 +0,0 @@ -import 'package:bb_mobile/_pkg/fee_rate/models/fee_rate.dart'; -import 'package:bb_mobile/_ui/utils.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -const minDefaultFeeRate = 10; -const maxDefaultFeeRate = 1000; - -class FeeRatePicker extends StatefulWidget { - const FeeRatePicker({ - super.key, - required this.selectedFeeRate, - required this.feeRate, - required this.currentFeeRate, - required this.onDefaultFeeRateChange, - }); - - final FeeRateType selectedFeeRate; - final int feeRate; - final FeeRate currentFeeRate; - - final Function({ - required int updatedDefaultFeeRate, - required FeeRateType selectedFeeRate, - }) onDefaultFeeRateChange; - - @override - State createState() => _FeeRatePickerState(); -} - -class _FeeRatePickerState extends State { - FeeRateType selectedFeeRate = FeeRateType.fastest; - - TextEditingController textEditingController = - TextEditingController(text: '0'); - final _customFeeRateKey = GlobalKey(); - - @override - void initState() { - super.initState(); - selectedFeeRate = widget.selectedFeeRate; - - if (selectedFeeRate == FeeRateType.custom) { - textEditingController.text = widget.feeRate.toString(); - } - - textEditingController.addListener(() { - if (textEditingController.text != '0') { - selectedFeeRate = FeeRateType.custom; - } - }); - } - - @override - void dispose() { - super.dispose(); - textEditingController.dispose(); - } - - void _onFeeRateSelected(FeeRateType feeRate) { - setState(() { - selectedFeeRate = feeRate; - textEditingController.text = '0'; - }); - } - - void _onDoneTap() { - int rate = widget.currentFeeRate.getFeeValue(selectedFeeRate); - - if (selectedFeeRate == FeeRateType.custom) { - rate = int.parse(textEditingController.text); - } - - widget.onDefaultFeeRateChange( - updatedDefaultFeeRate: rate, - selectedFeeRate: selectedFeeRate, - ); - } - - @override - Widget build(BuildContext context) { - return ConstrainedBox( - constraints: const BoxConstraints(minWidth: double.infinity), - child: Padding( - padding: const EdgeInsets.all(20), - child: Column( - children: [ - RadioListTile( - value: FeeRateType.fastest, - groupValue: selectedFeeRate, - title: Text('Fastest ${widget.currentFeeRate.fastest}'), - onChanged: (val) { - _onFeeRateSelected(FeeRateType.fastest); - }, - ), - RadioListTile( - value: FeeRateType.fast, - groupValue: selectedFeeRate, - title: Text('Fast ${widget.currentFeeRate.fast}'), - onChanged: (val) { - _onFeeRateSelected(FeeRateType.fast); - }, - ), - RadioListTile( - value: FeeRateType.medium, - groupValue: selectedFeeRate, - title: Text('Medium ${widget.currentFeeRate.medium}'), - onChanged: (val) { - _onFeeRateSelected(FeeRateType.medium); - }, - ), - RadioListTile( - value: FeeRateType.slow, - groupValue: selectedFeeRate, - title: Text('Slow ${widget.currentFeeRate.slow}'), - onChanged: (val) { - _onFeeRateSelected(FeeRateType.slow); - }, - ), - TextFormField( - key: _customFeeRateKey, - controller: textEditingController, - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly, - ], - decoration: const InputDecoration( - focusedBorder: OutlineInputBorder(), - enabledBorder: OutlineInputBorder(), - focusedErrorBorder: errorBorder, - errorBorder: errorBorder, - errorStyle: TextStyle( - color: Colors.red, - ), - ), - validator: (value) { - if (value == null || value.isEmpty) { - return 'Please enter number between $minDefaultFeeRate to $maxDefaultFeeRate'; - } - - final int? intValue = int.tryParse(value); - - if (intValue == null) { - return 'Please enter valid number between $minDefaultFeeRate to $maxDefaultFeeRate'; - } - - if (intValue < minDefaultFeeRate) { - return 'Fee rate should not be less than $minDefaultFeeRate'; - } - - if (intValue > maxDefaultFeeRate) { - return 'Fee rate should not be greater than $maxDefaultFeeRate'; - } - - return null; - }, - ), - ElevatedButton( - child: const Text('Done'), - onPressed: () { - if (selectedFeeRate != FeeRateType.custom || - _customFeeRateKey.currentState!.validate()) { - _onDoneTap(); - Navigator.pop(context); - } - }, - ), - ], - ), - ), - ); - } -} diff --git a/lib/address/bloc/address_cubit.dart b/lib/address/bloc/address_cubit.dart index 10d8d45e..ccb42e9b 100644 --- a/lib/address/bloc/address_cubit.dart +++ b/lib/address/bloc/address_cubit.dart @@ -1,26 +1,29 @@ import 'package:bb_mobile/_model/address.dart'; +import 'package:bb_mobile/_model/wallet.dart'; import 'package:bb_mobile/_pkg/wallet/address.dart'; import 'package:bb_mobile/_pkg/wallet/bdk/utxo.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; +import 'package:bb_mobile/_repository/wallet_service.dart'; import 'package:bb_mobile/address/bloc/address_state.dart'; -import 'package:bb_mobile/wallet/bloc/event.dart'; -import 'package:bb_mobile/wallet/bloc/wallet_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class AddressCubit extends Cubit { AddressCubit({ required Address address, - required WalletBloc walletBloc, + required Wallet wallet, required WalletAddress walletAddress, required BDKUtxo bdkUtxo, + required AppWalletsRepository appWalletsRepository, }) : _walletAddress = walletAddress, - _walletBloc = walletBloc, + _wallet = wallet, _bdkUtxo = bdkUtxo, + _appWalletsRepository = appWalletsRepository, super(AddressState(address: address)); - // final WalletStorage walletStorage; - final WalletBloc _walletBloc; + final Wallet _wallet; final WalletAddress _walletAddress; final BDKUtxo _bdkUtxo; + final AppWalletsRepository _appWalletsRepository; Future freezeAddress() async { emit(state.copyWith(freezingAddress: true, errFreezingAddress: '')); @@ -28,7 +31,7 @@ class AddressCubit extends Cubit { final (address, w) = await _walletAddress.addAddressToWallet( address: (state.address!.index, state.address!.address), label: state.address?.label, - wallet: _walletBloc.state.wallet!, + wallet: _wallet, kind: state.address!.kind, state: state.address!.state, spendable: false, @@ -50,11 +53,14 @@ class AddressCubit extends Cubit { return; } - _walletBloc.add( - UpdateWallet( - utxoWallet!, - updateTypes: [UpdateWalletTypes.addresses, UpdateWalletTypes.utxos], - ), + await _appWalletsRepository + .getWalletServiceById(utxoWallet!.id) + ?.updateWallet( + utxoWallet, + updateTypes: [ + UpdateWalletTypes.addresses, + UpdateWalletTypes.utxos, + ], ); emit( @@ -72,7 +78,7 @@ class AddressCubit extends Cubit { final (address, w) = await _walletAddress.addAddressToWallet( address: (state.address!.index, state.address!.address), label: state.address?.label, - wallet: _walletBloc.state.wallet!, + wallet: _wallet, kind: state.address!.kind, state: state.address!.state, ); @@ -93,27 +99,16 @@ class AddressCubit extends Cubit { return; } - _walletBloc.add( - UpdateWallet( - utxoWallet!, - updateTypes: [UpdateWalletTypes.addresses, UpdateWalletTypes.utxos], - ), + await _appWalletsRepository + .getWalletServiceById(utxoWallet!.id) + ?.updateWallet( + utxoWallet, + updateTypes: [ + UpdateWalletTypes.addresses, + UpdateWalletTypes.utxos, + ], ); - // final errUpdate = await walletsStorageRepository.updateWallet( - // wallet: w, - // hiveStore: hiveStorage, - // ); - // if (errUpdate != null) { - // emit( - // state.copyWith( - // freezingAddress: false, - // errFreezingAddress: errUpdate.toString(), - // ), - // ); - // return; - // } - emit( state.copyWith( freezingAddress: false, @@ -129,42 +124,40 @@ class AddressCubit extends Cubit { final (addr, w) = await _walletAddress.addAddressToWallet( address: (address.index, address.address), label: label, - wallet: _walletBloc.state.wallet!, + wallet: _wallet, kind: address.kind, state: address.state, ); if (!w.isLiquid()) { - // pass utxo into cubit - // make utxo interface final updatedWallet = await BDKUtxo().updateUtxoLabel( addressStr: address.address, wallet: w, label: label, ); if (updatedWallet == null) { - _walletBloc.add( - UpdateWallet( - w, - updateTypes: [ - UpdateWalletTypes.addresses, - UpdateWalletTypes.utxos, - ], - ), + await _appWalletsRepository.getWalletServiceById(w.id)?.updateWallet( + w, + updateTypes: [ + UpdateWalletTypes.addresses, + UpdateWalletTypes.utxos, + ], ); } else { - _walletBloc.add( - UpdateWallet( - updatedWallet, - updateTypes: [ - UpdateWalletTypes.addresses, - UpdateWalletTypes.utxos, - ], - ), + await _appWalletsRepository + .getWalletServiceById(updatedWallet.id) + ?.updateWallet( + updatedWallet, + updateTypes: [ + UpdateWalletTypes.addresses, + UpdateWalletTypes.utxos, + ], ); } } else { - _walletBloc - .add(UpdateWallet(w, updateTypes: [UpdateWalletTypes.addresses])); + await _appWalletsRepository.getWalletServiceById(w.id)?.updateWallet( + w, + updateTypes: [UpdateWalletTypes.addresses], + ); } emit( state.copyWith( diff --git a/lib/address/pop_up.dart b/lib/address/pop_up.dart index dc5e91b9..44c0697e 100644 --- a/lib/address/pop_up.dart +++ b/lib/address/pop_up.dart @@ -3,6 +3,7 @@ import 'package:bb_mobile/_pkg/clipboard.dart'; import 'package:bb_mobile/_pkg/launcher.dart'; import 'package:bb_mobile/_pkg/wallet/address.dart'; import 'package:bb_mobile/_pkg/wallet/bdk/utxo.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; import 'package:bb_mobile/_ui/bottom_sheet.dart'; import 'package:bb_mobile/_ui/components/button.dart'; import 'package:bb_mobile/_ui/components/text.dart'; @@ -12,7 +13,7 @@ import 'package:bb_mobile/address/bloc/address_cubit.dart'; import 'package:bb_mobile/address/bloc/address_state.dart'; import 'package:bb_mobile/currency/bloc/currency_cubit.dart'; import 'package:bb_mobile/locator.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/network/bloc/network_bloc.dart'; import 'package:bb_mobile/settings/bloc/settings_cubit.dart'; import 'package:bb_mobile/styles.dart'; import 'package:bb_mobile/wallet/bloc/wallet_bloc.dart'; @@ -38,7 +39,8 @@ class AddressPopUp extends StatelessWidget { final addressCubit = AddressCubit( address: address, walletAddress: locator(), - walletBloc: wallet, + appWalletsRepository: locator(), + wallet: wallet.state.wallet, bdkUtxo: locator(), ); @@ -110,9 +112,9 @@ class Title extends StatelessWidget { .select((AddressCubit cubit) => cubit.state.address!.miniString()); final walletName = - context.select((WalletBloc cubit) => cubit.state.wallet!.name ?? ''); + context.select((WalletBloc cubit) => cubit.state.wallet.name ?? ''); final walletFingerprint = context.select( - (WalletBloc cubit) => cubit.state.wallet!.sourceFingerprint, + (WalletBloc cubit) => cubit.state.wallet.sourceFingerprint, ); final title = walletName.isEmpty ? walletFingerprint : walletName; @@ -138,7 +140,7 @@ class AddressQR extends StatelessWidget { final address = context.select((AddressCubit cubit) => cubit.state.address!); final url = context.select( - (NetworkCubit _) => _.state + (NetworkBloc _) => _.state .explorerAddressUrl(address.address, isLiquid: address.isLiquid), ); diff --git a/lib/auth/page.dart b/lib/auth/page.dart index e9e16e04..8e7dc954 100644 --- a/lib/auth/page.dart +++ b/lib/auth/page.dart @@ -7,7 +7,8 @@ import 'package:bb_mobile/_ui/page_template.dart'; import 'package:bb_mobile/_ui/toast.dart'; import 'package:bb_mobile/auth/bloc/cubit.dart'; import 'package:bb_mobile/auth/bloc/state.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; +import 'package:bb_mobile/home/bloc/home_bloc.dart'; +import 'package:bb_mobile/home/bloc/home_event.dart'; import 'package:bb_mobile/locator.dart'; import 'package:bb_mobile/styles.dart'; import 'package:flutter/material.dart'; @@ -38,7 +39,7 @@ class AuthPage extends StatelessWidget { listener: (context, state) async { if (state.loggedIn) { if (!state.fromSettings) { - locator().getWalletsFromStorage(); + locator().add(LoadWalletsFromStorage()); context.go('/home'); } else { ScaffoldMessenger.of(context).showSnackBar( diff --git a/lib/create/bloc/create_cubit.dart b/lib/create/bloc/create_cubit.dart index 5c3e8574..6e3d925f 100644 --- a/lib/create/bloc/create_cubit.dart +++ b/lib/create/bloc/create_cubit.dart @@ -4,10 +4,10 @@ import 'package:bb_mobile/_pkg/wallet/bdk/sensitive_create.dart'; import 'package:bb_mobile/_pkg/wallet/create.dart'; import 'package:bb_mobile/_pkg/wallet/create_sensitive.dart'; import 'package:bb_mobile/_pkg/wallet/lwk/sensitive_create.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/sensitive_storage.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/storage.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; +import 'package:bb_mobile/_repository/wallet/sensitive_wallet_storage.dart'; +import 'package:bb_mobile/_repository/wallet/wallet_storage.dart'; import 'package:bb_mobile/create/bloc/state.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class CreateWalletCubit extends Cubit { @@ -15,18 +15,17 @@ class CreateWalletCubit extends Cubit { required WalletSensitiveCreate walletSensCreate, required WalletsStorageRepository walletsStorageRepository, required WalletSensitiveStorageRepository walletSensRepository, - required NetworkCubit networkCubit, + required NetworkRepository networkRepository, required WalletCreate walletCreate, required BDKSensitiveCreate bdkSensitiveCreate, required LWKSensitiveCreate lwkSensitiveCreate, - // bool fromHome = false, bool mainWallet = false, }) : _lwkSensitiveCreate = lwkSensitiveCreate, _bdkSensitiveCreate = bdkSensitiveCreate, _walletCreate = walletCreate, - _networkCubit = networkCubit, _walletSensRepository = walletSensRepository, _walletsStorageRepository = walletsStorageRepository, + _networkRepository = networkRepository, _walletSensCreate = walletSensCreate, super( CreateWalletState(mainWallet: mainWallet), @@ -37,10 +36,11 @@ class CreateWalletCubit extends Cubit { final WalletSensitiveCreate _walletSensCreate; final WalletsStorageRepository _walletsStorageRepository; final WalletSensitiveStorageRepository _walletSensRepository; - final NetworkCubit _networkCubit; + final WalletCreate _walletCreate; final BDKSensitiveCreate _bdkSensitiveCreate; final LWKSensitiveCreate _lwkSensitiveCreate; + final NetworkRepository _networkRepository; Future createMne({bool fromHome = false}) async { emit(state.copyWith(creatingNmemonic: true)); @@ -61,8 +61,6 @@ class CreateWalletCubit extends Cubit { creatingNmemonic: false, ), ); - - // if (fromHome) firstTime(); } void passPhraseChanged(String text) { @@ -72,14 +70,6 @@ class CreateWalletCubit extends Cubit { void walletLabelChanged(String text) { emit(state.copyWith(walletLabel: text)); } - // void _showSavingErr(String err) { - // emit( - // state.copyWith( - // errSaving: err, - // creatingNmemonic: false, - // ), - // ); - // } void toggleIsInstant(bool isInstant) => emit(state.copyWith(isInstant: isInstant)); @@ -123,8 +113,8 @@ class CreateWalletCubit extends Cubit { } } - final network = - _networkCubit.state.testnet ? BBNetwork.Testnet : BBNetwork.Mainnet; + final network = _networkRepository.getBBNetwork; + final mnemonic = state.mnemonic!.join(' '); final (seed, sErr) = await _walletSensCreate.mnemonicSeed(mnemonic, network); @@ -139,8 +129,6 @@ class CreateWalletCubit extends Cubit { network: network, walletType: BBWalletType.main, walletCreate: _walletCreate, - // walletType: network, - // false, ); if (wErr != null) { emit(state.copyWith(saving: false, errSaving: 'Error Creating Wallet')); @@ -222,8 +210,6 @@ class CreateWalletCubit extends Cubit { network: network, walletType: BBWalletType.main, walletCreate: _walletCreate, - // walletType: network, - // false, ); if (wErr != null) { emit(state.copyWith(saving: false, errSaving: 'Error Creating Wallet')); @@ -244,73 +230,6 @@ class CreateWalletCubit extends Cubit { return updatedWallet; } - // void firstTime() async { - // if (state.mnemonic == null) return; - // emit(state.copyWith(saving: true, errSaving: '')); - - // final mnemonic = state.mnemonic!.join(' '); - // final (seed, errMne) = await walletSensCreate.mnemonicSeed( - // mnemonic, - // BBNetwork.Mainnet, - // ); - // if (errMne != null) { - // emit(state.copyWith(saving: false, errSaving: 'Error Creating Seed')); - // } - // var (walletSecure, errCreating1) = await walletSensCreate.oneFromBIP39( - // seed: seed!, - // passphrase: '', - // scriptType: ScriptType.bip84, - // network: BBNetwork.Mainnet, - // walletType: BBWalletType.secure, - // ); - // if (errCreating1 != null) { - // emit(state.copyWith(saving: false, errSaving: 'Error Creating Wallet')); - // return; - // } - - // // var (walletInstant, errCreating2) = await walletSensCreate.oneFromBIP39( - // // seed: seed, - // // passphrase: '', - // // scriptType: ScriptType.bip84, - // // network: BBNetwork.Mainnet, - // // walletType: BBWalletType.instant, - // // ); - // // if (errCreating2 != null) { - // // emit(state.copyWith(saving: false, errSaving: 'Error Creating Wallet')); - // // return; - // // } - - // walletSecure = walletSecure!.copyWith(name: 'Bull Wallet'); - // // walletInstant = walletInstant!.copyWith(name: 'Instant Wallet'); - - // final errSavingSeed = - // await walletSensRepository.newSeed(seed: seed, secureStore: secureStorage); - // if (errSavingSeed != null) { - // emit(state.copyWith(saving: false, errSaving: 'Error Saving Seed')); - // } - - // final errSaving1 = - // await walletsStorageRepository.newWallet(wallet: walletSecure, hiveStore: hiveStorage); - // if (errSaving1 != null) { - // emit(state.copyWith(saving: false, errSaving: 'Error Saving Wallet')); - // } - // // final errSaving2 = - // // await walletsStorageRepository.newWallet(wallet: walletInstant, hiveStore: hiveStorage); - // // if (errSaving2 != null) { - // // emit(state.copyWith(saving: false, errSaving: 'Error Saving Wallet')); - // // } - - // clearSensitive(); - - // emit( - // state.copyWith( - // savedWallets: [walletSecure], - // saving: false, - // saved: true, - // ), - // ); - // } - void clearSensitive() { emit(state.copyWith(mnemonic: [], passPhrase: '')); } diff --git a/lib/create/page.dart b/lib/create/page.dart index 394d628c..c633b96d 100644 --- a/lib/create/page.dart +++ b/lib/create/page.dart @@ -3,8 +3,9 @@ import 'package:bb_mobile/_pkg/wallet/bdk/sensitive_create.dart'; import 'package:bb_mobile/_pkg/wallet/create.dart'; import 'package:bb_mobile/_pkg/wallet/create_sensitive.dart'; import 'package:bb_mobile/_pkg/wallet/lwk/sensitive_create.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/sensitive_storage.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/storage.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; +import 'package:bb_mobile/_repository/wallet/sensitive_wallet_storage.dart'; +import 'package:bb_mobile/_repository/wallet/wallet_storage.dart'; import 'package:bb_mobile/_ui/app_bar.dart'; import 'package:bb_mobile/_ui/components/button.dart'; import 'package:bb_mobile/_ui/components/controls.dart'; @@ -16,9 +17,9 @@ import 'package:bb_mobile/_ui/word_grid.dart'; import 'package:bb_mobile/create/bloc/create_cubit.dart'; import 'package:bb_mobile/create/bloc/state.dart'; import 'package:bb_mobile/create/confirm_popup.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; +import 'package:bb_mobile/home/bloc/home_bloc.dart'; +import 'package:bb_mobile/home/bloc/home_event.dart'; import 'package:bb_mobile/locator.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; import 'package:bb_mobile/styles.dart'; import 'package:extra_alignments/extra_alignments.dart'; import 'package:flutter/material.dart'; @@ -40,7 +41,8 @@ class CreateWalletPage extends StatelessWidget { walletSensCreate: locator(), walletsStorageRepository: locator(), walletSensRepository: locator(), - networkCubit: locator(), + // networkCubit: locator(), + networkRepository: locator(), walletCreate: locator(), bdkSensitiveCreate: locator(), lwkSensitiveCreate: locator(), @@ -58,11 +60,11 @@ class CreateWalletPage extends StatelessWidget { if (state.savedWallets == null) return; //if (state.mainWallet) await locator().sortWallets(); - locator().getWalletsFromStorage(); + locator().add(LoadWalletsFromStorage()); // final wallets = state.savedWallets!; - // locator().addWallets(wallets); + // locator().addWallets(wallets); // await Future.delayed(500.milliseconds); - // locator().changeMoveToIdx(wallets.first); + // locator().changeMoveToIdx(wallets.first); // await Future.delayed(300.milliseconds); if (!context.mounted) return; context.go('/home'); diff --git a/lib/currency/bloc/currency_cubit.dart b/lib/currency/bloc/currency_cubit.dart index 2e98bbf6..5f5ffe5c 100644 --- a/lib/currency/bloc/currency_cubit.dart +++ b/lib/currency/bloc/currency_cubit.dart @@ -1,5 +1,4 @@ import 'dart:convert'; -import 'dart:math'; import 'package:bb_mobile/_model/currency.dart'; import 'package:bb_mobile/_pkg/bull_bitcoin_api.dart'; @@ -185,16 +184,16 @@ class CurrencyCubit extends Cubit { updateAmount(amt); } - int convertBtcStringToSats(String btcAmt) { - final split = btcAmt.split('.'); - final amountNo = int.parse(split[0]); - final len = min(8, split[1].length); - final amountDecimal = - int.parse(split[1].substring(0, len)) * pow(10, 8 - len); + // int convertBtcStringToSats(String btcAmt) { + // final split = btcAmt.split('.'); + // final amountNo = int.parse(split[0]); + // final len = min(8, split[1].length); + // final amountDecimal = + // int.parse(split[1].substring(0, len)) * pow(10, 8 - len); - final amountInSats = amountNo * 100000000 + amountDecimal; - return amountInSats.toInt(); - } + // final amountInSats = amountNo * 100000000 + amountDecimal; + // return amountInSats.toInt(); + // } void updateAmount(String txt) { var clean = txt.replaceAll(',', '').replaceAll(' ', ''); diff --git a/lib/currency/conversion.dart b/lib/currency/conversion.dart index 15aad5ea..f5cda3c3 100644 --- a/lib/currency/conversion.dart +++ b/lib/currency/conversion.dart @@ -1,6 +1,6 @@ import 'package:bb_mobile/_ui/components/text.dart'; import 'package:bb_mobile/currency/bloc/currency_cubit.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/network/bloc/network_bloc.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:gap/gap.dart'; @@ -10,15 +10,19 @@ class ConversionAmt extends StatelessWidget { @override Widget build(BuildContext context) { - final fiatSelected = context.select((CurrencyCubit cubit) => cubit.state.fiatSelected); - final isDefaultSats = context.select((CurrencyCubit cubit) => cubit.state.unitsInSats); + final fiatSelected = + context.select((CurrencyCubit cubit) => cubit.state.fiatSelected); + final isDefaultSats = + context.select((CurrencyCubit cubit) => cubit.state.unitsInSats); - final fiatAmt = context.select((CurrencyCubit cubit) => cubit.state.fiatAmt); + final fiatAmt = + context.select((CurrencyCubit cubit) => cubit.state.fiatAmt); final satsAmt = context.select((CurrencyCubit cubit) => cubit.state.amount); - final defaultCurrency = - context.select((CurrencyCubit cubit) => cubit.state.defaultFiatCurrency); + final defaultCurrency = context + .select((CurrencyCubit cubit) => cubit.state.defaultFiatCurrency); - final isTestNet = context.select((NetworkCubit cubit) => cubit.state.testnet); + final isTestNet = + context.select((NetworkBloc cubit) => cubit.state.networkData.testnet); var amt = ''; var unit = ''; diff --git a/lib/home/bloc/home_bloc.dart b/lib/home/bloc/home_bloc.dart new file mode 100644 index 00000000..8b865b4b --- /dev/null +++ b/lib/home/bloc/home_bloc.dart @@ -0,0 +1,107 @@ +import 'dart:async'; + +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; +import 'package:bb_mobile/home/bloc/home_event.dart'; +import 'package:bb_mobile/home/bloc/home_state.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class HomeBloc extends Bloc { + HomeBloc({ + required AppWalletsRepository appWalletsRepository, + required NetworkRepository networkRepository, + }) : _appWalletsRepository = appWalletsRepository, + _networkRepository = networkRepository, + super(const HomeState()) { + on(_onLoadWalletsFromStorage); + on(_onClearWallets); + on(_onUpdateErrDeepLink); + on(_onUpdatedNotifier); + on(_onLoadWalletsForNetwork); + on(_onWalletUpdated); + // on( + // (event, emit) async { + // print('wallets updated'); + // await emit.forEach( + // _appWalletsRepository.wallets, + // onData: (List ws) { + // add(UpdatedNotifier()); + // return state.copyWith(wallets: ws.map((_) => _.wallet).toList()); + // }, + // ); + // }, + // ); + + // add(LoadWalletsFromStorage()); + // add(WalletsSubscribe()); + } + + final AppWalletsRepository _appWalletsRepository; + final NetworkRepository _networkRepository; + + Future _onLoadWalletsFromStorage( + LoadWalletsFromStorage event, + Emitter emit, + ) async { + emit(state.copyWith(loadingWallets: true)); + await _appWalletsRepository.getWalletsFromStorage(); + final wallets = _appWalletsRepository.allWallets; + emit(state.copyWith(wallets: wallets)); + await _appWalletsRepository + .loadAllInNetwork(_networkRepository.getBBNetwork); + _appWalletsRepository.syncAllInNetwork(_networkRepository.getBBNetwork); + emit(state.copyWith(loadingWallets: false)); + } + + void _onClearWallets( + ClearWallets event, + Emitter emit, + ) { + emit(state.copyWith(wallets: [])); + } + + Future _onUpdateErrDeepLink( + UpdateErrDeepLink event, + Emitter emit, + ) async { + emit(state.copyWith(errDeepLinking: event.err)); + await Future.delayed(const Duration(seconds: 5)); + emit(state.copyWith(errDeepLinking: '')); + } + + Future _onUpdatedNotifier( + UpdatedNotifier event, + Emitter emit, + ) async { + if (event.fromStart) { + await Future.delayed(const Duration(seconds: 1)); + } + emit(state.copyWith(updated: true)); + await Future.delayed(const Duration(seconds: 2)); + emit(state.copyWith(updated: false)); + } + + void _onLoadWalletsForNetwork( + LoadWalletsForNetwork event, + Emitter emit, + ) { + final wallets = + _appWalletsRepository.walletServiceFromNetwork(event.network); + if (wallets.isEmpty) return; + for (final w in wallets) { + w.loadWallet(); + } + } + + void _onWalletUpdated( + WalletUpdated event, + Emitter emit, + ) { + final wallet = event.wallet; + final wallets = state.wallets.toList(); + final idx = wallets.indexWhere((w) => w.id == wallet.id); + if (idx == -1) return; + wallets[idx] = wallet; + emit(state.copyWith(wallets: wallets)); + } +} diff --git a/lib/home/bloc/home_cubit.dart b/lib/home/bloc/home_cubit.dart deleted file mode 100644 index dc120515..00000000 --- a/lib/home/bloc/home_cubit.dart +++ /dev/null @@ -1,207 +0,0 @@ -import 'dart:async'; - -import 'package:bb_mobile/_model/wallet.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/storage.dart'; -import 'package:bb_mobile/home/bloc/home_state.dart'; -import 'package:bb_mobile/wallet/bloc/event.dart'; -import 'package:bb_mobile/wallet/bloc/wallet_bloc.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -class HomeCubit extends Cubit { - HomeCubit({ - required WalletsStorageRepository walletsStorageRepository, - }) : _walletsStorageRepository = walletsStorageRepository, - super(const HomeState()); - - final WalletsStorageRepository _walletsStorageRepository; - - Future getWalletsFromStorage() async { - emit(state.copyWith(loadingWallets: true)); - - final (wallets, err) = await _walletsStorageRepository.readAllWallets(); - if (err != null && err.toString() != 'No Key') { - emit(state.copyWith(loadingWallets: false)); - return; - } - - if (wallets == null) { - emit(state.copyWith(loadingWallets: false)); - return; - } - - await Future.delayed(const Duration(milliseconds: 300)); - - emit( - state.copyWith( - tempwallets: wallets, - walletBlocs: null, - loadingWallets: false, - ), - ); - - // if (err.toString() == 'No Key') - // createWalletCubit.createMne( - // fromHome: true, - // ); - } - - void clearWallets() => emit(state.copyWith(tempwallets: null)); - - Future updateErrDeepLink(String err) async { - emit(state.copyWith(errDeepLinking: err)); - await Future.delayed(const Duration(seconds: 5)); - emit(state.copyWith(errDeepLinking: '')); - } - - // void updateWalletBloc(WalletBloc bloc) { - // final walletBlocs = state.walletBlocs != null - // ? state.walletBlocs!.toList() - // : []; - // final idx = walletBlocs - // .indexWhere((wB) => wB.state.wallet!.id == bloc.state.wallet!.id); - // walletBlocs[idx] = bloc; - - // emit(state.copyWith(walletBlocs: walletBlocs)); - // } - - void updateWalletBlocs(List blocs) => - emit(state.copyWith(walletBlocs: blocs)); - - void updateWalletBloc(WalletBloc bloc) { - final walletBlocs = state.walletBlocs != null - ? state.walletBlocs!.toList() - : []; - final idx = walletBlocs - .indexWhere((wB) => wB.state.wallet!.id == bloc.state.wallet!.id); - walletBlocs[idx] = bloc; - - emit(state.copyWith(walletBlocs: walletBlocs)); - - updatedNotifier(); - } - - Future updatedNotifier() async { - emit(state.copyWith(updated: true)); - await Future.delayed(const Duration(seconds: 2)); - emit(state.copyWith(updated: false)); - } - - void loadWalletsForNetwork(BBNetwork network) { - final blocs = state.walletBlocsFromNetwork(network); - - if (blocs.isEmpty) return; - - for (final bloc in blocs) { - final w = bloc.state.wallet!; - bloc.add(LoadWallet(w.getWalletStorageString())); - } - } - - // void addWallets(List wallets) { - // emit(state.copyWith(loadingWallets: true)); - - // final walletss = state.wallets != null ? state.wallets!.toList() : []; - // walletss.addAll(wallets); - - // emit( - // state.copyWith( - // wallets: walletss, - // loadingWallets: false, - // ), - // ); - // } - - // void removeWalletPostDelete(String id) { - // final wallets = state.wallets != null ? state.wallets!.toList() : []; - // final walletBlocs = state.walletBlocs != null ? state.walletBlocs!.toList() : []; - // wallets.removeWhere( - // (w) => w.id == id, - // ); - // walletBlocs.removeWhere((wB) => wB.state.wallet!.id == id); - // emit( - // state.copyWith( - // wallets: wallets, - // walletBlocs: walletBlocs, - // // selectedWallet: null, - // ), - // ); - // } - - // void updateSelectedWallet(WalletBloc walletBloc) { - // // final wallet = walletBloc.state.wallet!; - // // final wallets = state.wallets != null ? state.wallets!.toList() : []; - // // final idx = wallets.indexWhere((w) => w.id == wallet.id); - // // wallets[idx] = wallet; - - // emit( - // state.copyWith( - // // wallets: wallets, - // selectedWalletCubit: walletBloc, - // ), - // ); - // } - - // void walletSelected(WalletBloc walletBloc) async { - // await Future.delayed(500.microseconds); - // emit(state.copyWith(selectedWalletCubit: walletBloc)); - - // final network = walletBloc.state.wallet!.network; - // final idx = state.getWalletBlocIdx(walletBloc); - - // emit( - // state.copyWith( - // lastMainnetWalletIdx: network == BBNetwork.Mainnet ? idx : state.lastMainnetWalletIdx, - // lastTestnetWalletIdx: network == BBNetwork.Testnet ? idx : state.lastTestnetWalletIdx, - // ), - // ); - // } - - // void changeMoveToIdx(Wallet wallet) async { - // final idx = state.getWalletIdx(wallet); - // emit(state.copyWith(moveToIdx: idx)); - // await Future.delayed(const Duration(seconds: 5)); - // emit(state.copyWith(moveToIdx: null)); - // } - - // void moveToLastWallet() async { - // await Future.delayed(const Duration(milliseconds: 500)); - - // emit(state.copyWith(moveToIdx: state.wallets!.length - 1)); - // await Future.delayed(const Duration(seconds: 5)); - // emit(state.copyWith(moveToIdx: null)); - // } - - // void networkChanged(BBNetwork network) async { - // final wallets = state.walletBlocsFromNetwork(network); - // if (wallets.isEmpty) { - // emit(state.copyWith(selectedWalletCubit: null)); - // return; - // } - - // final lastWalletIdx = state.getLastWalletIdx(network); - // if (lastWalletIdx != null && wallets.length > lastWalletIdx) { - // final wallet = wallets[lastWalletIdx].state.wallet!; - // changeMoveToIdx(wallet); - // } else - // emit(state.copyWith(selectedWalletCubit: wallets.first)); - // } - - void removeWallet(WalletBloc walletBloc) { - // final wallets = state.wallets != null ? state.wallets!.toList() : []; - // wallets.removeWhere((w) => w.id == walletBloc.state.wallet!.id); - final walletBlocs = state.walletBlocs != null - ? state.walletBlocs!.toList() - : []; - walletBlocs.removeWhere( - (wB) => wB.state.wallet!.id == walletBloc.state.wallet!.id, - ); - - emit( - state.copyWith( - // wallets: wallets, - // selectedWalletCubit: null, - walletBlocs: walletBlocs, - ), - ); - } -} diff --git a/lib/home/bloc/home_event.dart b/lib/home/bloc/home_event.dart new file mode 100644 index 00000000..fce8252c --- /dev/null +++ b/lib/home/bloc/home_event.dart @@ -0,0 +1,30 @@ +import 'package:bb_mobile/_model/wallet.dart'; + +class HomeEvent {} + +class LoadWalletsFromStorage extends HomeEvent {} + +class ClearWallets extends HomeEvent {} + +class UpdateErrDeepLink extends HomeEvent { + UpdateErrDeepLink(this.err); + final String err; +} + +class UpdatedNotifier extends HomeEvent { + bool fromStart; + + UpdatedNotifier({this.fromStart = false}); +} + +class LoadWalletsForNetwork extends HomeEvent { + LoadWalletsForNetwork(this.network); + final BBNetwork network; +} + +class WalletUpdated extends HomeEvent { + WalletUpdated(this.wallet); + final Wallet wallet; +} + +class WalletsSubscribe extends HomeEvent {} diff --git a/lib/home/bloc/home_state.dart b/lib/home/bloc/home_state.dart index e4493c69..75054621 100644 --- a/lib/home/bloc/home_state.dart +++ b/lib/home/bloc/home_state.dart @@ -1,7 +1,6 @@ import 'package:bb_mobile/_model/swap.dart'; import 'package:bb_mobile/_model/transaction.dart'; import 'package:bb_mobile/_model/wallet.dart'; -import 'package:bb_mobile/wallet/bloc/wallet_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'home_state.freezed.dart'; @@ -9,12 +8,13 @@ part 'home_state.freezed.dart'; @freezed class HomeState with _$HomeState { const factory HomeState({ - List? tempwallets, - List? walletBlocs, + @Default([]) List wallets, + // List? tempwallets, + // List? wallets, @Default(true) bool loadingWallets, @Default('') String errLoadingWallets, // Wallet? selectedWallet, - // WalletBloc? selectedWalletCubit, + // Wallet? selectedWalletCubit, // int? lastTestnetWalletIdx, // int? lastMainnetWalletIdx, @Default('') String errDeepLinking, @@ -24,17 +24,16 @@ class HomeState with _$HomeState { const HomeState._(); bool hasWallets() => - !loadingWallets && walletBlocs != null && walletBlocs!.isNotEmpty; + !loadingWallets && wallets != null && wallets!.isNotEmpty; - // List walletsFromNetwork(BBNetwork network) => - // walletBlocs?.where((wallet) => wallet.network == network).toList().reversed.toList() ?? []; + // List walletsFromNetwork(BBNetwork network) => + // wallets?.where((wallet) => wallet.network == network).toList().reversed.toList() ?? []; - bool hasMainWallets() => - walletBlocs?.any((wallet) => wallet.state.wallet!.mainWallet) ?? false; + bool hasMainWallets() => wallets?.any((_) => _.mainWallet) ?? false; - List walletBlocsFromNetwork(BBNetwork network) { - final blocs = walletBlocs - ?.where((walletBloc) => walletBloc.state.wallet?.network == network) + List walletsFromNetwork(BBNetwork network) { + final blocs = wallets + ?.where((_) => _.network == network) //.toList() //.reversed .toList() ?? @@ -43,12 +42,12 @@ class HomeState with _$HomeState { return blocs; } - List walletBlocsFromNetworkExcludeWatchOnly(BBNetwork network) { - final blocs = walletBlocs + List walletsFromNetworkExcludeWatchOnly(BBNetwork network) { + final blocs = wallets ?.where( (walletBloc) => - walletBloc.state.wallet?.network == network && - walletBloc.state.wallet!.watchOnly() == false, + walletBloc.network == network && + walletBloc.watchOnly() == false, ) .toList() ?? []; @@ -56,12 +55,10 @@ class HomeState with _$HomeState { return blocs; } - List walletBlocsNotMainFromNetwork(BBNetwork network) { - final blocs = walletBlocs + List walletsNotMainFromNetwork(BBNetwork network) { + final blocs = wallets ?.where( - (wallet) => - wallet.state.wallet?.network == network && - !wallet.state.wallet!.mainWallet, + (wallet) => wallet.network == network && !wallet.mainWallet, ) .toList() .reversed @@ -72,55 +69,54 @@ class HomeState with _$HomeState { } int lenWalletsFromNetwork(BBNetwork network) => - walletBlocsFromNetwork(network).length; - - List getMainWallets(bool isTestnet) { - final network = isTestnet ? BBNetwork.Testnet : BBNetwork.Mainnet; - final instantwallet = getMainInstantWallet(network); - final securewallet = getMainSecureWallet(network); - return [ - if (instantwallet != null) instantwallet, - if (securewallet != null) securewallet, - ]; - } + walletsFromNetwork(network).length; + + // List getMainWallets(bool isTestnet) { + // final network = isTestnet ? BBNetwork.Testnet : BBNetwork.Mainnet; + // final instantwallet = getMainInstantWallet(network); + // final securewallet = getMainSecureWallet(network); + // return [ + // if (instantwallet != null) instantwallet, + // if (securewallet != null) securewallet, + // ]; + // } - List getMainWalletIDs(bool isTestnet) => - getMainWallets(isTestnet).map((e) => e.state.wallet!.id).toList(); + // List getMainWalletIDs(bool isTestnet) => + // getMainWallets(isTestnet).map((e) => e.id).toList(); - WalletBloc? getMainInstantWallet(BBNetwork network) { - final wallets = walletBlocsFromNetwork(network); - final idx = wallets.indexWhere( - (w) => w.state.wallet!.isInstant() && w.state.wallet!.mainWallet, - ); - if (idx == -1) return null; - return wallets[idx]; - } + // Wallet? getMainInstantWallet(BBNetwork network) { + // final wallets = walletsFromNetwork(network); + // final idx = wallets.indexWhere( + // (w) => w.isInstant() && w.mainWallet, + // ); + // if (idx == -1) return null; + // return wallets[idx]; + // } - WalletBloc? getMainSecureWallet(BBNetwork network) { - final wallets = walletBlocsFromNetwork(network); - final idx = wallets.indexWhere( - (w) => w.state.wallet!.isSecure() && w.state.wallet!.mainWallet, - ); - if (idx == -1) return null; - return wallets[idx]; - } + // Wallet? getMainSecureWallet(BBNetwork network) { + // final wallets = walletsFromNetwork(network); + // final idx = wallets.indexWhere( + // (w) => w.isSecure() && w.mainWallet, + // ); + // if (idx == -1) return null; + // return wallets[idx]; + // } bool noNetworkWallets(BBNetwork network) => - walletBlocsFromNetwork(network).isEmpty; + walletsFromNetwork(network).isEmpty; - WalletBloc? getWalletBloc(Wallet wallet) { - final walletBlocs = walletBlocsFromNetwork(wallet.network); - final idx = walletBlocs.indexWhere((w) => w.state.wallet!.id == wallet.id); + Wallet? getWallet(Wallet wallet) { + final wallets = walletsFromNetwork(wallet.network); + final idx = wallets.indexWhere((w) => w.id == wallet.id); if (idx == -1) return null; - return walletBlocs[idx]; + return wallets[idx]; } - WalletBloc? getWalletBlocFromTx(Transaction tx) { - if (walletBlocs == null) return null; + Wallet? getWalletFromTx(Transaction tx) { + if (wallets == null) return null; - for (final walletBloc in walletBlocs!) { - final wallet = walletBloc.state.wallet; - if (wallet == null) continue; + for (final walletBloc in wallets!) { + final wallet = walletBloc; if (wallet.transactions.indexWhere((t) => t.txid == tx.txid) != -1) { return walletBloc; } @@ -129,12 +125,11 @@ class HomeState with _$HomeState { return null; } - WalletBloc? getWalletBlocFromSwapTx(SwapTx swaptx) { - if (walletBlocs == null) return null; + Wallet? getWalletFromSwapTx(SwapTx swaptx) { + if (wallets == null) return null; - for (final walletBloc in walletBlocs!) { - final wallet = walletBloc.state.wallet; - if (wallet == null) continue; + for (final walletBloc in wallets!) { + final wallet = walletBloc; if (wallet.transactions.indexWhere( (t) => t.swapTx?.id == swaptx.id, ) != @@ -146,10 +141,10 @@ class HomeState with _$HomeState { return null; } - Wallet? getWalletFromTx(Transaction tx) { - final walletBloc = getWalletBlocFromTx(tx); - return walletBloc?.state.wallet; - } + // Wallet? getWalletFromTx(Transaction tx) { + // final walletBloc = getWalletFromTx(tx); + // return walletBloc?; + // } bool walletIsLiquidFromTx(Transaction tx) { final wallet = getWalletFromTx(tx); @@ -158,27 +153,27 @@ class HomeState with _$HomeState { } bool walletIsWatchOnlyFromTx(Transaction tx) { - final walletBloc = getWalletBlocById(tx.walletId!); - return walletBloc?.state.wallet!.watchOnly() ?? false; + final walletBloc = getWalletById(tx.walletId!); + return walletBloc?.watchOnly() ?? false; } - WalletBloc? getWalletBlocById(String id) { + Wallet? getWalletById(String id) { // final walletIdx = wallets!.indexWhere((w) => w.id == id); // if (walletIdx == -1) return null; // final wallet = wallets![walletIdx]; - // final walletBlocs = walletBlocsFromNetwork(wallet.network); - final idx = walletBlocs?.indexWhere((w) => id == w.state.wallet!.id); + // final wallets = walletsFromNetwork(wallet.network); + final idx = wallets?.indexWhere((w) => id == w.id); if (idx == -1 || idx == null) return null; - return walletBlocs![idx]; + return wallets![idx]; } Wallet? getFirstWithSpendableAndBalance(BBNetwork network, {int amt = 0}) { - final wallets = walletBlocsFromNetwork(network); + final wallets = walletsFromNetwork(network); if (wallets.isEmpty) return null; Wallet? wallet; for (final w in wallets) { - final ww = w.state.wallet; - if (!ww!.watchOnly()) { + final ww = w; + if (!ww.watchOnly()) { if ((ww.balance ?? 0) > amt) return ww; wallet = ww; } @@ -187,16 +182,16 @@ class HomeState with _$HomeState { } SwapTx? getSwapTxById(String id) { - for (final walletBloc in walletBlocs!) { - final wallet = walletBloc.state.wallet; - if (wallet == null || wallet.swaps.isEmpty) continue; + for (final walletBloc in wallets!) { + final wallet = walletBloc; + if (wallet.swaps.isEmpty) continue; final idx = wallet.swaps.indexWhere((_) => _.id == id); if (idx != -1) return wallet.swaps[idx]; } - for (final walletBloc in walletBlocs!) { - final wallet = walletBloc.state.wallet; - if (wallet == null || wallet.transactions.isEmpty) continue; + for (final walletBloc in wallets!) { + final wallet = walletBloc; + if (wallet.transactions.isEmpty) continue; final idx = wallet.transactions.indexWhere((_) => _.swapTx?.id == id); if (idx != -1) return wallet.transactions[idx].swapTx; } @@ -204,17 +199,17 @@ class HomeState with _$HomeState { return null; } - Transaction? getTxFromSwap(SwapTx swap) { - final isLiq = swap.isLiquid(); - final network = swap.network; - final wallet = !isLiq - ? getMainSecureWallet(network)?.state.wallet - : getMainInstantWallet(network)?.state.wallet; - if (wallet == null) return null; - final idx = wallet.transactions.indexWhere((t) => t.swapTx?.id == swap.id); - if (idx == -1) return null; - return wallet.transactions[idx]; - } + // Transaction? getTxFromSwap(SwapTx swap) { + // final isLiq = swap.isLiquid(); + // final network = swap.network; + // final wallet = !isLiq + // ? getMainSecureWallet(network)? + // : getMainInstantWallet(network)?; + // if (wallet == null) return null; + // final idx = wallet.transactions.indexWhere((t) => t.swapTx?.id == swap.id); + // if (idx == -1) return null; + // return wallet.transactions[idx]; + // } // int? getLastWalletIdx(BBNetwork network) { // if (network == BBNetwork.Testnet) return lastTestnetWalletIdx; @@ -222,37 +217,35 @@ class HomeState with _$HomeState { // } int? getWalletIdx(Wallet wallet) { - final walletsFromNetwork = walletBlocsFromNetwork(wallet.network); - final idx = - walletsFromNetwork.indexWhere((w) => w.state.wallet!.id == wallet.id); + final walletssFromNetwork = walletsFromNetwork(wallet.network); + final idx = walletssFromNetwork.indexWhere((w) => w.id == wallet.id); if (idx == -1) return null; return idx; } - int? getWalletBlocIdx(WalletBloc walletBloc) { - final walletsFromNetwork = - walletBlocsFromNetwork(walletBloc.state.wallet!.network); - final idx = walletsFromNetwork - .indexWhere((w) => w.state.wallet!.id == walletBloc.state.wallet!.id); - if (idx == -1) return null; - return idx; - } + // int? getWalletIdx(Wallet walletBloc) { + // final walletssFromNetwork = + // walletsFromNetwork(walletBloc.network); + // final idx = walletssFromNetwork + // .indexWhere((w) => w.id == walletBloc.id); + // if (idx == -1) return null; + // return idx; + // } // int? getSelectedWalletIdx() { // if (selectedWalletCubit == null) return null; - // final walletsFromNetwork = walletBlocsFromNetwork(selectedWalletCubit!.state.wallet!.network); + // final walletsFromNetwork = walletsFromNetwork(selectedWalletCubit!!.network); // final idx = walletsFromNetwork - // .indexWhere((w) => w.state.wallet!.id == selectedWalletCubit!.state.wallet!.id); + // .indexWhere((w) => w!.id == selectedWalletCubit!!.id); // if (idx == -1) return null; // return idx; // } List allTxs(BBNetwork network) { final txs = []; - for (final walletBloc in walletBlocsFromNetwork(network)) { - final walletTxs = - walletBloc.state.wallet?.transactions ?? []; - // final wallet = walletBloc.state.wallet; + for (final walletBloc in walletsFromNetwork(network)) { + final walletTxs = walletBloc.transactions; + // final wallet = walletBloc; for (final tx in walletTxs) { txs.add(tx); } @@ -263,17 +256,16 @@ class HomeState with _$HomeState { List getAllTxs(BBNetwork network) { final txs = []; - for (final walletBloc in walletBlocsFromNetwork(network)) { - final walletTxs = - walletBloc.state.wallet?.transactions ?? []; - // final wallet = walletBloc.state.wallet; + for (final walletBloc in walletsFromNetwork(network)) { + final walletTxs = walletBloc.transactions; + // final wallet = walletBloc; for (final tx in walletTxs) { // final isInSwapTx = // swapTxs.where((swap) => swap.txid == tx.txid).isNotEmpty; // if (isInSwapTx == true) continue; txs.add( tx.copyWith( - walletId: walletBloc.state.wallet!.id, + walletId: walletBloc.id, ), ); } @@ -339,45 +331,42 @@ class HomeState with _$HomeState { int totalBalanceSats(BBNetwork network) { var total = 0; - for (final walletBloc in walletBlocsFromNetwork(network)) { - final wallet = walletBloc.state.wallet; - if (wallet == null) continue; + for (final walletBloc in walletsFromNetwork(network)) { + final wallet = walletBloc; total += wallet.balance ?? 0; } return total; } - WalletBloc? firstWalletWithEnoughBalance(int sats, BBNetwork network) { - for (final walletBloc in walletBlocsFromNetwork(network)) { - final enoughBalance = walletBloc.state.balanceSats() >= sats; - if (enoughBalance) return walletBloc; + Wallet? firstWalletWithEnoughBalance(int sats, BBNetwork network) { + for (final wallet in walletsFromNetwork(network)) { + final enoughBalance = wallet.balanceSats() >= sats; + if (enoughBalance) return wallet; } return null; } - WalletBloc? selectWalletWithHighestBalance( + Wallet? selectWalletWithHighestBalance( int sats, BBNetwork network, { bool onlyMain = false, bool onlyBitcoin = false, bool onlyLiquid = false, }) { - final List filteredWallets = - walletBlocsFromNetwork(network).where((w) { - final wallet = w.state.wallet!; + final List filteredWallets = walletsFromNetwork(network).where((w) { + final wallet = w; if (onlyMain && !wallet.mainWallet) return false; if (onlyBitcoin && !wallet.isBitcoin()) return false; if (onlyLiquid && !wallet.isLiquid()) return false; return true; }).toList(); - WalletBloc? walletBlocWithHighestBalance; - for (final walletBloc in filteredWallets) { - final enoughBalance = walletBloc.state.balanceSats() >= sats; + Wallet? walletBlocWithHighestBalance; + for (final wallet in filteredWallets) { + final enoughBalance = wallet.balanceSats() >= sats; if (enoughBalance) { if (walletBlocWithHighestBalance == null || - walletBloc.state.balanceSats() > - walletBlocWithHighestBalance.state.balanceSats()) { - walletBlocWithHighestBalance = walletBloc; + wallet.balanceSats() > walletBlocWithHighestBalance.balanceSats()) { + walletBlocWithHighestBalance = wallet; } } } @@ -389,16 +378,16 @@ class HomeState with _$HomeState { return null; } - List walletsWithEnoughBalance( + List walletsWithEnoughBalance( int sats, BBNetwork network, { bool onlyMain = false, bool onlyBitcoin = false, bool onlyLiquid = false, }) { - final wallets = walletBlocsFromNetwork(network).where( + final wallets = walletsFromNetwork(network).where( (_) { - final wallet = _.state.wallet!; + final wallet = _; if (onlyMain && !wallet.mainWallet) return false; if (onlyBitcoin && !wallet.isBitcoin()) return false; if (onlyLiquid && !wallet.isLiquid()) return false; @@ -406,10 +395,10 @@ class HomeState with _$HomeState { }, ).toList(); - final List walletsWithEnoughBalance = []; + final List walletsWithEnoughBalance = []; for (final walletBloc in wallets) { - final enoughBalance = walletBloc.state.balanceSats() >= sats; + final enoughBalance = walletBloc.balanceSats() >= sats; if (enoughBalance) walletsWithEnoughBalance.add(walletBloc); } return walletsWithEnoughBalance.isEmpty @@ -417,25 +406,25 @@ class HomeState with _$HomeState { : walletsWithEnoughBalance; } - Set<({String info, WalletBloc walletBloc})> homeWarnings(BBNetwork network) { - bool instantBalWarning(WalletBloc wb) { - if (wb.state.wallet?.isInstant() == false) return false; - return wb.state.balanceSats() > 100000000; + Set<({String info, Wallet walletBloc})> homeWarnings(BBNetwork network) { + bool instantBalWarning(Wallet wb) { + if (wb.isInstant() == false) return false; + return wb.balanceSats() > 100000000; } - bool backupWarning(WalletBloc wb) => !wb.state.wallet!.backupTested; + bool backupWarning(Wallet wb) => !wb.backupTested; - final warnings = <({String info, WalletBloc walletBloc})>{}; + final warnings = <({String info, Wallet walletBloc})>{}; final List backupWalletFngrforBackupWarning = []; - for (final walletBloc in walletBlocsFromNetwork(network)) { + for (final walletBloc in walletsFromNetwork(network)) { if (instantBalWarning(walletBloc)) { warnings.add( (info: 'Instant wallet balance is high', walletBloc: walletBloc), ); } if (backupWarning(walletBloc)) { - final fngr = walletBloc.state.wallet!.sourceFingerprint; + final fngr = walletBloc.sourceFingerprint; if (backupWalletFngrforBackupWarning.contains(fngr)) continue; warnings.add( ( @@ -450,9 +439,9 @@ class HomeState with _$HomeState { return warnings; } - WalletBloc? findWalletBlocWithSameFngr(Wallet wallet) { - for (final wb in walletBlocs!) { - final w = wb.state.wallet!; + Wallet? findWalletWithSameFngr(Wallet wallet) { + for (final wb in wallets!) { + final w = wb; if (w.id == wallet.id) continue; if (w.sourceFingerprint == wallet.sourceFingerprint) return wb; } @@ -460,7 +449,7 @@ class HomeState with _$HomeState { } // int? selectedWalletIdx(BBNetwork network) { - // final wallet = selectedWalletCubit?.state.wallet; + // final wallet = selectedWalletCubit?; // if (wallet == null) return null; // final wallets = walletsFromNetwork(network); @@ -471,14 +460,14 @@ class HomeState with _$HomeState { // } // static int? selectedWalletIdx({ - // required WalletBloc selectedWalletCubit, - // required List walletCubits, + // required Wallet selectedWalletCubit, + // required List walletCubits, // }) { - // final wallet = selectedWalletCubit.state.wallet; + // final wallet = selectedWalletCubit; // if (wallet == null) return -1; // for (var i = 0; i < walletCubits.length; i++) - // if (walletCubits[i].state.wallet!.getWalletStorageString() == wallet.getWalletStorageString()) + // if (walletCubits[i]!.getWalletStorageString() == wallet.getWalletStorageString()) // return i; // return null; diff --git a/lib/home/deep_linking.dart b/lib/home/deep_linking.dart index 2d9ca1e1..20a50102 100644 --- a/lib/home/deep_linking.dart +++ b/lib/home/deep_linking.dart @@ -1,7 +1,8 @@ import 'package:bb_mobile/_pkg/deep_link.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; +import 'package:bb_mobile/home/bloc/home_bloc.dart'; +import 'package:bb_mobile/home/bloc/home_event.dart'; import 'package:bb_mobile/locator.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/network/bloc/network_bloc.dart'; import 'package:flutter/material.dart'; class DeepLinker extends StatefulWidget { @@ -29,20 +30,20 @@ class _DeepLinkerState extends State { } Future linkReceived(String link) async { - final homeCubit = locator(); + final homeCubit = locator(); final err = await locator().handleUri( link: link, // settingsCubit: locator(), - networkCubit: locator(), + networkCubit: locator(), homeCubit: homeCubit, context: context, ); - if (err != null) homeCubit.updateErrDeepLink(err.toString()); + if (err != null) homeCubit.add(UpdateErrDeepLink(err.toString())); } void errReceived(String err) { - locator().updateErrDeepLink(err); + locator().add(UpdateErrDeepLink(err)); } @override diff --git a/lib/home/home_page.dart b/lib/home/home_page.dart index f9c689ac..d12dc2df 100644 --- a/lib/home/home_page.dart +++ b/lib/home/home_page.dart @@ -7,8 +7,9 @@ import 'package:bb_mobile/_pkg/wallet/bdk/sensitive_create.dart'; import 'package:bb_mobile/_pkg/wallet/create.dart'; import 'package:bb_mobile/_pkg/wallet/create_sensitive.dart'; import 'package:bb_mobile/_pkg/wallet/lwk/sensitive_create.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/sensitive_storage.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/storage.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; +import 'package:bb_mobile/_repository/wallet/sensitive_wallet_storage.dart'; +import 'package:bb_mobile/_repository/wallet/wallet_storage.dart'; import 'package:bb_mobile/_ui/components/button.dart'; import 'package:bb_mobile/_ui/components/indicators.dart'; import 'package:bb_mobile/_ui/components/text.dart'; @@ -16,11 +17,12 @@ import 'package:bb_mobile/_ui/warning.dart'; import 'package:bb_mobile/create/bloc/create_cubit.dart'; import 'package:bb_mobile/create/bloc/state.dart'; import 'package:bb_mobile/currency/bloc/currency_cubit.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; +import 'package:bb_mobile/home/bloc/home_bloc.dart'; +import 'package:bb_mobile/home/bloc/home_event.dart'; import 'package:bb_mobile/home/listeners.dart'; import 'package:bb_mobile/home/transactions.dart'; import 'package:bb_mobile/locator.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/network/bloc/network_bloc.dart'; import 'package:bb_mobile/settings/bloc/lighting_cubit.dart'; import 'package:bb_mobile/styles.dart'; import 'package:bb_mobile/wallet/bloc/wallet_bloc.dart'; @@ -35,6 +37,12 @@ import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:google_fonts/google_fonts.dart'; +class AppWalletBlocs extends Cubit> { + AppWalletBlocs() : super([]); + void updateWalletBlocs(List _) => emit(_); + void clearWallets() => emit([]); +} + class HomePage extends StatelessWidget { const HomePage({super.key}); @@ -95,25 +103,25 @@ class _ScreenState extends State<_Screen> { @override Widget build(BuildContext context) { - // final _ = context.select((HomeCubit x) => x.state.updated); + // final _ = context.select((HomeBloc x) => x.state.updated); final loading = context.select( - (HomeCubit x) => x.state.loadingWallets, + (HomeBloc x) => x.state.loadingWallets, ); - final network = context.select((NetworkCubit x) => x.state.getBBNetwork()); + final network = context.select((NetworkBloc x) => x.state.getBBNetwork()); - final walletBlocs = context.select( - (HomeCubit x) => x.state.walletBlocsFromNetwork(network), + final wallets = context.select( + (HomeBloc x) => x.state.walletsFromNetwork(network), ); - // final hasWallets = context.select((HomeCubit x) => x.state.hasWallets()); + // final hasWallets = context.select((HomeBloc x) => x.state.hasWallets()); final hasMainWallets = context.select( - (HomeCubit x) => x.state.hasMainWallets(), + (HomeBloc x) => x.state.hasMainWallets(), ); // final walletBlocsLen = - // context.select((HomeCubit x) => x.state.lenWalletsFromNetwork(network)); + // context.select((HomeBloc x) => x.state.lenWalletsFromNetwork(network)); - if (!loading && (walletBlocs.isEmpty || !hasMainWallets)) { + if (!loading && (wallets.isEmpty || !hasMainWallets)) { final isTestnet = network == BBNetwork.Testnet; Widget widget = Scaffold( @@ -131,10 +139,10 @@ class _ScreenState extends State<_Screen> { } final warningsSize = - context.select((HomeCubit x) => x.state.homeWarnings(network)).length * + context.select((HomeBloc x) => x.state.homeWarnings(network)).length * 40.0; - final h = _calculateHeight(walletBlocs.length); + final h = _calculateHeight(wallets.length); scheduleMicrotask(() async { await Future.delayed(50.ms); @@ -184,7 +192,7 @@ class _ScreenState extends State<_Screen> { child: Container( height: 128, margin: const EdgeInsets.only(top: 16), - child: const HomeBottomBar2(walletBloc: null), + child: const HomeBottomBar2(wallet: null), ), ), ], @@ -201,7 +209,7 @@ class CardsList extends StatelessWidget { final Function(int) onChanged; - static List buildCardColumns(List wallets) { + static List buildCardColumns(List wallets) { final List columns = []; final isOne = wallets.length == 1; for (var i = 0; i < wallets.length; i += 3) { @@ -224,12 +232,12 @@ class CardsList extends StatelessWidget { @override Widget build(BuildContext context) { - final _ = context.select((HomeCubit x) => x.state.updated); + final _ = context.select((HomeBloc x) => x.state.updated); - final network = context.select((NetworkCubit x) => x.state.getBBNetwork()); - final walletBlocs = context - .select((HomeCubit x) => x.state.walletBlocsFromNetwork(network)); - final columns = buildCardColumns(walletBlocs); + final network = context.select((NetworkBloc x) => x.state.getBBNetwork()); + final wallets = + context.select((HomeBloc x) => x.state.walletsFromNetwork(network)); + final columns = buildCardColumns(wallets); return PageView( scrollDirection: Axis.vertical, @@ -249,14 +257,19 @@ class CardColumn extends StatelessWidget { this.showSwap = false, }); - final WalletBloc walletTop; - final WalletBloc? walletBottom; - final WalletBloc? walletLast; + final Wallet walletTop; + final Wallet? walletBottom; + final Wallet? walletLast; final bool onlyOne; final bool showSwap; @override Widget build(BuildContext context) { + final hasWallets = context.select( + (AppWalletBlocs _) => _.state.isNotEmpty, + ); + if (!hasWallets) return const SizedBox.shrink(); + return Padding( padding: const EdgeInsets.symmetric( horizontal: 26, @@ -267,17 +280,28 @@ class CardColumn extends StatelessWidget { Column( children: [ BlocProvider.value( - value: walletTop, + value: context.read().state.firstWhere( + (_) => _.state.wallet.id == walletTop.id, + ), + // create: (BuildContext context) => createWalletBloc(walletTop), child: const CardItem(), ), if (walletBottom != null) BlocProvider.value( - value: walletBottom!, + value: context.read().state.firstWhere( + (_) => _.state.wallet.id == walletBottom!.id, + ), + // create: (BuildContext context) => + // createWalletBloc(walletBottom!), child: const CardItem(), ), if (walletLast != null) BlocProvider.value( - value: walletLast!, + value: context.read().state.firstWhere( + (_) => _.state.wallet.id == walletLast!.id, + ), + // create: (BuildContext context) => + // createWalletBloc(walletLast!), child: const CardItem(), ) else if (!onlyOne) @@ -346,17 +370,16 @@ class CardItem extends StatelessWidget { @override Widget build(BuildContext context) { final wallet = context.select((WalletBloc x) => x.state.wallet); - if (wallet == null) return const SizedBox.shrink(); final (color, _) = WalletCardDetails.cardDetails(context, wallet); - final name = context.select((WalletBloc x) => x.state.wallet?.name); - final fingerprint = context - .select((WalletBloc x) => x.state.wallet?.sourceFingerprint ?? ''); + final name = context.select((WalletBloc x) => x.state.wallet.name); + final fingerprint = + context.select((WalletBloc x) => x.state.wallet.sourceFingerprint); final walletStr = - context.select((WalletBloc x) => x.state.wallet?.getWalletTypeStr()); + context.select((WalletBloc x) => x.state.wallet.getWalletTypeStr()); - final sats = context.select((WalletBloc x) => x.state.balanceSats()); + final sats = context.select((WalletBloc x) => x.state.wallet.balanceSats()); final balance = context.select( (CurrencyCubit x) => x.state.getAmountInUnits(sats, removeText: true), @@ -369,7 +392,7 @@ class CardItem extends StatelessWidget { context.select((CurrencyCubit x) => x.state.defaultFiatCurrency); final fiatAmt = context - .select((NetworkCubit x) => x.state.calculatePrice(sats, fiatCurrency)); + .select((NetworkBloc x) => x.state.calculatePrice(sats, fiatCurrency)); return SizedBox( width: double.infinity, @@ -390,8 +413,8 @@ class CardItem extends StatelessWidget { ), child: InkWell( onTap: () { - final walletBloc = context.read(); - context.push('/wallet', extra: walletBloc); + final w = context.read().state.wallet.id; + context.push('/wallet', extra: w); }, child: Padding( padding: const EdgeInsets.only( @@ -434,7 +457,7 @@ class CardItem extends StatelessWidget { Opacity( opacity: 0.7, child: BBText.bodySmall( - walletStr ?? '', + walletStr, onSurface: true, isBold: true, fontSize: 12, @@ -521,7 +544,7 @@ class WalletTag extends StatelessWidget { @override Widget build(BuildContext context) { final watchOnly = - context.read().state.walletIsWatchOnlyFromTx(tx); + context.read().state.walletIsWatchOnlyFromTx(tx); final darkMode = context.select( (Lighting x) => x.state.currentTheme(context) == ThemeMode.dark, @@ -660,22 +683,22 @@ class HomeTopBar2 extends StatelessWidget { } class HomeBottomBar2 extends StatefulWidget { - const HomeBottomBar2({super.key, required this.walletBloc}); + const HomeBottomBar2({super.key, required this.wallet}); - final WalletBloc? walletBloc; + final Wallet? wallet; @override State createState() => _HomeBottomBar2State(); } class _HomeBottomBar2State extends State { - WalletBloc? wb; + Wallet? wb; @override void initState() { - if (widget.walletBloc == null) { - final network = context.read().state.getBBNetwork(); + if (widget.wallet == null) { + final network = context.read().state.getBBNetwork(); final walletBlocs = - context.read().state.walletBlocsFromNetwork(network); + context.read().state.walletsFromNetwork(network); if (walletBlocs.length == 1) wb = walletBlocs.first; } super.initState(); @@ -703,7 +726,7 @@ class _HomeBottomBar2State extends State { onPressed: () { context.push( '/receive', - extra: widget.walletBloc ?? wb, + extra: widget.wallet?.id ?? wb?.id, ); }, ), @@ -762,7 +785,7 @@ class _HomeBottomBar2State extends State { class ScanButton extends StatelessWidget { const ScanButton({super.key, this.walletBloc}); - final WalletBloc? walletBloc; + final Wallet? walletBloc; @override Widget build(BuildContext context) { @@ -818,7 +841,7 @@ class ScanButton extends StatelessWidget { // Widget build(BuildContext context) { // final network = context.select((NetworkCubit x) => x.state.getBBNetwork()); // final walletBlocs = context -// .select((HomeCubit x) => x.state.walletBlocsFromNetwork(network)); +// .select((HomeBloc x) => x.state.walletBlocsFromNetwork(network)); // if (walletBlocs.isEmpty) return const SizedBox.shrink(); @@ -991,7 +1014,8 @@ class HomeNoWalletsWithCreation extends StatelessWidget { walletSensCreate: locator(), walletsStorageRepository: locator(), walletSensRepository: locator(), - networkCubit: locator(), + networkRepository: locator(), + // networkCubit: locator(), walletCreate: locator(), bdkSensitiveCreate: locator(), lwkSensitiveCreate: locator(), @@ -1053,7 +1077,8 @@ class HomeNoWalletsView extends StatelessWidget { if (state.savedWallets == null) return; //if (state.mainWallet) await locator().sortWallets(); - locator().getWalletsFromStorage(); + locator() + .add(LoadWalletsFromStorage()); //getWalletsFromStorage(); if (!context.mounted) return; context3.go('/home'); } @@ -1061,60 +1086,62 @@ class HomeNoWalletsView extends StatelessWidget { child: ColoredBox( color: context.colour.primary, child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SizedBox( - height: 148, - width: 184, - child: Image.asset('assets/bb-logo-white.png'), - ), - const Gap(24), - Text( - 'BULL BITCOIN', - style: font.copyWith( - fontSize: 80, - color: context.colour.primaryContainer, - height: 0.8, + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + height: 148, + width: 184, + child: Image.asset('assets/bb-logo-white.png'), ), - ), - Text( - 'OWN YOUR MONEY', - style: font.copyWith( - fontSize: 59, - height: 0.8, + const Gap(24), + Text( + 'BULL BITCOIN', + style: font.copyWith( + fontSize: 80, + color: context.colour.primaryContainer, + height: 0.8, + ), ), - ), - const Gap(8), - SizedBox( - width: w * 0.8, - child: const BBText.body( - 'Sovereign non-custodial Bitcoin wallet and Bitcoin-only exchange service. ', - onSurface: true, - textAlign: TextAlign.center, + Text( + 'OWN YOUR MONEY', + style: font.copyWith( + fontSize: 59, + height: 0.8, + ), ), - ), - const Gap(128), - Center( - child: BBButton.big( - label: 'Create new wallet', + const Gap(8), + SizedBox( + width: w * 0.8, + child: const BBText.body( + 'Sovereign non-custodial Bitcoin wallet and Bitcoin-only exchange service. ', + onSurface: true, + textAlign: TextAlign.center, + ), + ), + const Gap(110), + Center( + child: BBButton.big( + label: 'Create new wallet', + onPressed: () { + context.read().confirmClicked(); + // context.push('/create-wallet-main'); + }, + ), + ), + BBButton.text( + label: 'Recover wallet backup', + centered: true, + onSurface: true, + isBlue: false, + fontSize: 11, onPressed: () { - context.read().confirmClicked(); - // context.push('/create-wallet-main'); + context.push('/import-main'); }, ), - ), - BBButton.text( - label: 'Recover wallet backup', - centered: true, - onSurface: true, - isBlue: false, - fontSize: 11, - onPressed: () { - context.push('/import-main'); - }, - ), - ], + ], + ), ), ), ), @@ -1127,17 +1154,20 @@ class HomeWarnings extends StatelessWidget { @override Widget build(BuildContext context) { - final _ = context.select((HomeCubit _) => _.state.updated); - final network = context.select((NetworkCubit _) => _.state.getBBNetwork()); + final _ = context.select((HomeBloc _) => _.state.updated); + final network = context.select((NetworkBloc _) => _.state.getBBNetwork()); final warnings = - context.select((HomeCubit _) => _.state.homeWarnings(network)); + context.select((HomeBloc _) => _.state.homeWarnings(network)); return Column( children: [ for (final w in warnings) WarningBanner( onTap: () { - context.push('/wallet-settings/open-backup', extra: w.walletBloc); + context.push( + '/wallet-settings/open-backup', + extra: w.walletBloc.id, + ); }, info: w.info, ), diff --git a/lib/home/listeners.dart b/lib/home/listeners.dart index 02dfe42c..77c9539c 100644 --- a/lib/home/listeners.dart +++ b/lib/home/listeners.dart @@ -1,65 +1,80 @@ -import 'package:bb_mobile/_model/wallet.dart'; import 'package:bb_mobile/_pkg/logger.dart'; import 'package:bb_mobile/_pkg/payjoin/listeners.dart'; import 'package:bb_mobile/_pkg/payjoin/manager.dart'; -import 'package:bb_mobile/_pkg/wallet/address.dart'; -import 'package:bb_mobile/_pkg/wallet/balance.dart'; -import 'package:bb_mobile/_pkg/wallet/create.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/network.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/storage.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/wallets.dart'; -import 'package:bb_mobile/_pkg/wallet/sync.dart'; -import 'package:bb_mobile/_pkg/wallet/transaction.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; +import 'package:bb_mobile/home/bloc/home_bloc.dart'; +import 'package:bb_mobile/home/bloc/home_event.dart'; import 'package:bb_mobile/home/bloc/home_state.dart'; +import 'package:bb_mobile/home/home_page.dart'; import 'package:bb_mobile/locator.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; import 'package:bb_mobile/wallet/bloc/state.dart'; import 'package:bb_mobile/wallet/bloc/wallet_bloc.dart'; import 'package:bloc_concurrency/bloc_concurrency.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -class HomeWalletsSetupListener extends StatelessWidget { +class HomeWalletsSetupListener extends StatefulWidget { const HomeWalletsSetupListener({super.key, required this.child}); - List createWalletBlocs(List tempwallets) { - final walletCubits = [ - for (final w in tempwallets) - WalletBloc( - saveDir: w.getWalletStorageString(), - walletSync: locator(), - walletsStorageRepository: locator(), - walletBalance: locator(), - walletAddress: locator(), - networkCubit: locator(), - // swapBloc: locator(), - networkRepository: locator(), - walletsRepository: locator(), - walletTransactionn: locator(), - walletCreatee: locator(), - wallet: w, - ), - ]; - return walletCubits; - } + // List createWalletBlocs(List tempwallets) { + // final walletCubits = [ + // for (final w in tempwallets) + // WalletBloc( + // saveDir: w.getWalletStorageString(), + // walletSync: locator(), + // walletsStorageRepository: locator(), + // walletBalance: locator(), + // walletAddress: locator(), + // // networkCubit: locator(), + // // swapBloc: locator(), + // networkRepository: locator(), + // walletsRepository: locator(), + // walletTransactionn: locator(), + // walletCreatee: locator(), + // appWalletsRepository: locator(), + // wallet: w, + // ), + // ]; + // return walletCubits; + // } final Widget child; + @override + State createState() => + _HomeWalletsSetupListenerState(); +} + +class _HomeWalletsSetupListenerState extends State { + @override + void initState() { + // context.read().add(UpdatedNotifier(fromStart: true)); + final wallets = context.read().state.wallets; + if (wallets.isNotEmpty) { + context.read().updateWalletBlocs([ + for (final w in wallets) createOrRetreiveWalletBloc(w.id), + ]); + } + super.initState(); + } + @override Widget build(BuildContext context) { - return BlocListener( - listenWhen: (previous, current) => - previous.tempwallets != current.tempwallets, + // return WalletBlocListeners(child: child); + + return BlocListener( + listenWhen: (previous, current) => previous.wallets != current.wallets, listener: (context, state) { - if (state.tempwallets == null || state.tempwallets!.isEmpty) return; - // final walletBlocs = createWalletBlocs(state.tempwallets!); - context.read().updateWalletBlocs( - createWalletBlocs(state.tempwallets!), - ); - context.read().clearWallets(); + if (state.wallets.isEmpty) return; + + context.read().updateWalletBlocs([ + for (final w in state.wallets) createOrRetreiveWalletBloc(w.id), + ]); // final walletBlocs = createWalletBlocs(state.tempwallets!); + // context.read().updateWalletBlocs( + // createWalletBlocs(state.tempwallets!), + // ); + // context.read().clearWallets(); }, - child: WalletBlocListeners(child: child), + child: WalletBlocListeners(child: widget.child), ); } } @@ -71,48 +86,51 @@ class WalletBlocListeners extends StatelessWidget { @override Widget build(BuildContext context) { - final wallets = - context.select((HomeCubit cubit) => cubit.state.walletBlocs); - if (wallets == null || wallets.isEmpty) return child; - // .read().state.walletBlocs ?? []; + final wallets = context.select((HomeBloc cubit) => cubit.state.wallets); + final blocs = context.select((AppWalletBlocs _) => _.state); + if (wallets.isEmpty) return child; + // .read().state.walletBlocs ?? []; // print each wallet id for (final wallet in wallets) { - print('wallet id: ${wallet.state.wallet!.id}'); + // print('wallet id: ${wallet.id}'); } final mainWalletBloc = wallets.firstWhere( (bloc) => - bloc.state.wallet?.mainWallet == true && - !bloc.state.wallet!.watchOnly() && - bloc.state.wallet!.isSecure() && - bloc.state.wallet!.isActive(), + bloc.mainWallet == true && + !bloc.watchOnly() && + bloc.isSecure() && + bloc.isActive(), orElse: () => wallets.first, // Return first wallet if no main wallet found ); var walletChild = child; - if (mainWalletBloc.state.wallet?.mainWallet == true && - !mainWalletBloc.state.wallet!.watchOnly() && - mainWalletBloc.state.wallet!.isSecure()) { - print( - 'mainWalletBloc.state.wallet!.mainWallet: ${mainWalletBloc.state.wallet!.id}'); + if (mainWalletBloc.mainWallet == true && + !mainWalletBloc.watchOnly() && + mainWalletBloc.isSecure()) { + // print( + // 'mainWalletBloc.state.wallet!.mainWallet: ${mainWalletBloc.id}', + // ); walletChild = PayjoinLifecycleManager( - wallet: mainWalletBloc.state.wallet!, + wallet: mainWalletBloc, payjoinManager: locator(), child: child, ); } + if (blocs.isEmpty) return walletChild; + return MultiBlocListener( listeners: [ - for (final w in wallets) + for (final bloc in context.read().state) BlocListener( - bloc: w, + bloc: bloc, + // bloc: createWalletBloc(w), listenWhen: (previous, current) => previous.wallet != current.wallet, listener: (context, state) { - if (state.wallet == null) return; - context.read().updateWalletBloc(w); + context.read().add(WalletUpdated(state.wallet)); }, ), ], @@ -143,19 +161,41 @@ class HomeLoadingCubit extends Bloc> { } } -class HomeWalletLoadingListeners extends StatelessWidget { +class HomeWalletLoadingListeners extends StatefulWidget { const HomeWalletLoadingListeners({super.key, required this.child}); final Widget child; + @override + State createState() => + _HomeWalletLoadingListenersState(); +} + +class _HomeWalletLoadingListenersState + extends State { + @override + void initState() { + super.initState(); + } + + void _setup() { + if (!context.mounted) return; + final blocs = context.read().state; + + for (final bloc in blocs) { + context + .read() + .add(SetLoading(bloc.state.wallet.id, bloc.state.syncing)); + } + } + @override Widget build(BuildContext context) { - final network = context.select((NetworkCubit x) => x.state.getBBNetwork()); - final walletBlocs = context - .select((HomeCubit x) => x.state.walletBlocsFromNetwork(network)); + final walletBlocs = context.select((AppWalletBlocs x) => x.state); - if (walletBlocs.isEmpty) return child; + if (walletBlocs.isEmpty) return widget.child; + _setup(); return MultiBlocListener( listeners: [ for (final walletBloc in walletBlocs) @@ -167,16 +207,16 @@ class HomeWalletLoadingListeners extends StatelessWidget { if (state.syncing) { context .read() - .add(SetLoading(state.wallet!.id, true)); + .add(SetLoading(state.wallet.id, true)); } else { context .read() - .add(SetLoading(state.wallet!.id, false)); + .add(SetLoading(state.wallet.id, false)); } }, ), ], - child: child, + child: widget.child, ); } } @@ -187,4 +227,10 @@ class BBlocObserver extends BlocObserver { locator().log('$error\n$stackTrace', printToConsole: true); super.onError(bloc, error, stackTrace); } + + @override + void onEvent(Bloc bloc, Object? event) { + // locator().log('$event', printToConsole: true); + super.onEvent(bloc, event); + } } diff --git a/lib/home/transactions.dart b/lib/home/transactions.dart index 38757ac4..514e22e9 100644 --- a/lib/home/transactions.dart +++ b/lib/home/transactions.dart @@ -1,18 +1,19 @@ import 'package:bb_mobile/_model/swap.dart'; import 'package:bb_mobile/_model/transaction.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; import 'package:bb_mobile/_ui/app_bar.dart'; import 'package:bb_mobile/_ui/components/button.dart'; import 'package:bb_mobile/_ui/components/text.dart'; import 'package:bb_mobile/currency/bloc/currency_cubit.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; +import 'package:bb_mobile/home/bloc/home_bloc.dart'; import 'package:bb_mobile/home/home_page.dart'; import 'package:bb_mobile/locator.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/network/bloc/network_bloc.dart'; import 'package:bb_mobile/settings/bloc/lighting_cubit.dart'; import 'package:bb_mobile/styles.dart'; import 'package:bb_mobile/swap/watcher_bloc/watchtxs_bloc.dart'; import 'package:bb_mobile/swap/watcher_bloc/watchtxs_event.dart'; -import 'package:bb_mobile/wallet/bloc/event.dart'; import 'package:bb_mobile/wallet/bloc/state.dart'; import 'package:bb_mobile/wallet/bloc/wallet_bloc.dart'; import 'package:extra_alignments/extra_alignments.dart'; @@ -44,7 +45,7 @@ class _Listener extends StatelessWidget { @override Widget build(BuildContext context) { final walletBlocs = context.select( - (HomeCubit _) => _.state.walletBlocs ?? [], + (AppWalletBlocs _) => _.state, ); if (walletBlocs.isEmpty) return child; @@ -54,8 +55,8 @@ class _Listener extends StatelessWidget { BlocListener( bloc: walletBloc, listenWhen: (previous, current) => - previous.wallet?.transactions != current.wallet?.transactions || - previous.wallet?.swaps != current.wallet?.swaps, + previous.wallet.transactions != current.wallet.transactions || + previous.wallet.swaps != current.wallet.swaps, listener: (context, state) { onUpdated(); }, @@ -69,12 +70,12 @@ class _Listener extends StatelessWidget { class _HomeTransactionsState extends State { @override Widget build(BuildContext context) { - final _ = context.select((HomeCubit x) => x.state.updated); + final _ = context.select((HomeBloc x) => x.state.updated); - // final walletBlocs = context.select((HomeCubit _) => _.state.walletBlocs); - final network = context.select((NetworkCubit x) => x.state.getBBNetwork()); + // final walletBlocs = context.select((HomeBloc _) => _.state.walletBlocs); + final network = context.select((NetworkBloc x) => x.state.getBBNetwork()); final txs = - context.select((HomeCubit cubit) => cubit.state.getAllTxs(network)); + context.select((HomeBloc cubit) => cubit.state.getAllTxs(network)); return _Listener( onUpdated: () { @@ -82,14 +83,13 @@ class _HomeTransactionsState extends State { }, child: RefreshIndicator( onRefresh: () async { - final network = context.read().state; + final network = context.read(); final wallets = context - .read() - .state - .walletBlocsFromNetwork(network.getBBNetwork()); - for (final wallet in wallets) { - wallet.add(SyncWallet()); + .read() + .walletServiceFromNetwork(network.getBBNetwork); + for (final walletService in wallets) { + walletService.syncWallet(); } context.read().add(WatchWallets()); @@ -175,14 +175,12 @@ class NoTxs extends StatelessWidget { label: 'Sync transactions', fontSize: 11, onPressed: () { - final network = - context.read().state.getBBNetwork(); + final network = context.read().getBBNetwork; final wallets = context - .read() - .state - .walletBlocsFromNetwork(network); + .read() + .walletServiceFromNetwork(network); for (final wallet in wallets) { - wallet.add(SyncWallet()); + wallet.syncWallet(); } }, ), @@ -201,7 +199,7 @@ class TransactionHistoryPage extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider.value( - value: locator(), + value: locator(), child: Scaffold( appBar: AppBar( flexibleSpace: const _TopAppBar(), @@ -230,9 +228,9 @@ class _TxList extends StatelessWidget { @override Widget build(BuildContext context) { - context.select((HomeCubit _) => _.state.walletBlocs); - final network = context.select((NetworkCubit _) => _.state.getBBNetwork()); - final txs = context.select((HomeCubit _) => _.state.getAllTxs(network)); + context.select((HomeBloc _) => _.state.wallets); + final network = context.select((NetworkBloc _) => _.state.getBBNetwork()); + final txs = context.select((HomeBloc _) => _.state.getAllTxs(network)); if (txs.isEmpty) { return TopLeft( @@ -250,13 +248,12 @@ class _TxList extends StatelessWidget { fontSize: 11, onPressed: () { final network = - context.read().state.getBBNetwork(); + context.read().getBBNetwork; final wallets = context - .read() - .state - .walletBlocsFromNetwork(network); + .read() + .walletServiceFromNetwork(network); for (final wallet in wallets) { - wallet.add(SyncWallet()); + wallet.syncWallet(); } }, ), @@ -286,7 +283,10 @@ class HomeTxItem2 extends StatelessWidget { // final showOnlySwap = tx.pageLayout == TxLayout.onlySwapTx; // if (showOnlySwap) return _SwapTxHomeListItem(transaction: tx); - final label = tx.label ?? ''; + final label = (tx.label != null && tx.label!.length > 20) + ? '${tx.label!.substring(0, 20)}...' + : tx.label ?? ''; + final isReceive = tx.isReceived(); final amount = context.select( diff --git a/lib/import/bloc/import_cubit.dart b/lib/import/bloc/import_cubit.dart index fe87a7c3..3bd19f64 100644 --- a/lib/import/bloc/import_cubit.dart +++ b/lib/import/bloc/import_cubit.dart @@ -11,12 +11,12 @@ import 'package:bb_mobile/_pkg/wallet/bdk/sensitive_create.dart'; import 'package:bb_mobile/_pkg/wallet/create.dart'; import 'package:bb_mobile/_pkg/wallet/create_sensitive.dart'; import 'package:bb_mobile/_pkg/wallet/lwk/sensitive_create.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/sensitive_storage.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/storage.dart'; import 'package:bb_mobile/_pkg/wallet/testable_wallets.dart'; import 'package:bb_mobile/_pkg/wallet/utils.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; +import 'package:bb_mobile/_repository/wallet/sensitive_wallet_storage.dart'; +import 'package:bb_mobile/_repository/wallet/wallet_storage.dart'; import 'package:bb_mobile/import/bloc/import_state.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -29,13 +29,13 @@ class ImportWalletCubit extends Cubit { required WalletSensitiveCreate walletSensCreate, required WalletsStorageRepository walletsStorageRepository, required WalletSensitiveStorageRepository walletSensRepository, - required NetworkCubit networkCubit, + required NetworkRepository networkRepository, required BDKCreate bdkCreate, required BDKSensitiveCreate bdkSensitiveCreate, required LWKSensitiveCreate lwkSensitiveCreate, bool mainWallet = false, bool useTestWallet = false, - }) : _networkCubit = networkCubit, + }) : _networkRepository = networkRepository, _walletSensRepository = walletSensRepository, _walletsStorageRepository = walletsStorageRepository, _walletSensCreate = walletSensCreate, @@ -68,7 +68,8 @@ class ImportWalletCubit extends Cubit { final WalletsStorageRepository _walletsStorageRepository; final WalletSensitiveStorageRepository _walletSensRepository; - final NetworkCubit _networkCubit; + + final NetworkRepository _networkRepository; void backClicked() { switch (state.importStep) { @@ -436,8 +437,7 @@ class ImportWalletCubit extends Cubit { final type = state.importType; final wallets = []; - final network = - _networkCubit.state.testnet ? BBNetwork.Testnet : BBNetwork.Mainnet; + final network = _networkRepository.getBBNetwork; switch (type) { case ImportTypes.words12: @@ -482,7 +482,6 @@ class ImportWalletCubit extends Cubit { case ImportTypes.xpub: if (state.xpub.contains('[')) { - // has origin info final (wxpub, wErrs) = await _bdkCreate.oneFromXpubWithOrigin( state.xpub, ); @@ -585,8 +584,7 @@ class ImportWalletCubit extends Cubit { ? selectedWallet.copyWith(name: state.walletLabel) : selectedWallet; - final network = - _networkCubit.state.testnet ? BBNetwork.Testnet : BBNetwork.Mainnet; + final network = _networkRepository.getBBNetwork; if (selectedWallet.type == BBWalletType.words) { final mnemonic = (state.importType == ImportTypes.words12) @@ -599,8 +597,6 @@ class ImportWalletCubit extends Cubit { return; } - // if seed exists - this will error with Seed Exists, but we ignore it - // else we create the seed await _walletSensRepository.newSeed(seed: seed!); if (state.passPhrase.isNotEmpty) { @@ -684,8 +680,6 @@ class ImportWalletCubit extends Cubit { network: network, walletType: BBWalletType.main, walletCreate: _walletCreate, - // walletType: network, - // false, ); if (wErr != null) { emit( diff --git a/lib/import/hardware_import_bloc/hardware_import_cubit.dart b/lib/import/hardware_import_bloc/hardware_import_cubit.dart index 7ec98a2f..9b012e54 100644 --- a/lib/import/hardware_import_bloc/hardware_import_cubit.dart +++ b/lib/import/hardware_import_bloc/hardware_import_cubit.dart @@ -6,17 +6,17 @@ import 'package:bb_mobile/_model/wallet.dart'; import 'package:bb_mobile/_pkg/barcode.dart'; import 'package:bb_mobile/_pkg/file_picker.dart'; import 'package:bb_mobile/_pkg/wallet/bdk/create.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/storage.dart'; import 'package:bb_mobile/_pkg/wallet/testable_wallets.dart'; +import 'package:bb_mobile/_repository/wallet/wallet_storage.dart'; import 'package:bb_mobile/import/hardware_import_bloc/hardware_import_state.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/network/bloc/network_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class HardwareImportCubit extends Cubit { HardwareImportCubit({ required Barcode barcode, required WalletsStorageRepository walletsStorageRepository, - required NetworkCubit networkCubit, + required NetworkBloc networkCubit, required BDKCreate bdkCreate, required FilePick filePicker, }) : _barcode = barcode, @@ -34,7 +34,7 @@ class HardwareImportCubit extends Cubit { final BDKCreate _bdkCreate; final WalletsStorageRepository _walletsStorageRepository; - final NetworkCubit _networkCubit; + final NetworkBloc _networkCubit; void reset() => emit(const HardwareImportState()); diff --git a/lib/import/hardware_page.dart b/lib/import/hardware_page.dart index 575894b2..c131028d 100644 --- a/lib/import/hardware_page.dart +++ b/lib/import/hardware_page.dart @@ -3,7 +3,7 @@ import 'package:bb_mobile/_pkg/barcode.dart'; import 'package:bb_mobile/_pkg/clipboard.dart'; import 'package:bb_mobile/_pkg/file_picker.dart'; import 'package:bb_mobile/_pkg/wallet/bdk/create.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/storage.dart'; +import 'package:bb_mobile/_repository/wallet/wallet_storage.dart'; import 'package:bb_mobile/_ui/app_bar.dart'; import 'package:bb_mobile/_ui/components/button.dart'; import 'package:bb_mobile/_ui/components/controls.dart'; @@ -13,7 +13,7 @@ import 'package:bb_mobile/_ui/components/text_input.dart'; import 'package:bb_mobile/import/hardware_import_bloc/hardware_import_cubit.dart'; import 'package:bb_mobile/import/listeners.dart'; import 'package:bb_mobile/locator.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/network/bloc/network_bloc.dart'; import 'package:bb_mobile/styles.dart'; import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; @@ -37,7 +37,7 @@ class _HardwareImportPageState extends State { _hardwareImportCubit = HardwareImportCubit( barcode: locator(), walletsStorageRepository: locator(), - networkCubit: locator(), + networkCubit: locator(), bdkCreate: locator(), filePicker: locator(), ); diff --git a/lib/import/listeners.dart b/lib/import/listeners.dart index 0bae45bb..f9e901d8 100644 --- a/lib/import/listeners.dart +++ b/lib/import/listeners.dart @@ -1,4 +1,5 @@ -import 'package:bb_mobile/home/bloc/home_cubit.dart'; +import 'package:bb_mobile/home/bloc/home_bloc.dart'; +import 'package:bb_mobile/home/bloc/home_event.dart'; import 'package:bb_mobile/import/hardware_import_bloc/hardware_import_cubit.dart'; import 'package:bb_mobile/import/hardware_import_bloc/hardware_import_state.dart'; import 'package:bb_mobile/locator.dart'; @@ -20,7 +21,7 @@ class HardwareImportListeners extends StatelessWidget { previous.savedWallet != current.savedWallet && current.savedWallet, listener: (context, state) { - locator().getWalletsFromStorage(); + locator().add(LoadWalletsFromStorage()); context.go('/home'); }, ), diff --git a/lib/import/page.dart b/lib/import/page.dart index 4a71f37f..ec0865f5 100644 --- a/lib/import/page.dart +++ b/lib/import/page.dart @@ -6,8 +6,9 @@ import 'package:bb_mobile/_pkg/wallet/bdk/sensitive_create.dart'; import 'package:bb_mobile/_pkg/wallet/create.dart'; import 'package:bb_mobile/_pkg/wallet/create_sensitive.dart'; import 'package:bb_mobile/_pkg/wallet/lwk/sensitive_create.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/sensitive_storage.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/storage.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; +import 'package:bb_mobile/_repository/wallet/sensitive_wallet_storage.dart'; +import 'package:bb_mobile/_repository/wallet/wallet_storage.dart'; import 'package:bb_mobile/_ui/app_bar.dart'; import 'package:bb_mobile/_ui/components/controls.dart'; import 'package:bb_mobile/_ui/components/text.dart'; @@ -19,7 +20,6 @@ import 'package:bb_mobile/import/recover.dart'; import 'package:bb_mobile/import/wallet_type_selection.dart'; import 'package:bb_mobile/import/xpub.dart'; import 'package:bb_mobile/locator.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -54,7 +54,9 @@ class _ImportWalletPageState extends State { walletSensCreate: locator(), walletsStorageRepository: locator(), walletSensRepository: locator(), - networkCubit: locator(), + // networkCubit: locator(), + networkRepository: locator(), + bdkCreate: locator(), bdkSensitiveCreate: locator(), lwkSensitiveCreate: locator(), diff --git a/lib/import/wallet_type_selection.dart b/lib/import/wallet_type_selection.dart index aa2a9afa..c3db059e 100644 --- a/lib/import/wallet_type_selection.dart +++ b/lib/import/wallet_type_selection.dart @@ -1,21 +1,14 @@ import 'package:bb_mobile/_model/wallet.dart'; import 'package:bb_mobile/_pkg/consts/keys.dart'; -import 'package:bb_mobile/_pkg/wallet/address.dart'; -import 'package:bb_mobile/_pkg/wallet/balance.dart'; -import 'package:bb_mobile/_pkg/wallet/create.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/network.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/storage.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/wallets.dart'; -import 'package:bb_mobile/_pkg/wallet/sync.dart'; -import 'package:bb_mobile/_pkg/wallet/transaction.dart'; +import 'package:bb_mobile/_repository/wallet/wallet_storage.dart'; import 'package:bb_mobile/_ui/bottom_sheet.dart'; import 'package:bb_mobile/_ui/components/button.dart'; import 'package:bb_mobile/_ui/components/text.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; +import 'package:bb_mobile/home/bloc/home_bloc.dart'; +import 'package:bb_mobile/home/bloc/home_event.dart'; import 'package:bb_mobile/import/bloc/import_cubit.dart'; import 'package:bb_mobile/import/bloc/import_state.dart'; import 'package:bb_mobile/locator.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; import 'package:bb_mobile/styles.dart'; import 'package:bb_mobile/wallet/bloc/event.dart'; import 'package:bb_mobile/wallet/bloc/wallet_bloc.dart'; @@ -34,22 +27,7 @@ class ImportSelectWalletTypeScreen extends StatelessWidget { .select((ImportWalletCubit cubit) => cubit.state.walletDetails ?? []); final walletCubits = [ - for (final w in wallets) - WalletBloc( - saveDir: w.getWalletStorageString(), - walletSync: locator(), - walletsStorageRepository: locator(), - fromStorage: false, - walletBalance: locator(), - walletAddress: locator(), - networkCubit: locator(), - // swapBloc: locator(), - wallet: w, - networkRepository: locator(), - walletsRepository: locator(), - walletTransactionn: locator(), - walletCreatee: locator(), - ), + for (final w in wallets) createOrRetreiveWalletBloc(w.id, wallet: w), ]; return BlocListener( @@ -62,11 +40,11 @@ class ImportSelectWalletTypeScreen extends StatelessWidget { w.add(RemoveInternalWallet()); } await locator().sortWallets(); - locator().getWalletsFromStorage(); + locator().add(LoadWalletsFromStorage()); // final wallet = state.savedWallet!; - // locator().addWallets([wallet]); + // locator().addWallets([wallet]); // await Future.delayed(300.milliseconds); - // locator().changeMoveToIdx(wallet); + // locator().changeMoveToIdx(wallet); // await Future.delayed(300.milliseconds); if (!context.mounted) return; context.go('/home'); @@ -144,8 +122,8 @@ class _ScreenState extends State<_Screen> { ), const Gap(16), for (final walletBloc in widget.walletCubits) ...[ - BlocProvider.value( - value: walletBloc, + BlocProvider( + create: (context) => walletBloc, child: const _ImportWalletTypeButton(), ), const Gap(16), @@ -229,8 +207,6 @@ class _ImportWalletTypeButton extends StatelessWidget { Widget build(BuildContext context) { final wallet = context.select((WalletBloc cubit) => cubit.state.wallet); - if (wallet == null) return const SizedBox.shrink(); - final scriptType = wallet.scriptType; final selected = context.select( @@ -243,14 +219,14 @@ class _ImportWalletTypeButton extends StatelessWidget { final syncing = context.select((WalletBloc cubit) => cubit.state.syncing); - final ad = context.select((WalletBloc cubit) => cubit.state.firstAddress); + final ad = + context.select((WalletBloc cubit) => cubit.state.wallet.firstAddress); final balance = - context.select((WalletBloc cubit) => cubit.state.wallet?.fullBalance); + context.select((WalletBloc cubit) => cubit.state.wallet.fullBalance); final hasTxs = context.select( - (WalletBloc cubit) => - cubit.state.wallet?.transactions.isNotEmpty ?? false, + (WalletBloc cubit) => cubit.state.wallet.transactions.isNotEmpty, ); final address = ad?.miniString() ?? ''; diff --git a/lib/locator.dart b/lib/locator.dart index 076e6d8f..4e5ab3e1 100644 --- a/lib/locator.dart +++ b/lib/locator.dart @@ -34,17 +34,20 @@ import 'package:bb_mobile/_pkg/wallet/lwk/sync.dart'; import 'package:bb_mobile/_pkg/wallet/lwk/transaction.dart'; import 'package:bb_mobile/_pkg/wallet/mnemonic_word.dart'; import 'package:bb_mobile/_pkg/wallet/network.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/network.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/sensitive_storage.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/storage.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/wallets.dart'; import 'package:bb_mobile/_pkg/wallet/sync.dart'; import 'package:bb_mobile/_pkg/wallet/transaction.dart'; import 'package:bb_mobile/_pkg/wallet/update.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; +import 'package:bb_mobile/_repository/wallet/internal_network.dart'; +import 'package:bb_mobile/_repository/wallet/internal_wallets.dart'; +import 'package:bb_mobile/_repository/wallet/sensitive_wallet_storage.dart'; +import 'package:bb_mobile/_repository/wallet/wallet_storage.dart'; import 'package:bb_mobile/currency/bloc/currency_cubit.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; +import 'package:bb_mobile/home/bloc/home_bloc.dart'; +import 'package:bb_mobile/home/home_page.dart'; import 'package:bb_mobile/import/bloc/words_cubit.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/network/bloc/network_bloc.dart'; import 'package:bb_mobile/network_fees/bloc/networkfees_cubit.dart'; import 'package:bb_mobile/routes.dart'; import 'package:bb_mobile/settings/bloc/lighting_cubit.dart'; @@ -85,8 +88,12 @@ Future _setupAPIs() async { } Future _setupRepositories() async { - locator.registerSingleton(WalletsRepository()); - locator.registerSingleton(NetworkRepository()); + locator.registerSingleton( + InternalWalletsRepository(), + ); + locator.registerSingleton( + InternalNetworkRepository(), + ); locator.registerSingleton( WalletsStorageRepository(hiveStorage: locator()), ); @@ -95,12 +102,6 @@ Future _setupRepositories() async { secureStorage: locator(), ), ); - // locator.registerSingleton( - // HomeRepository( - // walletsStorageRepository: locator(), - // logger: locator(), - // ), - // ); } Future _setupAppServices() async { @@ -138,7 +139,7 @@ Future _setupWalletServices() async { SwapBoltz( secureStorage: locator(), dio: locator(), - networkRepository: locator(), + networkRepository: locator(), ), ); @@ -152,7 +153,7 @@ Future _setupWalletServices() async { locator.registerSingleton(BDKTransactions()); locator.registerSingleton( LWKTransactions( - networkRepository: locator(), + networkRepository: locator(), swapBoltz: locator(), ), ); @@ -161,12 +162,12 @@ Future _setupWalletServices() async { locator.registerSingleton( BDKCreate( - walletsRepository: locator(), + walletsRepository: locator(), ), ); locator.registerSingleton( BDKSensitiveCreate( - walletsRepository: locator(), + walletsRepository: locator(), bdkCreate: locator(), ), ); @@ -179,7 +180,7 @@ Future _setupWalletServices() async { locator.registerSingleton( WalletCreate( - walletsRepository: locator(), + walletsRepository: locator(), lwkCreate: locator(), bdkCreate: locator(), walletsStorageRepository: locator(), @@ -192,7 +193,7 @@ Future _setupWalletServices() async { WalletAddress( bdkAddress: locator(), lwkAddress: locator(), - walletsRepository: locator(), + walletsRepository: locator(), ), ); @@ -201,20 +202,19 @@ Future _setupWalletServices() async { bdkSensitiveCreate: locator(), ), ); - // locator.registerSingleton(WalletSensitiveTx()); locator.registerFactory( () => WalletSync( bdkSync: locator(), lwkSync: locator(), - walletsRepository: locator(), - networkRepository: locator(), + walletsRepository: locator(), + networkRepository: locator(), ), ); locator.registerSingleton( WalletBalance( - walletsRepository: locator(), + walletsRepository: locator(), bdkBalance: locator(), lwkBalance: locator(), ), @@ -222,8 +222,8 @@ Future _setupWalletServices() async { locator.registerSingleton( WalletTx( - walletsRepository: locator(), - networkRepository: locator(), + walletsRepository: locator(), + networkRepository: locator(), walletAddress: locator(), walletSensitiveStorageRepository: locator(), @@ -239,17 +239,32 @@ Future _setupWalletServices() async { locator.registerSingleton( WalletNetwork( - networkRepository: locator(), - // logger: locator(), + networkRepository: locator(), bdkNetwork: locator(), ), ); + + locator.registerSingleton( + NetworkRepository( + walletNetwork: locator(), + hiveStorage: locator(), + ), + ); + + locator.registerSingleton( + AppWalletsRepository( + walletsStorageRepository: locator(), + ), + ); } Future _setupBlocs() async { - locator.registerSingleton( - HomeCubit( - walletsStorageRepository: locator(), + locator.registerSingleton(AppWalletBlocs()); + locator.registerSingleton( + HomeBloc( + // walletsStorageRepository: locator(), + networkRepository: locator(), + appWalletsRepository: locator(), ), ); @@ -259,10 +274,10 @@ Future _setupBlocs() async { ), ); - locator.registerSingleton( - NetworkCubit( + locator.registerSingleton( + NetworkBloc( hiveStorage: locator(), - walletNetwork: locator(), + networkRepository: locator(), ), ); @@ -277,7 +292,7 @@ Future _setupBlocs() async { NetworkFeesCubit( hiveStorage: locator(), mempoolAPI: locator(), - networkCubit: locator(), + networkRepository: locator(), ), ); @@ -292,11 +307,10 @@ Future _setupBlocs() async { locator.registerSingleton( WatchTxsBloc( - // isTestnet: locator().state.testnet, swapBoltz: locator(), walletTx: locator(), - homeCubit: locator(), - networkCubit: locator(), + networkRepository: locator(), + appWalletsRepository: locator(), ), ); } diff --git a/lib/main.dart b/lib/main.dart index dde13a00..e6243fe3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,12 +3,15 @@ import 'dart:developer'; import 'package:bb_mobile/_pkg/logger.dart'; import 'package:bb_mobile/_pkg/payjoin/event.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; import 'package:bb_mobile/_ui/security_overlay.dart'; import 'package:bb_mobile/currency/bloc/currency_cubit.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; +import 'package:bb_mobile/home/bloc/home_bloc.dart'; +import 'package:bb_mobile/home/home_page.dart'; import 'package:bb_mobile/home/listeners.dart'; import 'package:bb_mobile/locator.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/network/bloc/network_bloc.dart'; import 'package:bb_mobile/network/listeners.dart'; import 'package:bb_mobile/network_fees/bloc/networkfees_cubit.dart'; import 'package:bb_mobile/routes.dart'; @@ -26,7 +29,6 @@ import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:go_router/go_router.dart'; import 'package:lwk_dart/lwk_dart.dart'; import 'package:oktoast/oktoast.dart'; - import 'package:payjoin_flutter/src/generated/frb_generated.dart'; Future main({bool fromTest = false}) async { @@ -59,62 +61,69 @@ class BullBitcoinWalletApp extends StatelessWidget { BlocProvider.value(value: locator()), BlocProvider.value(value: locator()), BlocProvider.value(value: locator()), - BlocProvider.value(value: locator()), + BlocProvider.value(value: locator()), BlocProvider.value(value: locator()), BlocProvider.value(value: locator()), - BlocProvider.value(value: locator()), + BlocProvider.value(value: locator()), BlocProvider.value(value: locator()), // BlocProvider.value(value: TestCub()), BlocProvider.value(value: locator()), + BlocProvider.value(value: locator()), ], - child: BlocBuilder( - builder: (context, lightingState) { - return AnimatedSwitcher( - duration: 600.ms, - switchInCurve: Curves.easeInOutCubic, - child: MaterialApp.router( - theme: Themes.lightTheme, - darkTheme: lightingState.dark(), - themeMode: lightingState.mode(), - routerConfig: locator(), - debugShowCheckedModeBanner: false, - // localizationsDelegates: [localizationDelegate], - // supportedLocales: localizationDelegate.supportedLocales, - // locale: localizationDelegate.currentLocale, - builder: (context, child) { - // scheduleMicrotask(() async { - // await Future.delayed(100.ms); - // SystemChrome.setSystemUIOverlayStyle( - // SystemUiOverlayStyle( - // statusBarColor: context.colour.primaryContainer, - // ), - // ); - // }); - SystemChrome.setPreferredOrientations([ - DeviceOrientation.portraitUp, - ]); - if (child == null) return Container(); - return OKToast( - child: _AppListeners( - child: GestureDetector( - onTap: () { - FocusScope.of(context).requestFocus(FocusNode()); - }, - child: MediaQuery( - data: MediaQuery.of(context).copyWith( - textScaler: TextScaler.noScaling, - ), - child: SecurityOverlay( - child: child, + child: MultiRepositoryProvider( + providers: [ + RepositoryProvider.value(value: locator()), + RepositoryProvider.value(value: locator()), + ], + child: BlocBuilder( + builder: (context, lightingState) { + return AnimatedSwitcher( + duration: 600.ms, + switchInCurve: Curves.easeInOutCubic, + child: MaterialApp.router( + theme: Themes.lightTheme, + darkTheme: lightingState.dark(), + themeMode: lightingState.mode(), + routerConfig: locator(), + debugShowCheckedModeBanner: false, + // localizationsDelegates: [localizationDelegate], + // supportedLocales: localizationDelegate.supportedLocales, + // locale: localizationDelegate.currentLocale, + builder: (context, child) { + // scheduleMicrotask(() async { + // await Future.delayed(100.ms); + // SystemChrome.setSystemUIOverlayStyle( + // SystemUiOverlayStyle( + // statusBarColor: context.colour.primaryContainer, + // ), + // ); + // }); + SystemChrome.setPreferredOrientations([ + DeviceOrientation.portraitUp, + ]); + if (child == null) return Container(); + return OKToast( + child: _AppListeners( + child: GestureDetector( + onTap: () { + FocusScope.of(context).requestFocus(FocusNode()); + }, + child: MediaQuery( + data: MediaQuery.of(context).copyWith( + textScaler: TextScaler.noScaling, + ), + child: SecurityOverlay( + child: child, + ), ), ), ), - ), - ); - }, - ), - ); - }, + ); + }, + ), + ); + }, + ), ), ); } diff --git a/lib/network/bloc/event.dart b/lib/network/bloc/event.dart new file mode 100644 index 00000000..e2e78e94 --- /dev/null +++ b/lib/network/bloc/event.dart @@ -0,0 +1,86 @@ +import 'package:bb_mobile/_model/network.dart'; + +abstract class NetworkEvent {} + +class InitNetworks extends NetworkEvent {} + +class LoadNetworks extends NetworkEvent {} + +class ToggleTestnet extends NetworkEvent {} + +class UpdateStopGapAndSave extends NetworkEvent { + final int gap; + UpdateStopGapAndSave(this.gap); +} + +class NetworkConfigsSave extends NetworkEvent { + final bool isLiq; + NetworkConfigsSave({required this.isLiq}); +} + +class NetworkTypeChanged extends NetworkEvent { + final ElectrumTypes type; + NetworkTypeChanged(this.type); +} + +class LiquidNetworkTypeChanged extends NetworkEvent { + final LiquidElectrumTypes type; + LiquidNetworkTypeChanged(this.type); +} + +class CloseNetworkError extends NetworkEvent {} + +class RetryNetwork extends NetworkEvent {} + +class UpdateTempLiquidMainnet extends NetworkEvent { + final String mainnet; + UpdateTempLiquidMainnet(this.mainnet); +} + +class UpdateTempLiquidTestnet extends NetworkEvent { + final String testnet; + UpdateTempLiquidTestnet(this.testnet); +} + +class UpdateTempStopGap extends NetworkEvent { + final int gap; + UpdateTempStopGap(this.gap); +} + +class UpdateTempTimeout extends NetworkEvent { + final int timeout; + UpdateTempTimeout(this.timeout); +} + +class UpdateTempRetry extends NetworkEvent { + final int retry; + UpdateTempRetry(this.retry); +} + +class UpdateTempMainnet extends NetworkEvent { + String mainnet; + + UpdateTempMainnet(this.mainnet); +} + +class UpdateTempTestnet extends NetworkEvent { + String testnet; + + UpdateTempTestnet(this.testnet); +} + +class UpdateTempValidateDomain extends NetworkEvent { + final bool validateDomain; + UpdateTempValidateDomain(this.validateDomain); +} + +class ResetTempNetwork extends NetworkEvent {} + +class SetupBlockchain extends NetworkEvent { + final bool? isLiquid; + final bool? isTestnetLocal; + + SetupBlockchain({this.isLiquid, this.isTestnetLocal}); +} + +class NetworkDataSubscribe extends NetworkEvent {} diff --git a/lib/network/bloc/network_bloc.dart b/lib/network/bloc/network_bloc.dart new file mode 100644 index 00000000..3c82dd6b --- /dev/null +++ b/lib/network/bloc/network_bloc.dart @@ -0,0 +1,439 @@ +import 'dart:convert'; + +import 'package:bb_mobile/_pkg/electrum_test.dart'; +import 'package:bb_mobile/_pkg/storage/hive.dart'; +import 'package:bb_mobile/_pkg/storage/storage.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; +import 'package:bb_mobile/_ui/alert.dart'; +import 'package:bb_mobile/network/bloc/event.dart'; +import 'package:bb_mobile/network/bloc/state.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class NetworkBloc extends Bloc { + NetworkBloc({ + required HiveStorage hiveStorage, + required NetworkRepository networkRepository, + }) : _hiveStorage = hiveStorage, + _networkRepository = networkRepository, + super(const NetworkState()) { + on(_onInitNetworks); + on(_onLoadNetworks); + on(_onToggleTestnet); + on(_onUpdateStopGapAndSave); + on(_onNetworkConfigsSave); + on(_onNetworkTypeChanged); + on(_onLiquidNetworkTypeChanged); + on(_onCloseNetworkError); + on(_onRetryNetwork); + on(_onUpdateTempMainnet); + on(_onUpdateTempTestnet); + on(_onUpdateTempLiquidMainnet); + on(_onUpdateTempLiquidTestnet); + on(_onUpdateTempStopGap); + on(_onUpdateTempTimeout); + on(_onUpdateTempRetry); + on(_onUpdateTempValidateDomain); + on(_onResetTempNetwork); + on(_onSetupBlockchain); + + on((event, emit) async { + await emit.forEach( + _networkRepository.dataStream, + onData: (NetworkRepoData _) { + return state.copyWith(networkData: _); + }, + ); + }); + + add(InitNetworks()); + } + + final HiveStorage _hiveStorage; + + final NetworkRepository _networkRepository; + + @override + void onChange(Change change) { + super.onChange(change); + _hiveStorage.saveValue( + key: StorageKeys.network, + value: jsonEncode( + change.nextState + .copyWith( + networkErrorOpened: false, + ) + .toJson(), + ), + ); + } + + Future _onInitNetworks( + InitNetworks event, + Emitter emit, + ) async { + // await Future.delayed(const Duration(milliseconds: 200)); + final (result, err) = await _hiveStorage.getValue(StorageKeys.network); + if (err != null) { + add(LoadNetworks()); + return; + } + + final network = + NetworkState.fromJson(jsonDecode(result!) as Map); + _networkRepository.setAllData(network.networkData); + emit(network.copyWith(networkErrorOpened: false)); + // await Future.delayed(const Duration(milliseconds: 100)); + add(NetworkDataSubscribe()); + + add(LoadNetworks()); + } + + Future _onToggleTestnet( + ToggleTestnet event, + Emitter emit, + ) async { + // final isTestnet = state.networkData.testnet; + // await Future.delayed(const Duration(milliseconds: 50)); + // try {} catch (e) { + // emit(state.copyWith(errLoadingNetworks: e.toString())); + // } + + await _networkRepository.changeTestnet(!_networkRepository.data.testnet); + // _networkRepository.setNetworkData( + // testnet: !_networkRepository.data.testnet, + // ); + } + + Future _onUpdateStopGapAndSave( + UpdateStopGapAndSave event, + Emitter emit, + ) async { + final network = state.networkData.tempNetworkDetails; + if (network == null) return; + + final updatedConfig = network.copyWith(stopGap: event.gap); + + _networkRepository.setNetworkData(tempNetworkDetails: updatedConfig); + + await Future.delayed(const Duration(milliseconds: 50)); + add(NetworkConfigsSave(isLiq: false)); + } + + Future _onNetworkTypeChanged( + NetworkTypeChanged event, + Emitter emit, + ) async { + final network = + state.networkData.networks.firstWhere((_) => _.type == event.type); + + _networkRepository.setNetworkData( + tempNetwork: event.type, + tempNetworkDetails: network, + ); + } + + Future _onLiquidNetworkTypeChanged( + LiquidNetworkTypeChanged event, + Emitter emit, + ) async { + final network = state.networkData.liquidNetworks + .firstWhere((_) => _.type == event.type); + + _networkRepository.setNetworkData( + tempLiquidNetwork: event.type, + tempLiquidNetworkDetails: network, + ); + } + + Future _onCloseNetworkError( + CloseNetworkError event, + Emitter emit, + ) async { + emit(state.copyWith(goToSettings: true)); + await Future.delayed(const Duration(milliseconds: 200)); + emit(state.copyWith(goToSettings: false)); + await Future.delayed(const Duration(seconds: 20)); + emit(state.copyWith(networkErrorOpened: false)); + } + + Future _onRetryNetwork( + RetryNetwork event, + Emitter emit, + ) async { + emit(state.copyWith(networkErrorOpened: false)); + await Future.delayed(const Duration(milliseconds: 100)); + add(SetupBlockchain()); + } + + Future _onNetworkConfigsSave( + NetworkConfigsSave event, + Emitter emit, + ) async { + emit(state.copyWith(errLoadingNetworks: '')); + _networkRepository.setNetworkData(networkConnected: false); + if (!event.isLiq) { + if (state.networkData.tempNetwork == null) return; + final networks = state.networkData.networks.toList(); + final tempNetwork = state.networkData.tempNetworkDetails; + final checkedTempNetworkDetails = tempNetwork!.copyWith( + mainnet: _checkURL(tempNetwork.mainnet), + testnet: _checkURL(tempNetwork.testnet), + ); + + final sslRegex = + RegExp(r'^ssl:\/\/([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})\:[0-9]{2,5}$'); + if (!sslRegex.hasMatch(tempNetwork.mainnet)) { + final String error = _networkLoadError(tempNetwork.mainnet); + final String formattedError = error.isNotEmpty ? (': $error') : ''; + emit( + state.copyWith( + errLoadingNetworks: 'Invalid mainnet electrum URL$formattedError', + ), + ); + return; + } + if (!sslRegex.hasMatch(tempNetwork.testnet)) { + final String error = _networkLoadError(tempNetwork.testnet); + final String formattedError = error.isNotEmpty ? ': $error' : ''; + emit( + state.copyWith( + errLoadingNetworks: 'Invalid testnet electrum URL$formattedError', + ), + ); + return; + } + + if (state.networkData.testnet) { + final testnetElectrumLive = await isElectrumLive(tempNetwork.testnet); + if (!testnetElectrumLive) { + emit( + state.copyWith( + errLoadingNetworks: + 'Check Testnet electrum URL. Could not connect to electrum.', + ), + ); + return; + } + } else { + final mainnetElectrumLive = await isElectrumLive(tempNetwork.mainnet); + if (!mainnetElectrumLive) { + emit( + state.copyWith( + errLoadingNetworks: + 'Check Mainnet electrum URL. Could not connect to electrum.', + ), + ); + return; + } + } + + final index = networks.indexWhere( + (element) => element.type == state.networkData.tempNetwork, + ); + networks.removeAt(index); + networks.insert(index, checkedTempNetworkDetails); + + _networkRepository.setNetworkData( + networks: networks, + selectedNetwork: tempNetwork.type, + ); + await Future.delayed(const Duration(milliseconds: 100)); + add(SetupBlockchain(isLiquid: false)); + return; + } + + if (state.networkData.tempLiquidNetwork == null) return; + final networks = state.networkData.liquidNetworks.toList(); + final tempNetwork = state.networkData.tempLiquidNetworkDetails; + final index = networks.indexWhere( + (element) => element.type == state.networkData.tempLiquidNetwork, + ); + networks.removeAt(index); + networks.insert(index, state.networkData.tempLiquidNetworkDetails!); + + _networkRepository.setNetworkData( + liquidNetworks: networks, + selectedLiquidNetwork: tempNetwork!.type, + ); + + await Future.delayed(const Duration(milliseconds: 100)); + add(SetupBlockchain(isLiquid: true)); + } + + Future _onUpdateTempMainnet( + UpdateTempMainnet event, + Emitter emit, + ) async { + final network = state.networkData.tempNetworkDetails; + if (network == null) return; + final updatedConfig = network.copyWith(mainnet: event.mainnet); + + _networkRepository.setNetworkData(tempNetworkDetails: updatedConfig); + } + + Future _onUpdateTempTestnet( + UpdateTempTestnet event, + Emitter emit, + ) async { + final network = state.networkData.tempNetworkDetails; + if (network == null) return; + final updatedConfig = network.copyWith(testnet: event.testnet); + + _networkRepository.setNetworkData(tempNetworkDetails: updatedConfig); + } + + void _onUpdateTempStopGap( + UpdateTempStopGap event, + Emitter emit, + ) { + final network = state.networkData.tempNetworkDetails; + if (network == null) return; + final updatedConfig = network.copyWith(stopGap: event.gap); + + _networkRepository.setNetworkData(tempNetworkDetails: updatedConfig); + } + + Future _onUpdateTempLiquidMainnet( + UpdateTempLiquidMainnet event, + Emitter emit, + ) async { + final network = state.networkData.tempLiquidNetworkDetails; + if (network == null) return; + final updatedConfig = network.copyWith(mainnet: event.mainnet); + + _networkRepository.setNetworkData(tempLiquidNetworkDetails: updatedConfig); + } + + Future _onUpdateTempLiquidTestnet( + UpdateTempLiquidTestnet event, + Emitter emit, + ) async { + final network = state.networkData.tempLiquidNetworkDetails; + if (network == null) return; + final updatedConfig = network.copyWith(testnet: event.testnet); + + _networkRepository.setNetworkData(tempLiquidNetworkDetails: updatedConfig); + } + + Future _onUpdateTempTimeout( + UpdateTempTimeout event, + Emitter emit, + ) async { + final network = state.networkData.tempNetworkDetails; + if (network == null) return; + final updatedConfig = network.copyWith(timeout: event.timeout); + + _networkRepository.setNetworkData(tempNetworkDetails: updatedConfig); + } + + Future _onUpdateTempRetry( + UpdateTempRetry event, + Emitter emit, + ) async { + final network = state.networkData.tempNetworkDetails; + if (network == null) return; + final updatedConfig = network.copyWith(retry: event.retry); + + _networkRepository.setNetworkData(tempNetworkDetails: updatedConfig); + } + + Future _onUpdateTempValidateDomain( + UpdateTempValidateDomain event, + Emitter emit, + ) async { + final network = state.networkData.tempNetworkDetails; + if (network == null) return; + final updatedConfig = + network.copyWith(validateDomain: event.validateDomain); + + _networkRepository.setNetworkData(tempNetworkDetails: updatedConfig); + } + + Future _onResetTempNetwork( + ResetTempNetwork event, + Emitter emit, + ) async { + final selectedNetwork = state.getNetwork(); + final selectedLiquidNetwork = state.getLiquidNetwork(); + + _networkRepository.setNetworkData( + tempNetworkDetails: selectedNetwork, + tempLiquidNetworkDetails: selectedLiquidNetwork, + ); + + _networkRepository.resetNetworkData( + tempNetwork: true, + tempLiquidNetwork: true, + ); + } + + String _checkURL(String url) { + if (!url.contains('://')) return 'ssl/:/$url'; + return url; + } + + String _networkLoadError(String url) { + if (_isTorAddress(url)) return "Tor isn't supported"; + return ''; + } + + bool _isTorAddress(String url) { + if (url.isEmpty) return false; + final split = url.split(':'); + String cleanUrl = split.length > 1 ? split[1] : split[0]; + cleanUrl = cleanUrl.split('//').last; + final torRegex = RegExp(r'^([a-z2-7]{16}|[a-zA-Z2-7]{56})\.onion$'); + return torRegex.hasMatch(cleanUrl); + } + + Future _onLoadNetworks( + LoadNetworks event, + Emitter emit, + ) async { + if (state.loadingNetworks) return; + emit(state.copyWith(loadingNetworks: true)); + + await _networkRepository.loadNetworks(); + + add(NetworkDataSubscribe()); + + emit(state.copyWith(loadingNetworks: false)); + } + + Future _onSetupBlockchain( + SetupBlockchain event, + Emitter emit, + ) async { + emit(state.copyWith(errLoadingNetworks: '')); + _networkRepository.setNetworkData(networkConnected: false); + final isTestnet = event.isTestnetLocal ?? state.networkData.testnet; + + final err = await _networkRepository.setupBlockchain( + isLiquid: event.isLiquid, + isTestnetLocal: isTestnet, + ); + + if (err != null) { + if (!state.networkErrorOpened) { + BBAlert.showErrorAlertPopUp( + title: err.title ?? '', + err: err.message, + onClose: () => add(CloseNetworkError()), + okButtonText: 'Change server', + onRetry: () => add(RetryNetwork()), + ); + } + + emit( + state.copyWith( + errLoadingNetworks: err.toString(), + networkErrorOpened: true, + ), + ); + } + + _networkRepository.setNetworkData(networkConnected: true); + + // emit(state.copyWith(networkConnected: true)); + } +} diff --git a/lib/network/bloc/network_cubit.dart b/lib/network/bloc/network_cubit.dart deleted file mode 100644 index 79684246..00000000 --- a/lib/network/bloc/network_cubit.dart +++ /dev/null @@ -1,458 +0,0 @@ -import 'dart:convert'; - -import 'package:bb_mobile/_model/network.dart'; -import 'package:bb_mobile/_pkg/consts/configs.dart'; -import 'package:bb_mobile/_pkg/electrum_test.dart'; -import 'package:bb_mobile/_pkg/storage/hive.dart'; -import 'package:bb_mobile/_pkg/storage/storage.dart'; -import 'package:bb_mobile/_pkg/wallet/network.dart'; -import 'package:bb_mobile/_ui/alert.dart'; -import 'package:bb_mobile/network/bloc/state.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -class NetworkCubit extends Cubit { - NetworkCubit({ - required HiveStorage hiveStorage, - required WalletNetwork walletNetwork, - }) : _walletNetwork = walletNetwork, - _hiveStorage = hiveStorage, - super(const NetworkState()) { - init(); - } - - final HiveStorage _hiveStorage; - final WalletNetwork _walletNetwork; - - @override - void onChange(Change change) { - super.onChange(change); - // final state = change.nextState; - // if (state.networkErrorOpened) return; - _hiveStorage.saveValue( - key: StorageKeys.network, - value: jsonEncode( - change.nextState - .copyWith( - networkErrorOpened: false, - ) - .toJson(), - ), - ); - } - - Future init() async { - Future.delayed(const Duration(milliseconds: 200)); - final (result, err) = await _hiveStorage.getValue(StorageKeys.network); - if (err != null) { - loadNetworks(); - return; - } - - final network = - NetworkState.fromJson(jsonDecode(result!) as Map); - emit(network.copyWith(networkErrorOpened: false)); - await Future.delayed(const Duration(milliseconds: 100)); - loadNetworks(); - } - - Future loadNetworks() async { - if (state.loadingNetworks) return; - emit(state.copyWith(loadingNetworks: true)); - - final networks = state.networks; - final liqNetworks = state.liquidNetworks; - - if (networks.isNotEmpty) { - final selectedNetwork = - networks.firstWhere((_) => _.type == state.selectedNetwork); - - emit( - state.copyWith( - loadingNetworks: false, - tempNetworkDetails: selectedNetwork, - tempNetwork: selectedNetwork.type, - selectedNetwork: selectedNetwork.type, - ), - ); - await Future.delayed(const Duration(milliseconds: 200)); - setupBlockchain(isLiquid: false); - } else { - final newNetworks = [ - const ElectrumNetwork.defaultElectrum(), - const ElectrumNetwork.bullbitcoin(), - const ElectrumNetwork.custom( - mainnet: 'ssl://$bbelectrumMain', - testnet: 'ssl://$openelectrumTest', - ), - ]; - - final selectedNetwork = newNetworks[2]; - //.firstWhere((_) => _.type == state.selectedNetwork); - emit( - state.copyWith( - networks: newNetworks, - tempNetworkDetails: selectedNetwork, - tempNetwork: selectedNetwork.type, - selectedNetwork: selectedNetwork.type, - ), - ); - await Future.delayed(const Duration(milliseconds: 200)); - await setupBlockchain(isLiquid: false); - } - - if (liqNetworks.isNotEmpty) { - var selectedNetwork = - liqNetworks.firstWhere((_) => _.type == state.selectedLiquidNetwork); - final updatedLiqNetworks = liqNetworks.toList(); - - if (liqNetworks.length == 2) { - updatedLiqNetworks.insert(1, const LiquidElectrumNetwork.bullbitcoin()); - selectedNetwork = updatedLiqNetworks[1]; - } - emit( - state.copyWith( - loadingNetworks: false, - tempLiquidNetworkDetails: selectedNetwork, - tempLiquidNetwork: selectedNetwork.type, - liquidNetworks: updatedLiqNetworks, - selectedLiquidNetwork: selectedNetwork.type, - ), - ); - await Future.delayed(const Duration(milliseconds: 200)); - setupBlockchain(isLiquid: true); - } else { - final newLiqNetworks = [ - const LiquidElectrumNetwork.blockstream(), - const LiquidElectrumNetwork.bullbitcoin(), - const LiquidElectrumNetwork.custom( - mainnet: liquidElectrumUrl, - testnet: liquidElectrumTestUrl, - ), - ]; - // final selectedLiqNetwork = newLiqNetworks - // .firstWhere((_) => _.type == state.selectedLiquidNetwork); - - final selectedLiqNetwork = newLiqNetworks[1]; - - emit( - state.copyWith( - liquidNetworks: newLiqNetworks, - tempLiquidNetworkDetails: selectedLiqNetwork, - tempLiquidNetwork: selectedLiqNetwork.type, - selectedLiquidNetwork: selectedLiqNetwork.type, - ), - ); - await Future.delayed(const Duration(milliseconds: 200)); - setupBlockchain(isLiquid: true); - } - - emit(state.copyWith(loadingNetworks: false)); - } - - Future toggleTestnet() async { - final isTestnet = state.testnet; - await Future.delayed(const Duration(milliseconds: 50)); - try { - await setupBlockchain(isTestnetLocal: !isTestnet); - } catch (e) { - emit(state.copyWith(errLoadingNetworks: e.toString())); - } - // await Future.delayed(const Duration(milliseconds: 50)); - emit(state.copyWith(testnet: !isTestnet)); - // homeCubit?.networkChanged(state.testnet ? BBNetwork.Testnet : BBNetwork.Mainnet); - } - - Future updateStopGapAndSave(int gap) async { - updateTempStopGap(gap); - await Future.delayed(const Duration(milliseconds: 50)); - networkConfigsSaveClicked(isLiq: false); - } - - Future closeNetworkError() async { - emit(state.copyWith(goToSettings: true)); - await Future.delayed(const Duration(milliseconds: 200)); - emit(state.copyWith(goToSettings: false)); - await Future.delayed(const Duration(seconds: 20)); - emit(state.copyWith(networkErrorOpened: false)); - } - - Future retryNetwork() async { - emit(state.copyWith(networkErrorOpened: false)); - await Future.delayed(const Duration(milliseconds: 100)); - setupBlockchain(); - } - - Future setupBlockchain({bool? isLiquid, bool? isTestnetLocal}) async { - emit(state.copyWith(errLoadingNetworks: '', networkConnected: false)); - final isTestnet = isTestnetLocal ?? state.testnet; - - if (isLiquid == null || !isLiquid) { - final selectedNetwork = state.getNetwork(); - if (selectedNetwork == null) return; - - final errBitcoin = await _walletNetwork.createBlockChain( - isTestnet: isTestnet, - stopGap: selectedNetwork.stopGap, - timeout: selectedNetwork.timeout, - retry: selectedNetwork.retry, - url: isTestnet ? selectedNetwork.testnet : selectedNetwork.mainnet, - validateDomain: selectedNetwork.validateDomain, - ); - if (errBitcoin != null) { - if (!state.networkErrorOpened) { - emit(state.copyWith(networkErrorOpened: true)); - BBAlert.showErrorAlertPopUp( - title: errBitcoin.title ?? '', - err: errBitcoin.message, - onClose: closeNetworkError, - okButtonText: 'Change server', - onRetry: retryNetwork, - ); - await Future.delayed(const Duration(seconds: 10)); - emit(state.copyWith(networkErrorOpened: false)); - } - return; - } - } - - if (isLiquid == null || isLiquid) { - final selectedLiqNetwork = state.getLiquidNetwork(); - if (selectedLiqNetwork == null) return; - - final errLiquid = await _walletNetwork.createBlockChain( - url: - isTestnet ? selectedLiqNetwork.testnet : selectedLiqNetwork.mainnet, - isTestnet: isTestnet, - ); - if (errLiquid != null) { - if (!state.networkErrorOpened) { - BBAlert.showErrorAlertPopUp( - title: errLiquid.title ?? '', - err: errLiquid.message, - onClose: closeNetworkError, - okButtonText: 'Change server', - onRetry: retryNetwork, - ); - } - - emit( - state.copyWith( - errLoadingNetworks: errLiquid.toString(), - networkErrorOpened: true, - ), - ); - } - } - - emit(state.copyWith(networkConnected: true)); - } - - void networkTypeTempChanged(ElectrumTypes type) { - final network = state.networks.firstWhere((_) => _.type == type); - - emit( - state.copyWith( - tempNetwork: type, - tempNetworkDetails: network, - ), - ); - } - - void liqNetworkTypeTempChanged(LiquidElectrumTypes type) { - final network = state.liquidNetworks.firstWhere((_) => _.type == type); - - emit( - state.copyWith( - tempLiquidNetwork: type, - tempLiquidNetworkDetails: network, - ), - ); - } - - void updateTempMainnet(String mainnet) { - final network = state.tempNetworkDetails; - if (network == null) return; - final updatedConfig = network.copyWith(mainnet: mainnet); - emit(state.copyWith(tempNetworkDetails: updatedConfig)); - } - - void updateTempTestnet(String testnet) { - final network = state.tempNetworkDetails; - if (network == null) return; - final updatedConfig = network.copyWith(testnet: testnet); - emit(state.copyWith(tempNetworkDetails: updatedConfig)); - } - - void updateTempLiquidMainnet(String mainnet) { - final network = state.tempLiquidNetworkDetails; - if (network == null) return; - final updatedConfig = network.copyWith(mainnet: mainnet); - emit(state.copyWith(tempLiquidNetworkDetails: updatedConfig)); - } - - void updateTempLiquidTestnet(String testnet) { - final network = state.tempLiquidNetworkDetails; - if (network == null) return; - final updatedConfig = network.copyWith(testnet: testnet); - emit(state.copyWith(tempLiquidNetworkDetails: updatedConfig)); - } - - void updateTempStopGap(int gap) { - final network = state.tempNetworkDetails; - if (network == null) return; - final updatedConfig = network.copyWith(stopGap: gap); - emit(state.copyWith(tempNetworkDetails: updatedConfig)); - } - - void updateTempTimeout(int timeout) { - final network = state.tempNetworkDetails; - if (network == null) return; - final updatedConfig = network.copyWith(timeout: timeout); - emit(state.copyWith(tempNetworkDetails: updatedConfig)); - } - - void updateTempRetry(int retry) { - final network = state.tempNetworkDetails; - if (network == null) return; - final updatedConfig = network.copyWith(retry: retry); - emit(state.copyWith(tempNetworkDetails: updatedConfig)); - } - - void updateTempValidateDomain(bool validateDomain) { - final network = state.tempNetworkDetails; - if (network == null) return; - final updatedConfig = network.copyWith(validateDomain: validateDomain); - emit(state.copyWith(tempNetworkDetails: updatedConfig)); - } - - String _checkURL(String url) { - if (!url.contains('://')) return 'ssl://$url'; - return url; - } - - bool isTorAddress(String url) { - if (url.isEmpty) return false; - - final split = url.split(':'); - String cleanUrl = split.length > 1 - ? split[1] - : split[0]; // remove uri schema and port number - - cleanUrl = cleanUrl.split('//').last; // to remove the slashes - - final torRegex = RegExp(r'^([a-z2-7]{16}|[a-zA-Z2-7]{56})\.onion$'); - return torRegex.hasMatch(cleanUrl); - } - - String networkLoadError(String url) { - if (isTorAddress(url)) { - return "Tor isn't supported"; - } - return ''; - } - - Future networkConfigsSaveClicked({required bool isLiq}) async { - emit(state.copyWith(errLoadingNetworks: '', networkConnected: false)); - if (!isLiq) { - if (state.tempNetwork == null) return; - final networks = state.networks.toList(); - final tempNetwork = state.tempNetworkDetails!; - final checkedTempNetworkDetails = tempNetwork.copyWith( - mainnet: _checkURL(tempNetwork.mainnet), - testnet: _checkURL(tempNetwork.testnet), - ); - - // Local validation - final sslRegex = - RegExp(r'^ssl:\/\/([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})\:[0-9]{2,5}$'); - if (!sslRegex.hasMatch(tempNetwork.mainnet)) { - final String error = networkLoadError(tempNetwork.mainnet); - final String formattedError = error.isNotEmpty ? (': $error') : ''; - emit( - state.copyWith( - errLoadingNetworks: 'Invalid mainnet electrum URL$formattedError', - ), - ); - return; - } - if (!sslRegex.hasMatch(tempNetwork.testnet)) { - final String error = networkLoadError(tempNetwork.testnet); - final String formattedError = error.isNotEmpty ? ': $error' : ''; - emit( - state.copyWith( - errLoadingNetworks: 'Invalid testnet electrum URL$formattedError', - ), - ); - return; - } - - // Connection test with electrum - if (state.testnet) { - final testnetElectrumLive = await isElectrumLive(tempNetwork.testnet); - if (!testnetElectrumLive) { - emit( - state.copyWith( - errLoadingNetworks: - 'Check Testnet electrum URL. Could not connect to electrum.', - ), - ); - return; - } - } else { - final mainnetElectrumLive = await isElectrumLive(tempNetwork.mainnet); - if (!mainnetElectrumLive) { - emit( - state.copyWith( - errLoadingNetworks: - 'Check Mainnet electrum URL. Could not connect to electrum.', - ), - ); - return; - } - } - - final index = - networks.indexWhere((element) => element.type == state.tempNetwork); - networks.removeAt(index); - networks.insert(index, checkedTempNetworkDetails); - emit( - state.copyWith( - networks: networks, - selectedNetwork: tempNetwork.type, - ), - ); - await Future.delayed(const Duration(milliseconds: 100)); - setupBlockchain(isLiquid: false); - return; - } - - if (state.tempLiquidNetwork == null) return; - final networks = state.liquidNetworks.toList(); - final tempNetwork = state.tempLiquidNetworkDetails!; - final index = networks - .indexWhere((element) => element.type == state.tempLiquidNetwork); - networks.removeAt(index); - networks.insert(index, state.tempLiquidNetworkDetails!); - emit( - state.copyWith( - liquidNetworks: networks, - selectedLiquidNetwork: tempNetwork.type, - ), - ); - await Future.delayed(const Duration(milliseconds: 100)); - setupBlockchain(isLiquid: true); - } - - void resetTempNetwork() { - final selectedNetwork = state.getNetwork(); - final selectedLiquidNetwork = state.getLiquidNetwork(); - emit( - state.copyWith( - tempNetworkDetails: selectedNetwork, - tempNetwork: null, - tempLiquidNetwork: null, - tempLiquidNetworkDetails: selectedLiquidNetwork, - ), - ); - } -} diff --git a/lib/network/bloc/state.dart b/lib/network/bloc/state.dart index f39d56ad..b2a87e4b 100644 --- a/lib/network/bloc/state.dart +++ b/lib/network/bloc/state.dart @@ -4,6 +4,7 @@ import 'package:bb_mobile/_model/currency.dart'; import 'package:bb_mobile/_model/network.dart'; import 'package:bb_mobile/_model/wallet.dart'; import 'package:bb_mobile/_pkg/consts/configs.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; import 'package:bdk_flutter/bdk_flutter.dart' as bdk; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:intl/intl.dart'; @@ -14,25 +15,24 @@ part 'state.g.dart'; @freezed class NetworkState with _$NetworkState { const factory NetworkState({ - @Default(false) bool testnet, @Default(20) int reloadWalletTimer, - @Default([]) List networks, - @Default(ElectrumTypes.bullbitcoin) ElectrumTypes selectedNetwork, - @Default([]) List liquidNetworks, - @Default(LiquidElectrumTypes.blockstream) - @Default(LiquidElectrumNetwork.bullbitcoin) - LiquidElectrumTypes selectedLiquidNetwork, @Default(false) bool loadingNetworks, @Default('') String errLoadingNetworks, - @Default(false) bool networkConnected, + // @Default(false) bool networkConnected, @Default(false) bool networkErrorOpened, - - // @Default(20) int stopGap, - ElectrumTypes? tempNetwork, - ElectrumNetwork? tempNetworkDetails, - LiquidElectrumTypes? tempLiquidNetwork, - LiquidElectrumNetwork? tempLiquidNetworkDetails, @Default(false) bool goToSettings, + // + @Default(NetworkRepoData()) NetworkRepoData networkData, + // @Default(false) bool testnet, + // @Default([]) List networks, + // @Default(ElectrumTypes.bullbitcoin) ElectrumTypes selectedNetwork, + // @Default([]) List liquidNetworks, + // @Default(LiquidElectrumTypes.blockstream) + // LiquidElectrumTypes selectedLiquidNetwork, + // ElectrumTypes? tempNetwork, + // ElectrumNetwork? tempNetworkDetails, + // LiquidElectrumTypes? tempLiquidNetwork, + // LiquidElectrumNetwork? tempLiquidNetworkDetails, }) = _NetworkState; const NetworkState._(); @@ -40,31 +40,33 @@ class NetworkState with _$NetworkState { _$NetworkStateFromJson(json); ElectrumNetwork? getNetwork() { - if (networks.isEmpty) return null; - return networks.firstWhere((_) => _.type == selectedNetwork); + if (networkData.networks.isEmpty) return null; + return networkData.networks + .firstWhere((_) => _.type == networkData.selectedNetwork); } LiquidElectrumNetwork? getLiquidNetwork() { - if (liquidNetworks.isEmpty) return null; - return liquidNetworks.firstWhere((_) => _.type == selectedLiquidNetwork); + if (networkData.liquidNetworks.isEmpty) return null; + return networkData.liquidNetworks + .firstWhere((_) => _.type == networkData.selectedLiquidNetwork); } ElectrumNetwork? getTempOrSelectedNetwork() { - if (networks.isEmpty) return null; + if (networkData.networks.isEmpty) return null; // return tempNetwork ?? selectedNetwork; - if (tempNetwork == null) return getNetwork(); - final n = networks; - final t = tempNetwork; + if (networkData.tempNetwork == null) return getNetwork(); + final n = networkData.networks; + final t = networkData.tempNetwork; return n.firstWhere((_) => _.type == t); } LiquidElectrumNetwork? getTempOrSelectedLiquidNetwork() { - if (liquidNetworks.isEmpty) return null; + if (networkData.liquidNetworks.isEmpty) return null; // return tempNetwork ?? selectedNetwork; - if (tempLiquidNetwork == null) return getLiquidNetwork(); - final n = liquidNetworks; - final t = tempLiquidNetwork; + if (networkData.tempLiquidNetwork == null) return getLiquidNetwork(); + final n = networkData.liquidNetworks; + final t = networkData.tempLiquidNetwork; return n.firstWhere((_) => _.type == t); } @@ -72,22 +74,22 @@ class NetworkState with _$NetworkState { String getNetworkUrl() { final network = getNetwork(); if (network == null) return ''; - return network.getNetworkUrl(testnet); + return network.getNetworkUrl(networkData.testnet); } String getLiquidNetworkUrl() { final network = getLiquidNetwork(); if (network == null) return ''; - return network.getNetworkUrl(testnet, split: false); + return network.getNetworkUrl(networkData.testnet, split: false); } bdk.Network getBdkNetwork() { - if (testnet) return bdk.Network.testnet; + if (networkData.testnet) return bdk.Network.testnet; return bdk.Network.bitcoin; } BBNetwork getBBNetwork() { - if (testnet) return BBNetwork.Testnet; + if (networkData.testnet) return BBNetwork.Testnet; return BBNetwork.Mainnet; } @@ -102,11 +104,11 @@ class NetworkState with _$NetworkState { String unblindedUrl = '', }) { if (isLiquid) { - return testnet + return networkData.testnet ? '$liquidMempoolTestnet/$unblindedUrl' : '$liquidMempool/$unblindedUrl'; } else { - return testnet + return networkData.testnet ? 'https://$mempoolapi/testnet/tx/$txid' : 'https://$mempoolapi/tx/$txid'; } @@ -114,11 +116,11 @@ class NetworkState with _$NetworkState { String explorerAddressUrl(String address, {bool isLiquid = false}) { if (isLiquid) { - return testnet + return networkData.testnet ? '$liquidMempoolTestnet/address/$address' : '$liquidMempool/address/$address'; } else { - return testnet + return networkData.testnet ? 'https://$mempoolapi/testnet/address/$address' : 'https://$mempoolapi/address/$address'; } @@ -154,7 +156,7 @@ class NetworkState with _$NetworkState { String calculatePrice(int sats, Currency? currency) { if (currency == null) return ''; - if (testnet) return '${currency.getSymbol()}0'; + if (networkData.testnet) return '${currency.getSymbol()}0'; return currency.getSymbol() + fiatFormatting( (sats / 100000000 * currency.price!).toStringAsFixed(2), @@ -170,7 +172,7 @@ class NetworkState with _$NetworkState { ({bool show, String? err}) showConfirmButton({required bool isLiquid}) { if (isLiquid) { - if (tempLiquidNetwork == null) { + if (networkData.tempLiquidNetwork == null) { return ( show: false, err: '', @@ -180,7 +182,7 @@ class NetworkState with _$NetworkState { return (show: true, err: null); } - final temp = tempNetworkDetails; + final temp = networkData.tempNetworkDetails; if (temp == null) return (show: false, err: ''); if (temp.retry == 0) return (show: false, err: 'Retry cannot be 0'); @@ -191,7 +193,7 @@ class NetworkState with _$NetworkState { } double pickLiquidFees() { - switch (selectedLiquidNetwork) { + switch (networkData.selectedLiquidNetwork) { case LiquidElectrumTypes.custom: case LiquidElectrumTypes.blockstream: return 0.1; diff --git a/lib/network/listeners.dart b/lib/network/listeners.dart index e6947a53..3eaaeccc 100644 --- a/lib/network/listeners.dart +++ b/lib/network/listeners.dart @@ -1,4 +1,4 @@ -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/network/bloc/network_bloc.dart'; import 'package:bb_mobile/network/bloc/state.dart'; import 'package:bb_mobile/network/popup.dart'; import 'package:bb_mobile/routes.dart'; @@ -14,7 +14,7 @@ class NetworkListeners extends StatelessWidget { Widget build(BuildContext context) { return MultiBlocListener( listeners: [ - BlocListener( + BlocListener( listenWhen: (previous, current) => previous.goToSettings != current.goToSettings && current.goToSettings, diff --git a/lib/network/popup.dart b/lib/network/popup.dart index a07e7063..c91daa67 100644 --- a/lib/network/popup.dart +++ b/lib/network/popup.dart @@ -5,7 +5,8 @@ import 'package:bb_mobile/_ui/components/controls.dart'; import 'package:bb_mobile/_ui/components/text.dart'; import 'package:bb_mobile/_ui/components/text_input.dart'; import 'package:bb_mobile/_ui/headers.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/network/bloc/event.dart'; +import 'package:bb_mobile/network/bloc/network_bloc.dart'; import 'package:bb_mobile/network/bloc/state.dart'; import 'package:bb_mobile/styles.dart'; import 'package:flutter/material.dart'; @@ -30,10 +31,10 @@ class NetworkPopup extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocListener( + return BlocListener( listenWhen: (previous, current) => - previous.networkConnected == false && - current.networkConnected == true && + previous.networkData.networkConnected == false && + current.networkData.networkConnected == true && current.errLoadingNetworks.isEmpty, listener: (context, state) async { await Future.delayed(const Duration(seconds: 1)); @@ -54,7 +55,8 @@ class NetworkScreen extends StatelessWidget { @override Widget build(BuildContext context) { - final networks = context.select((NetworkCubit _) => _.state.networks); + final networks = + context.select((NetworkBloc _) => _.state.networkData.networks); if (networks.isEmpty) return const SizedBox.shrink(); return const Padding( @@ -105,7 +107,7 @@ class _NetowrkHeader extends StatelessWidget { ), isLeft: true, onBack: () { - context.read().resetTempNetwork(); + context.read().add(ResetTempNetwork()); context.pop(); }, ); @@ -119,15 +121,16 @@ class NetworkStatus extends StatelessWidget { Widget build(BuildContext context) { final isLiq = context.select((_NetworkSelector _) => _.state); final networkConnected = - context.select((NetworkCubit x) => x.state.networkConnected); + context.select((NetworkBloc x) => x.state.networkData.networkConnected); final errLoadingNetwork = - context.select((NetworkCubit x) => x.state.errLoadingNetworks); - final isTestnet = context.select((NetworkCubit x) => x.state.testnet); + context.select((NetworkBloc x) => x.state.errLoadingNetworks); + final isTestnet = + context.select((NetworkBloc x) => x.state.networkData.testnet); var network = context.select( - (NetworkCubit x) => x.state.getNetwork()?.getNetworkUrl(isTestnet) ?? '', + (NetworkBloc x) => x.state.getNetwork()?.getNetworkUrl(isTestnet) ?? '', ); var liqNetwork = context.select( - (NetworkCubit x) => + (NetworkBloc x) => x.state.getLiquidNetwork()?.getNetworkUrl(isTestnet) ?? '', ); @@ -175,12 +178,13 @@ class SelectNetworkSegment extends StatelessWidget { final isLiq = context.select((_NetworkSelector _) => _.state); final tempSelected = - context.select((NetworkCubit x) => x.state.tempNetwork); - final tempLiqSelected = - context.select((NetworkCubit x) => x.state.tempLiquidNetwork); - final network = context.select((NetworkCubit x) => x.state.selectedNetwork); - final liqNetwork = - context.select((NetworkCubit x) => x.state.selectedLiquidNetwork); + context.select((NetworkBloc x) => x.state.networkData.tempNetwork); + final tempLiqSelected = context + .select((NetworkBloc x) => x.state.networkData.tempLiquidNetwork); + final network = + context.select((NetworkBloc x) => x.state.networkData.selectedNetwork); + final liqNetwork = context + .select((NetworkBloc x) => x.state.networkData.selectedLiquidNetwork); final selected = tempSelected ?? network; final liqSelected = tempLiqSelected ?? liqNetwork; @@ -238,16 +242,16 @@ class _SegmentButton extends StatelessWidget { onTap: () { if (!isLiq) { final network = - context.read().state.networkFromString(text); + context.read().state.networkFromString(text); if (network == null) return; - context.read().networkTypeTempChanged(network); + context.read().add(NetworkTypeChanged(network)); return; } final network = - context.read().state.liqNetworkFromString(text); + context.read().state.liqNetworkFromString(text); if (network == null) return; - context.read().liqNetworkTypeTempChanged(network); + context.read().add(LiquidNetworkTypeChanged(network)); }, child: AnimatedContainer( duration: const Duration(milliseconds: 200), @@ -289,28 +293,29 @@ class NetworkConfigFields extends StatelessWidget { final isLiq = context.select((_NetworkSelector _) => _.state); final network = - context.select((NetworkCubit _) => _.state.getTempOrSelectedNetwork()); + context.select((NetworkBloc _) => _.state.getTempOrSelectedNetwork()); if (network == null) return const SizedBox.shrink(); final liqNetwork = context - .select((NetworkCubit _) => _.state.getTempOrSelectedLiquidNetwork()); + .select((NetworkBloc _) => _.state.getTempOrSelectedLiquidNetwork()); if (liqNetwork == null) return const SizedBox.shrink(); - final tempNetworkDetails = - context.select((NetworkCubit _) => _.state.tempNetworkDetails); + final tempNetworkDetails = context + .select((NetworkBloc _) => _.state.networkData.tempNetworkDetails); if (tempNetworkDetails == null) return const SizedBox.shrink(); - final tempLiqNetworkDetails = - context.select((NetworkCubit _) => _.state.tempLiquidNetworkDetails); + final tempLiqNetworkDetails = context.select( + (NetworkBloc _) => _.state.networkData.tempLiquidNetworkDetails, + ); if (tempLiqNetworkDetails == null) return const SizedBox.shrink(); final type = network.type; final liqType = liqNetwork.type; - final loading = context.select((NetworkCubit x) => x.state.loadingNetworks); + final loading = context.select((NetworkBloc x) => x.state.loadingNetworks); final showButton = context - .select((NetworkCubit x) => x.state.showConfirmButton(isLiquid: isLiq)); + .select((NetworkBloc x) => x.state.showConfirmButton(isLiquid: isLiq)); var mainnet = isLiq ? liqNetwork.mainnet : network.mainnet; var testnet = isLiq ? liqNetwork.testnet : network.testnet; @@ -356,9 +361,9 @@ class NetworkConfigFields extends StatelessWidget { child: BBTextInput.big( onChanged: (t) { if (!isLiq) { - context.read().updateTempMainnet(t); + context.read().add(UpdateTempMainnet(t)); } else { - context.read().updateTempLiquidMainnet(t); + context.read().add(UpdateTempLiquidMainnet(t)); } }, value: mainnet, @@ -373,9 +378,9 @@ class NetworkConfigFields extends StatelessWidget { child: BBTextInput.big( onChanged: (t) { if (!isLiq) { - context.read().updateTempTestnet(t); + context.read().add(UpdateTempTestnet(t)); } else { - context.read().updateTempLiquidTestnet(t); + context.read().add(UpdateTempLiquidTestnet(t)); } }, value: testnet, @@ -394,7 +399,9 @@ class NetworkConfigFields extends StatelessWidget { child: BBSwitch( value: tempNetworkDetails.validateDomain, onChanged: (e) { - context.read().updateTempValidateDomain(e); + context + .read() + .add(UpdateTempValidateDomain(e)); }, ), ), @@ -431,8 +438,8 @@ class NetworkConfigFields extends StatelessWidget { } context - .read() - .networkConfigsSaveClicked(isLiq: isLiq); + .read() + .add(NetworkConfigsSave(isLiq: isLiq)); // await Future.delayed(const Duration(milliseconds: 500)); // final err = // context.read().state.errLoadingNetworks; @@ -497,8 +504,10 @@ class PrivacyNoticePopUp extends StatelessWidget { label: 'SAVE', filled: true, onPressed: () async { - context.read().networkConfigsSaveClicked( - isLiq: context.read<_NetworkSelector>().state, + context.read().add( + NetworkConfigsSave( + isLiq: context.read<_NetworkSelector>().state, + ), ); context.pop(); /* @@ -540,15 +549,18 @@ class ElectrumAdvancedOptions extends StatelessWidget { Widget build(BuildContext context) { final fieldWidth = MediaQuery.of(context).size.width * 0.7; - final sg = - context.select((NetworkCubit x) => x.state.tempNetworkDetails?.stopGap); - final r = - context.select((NetworkCubit x) => x.state.tempNetworkDetails?.retry); - final t = - context.select((NetworkCubit x) => x.state.tempNetworkDetails?.timeout); + final sg = context.select( + (NetworkBloc x) => x.state.networkData.tempNetworkDetails?.stopGap, + ); + final r = context.select( + (NetworkBloc x) => x.state.networkData.tempNetworkDetails?.retry, + ); + final t = context.select( + (NetworkBloc x) => x.state.networkData.tempNetworkDetails?.timeout, + ); final showButton = context - .select((NetworkCubit x) => x.state.showConfirmButton(isLiquid: false)); + .select((NetworkBloc x) => x.state.showConfirmButton(isLiquid: false)); return Padding( padding: const EdgeInsets.only(left: 24.0, right: 24), @@ -572,10 +584,10 @@ class ElectrumAdvancedOptions extends StatelessWidget { onChanged: (t) { final sg = int.tryParse(t); if (sg == null) { - context.read().updateTempStopGap(0); + context.read().add(UpdateTempStopGap(0)); return; } - context.read().updateTempStopGap(sg); + context.read().add(UpdateTempStopGap(sg)); }, value: sg.toString(), ), @@ -590,10 +602,10 @@ class ElectrumAdvancedOptions extends StatelessWidget { onChanged: (t) { final r = int.tryParse(t); if (r == null) { - context.read().updateTempRetry(0); + context.read().add(UpdateTempRetry(0)); return; } - context.read().updateTempRetry(r); + context.read().add(UpdateTempRetry(r)); }, value: r.toString(), ), @@ -608,10 +620,10 @@ class ElectrumAdvancedOptions extends StatelessWidget { onChanged: (t) { final tt = int.tryParse(t); if (tt == null) { - context.read().updateTempTimeout(0); + context.read().add(UpdateTempTimeout(0)); return; } - context.read().updateTempTimeout(tt); + context.read().add(UpdateTempTimeout(tt)); }, value: t.toString(), ), diff --git a/lib/network_fees/bloc/networkfees_cubit.dart b/lib/network_fees/bloc/networkfees_cubit.dart index b4c95974..675baaeb 100644 --- a/lib/network_fees/bloc/networkfees_cubit.dart +++ b/lib/network_fees/bloc/networkfees_cubit.dart @@ -3,7 +3,7 @@ import 'dart:convert'; import 'package:bb_mobile/_pkg/mempool_api.dart'; import 'package:bb_mobile/_pkg/storage/hive.dart'; import 'package:bb_mobile/_pkg/storage/storage.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; import 'package:bb_mobile/network_fees/bloc/networkfees_state.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -12,10 +12,10 @@ class NetworkFeesCubit extends Cubit { NetworkFeesCubit({ required HiveStorage hiveStorage, required MempoolAPI mempoolAPI, - required NetworkCubit networkCubit, + required NetworkRepository networkRepository, NetworkFeesCubit? defaultNetworkFeesCubit, }) : _defaultNetworkFeesCubit = defaultNetworkFeesCubit, - _networkCubit = networkCubit, + _networkRepository = networkRepository, _mempoolAPI = mempoolAPI, _hiveStorage = hiveStorage, super(const NetworkFeesState()) { @@ -24,7 +24,8 @@ class NetworkFeesCubit extends Cubit { final HiveStorage _hiveStorage; final MempoolAPI _mempoolAPI; - final NetworkCubit _networkCubit; + + final NetworkRepository _networkRepository; final NetworkFeesCubit? _defaultNetworkFeesCubit; static const int feemultiple = 4; @@ -66,7 +67,9 @@ class NetworkFeesCubit extends Cubit { Future loadFees() async { emit(state.copyWith(loadingFees: true, errLoadingFees: '')); - final testnet = _networkCubit.state.testnet; + final testnet = _networkRepository.testnet; + await Future.delayed(const Duration(milliseconds: 50)); + final (fees, err) = await _mempoolAPI.getFees(testnet); if (err != null) { emit( @@ -96,7 +99,6 @@ class NetworkFeesCubit extends Cubit { emit( state.copyWith( tempFees: 0, - // tempSelectedFeesOption: 2, ), ); await Future.delayed(const Duration(milliseconds: 50)); @@ -122,7 +124,7 @@ class NetworkFeesCubit extends Cubit { Future checkMinimumFees() async { await Future.delayed(50.ms); - final isTestnet = _networkCubit.state.testnet; + final isTestnet = _networkRepository.testnet; final minFees = isTestnet ? 0 : state.feesList!.last; int max; @@ -140,7 +142,6 @@ class NetworkFeesCubit extends Cubit { state.copyWith( errLoadingFees: "The selected fee is below the Bitcoin Network's minimum relay fee. Your transaction will likely never confirm. Please select a higher fee than $minFees sats/vbyte .", - // tempSelectedFeesOption: 2, ), ); } else if (state.tempFees != null && @@ -150,7 +151,6 @@ class NetworkFeesCubit extends Cubit { state.copyWith( errLoadingFees: 'The default selected fee is too high. Please select a lower fee than $max sats/vbyte .', - // tempSelectedFeesOption: 2, ), ); } else { @@ -161,16 +161,16 @@ class NetworkFeesCubit extends Cubit { Future confirmFeeClicked() async { await Future.delayed(200.ms); if (state.feesList == null) return; - // final minFees = state.feesList!.last; - // final max = state.feesList!.first * 2; - final isTestnet = _networkCubit.state.testnet; + + final isTestnet = _networkRepository.testnet; + int max; if (!isTestnet) { max = state.feesList!.first * feemultiple; } else { max = 1000; } - // can we not just call checkMinimumFees here? + final tempFees = state.tempFees; if (tempFees == null && state.tempSelectedFeesOption == null) return; if (tempFees != null && tempFees > max) return; diff --git a/lib/network_fees/popup.dart b/lib/network_fees/popup.dart index 4fb33630..f7eb6113 100644 --- a/lib/network_fees/popup.dart +++ b/lib/network_fees/popup.dart @@ -4,7 +4,7 @@ import 'package:bb_mobile/_ui/components/text.dart'; import 'package:bb_mobile/_ui/components/text_input.dart'; import 'package:bb_mobile/_ui/headers.dart'; import 'package:bb_mobile/currency/bloc/currency_cubit.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/network/bloc/network_bloc.dart'; import 'package:bb_mobile/network_fees/bloc/networkfees_cubit.dart'; import 'package:bb_mobile/network_fees/bloc/networkfees_state.dart'; import 'package:bb_mobile/styles.dart'; @@ -285,7 +285,8 @@ class SelectFeesItem extends StatelessWidget { final currency = context.select((CurrencyCubit x) => x.state.defaultFiatCurrency); - final isTestnet = context.select((NetworkCubit x) => x.state.testnet); + final isTestnet = + context.select((NetworkBloc x) => x.state.networkData.testnet); final fiatRateStr = context.select( (NetworkFeesCubit _) => _.state.calculateFiatPriceForFees( diff --git a/lib/receive/bloc/receive_cubit.dart b/lib/receive/bloc/receive_cubit.dart index 2bef3f38..906aaa08 100644 --- a/lib/receive/bloc/receive_cubit.dart +++ b/lib/receive/bloc/receive_cubit.dart @@ -2,48 +2,51 @@ import 'package:bb_mobile/_model/swap.dart'; import 'package:bb_mobile/_model/wallet.dart'; import 'package:bb_mobile/_pkg/payjoin/manager.dart'; import 'package:bb_mobile/_pkg/wallet/address.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/storage.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; +import 'package:bb_mobile/_repository/wallet/wallet_storage.dart'; +import 'package:bb_mobile/_repository/wallet_service.dart'; import 'package:bb_mobile/receive/bloc/state.dart'; -import 'package:bb_mobile/wallet/bloc/event.dart'; -import 'package:bb_mobile/wallet/bloc/wallet_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class ReceiveCubit extends Cubit { ReceiveCubit({ - WalletBloc? walletBloc, + Wallet? wallet, required WalletAddress walletAddress, required WalletsStorageRepository walletsStorageRepository, required PayjoinManager payjoinManager, + required AppWalletsRepository appWalletsRepository, }) : _walletsStorageRepository = walletsStorageRepository, _walletAddress = walletAddress, _payjoinManager = payjoinManager, + _appWalletsRepository = appWalletsRepository, super( ReceiveState( - walletBloc: walletBloc, - oneWallet: walletBloc != null, + wallet: wallet, + oneWallet: wallet != null, ), ) { loadAddress(); - if (walletBloc != null) updateWalletBloc(walletBloc, fromInit: true); + if (wallet != null) updateWallet(wallet, fromInit: true); } final WalletAddress _walletAddress; final WalletsStorageRepository _walletsStorageRepository; final PayjoinManager _payjoinManager; + final AppWalletsRepository _appWalletsRepository; Future updatePayjoinEndpoint(String payjoinEndpoint) async { emit(state.copyWith(payjoinEndpoint: payjoinEndpoint)); return; } - Future updateWalletBloc( - WalletBloc walletBloc, { + Future updateWallet( + Wallet wallet, { bool fromInit = false, }) async { if (state.oneWallet && !fromInit) return; emit( state.copyWith( - walletBloc: walletBloc, + wallet: wallet, defaultAddress: null, savedDescription: '', description: '', @@ -55,7 +58,7 @@ class ReceiveCubit extends Cubit { return; } - if (!walletBloc.state.wallet!.mainWallet) { + if (!wallet.mainWallet) { emit(state.copyWith(paymentNetwork: PaymentNetwork.bitcoin)); } @@ -66,18 +69,17 @@ class ReceiveCubit extends Cubit { } Future payjoinInit() async { - final baseType = state.walletBloc!.state.wallet!.baseWalletType; + final baseType = state.wallet!.baseWalletType; if (state.paymentNetwork == PaymentNetwork.bitcoin && state.defaultAddress != null && state.isPayjoin && baseType == BaseWalletType.Bitcoin) { await receivePayjoin( - state.walletBloc!.state.wallet!.isTestnet(), + state.wallet!.isTestnet(), state.defaultAddress!.address, ); } else { - // Clear payjoin receiver emit(state.copyWith(payjoinReceiver: null)); } } @@ -95,7 +97,7 @@ class ReceiveCubit extends Cubit { } final currentPayNetwork = state.paymentNetwork; - final walletType = state.walletBloc?.state.wallet?.type; + final walletType = state.wallet?.type; if (walletType == null) return; emit(state.copyWith(paymentNetwork: selectedPaymentNetwork)); @@ -130,12 +132,11 @@ class ReceiveCubit extends Cubit { } Future loadAddress() async { - if (state.walletBloc == null) return; + if (state.wallet == null) return; emit(state.copyWith(loadingAddress: true, errLoadingAddress: '')); - final Wallet wallet = state.walletBloc!.state.wallet!; + final Wallet wallet = state.wallet!; - // If currently selected wallet is bitcoin? wallet, then find and load the liquid wallet and get it's lastGeneratedAddress. if (wallet.isLiquid()) { emit( state.copyWith( @@ -167,7 +168,6 @@ class ReceiveCubit extends Cubit { defaultLiquidAddress: liquidWallet?.lastGeneratedAddress, ), ); - // If currently selected wallet is liquid? wallet, then find and load the bitcoin wallet and get it's lastGeneratedAddress. } else if (wallet.isBitcoin()) { emit( state.copyWith( @@ -221,7 +221,7 @@ class ReceiveCubit extends Cubit { isLiq ? state.defaultLiquidAddress : state.defaultAddress; if (defaultAddress == null) return; - final wallet = state.walletBloc?.state.wallet; + final wallet = state.wallet; if (wallet == null) return; final address = wallet.getAddressFromWallet(defaultAddress.address); @@ -243,9 +243,9 @@ class ReceiveCubit extends Cubit { state.copyWith(errLoadingAddress: '', savedInvoiceAmount: 0), ); - if (state.walletBloc == null) return; + if (state.wallet == null) return; - final wallet = state.walletBloc!.state.wallet!; + final wallet = state.wallet!; final (updatedWallet, err) = await _walletAddress.newAddress(wallet); if (err != null) { @@ -257,14 +257,12 @@ class ReceiveCubit extends Cubit { return; } - state.walletBloc!.add( - UpdateWallet( - updatedWallet!, - updateTypes: [UpdateWalletTypes.addresses], - ), + await _appWalletsRepository.getWalletServiceById(wallet.id)?.updateWallet( + updatedWallet!, + updateTypes: [UpdateWalletTypes.addresses], ); - final addressGap = updatedWallet.addressGap(); + final addressGap = updatedWallet!.addressGap(); if (addressGap >= 5 && addressGap <= 20) { emit( state.copyWith( @@ -315,7 +313,7 @@ class ReceiveCubit extends Cubit { } Future saveAddrressLabel() async { - if (state.walletBloc == null) return; + if (state.wallet == null) return; if (state.description == state.defaultAddress?.label) return; @@ -327,14 +325,18 @@ class ReceiveCubit extends Cubit { final (a, w) = await _walletAddress.addAddressToWallet( address: (address!.index, address.address), - wallet: state.walletBloc!.state.wallet!, + wallet: state.wallet!, label: state.description, kind: address.kind, state: address.state, ); - state.walletBloc! - .add(UpdateWallet(w, updateTypes: [UpdateWalletTypes.addresses])); + await _appWalletsRepository + .getWalletServiceById(state.wallet!.id) + ?.updateWallet( + w, + updateTypes: [UpdateWalletTypes.addresses], + ); emit( state.copyWith( @@ -368,14 +370,13 @@ class ReceiveCubit extends Cubit { _payjoinManager.spawnNewReceiver( isTestnet: isTestnet, receiver: receiver, - wallet: state.walletBloc!.state.wallet!, + wallet: state.wallet!, ); } Future isPayjoinEnabled() async { - final walletBloc = state.walletBloc; - final wallet = walletBloc?.state.wallet; - if (walletBloc == null || wallet == null) return; + final wallet = state.wallet; + if (wallet == null) return; if (wallet.utxos.isEmpty) { emit(state.copyWith(isPayjoin: false)); diff --git a/lib/receive/bloc/state.dart b/lib/receive/bloc/state.dart index 449e3541..bbb5c0a7 100644 --- a/lib/receive/bloc/state.dart +++ b/lib/receive/bloc/state.dart @@ -1,7 +1,7 @@ import 'package:bb_mobile/_model/address.dart'; import 'package:bb_mobile/_model/swap.dart'; +import 'package:bb_mobile/_model/wallet.dart'; import 'package:bb_mobile/_pkg/consts/configs.dart'; -import 'package:bb_mobile/wallet/bloc/wallet_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:payjoin_flutter/receive.dart'; @@ -15,7 +15,6 @@ class ReceiveState with _$ReceiveState { @Default('') String errLoadingAddress, Address? defaultAddress, Address? defaultLiquidAddress, - // @Default('') String privateLabel, @Default(false) bool savingLabel, @Default('') String errSavingLabel, @Default(false) bool labelSaved, @@ -26,15 +25,13 @@ class ReceiveState with _$ReceiveState { Receiver? payjoinReceiver, @Default(true) bool creatingInvoice, @Default('') String errCreatingInvoice, - WalletBloc? walletBloc, + Wallet? wallet, @Default(PaymentNetwork.bitcoin) PaymentNetwork paymentNetwork, int? updateAddressGap, @Default(false) bool switchToSecure, @Default(false) bool switchToInstant, @Default(false) bool receiveFormSubmitted, @Default(false) bool oneWallet, - - // required SwapCubit swapBloc, }) = _ReceiveState; const ReceiveState._(); @@ -52,14 +49,12 @@ class ReceiveState with _$ReceiveState { finalAddress = address; } else { if (isLiquid) { - // Refer spec: https://github.com/ElementsProject/elements/issues/805 final lqAssetId = isTestnet ? liquidTestnetAssetId : liquidMainnetAssetId; final liquidProtocol = isTestnet ? 'liquidtestnet' : 'liquidnetwork'; finalAddress = '$liquidProtocol:$address?amount=${amount.toStringAsFixed(8)}${description.isNotEmpty ? '&label=$description' : ''}&assetid=$lqAssetId'; } else if (payjoinReceiver != null) { - // Receiver session is active: build a payjoin URI var pjUrl = payjoinReceiver!.pjUriBuilder(); if (amount > 0) { pjUrl = pjUrl.amountSats(amount: BigInt.from(amount * 100000000)); @@ -82,8 +77,6 @@ class ReceiveState with _$ReceiveState { } if (paymentNetwork == PaymentNetwork.lightning && swapTx != null) { return swapTx.lnSwapDetails!.invoice; - // if (swapBloc.state.swapTx == null) return ''; - // return swapBloc.state.swapTx!.invoice; } if (savedInvoiceAmount > 0 || savedDescription.isNotEmpty) { @@ -107,13 +100,11 @@ class ReceiveState with _$ReceiveState { } bool isChainSwap() { - if (walletBloc == null || walletBloc?.state.wallet == null) return false; - if (paymentNetwork == PaymentNetwork.bitcoin && - walletBloc!.state.wallet!.isLiquid()) { + if (wallet == null) return false; + if (paymentNetwork == PaymentNetwork.bitcoin && wallet!.isLiquid()) { return true; } - if (paymentNetwork == PaymentNetwork.liquid && - walletBloc!.state.wallet!.isBitcoin()) { + if (paymentNetwork == PaymentNetwork.liquid && wallet!.isBitcoin()) { return true; } return false; @@ -128,22 +119,14 @@ class ReceiveState with _$ReceiveState { bool isLn() => paymentNetwork == PaymentNetwork.lightning; - bool checkIfMainWalletSelected() => - walletBloc?.state.wallet?.mainWallet ?? false; - - // bool _swapTxIsNotNull() => swapBloc.state.swapTx != null; - - // bool showActionButtons() => - // paymentNetwork == ReceiveWalletType.secure || - // (walletType == ReceiveWalletType.lightning && _swapTxIsNotNull()); + bool checkIfMainWalletSelected() => wallet?.mainWallet ?? false; bool allowedSwitch(PaymentNetwork network) { if (!oneWallet) return true; - if (walletBloc == null || walletBloc?.state.wallet == null) return false; + if (wallet == null) return false; - final wallet = walletBloc!.state.wallet!; - if (network == PaymentNetwork.bitcoin && wallet.isLiquid()) return false; - if (network == PaymentNetwork.liquid && wallet.isBitcoin()) return false; + if (network == PaymentNetwork.bitcoin && wallet!.isLiquid()) return false; + if (network == PaymentNetwork.liquid && wallet!.isBitcoin()) return false; return true; } diff --git a/lib/receive/listeners.dart b/lib/receive/listeners.dart index b935d5a4..9a723d65 100644 --- a/lib/receive/listeners.dart +++ b/lib/receive/listeners.dart @@ -1,7 +1,9 @@ +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; import 'package:bb_mobile/currency/bloc/currency_cubit.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; import 'package:bb_mobile/locator.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/network/bloc/event.dart'; +import 'package:bb_mobile/network/bloc/network_bloc.dart'; import 'package:bb_mobile/receive/bloc/receive_cubit.dart'; import 'package:bb_mobile/receive/bloc/state.dart'; import 'package:bb_mobile/routes.dart'; @@ -33,8 +35,8 @@ class ReceiveListeners extends StatelessWidget { if (state.updateAddressGap == null) return; context - .read() - .updateStopGapAndSave(state.updateAddressGap!); + .read() + .add(UpdateStopGapAndSave(state.updateAddressGap!)); }, ), BlocListener( @@ -43,11 +45,14 @@ class ReceiveListeners extends StatelessWidget { listener: (context, state) { if (!state.switchToSecure) return; - final network = context.read().state.getBBNetwork(); - final secureWallet = - context.read().state.getMainSecureWallet(network); + final network = context.read().getBBNetwork; + // final secureWallet = + // context.read().state.getMainSecureWallet(network); + final secureWallet = context + .read() + .getMainSecureWallet(network); if (secureWallet == null) return; - context.read().updateWalletBloc(secureWallet); + context.read().updateWallet(secureWallet); context.read().clearSwitch(); }, ), @@ -57,11 +62,12 @@ class ReceiveListeners extends StatelessWidget { listener: (context, state) { if (!state.switchToInstant) return; - final network = context.read().state.getBBNetwork(); - final instantWallet = - context.read().state.getMainInstantWallet(network); + final network = context.read().getBBNetwork; + final instantWallet = context + .read() + .getMainInstantWallet(network); if (instantWallet == null) return; - context.read().updateWalletBloc(instantWallet); + context.read().updateWallet(instantWallet); context.read().clearSwitch(); }, ), diff --git a/lib/receive/receive_page.dart b/lib/receive/receive_page.dart index c9448c49..e8626aa0 100644 --- a/lib/receive/receive_page.dart +++ b/lib/receive/receive_page.dart @@ -1,4 +1,5 @@ import 'package:bb_mobile/_model/swap.dart'; +import 'package:bb_mobile/_model/wallet.dart'; import 'package:bb_mobile/_pkg/boltz/swap.dart'; import 'package:bb_mobile/_pkg/bull_bitcoin_api.dart'; import 'package:bb_mobile/_pkg/clipboard.dart'; @@ -6,32 +7,35 @@ import 'package:bb_mobile/_pkg/consts/keys.dart'; import 'package:bb_mobile/_pkg/payjoin/manager.dart'; import 'package:bb_mobile/_pkg/storage/hive.dart'; import 'package:bb_mobile/_pkg/wallet/address.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/sensitive_storage.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/storage.dart'; import 'package:bb_mobile/_pkg/wallet/transaction.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; +import 'package:bb_mobile/_repository/wallet/sensitive_wallet_storage.dart'; +import 'package:bb_mobile/_repository/wallet/wallet_storage.dart'; import 'package:bb_mobile/_ui/app_bar.dart'; import 'package:bb_mobile/_ui/components/button.dart'; import 'package:bb_mobile/_ui/components/controls.dart'; import 'package:bb_mobile/_ui/components/text.dart'; import 'package:bb_mobile/_ui/components/text_input.dart'; -import 'package:bb_mobile/_ui/molecules/wallet/wallet_dropdown.dart'; import 'package:bb_mobile/_ui/warning.dart'; import 'package:bb_mobile/currency/amount_input.dart'; import 'package:bb_mobile/currency/bloc/currency_cubit.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; +import 'package:bb_mobile/home/bloc/home_bloc.dart'; import 'package:bb_mobile/locator.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/network/bloc/network_bloc.dart'; import 'package:bb_mobile/receive/bloc/receive_cubit.dart'; import 'package:bb_mobile/receive/listeners.dart'; import 'package:bb_mobile/settings/bloc/lighting_cubit.dart'; import 'package:bb_mobile/settings/bloc/settings_cubit.dart'; import 'package:bb_mobile/styles.dart'; import 'package:bb_mobile/swap/create_swap_bloc/swap_cubit.dart'; +import 'package:bb_mobile/swap/ui_swapwidget/wallet_dropdown.dart'; import 'package:bb_mobile/swap/watcher_bloc/watchtxs_bloc.dart'; import 'package:bb_mobile/wallet/bloc/wallet_bloc.dart'; import 'package:boltz_dart/boltz_dart.dart' as boltz; import 'package:boltz_dart/boltz_dart.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_animate/flutter_animate.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:gap/gap.dart'; @@ -48,9 +52,9 @@ const lqMainnetAddress = 'lq1qq23h89g7u7ngp2n7p7tvek7n97dckyfyu89e3j875rqz35u8rd9tmy8fss0q7zke3lzj80834zl6t72pw2khqz0fkf6hnswne'; class ReceivePage extends StatefulWidget { - const ReceivePage({super.key, this.walletBloc}); + const ReceivePage({super.key, this.wallet}); - final WalletBloc? walletBloc; + final String? wallet; @override State createState() => _ReceivePageState(); @@ -63,70 +67,163 @@ class _ReceivePageState extends State { @override void initState() { + // print('-----2 - ${DateTime.now()}'); + // _swapCubit = CreateSwapCubit( + // walletSensitiveRepository: locator(), + // swapBoltz: locator(), + // walletTx: locator(), + // appWalletsRepository: locator(), + // // homeCubit: context.read(), + // watchTxsBloc: context.read(), + // networkRepository: context.read(), + // // networkCubit: context.read(), + // )..fetchFees(context.read().state.networkData.testnet); + // print('-----3 - ${DateTime.now()}'); + + // _currencyCubit = CurrencyCubit( + // hiveStorage: locator(), + // bbAPI: locator(), + // defaultCurrencyCubit: context.read(), + // ); + // print('-----4 - ${DateTime.now()}'); + + // final w = widget.wallet != null + // ? context.read().getWalletById(widget.wallet!) + // : null; + // print('-----5 - ${DateTime.now()}'); + + // _receiveCubit = ReceiveCubit( + // walletAddress: locator(), + // walletsStorageRepository: locator(), + // appWalletsRepository: locator(), + // wallet: w, + + // // walletBloc: + // // widget.wallet != null ? createWalletBloc(widget.wallet!) : null, + // payjoinManager: locator(), + // ); + // print('-----6 - ${DateTime.now()}'); + + // final network = context.read().getBBNetwork; + // print('-----7 - ${DateTime.now()}'); + + // final wallet = + // w ?? context.read().getMainInstantWallet(network); + // print('-----8 - ${DateTime.now()}'); + + // if (wallet!.isLiquid()) { + // _receiveCubit.updateWalletType( + // PaymentNetwork.lightning, + // context.read().state.networkData.testnet, + // onStart: true, + // ); + // } else { + // _receiveCubit.updateWalletType( + // PaymentNetwork.bitcoin, + // context.read().state.networkData.testnet, + // onStart: true, + // ); + // } + // print('-----10 - ${DateTime.now()}'); + + // _receiveCubit.updateWallet(wallet); + // print('-----11 - ${DateTime.now()}'); + + super.initState(); + } + + Future _setupBlocs() async { + print('-----2 - ${DateTime.now()}'); _swapCubit = CreateSwapCubit( walletSensitiveRepository: locator(), swapBoltz: locator(), walletTx: locator(), - homeCubit: context.read(), + appWalletsRepository: locator(), + // homeCubit: context.read(), watchTxsBloc: context.read(), - networkCubit: context.read(), - )..fetchFees(context.read().state.testnet); + networkRepository: context.read(), + // networkCubit: context.read(), + )..fetchFees(context.read().state.networkData.testnet); + print('-----3 - ${DateTime.now()}'); _currencyCubit = CurrencyCubit( hiveStorage: locator(), bbAPI: locator(), defaultCurrencyCubit: context.read(), ); + print('-----4 - ${DateTime.now()}'); + + final w = widget.wallet != null + ? context.read().getWalletById(widget.wallet!) + : null; + print('-----5 - ${DateTime.now()}'); _receiveCubit = ReceiveCubit( walletAddress: locator(), walletsStorageRepository: locator(), - walletBloc: widget.walletBloc, + appWalletsRepository: locator(), + wallet: w, + + // walletBloc: + // widget.wallet != null ? createWalletBloc(widget.wallet!) : null, payjoinManager: locator(), ); + print('-----6 - ${DateTime.now()}'); - final network = context.read().state.getBBNetwork(); - final walletBloc = widget.walletBloc ?? - context.read().state.getMainInstantWallet(network); + final network = context.read().getBBNetwork; + print('-----7 - ${DateTime.now()}'); - if (walletBloc!.state.wallet!.isLiquid()) { + final wallet = + w ?? context.read().getMainInstantWallet(network); + print('-----8 - ${DateTime.now()}'); + + if (wallet!.isLiquid()) { _receiveCubit.updateWalletType( PaymentNetwork.lightning, - context.read().state.testnet, + context.read().state.networkData.testnet, onStart: true, ); } else { _receiveCubit.updateWalletType( PaymentNetwork.bitcoin, - context.read().state.testnet, + context.read().state.networkData.testnet, onStart: true, ); } + print('-----10 - ${DateTime.now()}'); - _receiveCubit.updateWalletBloc(walletBloc); - - super.initState(); + _receiveCubit.updateWallet(wallet); + print('-----11 - ${DateTime.now()}'); } @override Widget build(BuildContext context) { - return MultiBlocProvider( - providers: [ - BlocProvider.value(value: _receiveCubit), - BlocProvider.value(value: _currencyCubit), - BlocProvider.value(value: _swapCubit), - ], - child: ReceiveListeners( - child: Scaffold( - appBar: AppBar( - flexibleSpace: const _ReceiveAppBar(), - automaticallyImplyLeading: false, - ), - body: const _WalletProvider( - child: _Screen(), + return FutureBuilder( + future: _setupBlocs(), + builder: (context, snapshot) { + if (snapshot.connectionState != ConnectionState.done) { + return const SizedBox.shrink(); + } + + return MultiBlocProvider( + providers: [ + BlocProvider.value(value: _receiveCubit), + BlocProvider.value(value: _currencyCubit), + BlocProvider.value(value: _swapCubit), + ], + child: ReceiveListeners( + child: Scaffold( + appBar: AppBar( + flexibleSpace: const _ReceiveAppBar(), + automaticallyImplyLeading: false, + ), + body: _WalletProvider( + child: const _Screen().animate(delay: 400.ms).fadeIn(), + ), + ), ), - ), - ), + ); + }, ); } } @@ -147,13 +244,13 @@ class _Screen extends StatelessWidget { context.select((ReceiveCubit x) => x.state.loadingAddress); final watchOnly = - context.select((WalletBloc x) => x.state.wallet!.watchOnly()); + context.select((WalletBloc x) => x.state.wallet.watchOnly()); final mainWallet = context.select((ReceiveCubit x) => x.state.checkIfMainWalletSelected()); context.select((WalletBloc x) => x.state.wallet); final walletIsLiquid = context.select( - (WalletBloc x) => x.state.wallet!.isLiquid(), + (WalletBloc x) => x.state.wallet.isLiquid(), ); final showWarning = context.select((CreateSwapCubit x) => x.state.showWarning()); @@ -258,17 +355,30 @@ class _Screen extends StatelessWidget { } } -class ReceiveWalletsDropDown extends StatelessWidget { +class ReceiveWalletsDropDown extends StatefulWidget { const ReceiveWalletsDropDown({super.key}); + @override + State createState() => _ReceiveWalletsDropDownState(); +} + +class _ReceiveWalletsDropDownState extends State { + List wallets = []; + + @override + void initState() { + final network = context.read().getBBNetwork; + wallets = context.read().walletsFromNetwork(network); + super.initState(); + } + @override Widget build(BuildContext context) { final oneWallet = context.select((ReceiveCubit _) => _.state.oneWallet); - final network = context.select((NetworkCubit _) => _.state.getBBNetwork()); - final walletBlocs = context - .select((HomeCubit _) => _.state.walletBlocsFromNetwork(network)); - final selectedWalletBloc = - context.select((ReceiveCubit _) => _.state.walletBloc); + // final network = context.select((NetworkCubit _) => _.state.getBBNetwork()); + // final walletBlocs = context + // .select((HomeBloc _) => _.state.walletBlocsFromNetwork(network)); + final selectedWallet = context.select((ReceiveCubit _) => _.state.wallet); // final walletBloc = selectedWalletBloc ?? walletBlocs.first; @@ -278,17 +388,16 @@ class ReceiveWalletsDropDown extends StatelessWidget { child: IgnorePointer( ignoring: oneWallet, child: WalletDropDown( - items: walletBlocs.map((wb) => wb.state.wallet!).toList(), + items: wallets, + // walletBlocs.map((wb) => wb.state.wallet).toList(), onChanged: (wallet) { - final blocs = - walletBlocs.where((wb) => wb.state.wallet == wallet).toList(); - if (blocs.isNotEmpty) { + // final blocs = wallets.where((_) => _ == wallet).toList(); + if (wallets.isNotEmpty) { context.read().removeWarnings(); - context.read().updateWalletBloc(blocs[0]); + context.read().updateWallet(wallets[0]); } }, - value: - selectedWalletBloc?.state.wallet ?? walletBlocs[0].state.wallet!, + value: selectedWallet ?? wallets[0], ), /* @@ -352,7 +461,7 @@ class SelectWalletType extends StatelessWidget { context.read().updateAmountDirect(0); context.read().removeWarnings(); - final isTestnet = context.read().state.testnet; + final isTestnet = context.read().state.networkData.testnet; context.read().updateWalletType(value, isTestnet); }, ); @@ -366,10 +475,13 @@ class _WalletProvider extends StatelessWidget { @override Widget build(BuildContext context) { - final wallet = context.select((ReceiveCubit _) => _.state.walletBloc); + final wallet = context.select((ReceiveCubit _) => _.state.wallet); if (wallet == null) return child; - return BlocProvider.value(value: wallet, child: child); + return BlocProvider.value( + value: createOrRetreiveWalletBloc(wallet.id), + child: child, + ); } } @@ -628,7 +740,8 @@ class ChainSwapDisplayReceive extends StatelessWidget { if (swapTx == null) return const SizedBox.shrink(); final amount = swapTx.outAmount / 100000000.0; - final isTestnet = context.select((NetworkCubit x) => x.state.testnet); + final isTestnet = + context.select((NetworkBloc x) => x.state.networkData.testnet); final bip21Address = context.select( (ReceiveCubit x) => x.state.getAddressWithAmountAndLabel( amount, @@ -658,7 +771,7 @@ class ChainSwapForm extends StatelessWidget { context.select((CurrencyCubit x) => x.state.amount); context.select( - (ReceiveCubit x) => x.state.walletBloc?.state.wallet?.isLiquid(), + (ReceiveCubit x) => x.state.wallet?.isLiquid(), ); final err = context.select((CreateSwapCubit _) => _.state.err()); @@ -704,22 +817,21 @@ class ChainSwapForm extends StatelessWidget { loadingText: 'Creating Swap', onPressed: () async { final amt = context.read().state.amount; - final receiveWallet = - context.read().state.walletBloc!.state.wallet!; + final receiveWallet = context.read().state.wallet!; final label = context.read().state.description; final matchingWalletForRefund = context - .read() + .read() .state - .walletBlocsFromNetwork(receiveWallet.network) - .map((bloc) => bloc.state.wallet) + .walletsFromNetwork(receiveWallet.network) + // .map((bloc) => bloc.state.wallet) .where( (wallet) => - wallet?.baseWalletType != receiveWallet.baseWalletType, + wallet.baseWalletType != receiveWallet.baseWalletType, ) .first; final refundAddress = - matchingWalletForRefund?.lastGeneratedAddress; + matchingWalletForRefund.lastGeneratedAddress; context.read().createOnChainSwapForReceive( toWallet: receiveWallet, @@ -757,7 +869,7 @@ class CreateLightningInvoice extends StatelessWidget { final amount = context.select((CurrencyCubit x) => x.state.amount); final isLiquid = context.select( - (ReceiveCubit x) => x.state.walletBloc?.state.wallet?.isLiquid(), + (ReceiveCubit x) => x.state.wallet?.isLiquid(), ); int finalFee = 0; @@ -823,14 +935,14 @@ class CreateLightningInvoice extends StatelessWidget { loadingText: 'Creating Invoice', onPressed: () async { final amt = context.read().state.amount; - final wallet = - context.read().state.walletBloc!.state.wallet!; + final wallet = context.read().state.wallet!; final walletIsLiquid = wallet.isLiquid(); final label = context.read().state.description; - final isTestnet = context.read().state.testnet; + final isTestnet = + context.read().state.networkData.testnet; final networkUrl = !walletIsLiquid - ? context.read().state.getNetworkUrl() - : context.read().state.getLiquidNetworkUrl(); + ? context.read().getNetworkUrl + : context.read().getLiquidNetworkUrl; context.read().createRevSwapForReceive( amount: amt, @@ -958,9 +1070,10 @@ class ReceiveQR extends StatelessWidget { final swapTx = context.select((CreateSwapCubit x) => x.state.swapTx); final amount = context.select((CurrencyCubit x) => x.state.amount / 100000000.0); - final isTestnet = context.select((NetworkCubit x) => x.state.testnet); + final isTestnet = + context.select((NetworkBloc x) => x.state.networkData.testnet); final isLiquid = context.select( - (ReceiveCubit x) => x.state.walletBloc?.state.wallet?.isLiquid() ?? false, + (ReceiveCubit x) => x.state.wallet?.isLiquid() ?? false, ); final bip21Address = context.select( (ReceiveCubit x) => x.state.getAddressWithAmountAndLabel( @@ -1107,9 +1220,10 @@ class _ReceiveDisplayAddressState extends State { final swapTx = context.select((CreateSwapCubit x) => x.state.swapTx); final amount = context.select((CurrencyCubit x) => x.state.amount / 100000000.0); - final isTestnet = context.select((NetworkCubit x) => x.state.testnet); + final isTestnet = + context.select((NetworkBloc x) => x.state.networkData.testnet); final isLiquid = context.select( - (ReceiveCubit x) => x.state.walletBloc?.state.wallet?.isLiquid() ?? false, + (ReceiveCubit x) => x.state.wallet?.isLiquid() ?? false, ); final bip21Address = context.select( (ReceiveCubit x) => x.state.getAddressWithAmountAndLabel( diff --git a/lib/routes.dart b/lib/routes.dart index e69f2f5e..6bfd08ad 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -27,16 +27,13 @@ import 'package:bb_mobile/swap/swap_confirmation.dart'; import 'package:bb_mobile/swap/swap_history_page.dart'; import 'package:bb_mobile/swap/swap_page.dart'; import 'package:bb_mobile/swap/swap_page_progress_page.dart'; -import 'package:bb_mobile/testground.dart'; import 'package:bb_mobile/transaction/bump_fees.dart'; import 'package:bb_mobile/transaction/transaction_page.dart'; -import 'package:bb_mobile/wallet/bloc/wallet_bloc.dart'; import 'package:bb_mobile/wallet/details.dart'; import 'package:bb_mobile/wallet/information_page.dart'; import 'package:bb_mobile/wallet/wallet_page.dart'; import 'package:bb_mobile/wallet_settings/accounting.dart'; import 'package:bb_mobile/wallet_settings/backup.dart'; -import 'package:bb_mobile/wallet_settings/bloc/wallet_settings_cubit.dart'; import 'package:bb_mobile/wallet_settings/test_backup.dart'; import 'package:bb_mobile/wallet_settings/wallet_settings_page.dart'; import 'package:flutter/material.dart'; @@ -52,12 +49,12 @@ GoRouter setupRouter() => GoRouter( initialLocation: '/', observers: [GoRouterObserver()], routes: [ - GoRoute( - path: '/testground', - builder: (context, state) { - return const Testground(); - }, - ), + // GoRoute( + // path: '/testground', + // builder: (context, state) { + // return const Testground(); + // }, + // ), GoRoute( path: '/', builder: (context, state) { @@ -65,28 +62,6 @@ GoRouter setupRouter() => GoRouter( }, ), - GoRoute( - path: '/swap-page', - builder: (context, state) { - final q = state.uri.queryParameters; - return SwapPage(fromWalletId: q['fromWalletId']); - }, - ), - - GoRoute( - path: '/swap-confirmation', - builder: (context, state) { - // TODO: Convert this to proper map - final params = state.extra! as List; - final sendCubit = params[0] as SendCubit; - final swapCubit = params[1] as CreateSwapCubit; - return SwapConfirmationPage( - send: sendCubit, - swap: swapCubit, - ); - }, - ), - GoRoute( path: '/change-pin', builder: (context, state) { @@ -215,8 +190,8 @@ GoRouter setupRouter() => GoRouter( GoRoute( path: '/wallet-settings', builder: (context, state) { - final walletBloc = state.extra! as WalletBloc; - return WalletSettingsPage(walletBloc: walletBloc); + final wallet = state.extra! as String; + return WalletSettingsPage(wallet: wallet); }, ), // GoRoute( @@ -228,37 +203,16 @@ GoRouter setupRouter() => GoRouter( GoRoute( path: '/wallet-settings/open-backup', builder: (context, state) { - final walletBloc = state.extra! as WalletBloc; - return WalletSettingsPage(openBackup: true, walletBloc: walletBloc); - }, - ), - GoRoute( - path: '/wallet-settings/test-backup', - builder: (context, state) { - final blocs = state.extra! as (WalletBloc, WalletSettingsCubit); - return TestBackupPage( - walletBloc: blocs.$1, - walletSettings: blocs.$2, - ); - // const WalletSettingsPage(openTestBackup: true); + final wallet = state.extra! as String; + return WalletSettingsPage(openBackup: true, wallet: wallet); }, ), - GoRoute( - path: '/wallet-settings/backup', - builder: (context, state) { - final blocs = state.extra! as (WalletBloc, WalletSettingsCubit); - return BackupPage( - walletBloc: blocs.$1, - walletSettings: blocs.$2, - ); - }, - ), GoRoute( path: '/wallet-settings/accounting', builder: (context, state) { - final walletBloc = state.extra! as WalletBloc; - return AccountingPage(walletBloc: walletBloc); + final wallet = state.extra! as String; + return AccountingPage(wallet: wallet); }, ), GoRoute( @@ -277,54 +231,24 @@ GoRouter setupRouter() => GoRouter( GoRoute( path: '/receive', builder: (context, state) { - final walletBloc = state.extra as WalletBloc?; + final wallet = state.extra as String?; - return ReceivePage(walletBloc: walletBloc); + return ReceivePage(wallet: wallet); }, ), - GoRoute( - path: '/swap-receive', - builder: (context, state) { - final tx = state.extra! as SwapTx; - return ReceivingSwapPage(tx: tx); - }, - ), - GoRoute( - path: '/onchain-swap-progress', - builder: (context, state) { - // TODO: Convert this to proper map - final list = state.extra! as List; - final swapTx = list[0] as SwapTx; - final isReceive = list[1] as bool; - final SendCubit? sendCubit = - list.length == 3 ? list[2] as SendCubit : null; - - return ChainSwapProgressPage( - swapTx: swapTx, - isReceive: isReceive, - sendCubit: sendCubit, - ); - }, - ), - GoRoute( - path: '/swap-history', - builder: (context, state) { - return const SwapHistoryPage(); - }, - ), GoRoute( path: '/wallet', builder: (context, state) { - final wallet = state.extra! as WalletBloc; - return WalletPage(walletBloc: wallet); + final wallet = state.extra! as String; + return WalletPage(wallet: wallet); }, ), GoRoute( path: '/wallet/details', builder: (context, state) { - final wallet = state.extra! as WalletBloc; - return WalletDetailsPage(walletBloc: wallet); + final wallet = state.extra! as String; + return WalletDetailsPage(wallet: wallet); }, ), GoRoute( @@ -360,6 +284,88 @@ GoRouter setupRouter() => GoRouter( return BumpFeesPage(tx: tx); }, ), + + // + // + + GoRoute( + path: '/wallet-settings/test-backup', + builder: (context, state) { + final wallet = state.extra! as String; + return TestBackupPage( + wallet: wallet, + // walletSettings: blocs.$2, + ); + // const WalletSettingsPage(openTestBackup: true); + }, + ), + GoRoute( + path: '/wallet-settings/backup', + builder: (context, state) { + final wallet = state.extra! as String; + + return BackupPage( + wallet: wallet, + ); + }, + ), + + // + // + // + + GoRoute( + path: '/swap-page', + builder: (context, state) { + final q = state.uri.queryParameters; + return SwapPage(fromWalletId: q['fromWalletId']); + }, + ), + + GoRoute( + path: '/swap-confirmation', + builder: (context, state) { + // TODO: Convert this to proper map + final params = state.extra! as List; + final sendCubit = params[0] as SendCubit; + final swapCubit = params[1] as CreateSwapCubit; + return SwapConfirmationPage( + send: sendCubit, + swap: swapCubit, + ); + }, + ), + + GoRoute( + path: '/swap-receive', + builder: (context, state) { + final tx = state.extra! as SwapTx; + return ReceivingSwapPage(tx: tx); + }, + ), + GoRoute( + path: '/onchain-swap-progress', + builder: (context, state) { + // TODO: Convert this to proper map + final list = state.extra! as List; + final swapTx = list[0] as SwapTx; + final isReceive = list[1] as bool; + final SendCubit? sendCubit = + list.length == 3 ? list[2] as SendCubit : null; + + return ChainSwapProgressPage( + swapTx: swapTx, + isReceive: isReceive, + sendCubit: sendCubit, + ); + }, + ), + GoRoute( + path: '/swap-history', + builder: (context, state) { + return const SwapHistoryPage(); + }, + ), ], ); @@ -385,7 +391,6 @@ class GoRouterObserver extends NavigatorObserver { } } - // extension GoRouterExtension on GoRouter { // String location() { // final lastMatch = routerDelegate.currentConfiguration.last; diff --git a/lib/send/advanced.dart b/lib/send/advanced.dart index 7a854487..1e521884 100644 --- a/lib/send/advanced.dart +++ b/lib/send/advanced.dart @@ -121,7 +121,10 @@ class SendAllOption extends StatelessWidget { BBSwitch( value: sendAll, onChanged: (e) { - context.read().sendAllCoin(e); + final currency = context.read().state; + context + .read() + .sendAllCoin(e, currency.amount, currency.unitsInSats); }, ), ], @@ -156,7 +159,7 @@ class EnableRBFOption extends StatelessWidget { @override Widget build(BuildContext context) { - final isLiq = context.select((WalletBloc x) => x.state.isLiq()); + final isLiq = context.select((WalletBloc x) => x.state.wallet.isLiquid()); if (isLiq) return const SizedBox.shrink(); final disableRBF = context.select((SendCubit x) => x.state.disableRBF); @@ -197,7 +200,7 @@ class AddressSelectionPopUp extends StatelessWidget { @override Widget build(BuildContext context) { - final utxos = context.select((WalletBloc _) => _.state.wallet!.utxos); + final utxos = context.select((WalletBloc _) => _.state.wallet.utxos); final amount = context.select((CurrencyCubit _) => _.state.amount); final amt = context.select( (CurrencyCubit x) => x.state.getAmountInUnits(amount), diff --git a/lib/send/bloc/send_cubit.dart b/lib/send/bloc/send_cubit.dart index defd0576..ccdf1733 100644 --- a/lib/send/bloc/send_cubit.dart +++ b/lib/send/bloc/send_cubit.dart @@ -11,14 +11,11 @@ import 'package:bb_mobile/_pkg/payjoin/event.dart'; import 'package:bb_mobile/_pkg/payjoin/manager.dart'; import 'package:bb_mobile/_pkg/wallet/bip21.dart'; import 'package:bb_mobile/_pkg/wallet/transaction.dart'; -import 'package:bb_mobile/currency/bloc/currency_cubit.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; -import 'package:bb_mobile/network_fees/bloc/networkfees_cubit.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; +import 'package:bb_mobile/_repository/wallet_service.dart'; import 'package:bb_mobile/send/bloc/send_state.dart'; import 'package:bb_mobile/swap/create_swap_bloc/swap_cubit.dart'; -import 'package:bb_mobile/wallet/bloc/event.dart'; -import 'package:bb_mobile/wallet/bloc/wallet_bloc.dart'; import 'package:boltz_dart/boltz_dart.dart' as boltz; import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; @@ -27,23 +24,19 @@ import 'package:flutter_bloc/flutter_bloc.dart'; class SendCubit extends Cubit { SendCubit({ required Barcode barcode, - WalletBloc? walletBloc, required WalletTx walletTx, required FileStorage fileStorage, - required NetworkCubit networkCubit, - required NetworkFeesCubit networkFeesCubit, - required CurrencyCubit currencyCubit, + required NetworkRepository networkRepository, + required AppWalletsRepository appWalletsRepository, required bool openScanner, - required HomeCubit homeCubit, required bool defaultRBF, required PayjoinManager payjoinManager, required SwapBoltz swapBoltz, required CreateSwapCubit swapCubit, + Wallet? wallet, bool oneWallet = true, - }) : _homeCubit = homeCubit, - _networkCubit = networkCubit, - _networkFeesCubit = networkFeesCubit, - _currencyCubit = currencyCubit, + }) : _appWalletsRepository = appWalletsRepository, + _networkRepository = networkRepository, _walletTx = walletTx, _fileStorage = fileStorage, _barcode = barcode, @@ -52,21 +45,19 @@ class SendCubit extends Cubit { _swapCubit = swapCubit, super( SendState( - selectedWalletBloc: walletBloc, oneWallet: oneWallet, + selectedWallet: wallet, ), ) { emit( state.copyWith( disableRBF: !defaultRBF, - // selectedWalletBloc: walletBloc, ), ); if (openScanner) scanAddress(); - if (walletBloc != null) selectWallets(fromStart: true); + if (wallet != null) selectWallets(fromStart: true); - // Subscribe to payjoin events to update the send state _pjEventSubscription = PayjoinEventBus().stream.listen((event) { if (event is PayjoinSenderPostMessageASuccessEvent) { if (event.pjUri != state.payjoinEndpoint.toString()) return; @@ -76,7 +67,9 @@ class SendCubit extends Cubit { ), ); } else if (event is PayjoinBroadcastEvent) { - state.selectedWalletBloc!.add(SyncWallet()); + _appWalletsRepository + .getWalletServiceById(state.selectedWallet!.id) + ?.syncWallet(); } else if (event is PayjoinSendFailureEvent && event.pjUri == state.payjoinEndpoint.toString()) { emit( @@ -96,11 +89,9 @@ class SendCubit extends Cubit { final PayjoinManager _payjoinManager; final SwapBoltz _swapBoltz; - final NetworkCubit _networkCubit; - final NetworkFeesCubit _networkFeesCubit; - final CurrencyCubit _currencyCubit; - final HomeCubit _homeCubit; final CreateSwapCubit _swapCubit; + final NetworkRepository _networkRepository; + final AppWalletsRepository _appWalletsRepository; @override Future close() { @@ -108,7 +99,11 @@ class SendCubit extends Cubit { return super.close(); } - Future updateAddress(String? addr, {bool changeWallet = true}) async { + Future updateAddress( + String? addr, { + bool changeWallet = true, + int? currentAmt, + }) async { if (!state.oneWallet) resetWalletSelection(changeWallet: changeWallet); resetErrors(); _swapCubit.clearSwapTx(); @@ -119,8 +114,9 @@ class SendCubit extends Cubit { scanningAddress: true, ), ); + final address = addr ?? state.address; - final network = _networkCubit.state.getBBNetwork(); + final network = _networkRepository.getBBNetwork; final (paymentNetwork, err) = await state.getPaymentNetwork(address, network); if (err != null) { @@ -128,13 +124,13 @@ class SendCubit extends Cubit { state.copyWith( errScanningAddress: err.toString(), scanningAddress: false, - // address: '', invoice: null, note: '', ), ); - _currencyCubit.updateAmountDirect(0); - _currencyCubit.updateAmount(''); + + emit(state.copyWith(tempAmt: 0, tempStrAmt: '')); + resetWalletSelection(changeWallet: changeWallet); if (address.isEmpty) resetErrors(); return; @@ -152,8 +148,8 @@ class SendCubit extends Cubit { emit( state.copyWith( paymentNetwork: paymentNetwork, - payjoinEndpoint: null, // Reset payjoin endpoint for not pj addresses - payjoinSender: null, // Reset payjoin sender for not pj addresses + payjoinEndpoint: null, + payjoinSender: null, ), ); @@ -164,10 +160,14 @@ class SendCubit extends Cubit { emit(state.copyWith(address: newAddress)); final amount = bip21Obj.options['amount'] as num?; if (amount != null) { - _currencyCubit.btcToCurrentTempAmount(amount.toDouble()); final amountInSats = (amount * 100000000).toInt(); - _currencyCubit.updateAmountDirect(amountInSats); - emit(state.copyWith(tempAmt: amountInSats)); + + emit( + state.copyWith( + btcTempAmt: amount.toDouble(), + tempAmt: amountInSats, + ), + ); } final label = bip21Obj.options['label'] as String?; if (label != null) { @@ -175,8 +175,6 @@ class SendCubit extends Cubit { } final pjParam = bip21Obj.options['pj'] as String?; if (pjParam != null) { - // FIXME: this is an ugly hack because of ugliness in the bip21 module. - // Dart's URI encoding is not the same as the one used by the bip21 module. final parsedPjParam = Uri.parse(pjParam); final partialEncodedPjParam = parsedPjParam.toString().replaceAll('#', '%23'); @@ -193,12 +191,14 @@ class SendCubit extends Cubit { emit(state.copyWith(address: newAddress)); final amount = bip21Obj.options['amount'] as num?; if (amount != null) { - _currencyCubit.btcToCurrentTempAmount(amount.toDouble()); - final amountInSats = - _currencyCubit.convertBtcStringToSats(amount.toString()); - // final amountInSats = (amount * 100000000).toInt(); - _currencyCubit.updateAmountDirect(amountInSats); - emit(state.copyWith(tempAmt: amountInSats)); + final amountInSats = state.convertBtcStringToSats(amount.toString()); + + emit( + state.copyWith( + tempAmt: amountInSats, + btcTempAmt: amount.toDouble(), + ), + ); } final label = bip21Obj.options['label'] as String?; if (label != null) { @@ -206,7 +206,7 @@ class SendCubit extends Cubit { } case AddressNetwork.lightning: final boltzUrl = - _networkCubit.state.testnet ? boltzTestnetUrl : boltzMainnetUrl; + _networkRepository.testnet ? boltzTestnetUrl : boltzMainnetUrl; final (inv, errInv) = await _swapBoltz.decodeInvoice( invoice: address.toLowerCase(), boltzUrl: boltzUrl, @@ -227,12 +227,18 @@ class SendCubit extends Cubit { ); return; } - if (_networkCubit.state.testnet != inv.isTestnet()) { + if (_networkRepository.testnet != inv.isTestnet()) { emit(state.copyWith(errScanningAddress: 'Network mismatch')); return; } - _currencyCubit.updateAmountDirect(inv.getAmount()); - emit(state.copyWith(invoice: inv, address: address)); + + emit( + state.copyWith( + invoice: inv, + address: address, + tempAmt: inv.getAmount(), + ), + ); case AddressNetwork.bip21Lightning: final invoice = address.toLowerCase().replaceAll('lightning:', ''); @@ -251,13 +257,19 @@ class SendCubit extends Cubit { ); return; } - if (_networkCubit.state.testnet != inv.isTestnet()) { + if (_networkRepository.testnet != inv.isTestnet()) { emit(state.copyWith(errScanningAddress: 'Network mismatch')); return; } - _currencyCubit.updateAmountDirect(inv.getAmount()); - emit(state.copyWith(invoice: inv, address: invoice)); + + emit( + state.copyWith( + invoice: inv, + address: invoice, + tempAmt: inv.getAmount(), + ), + ); case AddressNetwork.bitcoin: emit(state.copyWith(address: address)); @@ -277,7 +289,7 @@ class SendCubit extends Cubit { if (!fromStart) { if (state.paymentNetwork == null) return; } else { - final isLiq = state.selectedWalletBloc!.state.isLiq(); + final isLiq = state.selectedWallet!.isLiquid(); emit( state.copyWith( paymentNetwork: @@ -286,19 +298,20 @@ class SendCubit extends Cubit { ); } - // Process address only if it has value. + final amt = fromStart ? 0 : state.tempAmt ?? 0; + if (state.address.isNotEmpty) { switch (state.paymentNetwork!) { case AddressNetwork.bip21Bitcoin: - _processBitcoinAddress(); + _processBitcoinAddress(amt); case AddressNetwork.bip21Liquid: - _processLiquidAddress(); + _processLiquidAddress(amt); case AddressNetwork.lightning: _processLnInvoice(); case AddressNetwork.bitcoin: - _processBitcoinAddress(); + _processBitcoinAddress(amt); case AddressNetwork.liquid: - _processLiquidAddress(); + _processLiquidAddress(amt); case AddressNetwork.bip21Lightning: _processLnInvoice(); } @@ -308,24 +321,21 @@ class SendCubit extends Cubit { Future _processLnInvoice() async { final amt = state.invoice!.getAmount(); - WalletBloc? walletBlocc; + Wallet? wallett; var storedSwapTxIdx = -1; if (state.oneWallet) { - walletBlocc = state.selectedWalletBloc; - storedSwapTxIdx = walletBlocc!.state.wallet!.swaps.indexWhere( + wallett = state.selectedWallet; + storedSwapTxIdx = wallett!.swaps.indexWhere( (element) => element.lnSwapDetails != null && element.lnSwapDetails!.invoice == state.invoice!.invoice, ); } else { - final mainWalletsBlocs = _homeCubit.state.walletBlocsFromNetwork( - _networkCubit.state.getBBNetwork(), - ); + final mainWallets = _appWalletsRepository + .walletsFromNetwork(_networkRepository.getBBNetwork); - // WalletBloc? walletBlocc; - for (final walletBloc in mainWalletsBlocs) { - final wallet = walletBloc.state.wallet!; + for (final wallet in mainWallets) { storedSwapTxIdx = wallet.swaps.indexWhere( (element) => element.lnSwapDetails != null && @@ -344,48 +354,46 @@ class SendCubit extends Cubit { ); return; } - walletBlocc = walletBloc; + wallett = wallet; break; } } } - if (storedSwapTxIdx != -1 && walletBlocc != null) { + if (storedSwapTxIdx != -1 && wallett != null) { emit( state.copyWith( - selectedWalletBloc: walletBlocc, - enabledWallets: [walletBlocc.state.wallet!.id], + selectedWallet: wallett, + enabledWallets: [wallett.id], ), ); - final selectedWallet = walletBlocc.state.wallet!; + final selectedWallet = wallett; final networkurl = selectedWallet.isLiquid() - ? _networkCubit.state.getLiquidNetworkUrl() - : _networkCubit.state.getNetworkUrl(); + ? _networkRepository.getLiquidNetworkUrl + : _networkRepository.getNetworkUrl; if (amt == 0) { emit(state.copyWith(showSendButton: false)); } else { - checkBalance(); + checkBalance(amt); } - // emit(state.copyWith(showSendButton: true)); await _swapCubit.createSubSwapForSend( wallet: selectedWallet, address: state.address, invoice: state.invoice!, amount: amt, - isTestnet: _networkCubit.state.testnet, + isTestnet: _networkRepository.testnet, networkUrl: networkurl, ); return; } if (!state.oneWallet) { - final wallets = _homeCubit.state.walletsWithEnoughBalance( + final wallets = _appWalletsRepository.walletsWithEnoughBalance( amt, - _networkCubit.state.getBBNetwork(), - // onlyMain: true, + _networkRepository.getBBNetwork, ); if (wallets.isEmpty) { emit( @@ -397,11 +405,11 @@ class SendCubit extends Cubit { return; } - final selectWalletBloc = state.selectLiqThenSecThenOtherBtc(wallets); + final selectWallet = state.selectLiqThenSecThenOtherBtc2(wallets); emit( state.copyWith( - selectedWalletBloc: selectWalletBloc, - enabledWallets: wallets.map((_) => _.state.wallet!.id).toList(), + selectedWallet: selectWallet, + enabledWallets: wallets.map((_) => _.id).toList(), ), ); } @@ -409,19 +417,15 @@ class SendCubit extends Cubit { if (amt == 0) { emit(state.copyWith(showSendButton: false)); } else { - checkBalance(); + checkBalance(amt); } - - // emit(state.copyWith(showSendButton: true)); } - Future _processBitcoinAddress() async { - final amount = _currencyCubit.state.amount; - + Future _processBitcoinAddress(int amount) async { if (!state.oneWallet) { - final wallets = _homeCubit.state.walletsWithEnoughBalance( + final wallets = _appWalletsRepository.walletsWithEnoughBalance( amount, - _networkCubit.state.getBBNetwork(), + _networkRepository.getBBNetwork, onlyBitcoin: true, ); if (wallets.isEmpty) { @@ -431,11 +435,11 @@ class SendCubit extends Cubit { ), ); resetWalletSelection(); - final mainBitcoinWallet = _homeCubit.state - .getMainSecureWallet(_networkCubit.state.getBBNetwork()); + final mainBitcoinWallet = _appWalletsRepository + .getMainSecureWallet(_networkRepository.getBBNetwork); emit( state.copyWith( - selectedWalletBloc: mainBitcoinWallet, + selectedWallet: mainBitcoinWallet, ), ); return; @@ -443,13 +447,13 @@ class SendCubit extends Cubit { final couldBeOnChainSwap = state.couldBeOnchainSwap(); final selectWallet = couldBeOnChainSwap == true - ? state.selectedWalletBloc - : state.selectMainBtcThenOtherHighestBalBtc(wallets); + ? state.selectedWallet + : state.selectMainBtcThenOtherHighestBalBtc2(wallets); emit( state.copyWith( - enabledWallets: wallets.map((_) => _.state.wallet!.id).toList(), - selectedWalletBloc: selectWallet, + enabledWallets: wallets.map((_) => _.id).toList(), + selectedWallet: selectWallet, ), ); } @@ -457,19 +461,15 @@ class SendCubit extends Cubit { if (amount == 0) { emit(state.copyWith(showSendButton: false)); } else { - checkBalance(); + checkBalance(amount); } - - // emit(state.copyWith(showSendButton: true)); } - Future _processLiquidAddress() async { - final amount = _currencyCubit.state.amount; - + Future _processLiquidAddress(int amount) async { if (!state.oneWallet) { - final wallets = _homeCubit.state.walletsWithEnoughBalance( + final wallets = _appWalletsRepository.walletsWithEnoughBalance( amount, - _networkCubit.state.getBBNetwork(), + _networkRepository.getBBNetwork, onlyLiquid: true, ); if (wallets.isEmpty) { @@ -484,8 +484,8 @@ class SendCubit extends Cubit { emit( state.copyWith( - selectedWalletBloc: wallets.first, - enabledWallets: wallets.map((_) => _.state.wallet!.id).toList(), + selectedWallet: wallets.first, + enabledWallets: wallets.map((_) => _.id).toList(), ), ); } @@ -493,14 +493,12 @@ class SendCubit extends Cubit { if (amount == 0) { emit(state.copyWith(showSendButton: false)); } else { - checkBalance(); + checkBalance(amount); } - // emit(state.copyWith(showSendButton: true)); } - void checkBalance() { - final balance = state.selectedWalletBloc?.state.balanceSats() ?? 0; - final amount = _currencyCubit.state.amount; + void checkBalance(int amount) { + final balance = state.selectedWallet?.balanceSats() ?? 0; if (amount == 0) { emit(state.copyWith(showSendButton: false)); @@ -528,8 +526,7 @@ class SendCubit extends Cubit { emit( state.copyWith( enabledWallets: [], - selectedWalletBloc: - changeWallet == true ? null : state.selectedWalletBloc, + selectedWallet: changeWallet == true ? null : state.selectedWallet, showSendButton: false, invoice: clearInv ? null : state.invoice, tempAmt: 0, @@ -547,10 +544,10 @@ class SendCubit extends Cubit { ), ); - void updateWalletBloc(WalletBloc walletBloc) { - emit(state.copyWith(selectedWalletBloc: walletBloc)); - sendAllCoin(false); - checkBalance(); + void updateWallet(Wallet wallet, int amt, bool unitsInSats) { + emit(state.copyWith(selectedWallet: wallet)); + sendAllCoin(false, amt, unitsInSats); + checkBalance(amt); } void disabledDropdownClicked() { @@ -591,36 +588,36 @@ class SendCubit extends Cubit { void disableRBF(bool disable) => emit(state.copyWith(disableRBF: disable)); - void sendAllCoin(bool sendAll) { - if (state.selectedWalletBloc == null) return; + void sendAllCoin(bool sendAll, int amount, bool unitsInSats) { + if (state.selectedWallet == null) return; emit( state.copyWith( sendAllCoin: sendAll, ), ); - final balance = state.selectedWalletBloc!.state.balanceSats(); + final balance = state.selectedWallet!.balanceSats(); final directAmt = sendAll ? balance : state.isLnInvoice() ? state.invoice!.getAmount() - : _currencyCubit.state.amount; + : amount; - _currencyCubit.updateAmountDirect(directAmt); + emit(state.copyWith(tempAmt: directAmt)); - final bal = _currencyCubit.state.unitsInSats - ? state.selectedWalletBloc!.state.balanceSats().toString() - : state.selectedWalletBloc!.state.balanceStr(); + final bal = unitsInSats + ? state.selectedWallet!.balanceSats().toString() + : state.selectedWallet!.balanceStr(); - _currencyCubit.updateAmount( - directAmt == 0 - ? '' - : !sendAll && state.isLnInvoice() - ? directAmt.toString() - : bal, - ); + final da = directAmt == 0 + ? '' + : !sendAll && state.isLnInvoice() + ? directAmt.toString() + : bal; + + emit(state.copyWith(tempStrAmt: da)); - checkBalance(); + checkBalance(amount); } void togglePayjoin(bool toggle) { @@ -697,24 +694,20 @@ class SendCubit extends Cubit { required SwapTx swaptx, }) async { if (state.sending) return; - if (state.selectedWalletBloc == null) return; - final w = state.selectedWalletBloc!.state.wallet; + if (state.selectedWallet == null) return; + final w = state.selectedWallet!; emit(state.copyWith(buildingOnChain: true)); - final localWalletBloc = _homeCubit.state.getWalletBlocById(w!.id); - if (localWalletBloc == null) return; - final localWallet = localWalletBloc.state.wallet; - final isLiq = localWallet!.isLiquid(); + final localWallet = _appWalletsRepository.getWalletById(w.id); + if (localWallet == null) return; - // if (!localWallet.mainWallet) return; + final isLiq = localWallet.isLiquid(); final address = swaptx.scriptAddress; - // final fee = networkFees; - final fee = - isLiq ? _networkCubit.state.pickLiquidFees() : networkFees.toDouble(); - // emit(state.copyWith(sending: true, errSending: '')); + final fee = + isLiq ? _networkRepository.pickLiquidFees : networkFees.toDouble(); final isBitcoinSweep = localWallet.isBitcoin() && state.onChainAbsFee != null && @@ -724,7 +717,6 @@ class SendCubit extends Cubit { isManualSend: false, address: address, amount: swaptx.outAmount, - // amount: 5000, // to test submarine refund sendAllCoin: false, feeRate: isBitcoinSweep ? 0 : fee, absFee: isBitcoinSweep ? state.onChainAbsFee : 0, @@ -773,30 +765,25 @@ class SendCubit extends Cubit { required SwapTx swaptx, }) async { if (state.sending) return; - if (state.selectedWalletBloc == null) return; - final w = state.selectedWalletBloc!.state.wallet; + if (state.selectedWallet == null) return; + final w = state.selectedWallet!; - final localWalletBloc = _homeCubit.state.getWalletBlocById(w!.id); - if (localWalletBloc == null) return; - final localWallet = localWalletBloc.state.wallet; - final isLiq = localWallet!.isLiquid(); + final localWallet = _appWalletsRepository.getWalletById(w.id); + if (localWallet == null) return; - // if (!localWallet.mainWallet) return; + final isLiq = localWallet.isLiquid(); final address = swaptx.scriptAddress; - // final fee = networkFees; - final fee = - isLiq ? _networkCubit.state.pickLiquidFees() : networkFees.toDouble(); - // emit(state.copyWith(sending: true, errSending: '')); + final fee = + isLiq ? _networkRepository.pickLiquidFees : networkFees.toDouble(); final (buildResp, err) = await _walletTx.buildTx( wallet: localWallet, isManualSend: false, address: address, amount: swaptx.outAmount, - // amount: 2500, // to test submarine refund - sendAllCoin: false, //swaptx.isChainSwap() ? state.sendAllCoin : false, + sendAllCoin: false, feeRate: swaptx.isChainSwap() && state.onChainAbsFee != null && state.onChainAbsFee! > 0 @@ -827,7 +814,6 @@ class SendCubit extends Cubit { return; } - // To auto broadcast the swap in case of LBTC -> LN Swap; if (state.couldBeOnchainSwap() == false && state.isLnInvoice() == true && tx?.isLiquid == true) { @@ -857,32 +843,25 @@ class SendCubit extends Cubit { } } - // ----------------- Future sendSwap() async { emit(state.copyWith(sending: true, errSending: '')); final tx = state.tx!; final swap = state.tx!.swapTx!; - final w = state.selectedWalletBloc!.state.wallet; + final w = state.selectedWallet!; - final localWalletBloc = _homeCubit.state.getWalletBlocById(w!.id); - if (localWalletBloc == null) return; - final wallet = localWalletBloc.state.wallet; + final wallet = _appWalletsRepository.getWalletById(w.id); + if (wallet == null) return; - // if (!wallet!.isMain()) { - // emit(state.copyWith(errSending: "Submarine swaps currently only supported via ")); - // return; - // }; final (wtxid, errBroadcast) = await _walletTx.broadcastTxWithWallet( - wallet: wallet!, + wallet: wallet, address: swap.scriptAddress, note: state.note, transaction: tx.copyWith( swapTx: swap, isSwap: true, - // isLiquid: wallet.isLiquid(), ), - useOnlyLwk: true, // !broadcastViaBoltz, + useOnlyLwk: true, ); if (errBroadcast != null) { emit(state.copyWith(errSending: errBroadcast.toString(), sending: false)); @@ -894,34 +873,34 @@ class SendCubit extends Cubit { final (updatedWallet, _) = wtxid!; - state.selectedWalletBloc!.add( - UpdateWallet( - updatedWallet, - syncAfter: true, - updateTypes: [ - UpdateWalletTypes.addresses, - UpdateWalletTypes.transactions, - UpdateWalletTypes.swaps, - ], - ), + await _appWalletsRepository.getWalletServiceById(wallet.id)?.updateWallet( + updatedWallet, + syncAfter: true, + updateTypes: [ + UpdateWalletTypes.addresses, + UpdateWalletTypes.transactions, + UpdateWalletTypes.swaps, + ], ); - // } + Future.delayed(50.ms); - // state.selectedWalletBloc!.add(SyncWallet()); emit(state.copyWith(sending: false, sent: true)); } - Future baseLayerBuild({required int networkFees}) async { + Future baseLayerBuild({ + required int networkFees, + required int amount, + }) async { if (state.sending) return; - if (state.selectedWalletBloc == null) return; + if (state.selectedWallet == null) return; - final localWallet = state.selectedWalletBloc!.state.wallet; - final isLiq = localWallet!.isLiquid(); + final localWallet = state.selectedWallet!; + final isLiq = localWallet.isLiquid(); final address = state.address; final fee = - isLiq ? _networkCubit.state.pickLiquidFees() : networkFees.toDouble(); + isLiq ? _networkRepository.pickLiquidFees : networkFees.toDouble(); final bool enableRbf; enableRbf = !state.disableRBF; @@ -932,7 +911,7 @@ class SendCubit extends Cubit { wallet: localWallet, isManualSend: state.selectedUtxos.isNotEmpty, address: address, - amount: _currencyCubit.state.amount, + amount: amount, sendAllCoin: state.sendAllCoin, feeRate: fee, enableRbf: enableRbf, @@ -964,16 +943,14 @@ class SendCubit extends Cubit { final amountDirect = (tx.sent ?? 0) - (tx.received ?? 0); debugPrint('amountDirect: $amountDirect'); - // _currencyCubit.updateAmountDirect(amountDirect); } else { - state.selectedWalletBloc!.add( - UpdateWallet( - wallet, - updateTypes: [ - UpdateWalletTypes.transactions, - UpdateWalletTypes.swaps, - ], - ), + await _appWalletsRepository.getWalletServiceById(wallet.id)?.updateWallet( + wallet, + updateTypes: [ + UpdateWalletTypes.addresses, + UpdateWalletTypes.transactions, + UpdateWalletTypes.swaps, + ], ); emit( @@ -990,10 +967,10 @@ class SendCubit extends Cubit { required int networkFees, required String originalPsbt, required Wallet wallet, + required int amount, }) async { - // TODO Serialize raw bip21 input instead of this monstrosity final pjUriString = - 'bitcoin:${state.address}?amount=${_currencyCubit.state.amount / 100000000}&label=${Uri.encodeComponent(state.note)}&pj=${state.payjoinEndpoint!}&pjos=0'; + 'bitcoin:${state.address}?amount=${amount / 100000000}&label=${Uri.encodeComponent(state.note)}&pj=${state.payjoinEndpoint!}&pjos=0'; final sender = await _payjoinManager.initSender( pjUriString, networkFees, @@ -1003,14 +980,12 @@ class SendCubit extends Cubit { } Future payjoinSend(Wallet wallet) async { - if (state.selectedWalletBloc == null) return; + if (state.selectedWallet == null) return; if (state.payjoinSender == null) return; - // TODO copy originalPsbt.extractTx() to state.tx - // emit(state.copyWith(tx: originalPsbtTxWithId)); emit(state.copyWith(sending: true, sent: false)); _payjoinManager.spawnNewSender( - isTestnet: _networkCubit.state.testnet, + isTestnet: _networkRepository.testnet, sender: state.payjoinSender!, wallet: wallet, pjUrl: state.payjoinEndpoint!.toString(), @@ -1018,18 +993,14 @@ class SendCubit extends Cubit { } Future baseLayerSend() async { - if (state.selectedWalletBloc == null) return; + if (state.selectedWallet == null) return; emit(state.copyWith(sending: true, errSending: '')); final address = state.address; final (wtxid, err) = await _walletTx.broadcastTxWithWallet( - wallet: state.selectedWalletBloc!.state.wallet!, + wallet: state.selectedWallet!, address: address, note: state.note, transaction: state.tx!, - // .copyWith( - // swapTx: swaptx, - // isSwap: swaptx != null, - // ), ); if (err != null) { emit(state.copyWith(errSending: err.toString(), sending: false)); @@ -1041,41 +1012,15 @@ class SendCubit extends Cubit { final (wallet, _) = wtxid!; - // if (swaptx != null) { - // final (updatedWalletWithTxid, err2) = await _walletTx.addSwapTxToWallet( - // wallet: wallet, - // swapTx: swaptx.copyWith(txid: txid), - // ); - - // if (err2 != null) { - // emit(state.copyWith(errSending: err.toString())); - // return; - // } - - // state.selectedWalletBloc!.add( - // UpdateWallet( - // updatedWalletWithTxid, - // updateTypes: [ - // UpdateWalletTypes.addresses, - // UpdateWalletTypes.transactions, - // UpdateWalletTypes.swaps, - // ], - // ), - // ); - // } else { - state.selectedWalletBloc!.add( - UpdateWallet( - wallet, - updateTypes: [ - UpdateWalletTypes.addresses, - UpdateWalletTypes.transactions, - UpdateWalletTypes.swaps, - ], - ), + await _appWalletsRepository.getWalletServiceById(wallet.id)?.updateWallet( + wallet, + syncAfter: true, + updateTypes: [ + UpdateWalletTypes.addresses, + UpdateWalletTypes.transactions, + UpdateWalletTypes.swaps, + ], ); - // } - Future.delayed(150.ms); - state.selectedWalletBloc!.add(SyncWallet()); emit(state.copyWith(sending: false, sent: true)); } @@ -1103,16 +1048,6 @@ class SendCubit extends Cubit { ); } - // void txSettled() { - // if (state.tx == null) return; - // emit(state.copyWith(txSettled: true)); - // } - - // void txPaid() { - // if (state.tx == null) return; - // emit(state.copyWith(txPaid: true)); - // } - Future calculateFeeForSend({ Wallet? wallet, String address = '', @@ -1121,10 +1056,7 @@ class SendCubit extends Cubit { final isLiq = wallet!.isLiquid(); final fee = - isLiq ? _networkCubit.state.pickLiquidFees() : networkFees.toDouble(); - - // final amount = - // wallet.balance! - 900 > 1000 ? wallet.balance! - 900 : wallet.balance!; + isLiq ? _networkRepository.pickLiquidFees : networkFees.toDouble(); final (buildResp, err) = await _walletTx.buildTx( wallet: wallet, @@ -1150,9 +1082,12 @@ class SendCubit extends Cubit { return feeAmt ?? 0; } - Future processSendButton(String label) async { - final network = - _networkCubit.state.testnet ? BBNetwork.Testnet : BBNetwork.Mainnet; + Future processSendButton({ + required String label, + required int feeRate, + required int amt, + }) async { + final network = _networkRepository.getBBNetwork; final (_, addressError) = await state.getPaymentNetwork(state.address, network); @@ -1162,13 +1097,12 @@ class SendCubit extends Cubit { } final isOnchainSwap = state.couldBeOnchainSwap(); - final wallet = state.selectedWalletBloc!.state.wallet!; + final wallet = state.selectedWallet!; if (isOnchainSwap) { int sweepAmount = 0; final refundAddress = wallet.lastGeneratedAddress?.address; if (state.sendAllCoin == true) { - final feeRate = _networkFeesCubit.state.selectedOrFirst(true); final fees = await calculateFeeForSend( wallet: wallet, address: refundAddress!, @@ -1178,21 +1112,17 @@ class SendCubit extends Cubit { reset(); if (wallet.isBitcoin()) { - // TODO: Absolute fee doesn't work for liquid build Tx now updateOnChainAbsFee(fees); } - // sweepAmount = walletBloc.state.wallet!.balance! - fees; - final int magicNumber = wallet.isBitcoin() - ? 0 // 30 // Rather abs fee is taken from above dummy drain tx - : 1500; - sweepAmount = wallet.balance! - fees - magicNumber; // TODO + final int magicNumber = wallet.isBitcoin() ? 0 : 1500; + sweepAmount = wallet.balance! - fees - magicNumber; } - final swapAmount = _currencyCubit.state.amount; + final swapAmount = amt; - final liqNetworkurl = _networkCubit.state.getLiquidNetworkUrl(); - final btcNetworkUrl = _networkCubit.state.getNetworkUrl(); + final liqNetworkurl = _networkRepository.getLiquidNetworkUrl; + final btcNetworkUrl = _networkRepository.getNetworkUrl; final btcNetworkUrlWithoutSSL = btcNetworkUrl.startsWith('ssl://') ? btcNetworkUrl.split('//')[1] : btcNetworkUrl; @@ -1200,11 +1130,10 @@ class SendCubit extends Cubit { _swapCubit.createOnChainSwap( wallet: wallet, amount: state.sendAllCoin == true ? sweepAmount : swapAmount, - isTestnet: _networkCubit.state.testnet, - btcElectrumUrl: - btcNetworkUrlWithoutSSL, // 'electrum.blockstream.info:60002', - lbtcElectrumUrl: liqNetworkurl, // 'blockstream.info:465', - toAddress: state.address, // recipientAddress.address; + isTestnet: _networkRepository.testnet, + btcElectrumUrl: btcNetworkUrlWithoutSSL, + lbtcElectrumUrl: liqNetworkurl, + toAddress: state.address, refundAddress: refundAddress!, direction: wallet.isBitcoin() ? boltz.ChainSwapDirection.btcToLbtc @@ -1220,29 +1149,29 @@ class SendCubit extends Cubit { if (!state.signed) { if (!isLn) { - final fees = _networkFeesCubit.state.selectedOrFirst(false); - await baseLayerBuild(networkFees: fees); + await baseLayerBuild(networkFees: feeRate, amount: amt); if (state.hasPjParam()) { await payjoinBuild( - networkFees: fees, + networkFees: feeRate, originalPsbt: state.psbtSigned!, wallet: wallet, + amount: amt, ); return; } return; } - // context.read().state.wallet; + final isLiq = wallet.isLiquid(); final networkurl = !isLiq - ? _networkCubit.state.getNetworkUrl() - : _networkCubit.state.getLiquidNetworkUrl(); + ? _networkRepository.getNetworkUrl + : _networkRepository.getLiquidNetworkUrl; _swapCubit.createSubSwapForSend( wallet: wallet, address: state.address, - amount: _currencyCubit.state.amount, - isTestnet: _networkCubit.state.testnet, + amount: amt, + isTestnet: _networkRepository.testnet, invoice: state.invoice!, networkUrl: networkurl, label: label, @@ -1261,12 +1190,14 @@ class SendCubit extends Cubit { sendSwap(); } - Future buildChainSwap( - Wallet fromWallet, - Wallet toWallet, - int amount, - bool sweep, - ) async { + Future buildChainSwap({ + required Wallet fromWallet, + required Wallet toWallet, + required int amount, + required bool sweep, + required int feeRate, + required bool unitsInSats, + }) async { if (amount == 0 && sweep == false) { _swapCubit.setValidationError( 'Please enter valid amount', @@ -1281,14 +1212,14 @@ class SendCubit extends Cubit { return; } - final walletBloc = _homeCubit.state.getWalletBlocById(fromWallet.id); - updateWalletBloc(walletBloc!); + final wallett = _appWalletsRepository.getWalletById(fromWallet.id); + updateWallet(wallett!, amount, unitsInSats); final recipientAddress = toWallet.lastGeneratedAddress?.address ?? ''; final refundAddress = fromWallet.lastGeneratedAddress?.address ?? ''; - final liqNetworkurl = _networkCubit.state.getLiquidNetworkUrl(); - final btcNetworkUrl = _networkCubit.state.getNetworkUrl(); + final liqNetworkurl = _networkRepository.getLiquidNetworkUrl; + final btcNetworkUrl = _networkRepository.getNetworkUrl; final btcNetworkUrlWithoutSSL = btcNetworkUrl.startsWith('ssl://') ? btcNetworkUrl.split('//')[1] : btcNetworkUrl; @@ -1298,26 +1229,21 @@ class SendCubit extends Cubit { int finalAmount = amount; bool finalSweep = sweep; if (sweep == true) { - // } else { - final feeRate = _networkFeesCubit.state.selectedOrFirst(true); - final fees = await calculateFeeForSend( - wallet: walletBloc.state.wallet, + wallet: wallett, address: refundAddress, networkFees: feeRate, ); reset(); - final wallet = walletBloc.state.wallet; - if (wallet == null) return; + final wallet = wallett; + if (wallet.isBitcoin()) { - // TODO: Absolute fee doesn't work for liquid build Tx now updateOnChainAbsFee(fees); } - // sweepAmount = walletBloc.state.wallet!.balance! - fees; final frozenUtxos = fromWallet.allFreezedUtxos().isNotEmpty; - int finalBalance = walletBloc.state.wallet!.balance!; + int finalBalance = wallett.balance!; if (frozenUtxos == true) { finalBalance = fromWallet.utxos .where((UTXO utxo) => utxo.spendable) @@ -1326,24 +1252,18 @@ class SendCubit extends Cubit { finalSweep = false; } - final int magicNumber = wallet.isBitcoin() - ? 0 // 30 // Rather abs fee is taken from above dummy drain tx - : 1500; - finalAmount = finalBalance - fees - magicNumber; // TODO: - // } - // -20 works for btc - // -1500 works for l-btc + final int magicNumber = wallet.isBitcoin() ? 0 : 1500; + finalAmount = finalBalance - fees - magicNumber; } _swapCubit.createOnChainSwap( wallet: fromWallet, - amount: finalAmount, //20000, + amount: finalAmount, sweep: finalSweep, - isTestnet: _networkCubit.state.testnet, - btcElectrumUrl: - btcNetworkUrlWithoutSSL, // 'electrum.blockstream.info:60002', - lbtcElectrumUrl: liqNetworkurl, // 'blockstream.info:465', - toAddress: recipientAddress, // recipientAddress.address; + isTestnet: _networkRepository.testnet, + btcElectrumUrl: btcNetworkUrlWithoutSSL, + lbtcElectrumUrl: liqNetworkurl, + toAddress: recipientAddress, refundAddress: refundAddress, direction: fromWallet.isBitcoin() ? boltz.ChainSwapDirection.btcToLbtc @@ -1357,29 +1277,3 @@ class SendCubit extends Cubit { super.close(); } } - -// bolt 11 testnet -//lntb11110n1pnrc620pp5mpdwk98cl7wnj9mwc69wf7v8t7fadt2n4rx22g8rzf2y48l8m4esdpz2djkuepqw3hjqnpdgf2yxgrpv3j8yetnwvcqz95xqyp2xqsp5vwyhphcdvhc399ffqqsphp4xzjg569rchzkh9kte048hajxu2hns9qyyssqqy24765myh9ew4zklqx8qhycg9g4rn7w56t75vhfqk9a2sjpedsp4t90ms20ufckmc0fgjrhvfxcrdmhv5780wmezl7ps2djcuqtnhsp07jm9w - - -// BIP21 URI with On-chain address -// bitcoin:BC1QYLH3U67J673H6Y6ALV70M0PL2YZ53TZHVXGG7U?amount=0.00001&label=sbddesign%3A%20For%20lunch%20Tuesday&message=For%20lunch%20Tuesday - -// BIP21 URI with BOLT 11 invoice -// bitcoin:BC1QYLH3U67J673H6Y6ALV70M0PL2YZ53TZHVXGG7U?amount=0.00001&label=sbddesign%3A%20For%20lunch%20Tuesday&message=For%20lunch%20Tuesday&lightning=LNBC10U1P3PJ257PP5YZTKWJCZ5FTL5LAXKAV23ZMZEKAW37ZK6KMV80PK4XAEV5QHTZ7QDPDWD3XGER9WD5KWM36YPRX7U3QD36KUCMGYP282ETNV3SHJCQZPGXQYZ5VQSP5USYC4LK9CHSFP53KVCNVQ456GANH60D89REYKDNGSMTJ6YW3NHVQ9QYYSSQJCEWM5CJWZ4A6RFJX77C490YCED6PEMK0UPKXHY89CMM7SCT66K8GNEANWYKZGDRWRFJE69H9U5U0W57RRCSYSAS7GADWMZXC8C6T0SPJAZUP6 - -// BIP21 URI with BOLT 12 offer -// bitcoin:BC1QYLH3U67J673H6Y6ALV70M0PL2YZ53TZHVXGG7U?amount=0.00021&label=sbddesign%3A%20For%20lunch%20Tuesday&message=For%20lunch%20Tuesday&lightning=LNO1PG257ENXV4EZQCNEYPE82UM50YNHXGRWDAJX283QFWDPL28QQMC78YMLVHMXCSYWDK5WRJNJ36JRYG488QWLRNZYJCZS - -// BOLT 11 Invoice mainnet -// LNBC10U1P3PJ257PP5YZTKWJCZ5FTL5LAXKAV23ZMZEKAW37ZK6KMV80PK4XAEV5QHTZ7QDPDWD3XGER9WD5KWM36YPRX7U3QD36KUCMGYP282ETNV3SHJCQZPGXQYZ5VQSP5USYC4LK9CHSFP53KVCNVQ456GANH60D89REYKDNGSMTJ6YW3NHVQ9QYYSSQJCEWM5CJWZ4A6RFJX77C490YCED6PEMK0UPKXHY89CMM7SCT66K8GNEANWYKZGDRWRFJE69H9U5U0W57RRCSYSAS7GADWMZXC8C6T0SPJAZUP6 - - -// 6000 sats -// lnbc60u1pnre9sysp5luy79ufxhywcnage3eswwra6tuk62x4x9p9djgyd5x2jy54gmpmspp5chhrwxtceu20k9nhlsy8zhzwsxht79lvfatu20eegjzmxljrlz8sdpz2djkuepqw3hjqnpdgf2yxgrpv3j8yetnwvxqyp2xqcqz959qxpqysgq8zseyvltvj5ny698mkg20pzccuqk9dpt5stues0jcc4hhdxe8ehrm3x7md52w493udwvz3yastu9ht4zvuykupmdaclf7323djl0mdsp2h2rmx - -// 1234 sats -// lnbc12340n1pnretv7sp5d87xcykdvf03adm2au86ssury8fggz3jra5af3pmya0ftn32pjhqpp5ekmafv4q72f25d0varnp5h0cmqpqkjv20t9klcj9vaevp7dxd5cqdpz2djkuepqw3hjqnpdgf2yxgrpv3j8yetnwvxqyp2xqcqz959qxpqysgq2357qtv82qgpdttzn82hsnyha3tfgvgldc0fc8nrf7qxaxq0yt79fsehc3wprjld7hqwdeau4ct6fl6gxq99gvaulqthhludgqzmxrgpk4zw6n - -// 1234 testnet -// lntb12340n1pnrevr8pp57e8n6mqr8zwajjpe4r7nxsy0v4aql2h3edfdyjerda4neghj564qdpz2djkuepqw3hjqnpdgf2yxgrpv3j8yetnwvcqz95xqyp2xqsp52m4ue6g3r56xfeac69e95ewvhnrna8upv25kd97890v8czdyvfnq9qyyssq4g2efr6ck9d8ylyzuv5ahudxfr4zh30p3c5g00xmmkpqex2c08vjlhtjqr7h5lpc04v0e84hav52um4ak2q94zuncxm0vs222pu733gpa6y7fa diff --git a/lib/send/bloc/send_state.dart b/lib/send/bloc/send_state.dart index ffce7bc1..e83c8072 100644 --- a/lib/send/bloc/send_state.dart +++ b/lib/send/bloc/send_state.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:bb_mobile/_model/address.dart'; import 'package:bb_mobile/_model/swap.dart'; import 'package:bb_mobile/_model/transaction.dart'; @@ -5,7 +7,6 @@ import 'package:bb_mobile/_model/wallet.dart'; import 'package:bb_mobile/_pkg/address_validation.dart'; import 'package:bb_mobile/_pkg/error.dart'; import 'package:bb_mobile/_pkg/utils.dart'; -import 'package:bb_mobile/wallet/bloc/wallet_bloc.dart'; import 'package:bdk_flutter/bdk_flutter.dart' as bdk; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:payjoin_flutter/send.dart'; @@ -18,22 +19,21 @@ class SendState with _$SendState { @Default('') String address, @Default([]) List enabledWallets, AddressNetwork? paymentNetwork, - WalletBloc? selectedWalletBloc, + Wallet? selectedWallet, Invoice? invoice, @Default(false) bool showSendButton, @Default(false) bool buildingOnChain, @Default('') String note, int? tempAmt, + double? btcTempAmt, + String? tempStrAmt, @Default(false) bool scanningAddress, @Default('') String errScanningAddress, - // @Default(false) bool showDropdown, @Default(false) bool sending, @Default('') String errSending, @Default(false) bool sent, @Default('') String psbt, Transaction? tx, - // @Default(false) bool txSettled, - // @Default(false) bool txPaid, @Default(false) bool downloadingFile, @Default('') String errDownloadingFile, @Default(false) bool downloaded, @@ -61,17 +61,12 @@ class SendState with _$SendState { return calculateTotalSelected() >= amount; } - bool isWatchOnly() => selectedWalletBloc?.state.wallet?.watchOnly() ?? false; + bool isWatchOnly() => selectedWallet?.watchOnly() ?? false; bool isLnInvoice() => invoice != null; bool hasPjParam() => payjoinEndpoint != null; - // String getAddressFromInvoiceOrAddress() { - // // if (invoice != null) return invoice!.; - // return address; - // } - int calculateTotalSelected() { return selectedUtxos.fold( 0, @@ -103,41 +98,12 @@ class SendState with _$SendState { return ''; } - // bool showSendButton() { - // if (selectedWalletBloc != null) return true; - // return false; - // } - - bool checkIfMainWalletSelected() => - selectedWalletBloc?.state.wallet?.mainWallet ?? false; + bool checkIfMainWalletSelected() => selectedWallet?.mainWallet ?? false; Future<(AddressNetwork?, Err?)> getPaymentNetwork( String address, BBNetwork network, ) async { - // final bitcoinMainnetPrefixes = ['1', '3', 'bc1', 'BC1']; - // final bitcoinTestnetPrefixesCase = ['m', 'n', '2', 'tb1', 'TB1']; - // final liquidMainnetPrefixesCase = ['lq1', 'LQ1', 'VJL', 'ex1', 'EX1', 'G']; - // final liquidTestnetPrefixes = ['tlq1', 'TLQ1']; - // final lightningPrefixes = [ - // 'lnbc', - // 'LNBC', - // 'lntb', - // 'LNTB', - // 'lnbs', - // 'LNBS', - // 'lnbcrt', - // 'LNBCRT', - // 'lightning:', - // ]; - // const lightningUri = 'lightning:'; - // const bitcoinUri = 'bitcoin:'; - // const liquidUris = ['liquidnetwork:', 'liquidtestnet:']; - - // const a = lwk.Address.new(standard: '', confidential: '', index: 1); - // lwk.Address.validate(addressString: ''); - // boltz.DecodedInvoice.fromString(s: ''); - final lowerAddress = address.toLowerCase(); final bdkNetwork = network == BBNetwork.Mainnet @@ -180,32 +146,32 @@ class SendState with _$SendState { } } - WalletBloc selectLiqThenSecThenOtherBtc(List blocs) { - final liqWalletIdx = blocs.indexWhere( - (_) => _.state.wallet!.isMain() && _.state.wallet!.isLiquid(), + Wallet selectLiqThenSecThenOtherBtc2(List wallets) { + final liqWalletIdx = wallets.indexWhere( + (_) => _.isMain() && _.isLiquid(), ); - if (liqWalletIdx != -1) return blocs[liqWalletIdx]; + if (liqWalletIdx != -1) return wallets[liqWalletIdx]; - final secWalletIdx = blocs.indexWhere( - (_) => _.state.wallet!.isMain() && _.state.wallet!.isBitcoin(), + final secWalletIdx = wallets.indexWhere( + (_) => _.isMain() && _.isBitcoin(), ); - if (secWalletIdx != -1) return blocs[secWalletIdx]; + if (secWalletIdx != -1) return wallets[secWalletIdx]; - blocs.sort( - (a, b) => b.state.balanceSats().compareTo(a.state.balanceSats()), + wallets.sort( + (a, b) => b.balanceSats().compareTo(a.balanceSats()), ); - return blocs.first; + return wallets.first; } - WalletBloc selectMainBtcThenOtherHighestBalBtc(List blocs) { + Wallet selectMainBtcThenOtherHighestBalBtc2(List blocs) { final mainWalletIdx = blocs.indexWhere( - (_) => _.state.wallet!.mainWallet, + (_) => _.mainWallet, ); if (mainWalletIdx != -1) return blocs[mainWalletIdx]; blocs.sort( - (a, b) => b.state.balanceSats().compareTo(a.state.balanceSats()), + (a, b) => b.balanceSats().compareTo(a.balanceSats()), ); return blocs.first; @@ -220,8 +186,7 @@ class SendState with _$SendState { bool allowedSwitch(PaymentNetwork network) { if (!oneWallet) return true; - final wallet = selectedWalletBloc!.state.wallet; - if (wallet == null) return false; + final wallet = selectedWallet!; if (network == PaymentNetwork.bitcoin && wallet.isLiquid()) return false; @@ -230,18 +195,28 @@ class SendState with _$SendState { return true; } + int convertBtcStringToSats(String btcAmt) { + final split = btcAmt.split('.'); + final amountNo = int.parse(split[0]); + final len = min(8, split[1].length); + final amountDecimal = + int.parse(split[1].substring(0, len)) * pow(10, 8 - len); + + final amountInSats = amountNo * 100000000 + amountDecimal; + return amountInSats.toInt(); + } + bool couldBeOnchainSwap() { - if (selectedWalletBloc == null || - selectedWalletBloc?.state.wallet == null) { + if (selectedWallet == null) { return false; } - if (selectedWalletBloc!.state.wallet!.isBitcoin() && + if (selectedWallet!.isBitcoin() && (paymentNetwork == AddressNetwork.liquid || paymentNetwork == AddressNetwork.bip21Liquid)) { return true; } - if (selectedWalletBloc!.state.wallet!.isLiquid() && + if (selectedWallet!.isLiquid() && (paymentNetwork == AddressNetwork.bitcoin || paymentNetwork == AddressNetwork.bip21Bitcoin)) { return true; @@ -253,7 +228,7 @@ class SendState with _$SendState { String getSendButtonLabel(bool sending) { if (couldBeOnchainSwap() == true) return 'Create Swap'; - final watchOnly = selectedWalletBloc?.state.wallet?.watchOnly() ?? false; + final watchOnly = selectedWallet?.watchOnly() ?? false; final isLn = isLnInvoice(); final String label = watchOnly diff --git a/lib/send/listeners.dart b/lib/send/listeners.dart index ec07aafd..6da4cc2f 100644 --- a/lib/send/listeners.dart +++ b/lib/send/listeners.dart @@ -26,7 +26,33 @@ class SendListeners extends StatelessWidget { BlocListener( listenWhen: (previous, current) => previous.amount != current.amount, listener: (context, state) { - context.read().checkBalance(); + final amt = context.read().state.amount; + context.read().checkBalance(amt); + }, + ), + BlocListener( + listenWhen: (previous, current) => + previous.tempAmt != current.tempAmt && current.tempAmt != null, + listener: (context, state) { + context.read().updateAmountDirect(state.tempAmt!); + }, + ), + BlocListener( + listenWhen: (previous, current) => + previous.btcTempAmt != current.btcTempAmt && + current.btcTempAmt != null, + listener: (context, state) { + context + .read() + .btcToCurrentTempAmount(state.btcTempAmt!); + }, + ), + BlocListener( + listenWhen: (previous, current) => + previous.tempStrAmt != current.tempStrAmt && + current.tempStrAmt != null, + listener: (context, state) { + context.read().updateAmount(state.tempStrAmt!); }, ), BlocListener( diff --git a/lib/send/psbt.dart b/lib/send/psbt.dart index d2474dfb..049330bc 100644 --- a/lib/send/psbt.dart +++ b/lib/send/psbt.dart @@ -5,7 +5,7 @@ import 'package:bb_mobile/_ui/components/text.dart'; import 'package:bb_mobile/_ui/headers.dart'; import 'package:bb_mobile/currency/bloc/currency_cubit.dart'; import 'package:bb_mobile/locator.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/network/bloc/network_bloc.dart'; import 'package:bb_mobile/send/bloc/send_cubit.dart'; import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; @@ -101,7 +101,7 @@ class PSBTPopUp extends StatelessWidget { InkWell( onTap: () { final url = context - .read() + .read() .state .explorerAddressUrl(toAddress); locator().launchApp(url); diff --git a/lib/send/send_page.dart b/lib/send/send_page.dart index 2b2c5bdb..dd4599a4 100644 --- a/lib/send/send_page.dart +++ b/lib/send/send_page.dart @@ -1,3 +1,4 @@ +import 'package:bb_mobile/_model/wallet.dart'; import 'package:bb_mobile/_pkg/barcode.dart'; import 'package:bb_mobile/_pkg/boltz/swap.dart'; import 'package:bb_mobile/_pkg/bull_bitcoin_api.dart'; @@ -6,19 +7,19 @@ import 'package:bb_mobile/_pkg/file_storage.dart'; import 'package:bb_mobile/_pkg/mempool_api.dart'; import 'package:bb_mobile/_pkg/payjoin/manager.dart'; import 'package:bb_mobile/_pkg/storage/hive.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/sensitive_storage.dart'; import 'package:bb_mobile/_pkg/wallet/transaction.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; +import 'package:bb_mobile/_repository/wallet/sensitive_wallet_storage.dart'; import 'package:bb_mobile/_ui/app_bar.dart'; import 'package:bb_mobile/_ui/components/button.dart'; import 'package:bb_mobile/_ui/components/text.dart'; import 'package:bb_mobile/_ui/components/text_input.dart'; -import 'package:bb_mobile/_ui/molecules/wallet/wallet_dropdown.dart'; import 'package:bb_mobile/_ui/warning.dart'; import 'package:bb_mobile/currency/amount_input.dart'; import 'package:bb_mobile/currency/bloc/currency_cubit.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; import 'package:bb_mobile/locator.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/network/bloc/network_bloc.dart'; import 'package:bb_mobile/network_fees/bloc/networkfees_cubit.dart'; import 'package:bb_mobile/network_fees/popup.dart'; import 'package:bb_mobile/send/advanced.dart'; @@ -30,6 +31,7 @@ import 'package:bb_mobile/settings/bloc/settings_cubit.dart'; import 'package:bb_mobile/styles.dart'; import 'package:bb_mobile/swap/create_swap_bloc/swap_cubit.dart'; import 'package:bb_mobile/swap/send.dart'; +import 'package:bb_mobile/swap/ui_swapwidget/wallet_dropdown.dart'; import 'package:bb_mobile/swap/watcher_bloc/watchtxs_bloc.dart'; import 'package:bb_mobile/wallet/bloc/wallet_bloc.dart'; import 'package:flutter/material.dart'; @@ -57,17 +59,83 @@ class _SendPageState extends State { @override void initState() { + // swap = CreateSwapCubit( + // walletSensitiveRepository: locator(), + // swapBoltz: locator(), + // walletTx: locator(), + // // homeCubit: context.read(), + // appWalletsRepository: locator(), + // watchTxsBloc: context.read(), + // // networkCubit: context.read(), + // networkRepository: locator(), + // )..fetchFees(context.read().state.networkData.testnet); + + // networkFees = NetworkFeesCubit( + // // networkCubit: locator(), + // networkRepository: locator(), + // hiveStorage: locator(), + // mempoolAPI: locator(), + // defaultNetworkFeesCubit: context.read(), + // ); + + // currency = CurrencyCubit( + // hiveStorage: locator(), + // bbAPI: locator(), + // defaultCurrencyCubit: context.read(), + // ); + + // Wallet? wallet; + + // if (widget.walletId != null) { + // wallet = + // context.read().getWalletById(widget.walletId!); + // } else { + // final isTestnet = context.read().testnet; + // // context.read().state.testnet; + // wallet = + // context.read().getMainWallets(isTestnet).first; + // // walletBloc = createWalletBloc(wallet); + // // context.read().state.getMainWallets(isTestnet).first; + // } + + // send = SendCubit( + // walletTx: locator(), + // barcode: locator(), + // defaultRBF: locator().state.defaultRBF, + // fileStorage: locator(), + // networkRepository: locator(), + // appWalletsRepository: locator(), + // // networkCubit: locator(), + // // networkFeesCubit: networkFees, + // // homeCubit: locator(), + // payjoinManager: locator(), + // swapBoltz: locator(), + // // currencyCubit: currency, + // openScanner: widget.openScanner, + // // walletBloc: walletBloc, + // swapCubit: swap, + // oneWallet: widget.walletId != null && wallet != null, + // wallet: wallet, + // ); + + super.initState(); + } + + Future _setupBloc() async { swap = CreateSwapCubit( walletSensitiveRepository: locator(), swapBoltz: locator(), walletTx: locator(), - homeCubit: context.read(), + // homeCubit: context.read(), + appWalletsRepository: locator(), watchTxsBloc: context.read(), - networkCubit: context.read(), - )..fetchFees(context.read().state.testnet); + // networkCubit: context.read(), + networkRepository: locator(), + )..fetchFees(context.read().state.networkData.testnet); networkFees = NetworkFeesCubit( - networkCubit: locator(), + // networkCubit: locator(), + networkRepository: locator(), hiveStorage: locator(), mempoolAPI: locator(), defaultNetworkFeesCubit: context.read(), @@ -79,15 +147,18 @@ class _SendPageState extends State { defaultCurrencyCubit: context.read(), ); - WalletBloc? walletBloc; + Wallet? wallet; if (widget.walletId != null) { - walletBloc = - context.read().state.getWalletBlocById(widget.walletId!); + wallet = + context.read().getWalletById(widget.walletId!); } else { - final isTestnet = context.read().state.testnet; - walletBloc = - context.read().state.getMainWallets(isTestnet).first; + final isTestnet = context.read().testnet; + // context.read().state.testnet; + wallet = + context.read().getMainWallets(isTestnet).first; + // walletBloc = createWalletBloc(wallet); + // context.read().state.getMainWallets(isTestnet).first; } send = SendCubit( @@ -95,41 +166,51 @@ class _SendPageState extends State { barcode: locator(), defaultRBF: locator().state.defaultRBF, fileStorage: locator(), - networkCubit: locator(), - networkFeesCubit: networkFees, - homeCubit: locator(), + networkRepository: locator(), + appWalletsRepository: locator(), + // networkCubit: locator(), + // networkFeesCubit: networkFees, + // homeCubit: locator(), payjoinManager: locator(), swapBoltz: locator(), - currencyCubit: currency, + // currencyCubit: currency, openScanner: widget.openScanner, - walletBloc: walletBloc, + // walletBloc: walletBloc, swapCubit: swap, - oneWallet: widget.walletId != null && walletBloc != null, + oneWallet: widget.walletId != null && wallet != null, + wallet: wallet, ); - - super.initState(); } @override Widget build(BuildContext context) { - return MultiBlocProvider( - providers: [ - BlocProvider.value(value: send), - BlocProvider.value(value: currency), - BlocProvider.value(value: swap), - BlocProvider.value(value: networkFees), - ], - child: Scaffold( - appBar: AppBar( - flexibleSpace: const _SendAppBar(), - automaticallyImplyLeading: false, - ), - body: const SendListeners( - child: _WalletProvider( - child: _Screen(), + return FutureBuilder( + future: _setupBloc(), + builder: (context, snapshot) { + if (snapshot.connectionState != ConnectionState.done) { + return const SizedBox.shrink(); + } + + return MultiBlocProvider( + providers: [ + BlocProvider.value(value: send), + BlocProvider.value(value: currency), + BlocProvider.value(value: swap), + BlocProvider.value(value: networkFees), + ], + child: Scaffold( + appBar: AppBar( + flexibleSpace: const _SendAppBar(), + automaticallyImplyLeading: false, + ), + body: SendListeners( + child: _WalletProvider( + child: const _Screen().animate(delay: 400.ms).fadeIn(), + ), + ), ), - ), - ), + ); + }, ); } } @@ -141,11 +222,13 @@ class _WalletProvider extends StatelessWidget { @override Widget build(BuildContext context) { - final sendWalletBloc = - context.select((SendCubit _) => _.state.selectedWalletBloc); + final sendWallet = context.select((SendCubit _) => _.state.selectedWallet); - if (sendWalletBloc == null) return child; - return BlocProvider.value(value: sendWalletBloc, child: child); + if (sendWallet == null) return child; + return BlocProvider.value( + value: createOrRetreiveWalletBloc(sendWallet.id), + child: child, + ); } } @@ -166,8 +249,7 @@ class _Screen extends StatelessWidget { context.select((CreateSwapCubit x) => x.state.showWarning()); final walletIsLiquid = context.select( - (SendCubit x) => - x.state.selectedWalletBloc?.state.wallet?.isLiquid() ?? false, + (SendCubit x) => x.state.selectedWallet?.isLiquid() ?? false, ); if (isPayjoinPostSuccess) return const PjSuccess(); @@ -229,9 +311,25 @@ class _Screen extends StatelessWidget { } } -class WalletSelectionDropDown extends StatelessWidget { +class WalletSelectionDropDown extends StatefulWidget { const WalletSelectionDropDown(); + @override + State createState() => + _WalletSelectionDropDownState(); +} + +class _WalletSelectionDropDownState extends State { + List wallets = []; + @override + void initState() { + final network = context.read().getBBNetwork; + wallets = context + .read() + .walletFromNetworkExcludeWatchOnly(network); + super.initState(); + } + @override Widget build(BuildContext context) { final oneWallet = context.select( @@ -240,12 +338,8 @@ class WalletSelectionDropDown extends StatelessWidget { final _ = context.select((SendCubit cubit) => cubit.state.enabledWallets); - final network = context.select((NetworkCubit _) => _.state.getBBNetwork()); - final walletBlocs = context.select( - (HomeCubit _) => _.state.walletBlocsFromNetworkExcludeWatchOnly(network), - ); - final selectedWalletBloc = - context.select((SendCubit _) => _.state.selectedWalletBloc); + final selectedWallet = + context.select((SendCubit _) => _.state.selectedWallet); return AnimatedOpacity( duration: const Duration(milliseconds: 300), @@ -254,16 +348,17 @@ class WalletSelectionDropDown extends StatelessWidget { ignoring: oneWallet, child: WalletDropDown( showSpendableBalance: true, - items: walletBlocs.map((wb) => wb.state.wallet!).toList(), + items: wallets, onChanged: (wallet) { - final blocs = - walletBlocs.where((wb) => wb.state.wallet == wallet).toList(); - if (blocs.isNotEmpty) { - context.read().updateWalletBloc(blocs[0]); + final wss = wallets.where((wb) => wb == wallet).toList(); + if (wss.isNotEmpty) { + final currency = context.read().state; + context + .read() + .updateWallet(wss[0], currency.amount, currency.unitsInSats); } }, - value: - selectedWalletBloc?.state.wallet ?? walletBlocs[0].state.wallet!, + value: selectedWallet ?? wallets[0], ).animate().fadeIn(), ), ); @@ -398,12 +493,11 @@ class NetworkFees extends StatelessWidget { @override Widget build(BuildContext context) { final walletSelected = context.select( - (SendCubit cubit) => cubit.state.selectedWalletBloc != null, + (SendCubit cubit) => cubit.state.selectedWallet != null, ); final isSelectedWalletLiquid = context.select( - (SendCubit cubit) => - cubit.state.selectedWalletBloc?.state.wallet?.isLiquid() ?? false, + (SendCubit cubit) => cubit.state.selectedWallet?.isLiquid() ?? false, ); final sending = context.select((SendCubit cubit) => cubit.state.sending); @@ -436,13 +530,12 @@ class AdvancedOptions extends StatelessWidget { @override Widget build(BuildContext context) { final walletSelected = context.select( - (SendCubit cubit) => cubit.state.selectedWalletBloc != null, + (SendCubit cubit) => cubit.state.selectedWallet != null, ); final sending = context.select((SendCubit _) => _.state.sending); final isLn = context.select((SendCubit _) => _.state.isLnInvoice()); final isLiquid = context.select( - (SendCubit _) => - _.state.selectedWalletBloc?.state.wallet?.isLiquid() ?? false, + (SendCubit _) => _.state.selectedWallet?.isLiquid() ?? false, ); final addressReady = context.select((SendCubit _) => _.state.address.isNotEmpty); @@ -534,8 +627,15 @@ class _SendButton extends StatelessWidget { leftIcon: Icons.send, onPressed: () async { if (sending) return; + final amt = context.read().state.amount; + final feeRate = context + .read() + .state + .selectedOrFirst(true); context.read().processSendButton( - txLabel, + label: txLabel, + feeRate: feeRate, + amt: amt, ); }, label: buttonLabel, @@ -576,14 +676,14 @@ class SendWalletBalance extends StatelessWidget { @override Widget build(BuildContext context) { final totalFrozen = context.select( - (WalletBloc cubit) => cubit.state.wallet?.frozenUTXOTotal() ?? 0, + (WalletBloc cubit) => cubit.state.wallet.frozenUTXOTotal(), ); - final isLiq = context - .select((WalletBloc cubit) => cubit.state.wallet?.isLiquid() ?? false); + final isLiq = + context.select((WalletBloc cubit) => cubit.state.wallet.isLiquid()); if (totalFrozen == 0) { final balance = context.select( - (WalletBloc cubit) => cubit.state.wallet?.fullBalance?.total ?? 0, + (WalletBloc cubit) => cubit.state.wallet.fullBalance?.total ?? 0, ); final balStr = context.select( @@ -593,8 +693,7 @@ class SendWalletBalance extends StatelessWidget { return BBText.body(balStr, isBold: true); } else { final balanceWithoutFrozenUTXOs = context.select( - (WalletBloc cubit) => - cubit.state.wallet?.balanceWithoutFrozenUTXOs() ?? 0, + (WalletBloc cubit) => cubit.state.wallet.balanceWithoutFrozenUTXOs(), ); final balStr = context.select( (CurrencyCubit cubit) => cubit.state @@ -639,10 +738,10 @@ class TxDetailsScreen extends StatelessWidget { final currency = context.select((CurrencyCubit _) => _.state.defaultFiatCurrency); final amtFiat = context.select( - (NetworkCubit cubit) => cubit.state.calculatePrice(amount, currency), + (NetworkBloc cubit) => cubit.state.calculatePrice(amount, currency), ); final feeFiat = context.select( - (NetworkCubit cubit) => cubit.state.calculatePrice(fee, currency), + (NetworkBloc cubit) => cubit.state.calculatePrice(fee, currency), ); final fiatCurrency = context.select( @@ -926,21 +1025,21 @@ class _Warnings extends StatelessWidget { .select((CurrencyCubit cubit) => cubit.state.getAmountInUnits(fees)); final feesFiatStr = context.select( - (NetworkCubit cubit) => cubit.state.calculatePrice(fees, currency), + (NetworkBloc cubit) => cubit.state.calculatePrice(fees, currency), ); final amtStr = context .select((CurrencyCubit cubit) => cubit.state.getAmountInUnits(amt)); final amtFiatStr = context.select( - (NetworkCubit cubit) => cubit.state.calculatePrice(amt, currency), + (NetworkBloc cubit) => cubit.state.calculatePrice(amt, currency), ); final minAmtStr = context .select((CurrencyCubit cubit) => cubit.state.getAmountInUnits(minAmt)); final minAmtFiatStr = context.select( - (NetworkCubit cubit) => cubit.state.calculatePrice(minAmt, currency), + (NetworkBloc cubit) => cubit.state.calculatePrice(minAmt, currency), ); // final minAmtFiat = context.select( diff --git a/lib/settings/bitcoin_settings_page.dart b/lib/settings/bitcoin_settings_page.dart index 8b995b09..9fb76082 100644 --- a/lib/settings/bitcoin_settings_page.dart +++ b/lib/settings/bitcoin_settings_page.dart @@ -6,9 +6,11 @@ import 'package:bb_mobile/_ui/components/button.dart'; import 'package:bb_mobile/_ui/components/controls.dart'; import 'package:bb_mobile/_ui/components/text.dart'; import 'package:bb_mobile/currency/bloc/currency_cubit.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; +import 'package:bb_mobile/home/bloc/home_bloc.dart'; +import 'package:bb_mobile/home/bloc/home_event.dart'; import 'package:bb_mobile/locator.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/network/bloc/event.dart'; +import 'package:bb_mobile/network/bloc/network_bloc.dart'; import 'package:bb_mobile/network/bloc/state.dart'; import 'package:bb_mobile/network/popup.dart'; import 'package:bb_mobile/network_fees/bloc/networkfees_cubit.dart'; @@ -312,16 +314,18 @@ class TestNetButton extends StatelessWidget { @override Widget build(BuildContext context) { - final testnet = context.select((NetworkCubit _) => _.state.testnet); + final testnet = + context.select((NetworkBloc _) => _.state.networkData.testnet); // if (!testnet) return const SizedBox.shrink(); - return BlocListener( - listenWhen: (previous, current) => previous.testnet != current.testnet, + return BlocListener( + listenWhen: (previous, current) => + previous.networkData.testnet != current.networkData.testnet, listener: (context, state) { context.read().loadFees(); final network = state.getBBNetwork(); - context.read().loadWalletsForNetwork(network); + context.read().add(LoadWalletsForNetwork(network)); // context.read().add(InitializeSwapWatcher(isTestnet: state.testnet)); }, child: Row( @@ -335,7 +339,7 @@ class TestNetButton extends StatelessWidget { key: UIKeys.settingsTestnetSwitch, value: testnet, onChanged: (e) { - context.read().toggleTestnet(); + context.read().add(ToggleTestnet()); }, ), ], @@ -350,9 +354,9 @@ class ElectrumServerButton extends StatelessWidget { @override Widget build(BuildContext context) { final selectedNetwork = - context.select((NetworkCubit _) => _.state.getNetwork()); + context.select((NetworkBloc _) => _.state.getNetwork()); if (selectedNetwork == null) return const SizedBox.shrink(); - final err = context.select((NetworkCubit _) => _.state.errLoadingNetworks); + final err = context.select((NetworkBloc _) => _.state.errLoadingNetworks); return Column( children: [ diff --git a/lib/settings/bloc/broadcasttx_cubit.dart b/lib/settings/bloc/broadcasttx_cubit.dart index 78bcbe3b..e257ce23 100644 --- a/lib/settings/bloc/broadcasttx_cubit.dart +++ b/lib/settings/bloc/broadcasttx_cubit.dart @@ -1,16 +1,16 @@ import 'package:bb_mobile/_model/address.dart'; import 'package:bb_mobile/_model/transaction.dart'; +import 'package:bb_mobile/_model/wallet.dart'; import 'package:bb_mobile/_pkg/barcode.dart'; import 'package:bb_mobile/_pkg/error.dart'; import 'package:bb_mobile/_pkg/file_picker.dart'; import 'package:bb_mobile/_pkg/file_storage.dart'; import 'package:bb_mobile/_pkg/wallet/bdk/transaction.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/network.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; +import 'package:bb_mobile/_repository/wallet/internal_network.dart'; import 'package:bb_mobile/_ui/alert.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; import 'package:bb_mobile/settings/bloc/broadcasttx_state.dart'; -import 'package:bb_mobile/wallet/bloc/wallet_bloc.dart'; import 'package:bdk_flutter/bdk_flutter.dart' as bdk; import 'package:convert/convert.dart'; import 'package:flutter_animate/flutter_animate.dart'; @@ -21,14 +21,14 @@ class BroadcastTxCubit extends Cubit { required Barcode barcode, required FilePick filePicker, required FileStorage fileStorage, - required HomeCubit homeCubit, - required NetworkCubit networkCubit, + required AppWalletsRepository appWalletsRepository, required NetworkRepository networkRepository, + required InternalNetworkRepository internalNetworkRepository, required BDKTransactions bdkTransactions, }) : _bdkTransactions = bdkTransactions, + _internalNetworkRepository = internalNetworkRepository, _networkRepository = networkRepository, - _networkCubit = networkCubit, - _homeCubit = homeCubit, + _appWalletsRepository = appWalletsRepository, _fileStorage = fileStorage, _barcode = barcode, _filePicker = filePicker, @@ -39,9 +39,10 @@ class BroadcastTxCubit extends Cubit { final FilePick _filePicker; final Barcode _barcode; final FileStorage _fileStorage; - final HomeCubit _homeCubit; - final NetworkCubit _networkCubit; + + final AppWalletsRepository _appWalletsRepository; final NetworkRepository _networkRepository; + final InternalNetworkRepository _internalNetworkRepository; final BDKTransactions _bdkTransactions; @override @@ -86,7 +87,7 @@ class BroadcastTxCubit extends Cubit { emit(state.copyWith(loadingFile: false, errLoadingFile: err.toString())); return; } - // remove carriage return and newline from file read strings + final tx = file!.replaceAll('\n', '').replaceAll('\r', '').replaceAll(' ', ''); emit(state.copyWith(loadingFile: false, tx: tx)); @@ -114,13 +115,11 @@ class BroadcastTxCubit extends Cubit { bdk.Transaction bdkTx; final tx = state.tx; try { - // check if = is in the string final decodedTx = hex.decode(tx); bdkTx = await bdk.Transaction.fromBytes(transactionBytes: decodedTx); } catch (e) { final psbt = await bdk.PartiallySignedTransaction.fromString(tx); bdkTx = psbt.extractTx(); - // maybe this psbt needs to be finalized? } final txid = await bdkTx.txid(); final outputs = await bdkTx.output(); @@ -128,11 +127,12 @@ class BroadcastTxCubit extends Cubit { final isSigned = await checkWitnesses(inputs); Transaction? transaction; - WalletBloc? relatedWallet; + Wallet? relatedWallet; + + final wallets = _appWalletsRepository.allWallets; - final wallets = _homeCubit.state.walletBlocs ?? []; for (final wallet in wallets) { - for (final tx in wallet.state.wallet?.unsignedTxs ?? []) { + for (final tx in wallet.unsignedTxs) { if (tx.txid == txid && !tx.isReceived()) { transaction = tx; relatedWallet = wallet; @@ -172,7 +172,7 @@ class BroadcastTxCubit extends Cubit { ); final addressStruct = await bdk.Address.fromScript( script: scriptBuf, - network: _networkCubit.state.getBdkNetwork(), + network: _networkRepository.getBdkNetwork, ); final addressStr = addressStruct.asString(); if (transaction != null) { @@ -210,14 +210,12 @@ class BroadcastTxCubit extends Cubit { } } final int feeAmount = transaction?.fee ?? 0; - // sum of input values - output values = fees - // TODO: timestamp needs to be properly set transaction ??= Transaction( txid: txid, timestamp: 0, ); - // transaction ??= Transaction(txid: txid); + transaction = transaction.copyWith( fee: feeAmount, outAddrs: outAddrs, @@ -260,7 +258,6 @@ class BroadcastTxCubit extends Cubit { state.copyWith( extractingTx: false, errExtractingTx: e.toString(), - // step: BroadcastTxStep.import, tx: '', ), ); @@ -280,7 +277,7 @@ class BroadcastTxCubit extends Cubit { final bdkTx = await bdk.Transaction.fromBytes(transactionBytes: hex.decode(tx)); - final (blockchain, errB) = _networkRepository.bdkBlockchain; + final (blockchain, errB) = _internalNetworkRepository.bdkBlockchain; if (errB != null) { emit( state.copyWith( @@ -294,7 +291,6 @@ class BroadcastTxCubit extends Cubit { final err = await _bdkTransactions.broadcastTx(tx: bdkTx, blockchain: blockchain!); if (err != null) { - // final error = emit( state.copyWith( broadcastingTx: false, @@ -332,17 +328,6 @@ class BroadcastTxCubit extends Cubit { return; } - // final (appDocDir, err) = await fileStorage.getDownloadDirectory(); - // if (err != null) { - // emit( - // state.copyWith( - // downloadingFile: false, - // errDownloadingFile: err.toString(), - // ), - // ); - // return; - // } - // final file = File(appDocDir! + '/bullbitcoin_psbt/$txid.psbt'); final errSave = await _fileStorage.savePSBT( psbt: psbt, txid: txid, diff --git a/lib/settings/broadcast.dart b/lib/settings/broadcast.dart index aa2b83e8..74ccec2b 100644 --- a/lib/settings/broadcast.dart +++ b/lib/settings/broadcast.dart @@ -2,7 +2,9 @@ import 'package:bb_mobile/_pkg/barcode.dart'; import 'package:bb_mobile/_pkg/file_picker.dart'; import 'package:bb_mobile/_pkg/file_storage.dart'; import 'package:bb_mobile/_pkg/wallet/bdk/transaction.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/network.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; +import 'package:bb_mobile/_repository/wallet/internal_network.dart'; import 'package:bb_mobile/_ui/app_bar.dart'; import 'package:bb_mobile/_ui/bottom_sheet.dart'; import 'package:bb_mobile/_ui/components/button.dart'; @@ -10,9 +12,7 @@ import 'package:bb_mobile/_ui/components/text.dart'; import 'package:bb_mobile/_ui/components/text_input.dart'; import 'package:bb_mobile/_ui/headers.dart'; import 'package:bb_mobile/currency/bloc/currency_cubit.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; import 'package:bb_mobile/locator.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; import 'package:bb_mobile/settings/bloc/broadcasttx_cubit.dart'; import 'package:bb_mobile/settings/bloc/broadcasttx_state.dart'; import 'package:bb_mobile/styles.dart'; @@ -31,11 +31,13 @@ class BroadcastPage extends StatelessWidget { final broadcast = BroadcastTxCubit( barcode: locator(), filePicker: locator(), + appWalletsRepository: locator(), // settingsCubit: locator(), - networkCubit: locator(), + // networkCubit: locator(), fileStorage: locator(), - homeCubit: locator(), + // homeCubit: locator(), networkRepository: locator(), + internalNetworkRepository: locator(), bdkTransactions: locator(), ); @@ -80,10 +82,14 @@ class BroadcastPopUp extends StatelessWidget { barcode: locator(), filePicker: locator(), // settingsCubit: locator(), - networkCubit: locator(), + // networkCubit: locator(), fileStorage: locator(), - homeCubit: locator(), + // homeCubit: locator(), + // networkRepository: locator(), + + appWalletsRepository: locator(), networkRepository: locator(), + internalNetworkRepository: locator(), bdkTransactions: locator(), ); diff --git a/lib/settings/core_wallet_settings_page.dart b/lib/settings/core_wallet_settings_page.dart index 9e1c807d..a3f31243 100644 --- a/lib/settings/core_wallet_settings_page.dart +++ b/lib/settings/core_wallet_settings_page.dart @@ -1,10 +1,10 @@ import 'package:bb_mobile/_model/wallet.dart'; import 'package:bb_mobile/_pkg/consts/keys.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; import 'package:bb_mobile/_ui/app_bar.dart'; import 'package:bb_mobile/_ui/components/button.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; import 'package:bb_mobile/locator.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; import 'package:bb_mobile/settings/bloc/settings_cubit.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -85,14 +85,14 @@ class SecureBitcoinWallet extends StatelessWidget { return BBButton.textWithStatusAndRightArrow( label: 'Secure Bitcoin wallet', onPressed: () { - // final walletBlocs = context.read().state.walletBlocs; - final network = context.read().state.getBBNetwork(); - final walletBloc = - context.read().state.getMainSecureWallet(network); + // final walletBlocs = context.read().state.walletBlocs; + final network = context.read().getBBNetwork; + final wallet = + context.read().getMainSecureWallet(network); // final walletBloc = walletBlocs // ?.where((w) => w.state.wallet?.network == network && w.state.wallet?.type == BBWalletType.secure) // .first; - context.push('/wallet-settings', extra: walletBloc); + context.push('/wallet-settings', extra: wallet!.id); }, ); } @@ -106,34 +106,44 @@ class InstantPaymentsWallet extends StatelessWidget { return BBButton.textWithStatusAndRightArrow( label: 'Instant Payments wallet', onPressed: () { - final network = context.read().state.getBBNetwork(); - final walletBloc = - context.read().state.getMainInstantWallet(network); - context.push('/wallet-settings', extra: walletBloc); + final network = context.read().getBBNetwork; + final wallet = + context.read().getMainInstantWallet(network); + context.push('/wallet-settings', extra: wallet!.id); }, ); } } -class _ButtonList extends StatelessWidget { +class _ButtonList extends StatefulWidget { const _ButtonList(); @override - Widget build(BuildContext context) { - final network = context.select((NetworkCubit _) => _.state.getBBNetwork()); - final walletBlocs = context.select( - (HomeCubit _) => _.state.walletBlocsNotMainFromNetwork(network), - ); + State<_ButtonList> createState() => _ButtonListState(); +} - if (walletBlocs.isEmpty) return const SizedBox.shrink(); +class _ButtonListState extends State<_ButtonList> { + List wallets = []; + + @override + void initState() { + final network = context.read().getBBNetwork; + wallets = + context.read().walletNotMainFromNetwork(network); + super.initState(); + } + + @override + Widget build(BuildContext context) { + if (wallets.isEmpty) return const SizedBox.shrink(); return Column( children: [ - for (final walletBloc in walletBlocs) ...[ + for (final wallet in wallets) ...[ BBButton.textWithStatusAndRightArrow( - label: walletBloc.state.wallet?.name ?? 'Wallet', + label: wallet.name ?? 'Wallet', onPressed: () { - context.push('/wallet-settings', extra: walletBloc); + context.push('/wallet-settings', extra: wallet.id); }, ), const Gap(8), @@ -151,18 +161,10 @@ class ColdcardWallet extends StatelessWidget { return BBButton.textWithStatusAndRightArrow( label: 'Coldcard wallet', onPressed: () { - final walletBlocs = context.read().state.walletBlocs; - final network = context.read().state.testnet - ? BBNetwork.Testnet - : BBNetwork.Mainnet; - final walletBloc = walletBlocs - ?.where( - (w) => - w.state.wallet?.network == network && - w.state.wallet?.type == BBWalletType.coldcard, - ) - .first; - context.push('/wallet-settings', extra: walletBloc); + final network = context.read().getBBNetwork; + final wallet = + context.read().getColdCardWallet(network); + context.push('/wallet-settings', extra: wallet.id); }, ); } diff --git a/lib/swap/create_swap_bloc/swap_cubit.dart b/lib/swap/create_swap_bloc/swap_cubit.dart index 1d22e337..d77fda00 100644 --- a/lib/swap/create_swap_bloc/swap_cubit.dart +++ b/lib/swap/create_swap_bloc/swap_cubit.dart @@ -2,14 +2,14 @@ import 'package:bb_mobile/_model/swap.dart'; import 'package:bb_mobile/_model/wallet.dart'; import 'package:bb_mobile/_pkg/boltz/swap.dart'; import 'package:bb_mobile/_pkg/consts/configs.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/sensitive_storage.dart'; import 'package:bb_mobile/_pkg/wallet/transaction.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; +import 'package:bb_mobile/_repository/wallet/sensitive_wallet_storage.dart'; +import 'package:bb_mobile/_repository/wallet_service.dart'; import 'package:bb_mobile/swap/create_swap_bloc/swap_state.dart'; import 'package:bb_mobile/swap/watcher_bloc/watchtxs_bloc.dart'; import 'package:bb_mobile/swap/watcher_bloc/watchtxs_event.dart'; -import 'package:bb_mobile/wallet/bloc/event.dart'; import 'package:boltz_dart/boltz_dart.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -19,23 +19,29 @@ class CreateSwapCubit extends Cubit { required WalletSensitiveStorageRepository walletSensitiveRepository, required SwapBoltz swapBoltz, required WalletTx walletTx, - required HomeCubit homeCubit, + // required HomeBloc homeCubit, required WatchTxsBloc watchTxsBloc, - required NetworkCubit networkCubit, + // required NetworkBloc networkCubit, + required NetworkRepository networkRepository, + required AppWalletsRepository appWalletsRepository, }) : _walletTx = walletTx, _swapBoltz = swapBoltz, - _homeCubit = homeCubit, + _appWalletsRepository = appWalletsRepository, + // _homeCubit = homeCubit, _watchTxsBloc = watchTxsBloc, _walletSensitiveRepository = walletSensitiveRepository, - _networkCubit = networkCubit, + _networkRepository = networkRepository, + // _networkCubit = networkCubit, super(const SwapState()); final WalletSensitiveStorageRepository _walletSensitiveRepository; final SwapBoltz _swapBoltz; final WalletTx _walletTx; - final HomeCubit _homeCubit; + // final HomeBloc _homeCubit; + final AppWalletsRepository _appWalletsRepository; + final NetworkRepository _networkRepository; final WatchTxsBloc _watchTxsBloc; - final NetworkCubit _networkCubit; + // final NetworkBloc _networkCubit; Future fetchFees(bool isTestnet) async { final boltzurl = isTestnet ? boltzTestnetUrl : boltzMainnetUrl; @@ -429,17 +435,27 @@ class CreateSwapCubit extends Cubit { return; } - _homeCubit.state.getWalletBloc(updatedWallet)?.add( - UpdateWallet( - updatedWallet, - updateTypes: [ - UpdateWalletTypes.swaps, - UpdateWalletTypes.transactions, - ], - ), - ); - - await Future.delayed(const Duration(milliseconds: 300)); + // _homeCubit.state.getWalletBloc(updatedWallet)?.add( + // UpdateWallet( + // updatedWallet, + // updateTypes: [ + // UpdateWalletTypes.swaps, + // UpdateWalletTypes.transactions, + // ], + // ), + // ); + + // await Future.delayed(const Duration(milliseconds: 300)); + + await _appWalletsRepository + .getWalletServiceById(updatedWallet.id) + ?.updateWallet( + updatedWallet, + updateTypes: [ + UpdateWalletTypes.swaps, + UpdateWalletTypes.transactions, + ], + ); _watchTxsBloc.add( WatchWallets(), @@ -646,9 +662,9 @@ class CreateSwapCubit extends Cubit { try { emit(state.copyWith(generatingSwapInv: true, errCreatingSwapInv: '')); - final isTestnet = _networkCubit.state.testnet; - final lbtcElectrumUrl = _networkCubit.state.getLiquidNetworkUrl(); - final btcNetworkUrl = _networkCubit.state.getNetworkUrl(); + final isTestnet = _networkRepository.data.testnet; + final lbtcElectrumUrl = _networkRepository.getLiquidNetworkUrl; + final btcNetworkUrl = _networkRepository.getNetworkUrl; final btcElectrumUrl = btcNetworkUrl.startsWith('ssl://') ? btcNetworkUrl.split('//')[1] : btcNetworkUrl; diff --git a/lib/swap/listeners.dart b/lib/swap/listeners.dart index ad1e1d9c..34121ff0 100644 --- a/lib/swap/listeners.dart +++ b/lib/swap/listeners.dart @@ -1,5 +1,5 @@ import 'package:bb_mobile/currency/bloc/currency_cubit.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; +import 'package:bb_mobile/home/bloc/home_bloc.dart'; import 'package:bb_mobile/home/bloc/home_state.dart'; import 'package:bb_mobile/routes.dart'; import 'package:bb_mobile/swap/receive.dart'; @@ -19,7 +19,7 @@ class SwapAppListener extends StatelessWidget { Widget build(BuildContext ctx) { return MultiBlocListener( listeners: [ - BlocListener( + BlocListener( listenWhen: (previous, current) => previous.loadingWallets != current.loadingWallets, listener: (context, state) { diff --git a/lib/swap/receive.dart b/lib/swap/receive.dart index c6bfaff0..635feecf 100644 --- a/lib/swap/receive.dart +++ b/lib/swap/receive.dart @@ -1,12 +1,14 @@ import 'package:bb_mobile/_model/swap.dart'; +import 'package:bb_mobile/_model/transaction.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; import 'package:bb_mobile/_ui/app_bar.dart'; import 'package:bb_mobile/_ui/bottom_sheet.dart'; import 'package:bb_mobile/_ui/components/button.dart'; import 'package:bb_mobile/_ui/components/text.dart'; import 'package:bb_mobile/_ui/headers.dart'; import 'package:bb_mobile/currency/bloc/currency_cubit.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/home/bloc/home_bloc.dart'; +import 'package:bb_mobile/network/bloc/network_bloc.dart'; import 'package:bb_mobile/receive/bloc/receive_cubit.dart'; import 'package:bb_mobile/receive/receive_page.dart'; import 'package:bb_mobile/styles.dart'; @@ -27,7 +29,7 @@ class SwapHistoryButton extends StatelessWidget { @override Widget build(BuildContext context) { - final txs = context.select((WalletBloc _) => _.state.wallet?.swaps ?? []); + final txs = context.select((WalletBloc _) => _.state.wallet.swaps); if (txs.isEmpty) return const SizedBox.shrink(); return BBButton.big( @@ -46,7 +48,7 @@ class SwapTxList extends StatelessWidget { final receiveCubit = context.read(); final swapBloc = context.read(); // receiveCubit.state.swapBloc; - final walletBloc = receiveCubit.state.walletBloc; + final wallet = receiveCubit.state.wallet; return showBBBottomSheet( context: context, @@ -54,7 +56,8 @@ class SwapTxList extends StatelessWidget { providers: [ BlocProvider.value(value: receiveCubit), BlocProvider.value(value: swapBloc), - if (walletBloc != null) BlocProvider.value(value: walletBloc), + if (wallet != null) + BlocProvider.value(value: createOrRetreiveWalletBloc(wallet.id)), ], child: const SwapTxList(), ), @@ -63,7 +66,7 @@ class SwapTxList extends StatelessWidget { @override Widget build(BuildContext context) { - final txs = context.select((WalletBloc _) => _.state.wallet?.swaps ?? []); + final txs = context.select((WalletBloc _) => _.state.wallet.swaps); if (txs.isEmpty) return const SizedBox.shrink(); return Padding( @@ -294,6 +297,7 @@ class ReceivingSwapPage extends StatefulWidget { class _ReceivingSwapPageState extends State with WidgetsBindingObserver { late SwapTx swapTx; + Transaction? tx; bool received = false; bool paid = false; @@ -306,6 +310,7 @@ class _ReceivingSwapPageState extends State WidgetsBinding.instance.addObserver(this); swapTx = widget.tx; + tx = context.read().getTxFromSwap(swapTx); super.initState(); } @@ -326,7 +331,7 @@ class _ReceivingSwapPageState extends State await Future.delayed(400.ms); if (!mounted) return; - final updatedSwapTx = context.read().state.getSwapTxById( + final updatedSwapTx = context.read().state.getSwapTxById( widget.tx.id, ); @@ -352,11 +357,11 @@ class _ReceivingSwapPageState extends State Widget build(BuildContext context) { var amt = swapTx.recievableAmount() ?? 0; - final tx = context.select( - (HomeCubit cubit) => cubit.state.getTxFromSwap(swapTx), - ); + // final tx = context.select( + // (HomeBloc cubit) => cubit.state.getTxFromSwap(swapTx), + // ); - if (tx != null) amt = tx.getNetAmountToPayee(); + if (tx != null) amt = tx!.getNetAmountToPayee(); final isSats = context.select((CurrencyCubit _) => _.state.unitsInSats); final amtDouble = isSats ? amt : amt / 100000000; @@ -376,7 +381,7 @@ class _ReceivingSwapPageState extends State final fiatAmt = context.select((CurrencyCubit cubit) => cubit.state.fiatAmt.abs()); final isTestNet = - context.select((NetworkCubit cubit) => cubit.state.testnet); + context.select((NetworkBloc cubit) => cubit.state.networkData.testnet); final fiatUnit = defaultCurrency?.name ?? ''; final fiatAmtStr = isTestNet ? '0' : fiatAmt.toStringAsFixed(2); @@ -403,7 +408,7 @@ class _ReceivingSwapPageState extends State }); // await Future.delayed(100.ms); - // tx = context.read().state.getTxFromSwap(widget.tx); + // tx = context.read().state.getTxFromSwap(widget.tx); // setState(() {}); } }, diff --git a/lib/swap/send.dart b/lib/swap/send.dart index 8622022f..e2fdf7d1 100644 --- a/lib/swap/send.dart +++ b/lib/swap/send.dart @@ -1,9 +1,10 @@ import 'package:bb_mobile/_model/swap.dart'; +import 'package:bb_mobile/_model/transaction.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; import 'package:bb_mobile/_ui/components/button.dart'; import 'package:bb_mobile/_ui/components/text.dart'; import 'package:bb_mobile/currency/bloc/currency_cubit.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/network/bloc/network_bloc.dart'; import 'package:bb_mobile/send/bloc/send_cubit.dart'; import 'package:bb_mobile/styles.dart'; import 'package:bb_mobile/swap/create_swap_bloc/swap_cubit.dart'; @@ -25,8 +26,8 @@ class SendInvAmtDisplay extends StatelessWidget { Widget build(BuildContext context) { final inv = context.select((SendCubit _) => _.state.invoice); if (inv == null) return const SizedBox.shrink(); - final isLiq = context - .select((SendCubit _) => _.state.selectedWalletBloc?.state.isLiq()); + final isLiq = + context.select((SendCubit _) => _.state.selectedWallet?.isLiquid()); final amtStr = context.select( (CurrencyCubit _) => _.state.getAmountInUnits( @@ -157,6 +158,7 @@ class SendingLnTx extends StatefulWidget { class _SendingLnTxState extends State { late SwapTx swapTx; + Transaction? tx; bool settled = false; bool paid = false; @@ -165,6 +167,7 @@ class _SendingLnTxState extends State { @override void initState() { swapTx = context.read().state.swapTx!; + tx = context.read().getTxFromSwap(swapTx); super.initState(); } @@ -180,7 +183,7 @@ class _SendingLnTxState extends State { isLiquid: isLiquid, ), ); - final tx = context.select((HomeCubit _) => _.state.getTxFromSwap(swapTx)); + // final tx = context.select((HomeBloc _) => _.state.getTxFromSwap(swapTx)); final isSats = context.select((CurrencyCubit _) => _.state.unitsInSats); final amtDouble = isSats ? amount : amount / 100000000; @@ -191,7 +194,7 @@ class _SendingLnTxState extends State { final fiatAmt = context.select((CurrencyCubit cubit) => cubit.state.fiatAmt); final isTestNet = - context.select((NetworkCubit cubit) => cubit.state.testnet); + context.select((NetworkBloc cubit) => cubit.state.networkData.testnet); final unit = defaultCurrency?.name ?? ''; final amt = isTestNet ? '0' : fiatAmt.toStringAsFixed(2); diff --git a/lib/swap/swap_confirmation.dart b/lib/swap/swap_confirmation.dart index 8f8b0a4a..5968b80a 100644 --- a/lib/swap/swap_confirmation.dart +++ b/lib/swap/swap_confirmation.dart @@ -1,13 +1,14 @@ import 'package:bb_mobile/_pkg/bull_bitcoin_api.dart'; import 'package:bb_mobile/_pkg/mempool_api.dart'; import 'package:bb_mobile/_pkg/storage/hive.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; import 'package:bb_mobile/_ui/app_bar.dart'; import 'package:bb_mobile/_ui/components/button.dart'; import 'package:bb_mobile/_ui/components/text.dart'; import 'package:bb_mobile/_ui/warning.dart'; import 'package:bb_mobile/currency/bloc/currency_cubit.dart'; import 'package:bb_mobile/locator.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/network/bloc/network_bloc.dart'; import 'package:bb_mobile/network_fees/bloc/networkfees_cubit.dart'; import 'package:bb_mobile/send/bloc/send_cubit.dart'; import 'package:bb_mobile/send/send_page.dart'; @@ -45,7 +46,9 @@ class _SwapConfirmationPageState extends State { @override void initState() { networkFees = NetworkFeesCubit( - networkCubit: locator(), + // networkCubit: locator(), + networkRepository: locator(), + hiveStorage: locator(), mempoolAPI: locator(), defaultNetworkFeesCubit: context.read(), @@ -105,7 +108,7 @@ class _Screen extends StatelessWidget { final currency = context.select((CurrencyCubit _) => _.state.defaultFiatCurrency); final amtFiat = context.select( - (NetworkCubit cubit) => cubit.state.calculatePrice(amount, currency), + (NetworkBloc cubit) => cubit.state.calculatePrice(amount, currency), ); final swapTx = @@ -117,7 +120,7 @@ class _Screen extends StatelessWidget { .select((CurrencyCubit cubit) => cubit.state.getAmountInUnits(fee)); final feeFiat = context.select( - (NetworkCubit cubit) => cubit.state.calculatePrice(fee, currency), + (NetworkBloc cubit) => cubit.state.calculatePrice(fee, currency), ); final fiatCurrency = context.select( @@ -132,7 +135,7 @@ class _Screen extends StatelessWidget { ); context.select( - (SendCubit x) => x.state.selectedWalletBloc?.state.wallet?.name ?? '', + (SendCubit x) => x.state.selectedWallet?.name ?? '', ); if (showWarning == true) { @@ -256,21 +259,21 @@ class _Warnings extends StatelessWidget { .select((CurrencyCubit cubit) => cubit.state.getAmountInUnits(fees)); final feesFiatStr = context.select( - (NetworkCubit cubit) => cubit.state.calculatePrice(fees, currency), + (NetworkBloc cubit) => cubit.state.calculatePrice(fees, currency), ); final amtStr = context .select((CurrencyCubit cubit) => cubit.state.getAmountInUnits(amt)); final amtFiatStr = context.select( - (NetworkCubit cubit) => cubit.state.calculatePrice(amt, currency), + (NetworkBloc cubit) => cubit.state.calculatePrice(amt, currency), ); final minAmtStr = context .select((CurrencyCubit cubit) => cubit.state.getAmountInUnits(minAmt)); final minAmtFiatStr = context.select( - (NetworkCubit cubit) => cubit.state.calculatePrice(minAmt, currency), + (NetworkBloc cubit) => cubit.state.calculatePrice(minAmt, currency), ); return SingleChildScrollView( diff --git a/lib/swap/swap_history_bloc/swap_history_cubit.dart b/lib/swap/swap_history_bloc/swap_history_cubit.dart index c58dc538..52c680c6 100644 --- a/lib/swap/swap_history_bloc/swap_history_cubit.dart +++ b/lib/swap/swap_history_bloc/swap_history_cubit.dart @@ -1,8 +1,8 @@ import 'package:bb_mobile/_model/swap.dart'; import 'package:bb_mobile/_model/transaction.dart'; import 'package:bb_mobile/_pkg/boltz/swap.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; import 'package:bb_mobile/swap/swap_history_bloc/swap_history_state.dart'; import 'package:bb_mobile/swap/watcher_bloc/watchtxs_bloc.dart'; import 'package:bb_mobile/swap/watcher_bloc/watchtxs_event.dart'; @@ -11,29 +11,28 @@ import 'package:flutter_bloc/flutter_bloc.dart'; class SwapHistoryCubit extends Cubit { SwapHistoryCubit({ - required HomeCubit homeCubit, - required NetworkCubit networkCubit, + required AppWalletsRepository appWalletsRepository, + required NetworkRepository networkRepository, required SwapBoltz boltz, required WatchTxsBloc watcher, - }) : _homeCubit = homeCubit, - _networkCubit = networkCubit, + }) : _appWalletsRepository = appWalletsRepository, + _networkRepository = networkRepository, _boltz = boltz, _watcher = watcher, super(const SwapHistoryState()); - final HomeCubit _homeCubit; - final NetworkCubit _networkCubit; + final AppWalletsRepository _appWalletsRepository; + final NetworkRepository _networkRepository; final SwapBoltz _boltz; final WatchTxsBloc _watcher; void loadSwaps() { - final network = _networkCubit.state.getBBNetwork(); - final walletBlocs = _homeCubit.state.walletBlocsFromNetwork(network); + final network = _networkRepository.getBBNetwork; + final wallets = _appWalletsRepository.walletsFromNetwork(network); final swapsToWatch = <(SwapTx, String)>[]; - final uniqueIds = []; // List to track unique swap IDs + final uniqueIds = []; - for (final walletBloc in walletBlocs) { - final wallet = walletBloc.state.wallet!; + for (final wallet in wallets) { for (final swap in wallet.swaps) { if (!uniqueIds.contains(swap.id)) { uniqueIds.add(swap.id); @@ -53,88 +52,17 @@ class SwapHistoryCubit extends Cubit { emit(state.copyWith(swaps: swapsToWatch)); final completedSwaps = []; - for (final walletBloc in walletBlocs) { - final wallet = walletBloc.state.wallet!; + for (final wallet in wallets) { final txs = wallet.transactions.where( (_) => _.isSwap && _.swapTx!.close(), ); completedSwaps.addAll(txs); } - // completedSwaps.removeWhere( - // (element) => swapsToWatch - // .map( - // (_) => _.$1.id, - // ) - // .contains( - // element.swapTx!.id, - // ), - // ); - // completedSwaps.removeWhere( - // (element) => element.swapTx!.close(), - // ); - emit(state.copyWith(completeSwaps: completedSwaps)); - - // migrateHistory(); } - // void migrateHistory() async { - // // for state.completeswaps - // // - if tx.txid == tx.swaptx.id - // // - if tx.swaptx.txid == null - // // - add to wallet swaps if not there - // // if empty return - - // // save wallet - // // loadswaps() and restart watchers - - // try { - // final swapsToAdd = []; - // for (final tx in state.completeSwaps) - // if (tx.txid == tx.swapTx!.id || (tx.swapTx!.txid == null)) { - // if (!state.checkSwapExists(tx.swapTx!.id)) { - // swapsToAdd.add(tx.swapTx!); - // } - // } - - // if (swapsToAdd.isEmpty) return; - - // for (final swap in swapsToAdd) { - // final walletBloc = _homeCubit.state.getWalletBlocFromSwapTx(swap); - // if (walletBloc == null) continue; - // final (updatedWallet, err) = await _walletTx.addSwapTxToWallet( - // wallet: walletBloc.state.wallet!, - // swapTx: swap, - // ); - // if (err != null) { - // print('Error: Adding SwapTx to Wallet: ${swap.id}, Error: $err'); - // continue; - // } - - // walletBloc.add( - // UpdateWallet( - // updatedWallet, - // updateTypes: [ - // UpdateWalletTypes.swaps, - // UpdateWalletTypes.transactions, - // ], - // ), - // ); - - // await Future.delayed(const Duration(milliseconds: 300)); - // } - - // _watcher.add(WatchWallets()); - - // loadSwaps(); - // } catch (e) { - // print('Error: Swap History Processing: $e'); - // } - // } - void swapUpdated(SwapTx swapTx) { - // print('Swap History Updating: ${swapTx.id} - ${swapTx.status?.status}'); emit(state.copyWith(updateSwaps: true)); final swaps = state.swaps; final index = swaps.indexWhere((_) => _.$1.id == swapTx.id); @@ -177,7 +105,6 @@ class SwapHistoryCubit extends Cubit { } final stream = boltz.SwapStreamStatus(id: id, status: status!.status); - // final updatedSwap = swaptx.copyWith(status: stream); _watcher.add( ProcessSwapTx( diff --git a/lib/swap/swap_history_page.dart b/lib/swap/swap_history_page.dart index cae63620..e62e1b24 100644 --- a/lib/swap/swap_history_page.dart +++ b/lib/swap/swap_history_page.dart @@ -1,11 +1,11 @@ import 'package:bb_mobile/_model/swap.dart'; import 'package:bb_mobile/_pkg/boltz/swap.dart'; import 'package:bb_mobile/_pkg/clipboard.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; import 'package:bb_mobile/_ui/app_bar.dart'; import 'package:bb_mobile/_ui/components/text.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; import 'package:bb_mobile/locator.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; import 'package:bb_mobile/styles.dart'; import 'package:bb_mobile/swap/swap_history_bloc/swap_history_cubit.dart'; import 'package:bb_mobile/swap/watcher_bloc/watchtxs_bloc.dart'; @@ -28,8 +28,8 @@ class _SwapHistoryPageState extends State { @override void initState() { _swapHistory = SwapHistoryCubit( - homeCubit: context.read(), - networkCubit: context.read(), + appWalletsRepository: context.read(), + networkRepository: context.read(), boltz: locator(), watcher: context.read(), )..loadSwaps(); diff --git a/lib/swap/swap_page.dart b/lib/swap/swap_page.dart index 0b293a16..dcce38e7 100644 --- a/lib/swap/swap_page.dart +++ b/lib/swap/swap_page.dart @@ -6,22 +6,23 @@ import 'package:bb_mobile/_pkg/file_storage.dart'; import 'package:bb_mobile/_pkg/mempool_api.dart'; import 'package:bb_mobile/_pkg/payjoin/manager.dart'; import 'package:bb_mobile/_pkg/storage/hive.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/sensitive_storage.dart'; import 'package:bb_mobile/_pkg/wallet/transaction.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; +import 'package:bb_mobile/_repository/wallet/sensitive_wallet_storage.dart'; import 'package:bb_mobile/_ui/app_bar.dart'; -import 'package:bb_mobile/_ui/organisms/swap_widget.dart'; import 'package:bb_mobile/currency/bloc/currency_cubit.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; +import 'package:bb_mobile/home/bloc/home_bloc.dart'; import 'package:bb_mobile/locator.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/network/bloc/network_bloc.dart'; import 'package:bb_mobile/network_fees/bloc/networkfees_cubit.dart'; import 'package:bb_mobile/send/bloc/send_cubit.dart'; import 'package:bb_mobile/send/send_page.dart'; import 'package:bb_mobile/settings/bloc/settings_cubit.dart'; import 'package:bb_mobile/swap/create_swap_bloc/swap_cubit.dart'; import 'package:bb_mobile/swap/onchain_listeners.dart'; +import 'package:bb_mobile/swap/ui_swapwidget/swap_widget.dart'; import 'package:bb_mobile/swap/watcher_bloc/watchtxs_bloc.dart'; -import 'package:bb_mobile/wallet/bloc/wallet_bloc.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; @@ -48,13 +49,17 @@ class _SwapPageState extends State { walletSensitiveRepository: locator(), swapBoltz: locator(), walletTx: locator(), - homeCubit: context.read(), + appWalletsRepository: locator(), + // homeCubit: context.read(), watchTxsBloc: context.read(), - networkCubit: context.read(), - )..fetchFees(context.read().state.testnet); + // networkCubit: context.read(), + networkRepository: locator(), + )..fetchFees(context.read().state.networkData.testnet); networkFees = NetworkFeesCubit( - networkCubit: locator(), + // networkCubit: locator(), + networkRepository: locator(), + hiveStorage: locator(), mempoolAPI: locator(), defaultNetworkFeesCubit: context.read(), @@ -66,23 +71,28 @@ class _SwapPageState extends State { defaultCurrencyCubit: context.read(), ); - WalletBloc? walletBloc; + // WalletBloc? walletBloc; - walletBloc = context.read().state.walletBlocs?[0]; + final wallet = context.read().allWallets.first; + // walletBloc = createWalletBloc(wallet); + // walletBloc = context.read().state.walletBlocs?[0]; send = SendCubit( walletTx: locator(), barcode: locator(), defaultRBF: locator().state.defaultRBF, fileStorage: locator(), - networkCubit: locator(), - networkFeesCubit: locator(), - homeCubit: locator(), + networkRepository: locator(), + appWalletsRepository: locator(), + // networkCubit: locator(), + // networkFeesCubit: locator(), + // homeCubit: locator(), payjoinManager: locator(), swapBoltz: locator(), - currencyCubit: currency, + // currencyCubit: currency, openScanner: false, - walletBloc: walletBloc, + wallet: wallet, + // walletBloc: walletBloc, swapCubit: swap, ); @@ -119,12 +129,12 @@ class _Screen extends StatelessWidget { @override Widget build(BuildContext context) { final network = - context.select((NetworkCubit cubit) => cubit.state.getBBNetwork()); - final walletBlocs = context.select( - (HomeCubit cubit) => - cubit.state.walletBlocsFromNetworkExcludeWatchOnly(network), + context.select((NetworkBloc cubit) => cubit.state.getBBNetwork()); + final wallets = context.select( + (HomeBloc cubit) => + cubit.state.walletsFromNetworkExcludeWatchOnly(network), ); - final wallets = walletBlocs.map((bloc) => bloc.state.wallet!).toList(); + // final wallets = walletBlocs.map((bloc) => bloc.state.wallet).toList(); final generatingInv = context .select((CreateSwapCubit cubit) => cubit.state.generatingSwapInv); @@ -172,9 +182,20 @@ class _Screen extends StatelessWidget { int amount, bool sweep, ) { - context - .read() - .buildChainSwap(fromWallet, toWallet, amount, sweep); + final feeRate = context + .read() + .state + .selectedOrFirst(true); + final unitsInSats = + context.read().state.unitsInSats; + context.read().buildChainSwap( + fromWallet: fromWallet, + toWallet: toWallet, + amount: amount, + sweep: sweep, + feeRate: feeRate, + unitsInSats: unitsInSats, + ); }, ), const SendErrDisplay(), diff --git a/lib/swap/swap_page_progress.dart b/lib/swap/swap_page_progress.dart index fa06b04b..95e02671 100644 --- a/lib/swap/swap_page_progress.dart +++ b/lib/swap/swap_page_progress.dart @@ -3,7 +3,7 @@ import 'package:bb_mobile/_model/transaction.dart'; import 'package:bb_mobile/_ui/components/button.dart'; import 'package:bb_mobile/_ui/components/text.dart'; import 'package:bb_mobile/currency/bloc/currency_cubit.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/network/bloc/network_bloc.dart'; import 'package:bb_mobile/send/bloc/send_cubit.dart'; import 'package:bb_mobile/swap/create_swap_bloc/swap_cubit.dart'; import 'package:bb_mobile/swap/watcher_bloc/watchtxs_bloc.dart'; @@ -56,7 +56,7 @@ class _ChainSwapProgressWidgetState extends State { context.read().updateAmount(amtDouble.toString()); context.select((CurrencyCubit cubit) => cubit.state.defaultFiatCurrency); context.select((CurrencyCubit cubit) => cubit.state.fiatAmt); - context.select((NetworkCubit cubit) => cubit.state.testnet); + context.select((NetworkBloc cubit) => cubit.state.networkData.testnet); Transaction? tx; if (swapTx?.isChainReceive() == false) { tx = context.select((SendCubit _) => _.state.tx); diff --git a/lib/swap/swap_page_progress_page.dart b/lib/swap/swap_page_progress_page.dart index f84b8870..6ddcafb8 100644 --- a/lib/swap/swap_page_progress_page.dart +++ b/lib/swap/swap_page_progress_page.dart @@ -1,11 +1,11 @@ import 'package:bb_mobile/_model/swap.dart'; import 'package:bb_mobile/_pkg/boltz/swap.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/sensitive_storage.dart'; import 'package:bb_mobile/_pkg/wallet/transaction.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; +import 'package:bb_mobile/_repository/wallet/sensitive_wallet_storage.dart'; import 'package:bb_mobile/_ui/app_bar.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; import 'package:bb_mobile/locator.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; import 'package:bb_mobile/send/bloc/send_cubit.dart'; import 'package:bb_mobile/swap/create_swap_bloc/swap_cubit.dart'; import 'package:bb_mobile/swap/swap_page_progress.dart'; @@ -38,9 +38,11 @@ class _ChainSwapProgressPageState extends State { walletSensitiveRepository: locator(), swapBoltz: locator(), walletTx: locator(), - homeCubit: context.read(), + appWalletsRepository: locator(), + // homeCubit: context.read(), watchTxsBloc: context.read(), - networkCubit: context.read(), + // networkCubit: context.read(), + networkRepository: locator(), ); _swapCubit.setSwapTx(widget.swapTx); super.initState(); diff --git a/lib/_ui/atoms/bb_form_field.dart b/lib/swap/ui_swapwidget/bb_form_field.dart similarity index 100% rename from lib/_ui/atoms/bb_form_field.dart rename to lib/swap/ui_swapwidget/bb_form_field.dart diff --git a/lib/_ui/molecules/currency_input_widget.dart b/lib/swap/ui_swapwidget/currency_input_widget.dart similarity index 99% rename from lib/_ui/molecules/currency_input_widget.dart rename to lib/swap/ui_swapwidget/currency_input_widget.dart index 0809a40f..c51b6f56 100644 --- a/lib/_ui/molecules/currency_input_widget.dart +++ b/lib/swap/ui_swapwidget/currency_input_widget.dart @@ -1,8 +1,8 @@ import 'package:bb_mobile/_model/currency.dart'; import 'package:bb_mobile/_model/currency_new.dart'; -import 'package:bb_mobile/_ui/atoms/bb_form_field.dart'; import 'package:bb_mobile/_ui/components/button.dart'; import 'package:bb_mobile/styles.dart'; +import 'package:bb_mobile/swap/ui_swapwidget/bb_form_field.dart'; import 'package:flutter/material.dart'; /// Constraints: This component always require sats and btc to be first two currencies. diff --git a/lib/_ui/organisms/swap_widget.dart b/lib/swap/ui_swapwidget/swap_widget.dart similarity index 98% rename from lib/_ui/organisms/swap_widget.dart rename to lib/swap/ui_swapwidget/swap_widget.dart index b0544d33..185e94f3 100644 --- a/lib/_ui/organisms/swap_widget.dart +++ b/lib/swap/ui_swapwidget/swap_widget.dart @@ -3,9 +3,9 @@ import 'package:bb_mobile/_model/currency_new.dart'; import 'package:bb_mobile/_model/wallet.dart'; import 'package:bb_mobile/_ui/components/button.dart'; import 'package:bb_mobile/_ui/components/text.dart'; -import 'package:bb_mobile/_ui/molecules/currency_input_widget.dart'; -import 'package:bb_mobile/_ui/molecules/wallet/wallet_dropdown.dart'; import 'package:bb_mobile/settings/bloc/lighting_cubit.dart'; +import 'package:bb_mobile/swap/ui_swapwidget/currency_input_widget.dart'; +import 'package:bb_mobile/swap/ui_swapwidget/wallet_dropdown.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:gap/gap.dart'; diff --git a/lib/_ui/molecules/wallet/wallet_card.dart b/lib/swap/ui_swapwidget/wallet_card.dart similarity index 100% rename from lib/_ui/molecules/wallet/wallet_card.dart rename to lib/swap/ui_swapwidget/wallet_card.dart diff --git a/lib/_ui/molecules/wallet/wallet_dropdown.dart b/lib/swap/ui_swapwidget/wallet_dropdown.dart similarity index 97% rename from lib/_ui/molecules/wallet/wallet_dropdown.dart rename to lib/swap/ui_swapwidget/wallet_dropdown.dart index c4d38820..89a1f61a 100644 --- a/lib/_ui/molecules/wallet/wallet_dropdown.dart +++ b/lib/swap/ui_swapwidget/wallet_dropdown.dart @@ -1,9 +1,9 @@ import 'package:bb_mobile/_model/wallet.dart'; import 'package:bb_mobile/_ui/components/text.dart'; -import 'package:bb_mobile/_ui/molecules/wallet/wallet_card.dart'; import 'package:bb_mobile/currency/bloc/currency_cubit.dart'; import 'package:bb_mobile/settings/bloc/lighting_cubit.dart'; import 'package:bb_mobile/styles.dart'; +import 'package:bb_mobile/swap/ui_swapwidget/wallet_card.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/lib/swap/watcher_bloc/watchtxs_bloc.dart b/lib/swap/watcher_bloc/watchtxs_bloc.dart index ac20b436..5bd4e66c 100644 --- a/lib/swap/watcher_bloc/watchtxs_bloc.dart +++ b/lib/swap/watcher_bloc/watchtxs_bloc.dart @@ -7,13 +7,12 @@ import 'package:bb_mobile/_model/wallet.dart'; import 'package:bb_mobile/_pkg/boltz/swap.dart'; import 'package:bb_mobile/_pkg/logger.dart'; import 'package:bb_mobile/_pkg/wallet/transaction.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; +import 'package:bb_mobile/_repository/wallet_service.dart'; import 'package:bb_mobile/locator.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; import 'package:bb_mobile/swap/watcher_bloc/watchtxs_event.dart'; import 'package:bb_mobile/swap/watcher_bloc/watchtxs_state.dart'; -import 'package:bb_mobile/wallet/bloc/event.dart'; -import 'package:bb_mobile/wallet/bloc/wallet_bloc.dart'; import 'package:bloc_concurrency/bloc_concurrency.dart'; import 'package:boltz_dart/boltz_dart.dart'; import 'package:flutter_animate/flutter_animate.dart'; @@ -23,22 +22,23 @@ class WatchTxsBloc extends Bloc { WatchTxsBloc({ required SwapBoltz swapBoltz, required WalletTx walletTx, - required HomeCubit homeCubit, - required NetworkCubit networkCubit, + required NetworkRepository networkRepository, + required AppWalletsRepository appWalletsRepository, }) : _walletTx = walletTx, - _homeCubit = homeCubit, - _networkCubit = networkCubit, + _appWalletsRepository = appWalletsRepository, + _networkRepository = networkRepository, _swapBoltz = swapBoltz, super(const WatchTxsState()) { on(_onWatchWallets); - // on(_onClearAlerts); + on(_onProcessSwapTx, transformer: sequential()); } final SwapBoltz _swapBoltz; final WalletTx _walletTx; - final HomeCubit _homeCubit; - final NetworkCubit _networkCubit; + + final NetworkRepository _networkRepository; + final AppWalletsRepository _appWalletsRepository; BoltzApi? _boltzMainnet; BoltzApi? _boltzTestnet; @@ -67,27 +67,20 @@ class WatchTxsBloc extends Bloc { WatchWallets event, Emitter emit, ) async { - final isTestnet = _networkCubit.state.testnet; + final isTestnet = _networkRepository.testnet; + await Future.delayed(100.ms); - final network = _networkCubit.state.getBBNetwork(); - final walletBlocs = _homeCubit.state.walletBlocsFromNetwork(network); + final network = _networkRepository.getBBNetwork; + + final wallets = _appWalletsRepository.walletsFromNetwork(network); + final swapsToWatch = []; - for (final walletBloc in walletBlocs) { - final wallet = walletBloc.state.wallet; - if (wallet == null) return; - // for (final swapTx in wallet!.swapsToProcess()) - // add( - // ProcessSwapTx( - // walletId: wallet.id, - // swapTxId: swapTx.id, - // ), - // ); + for (final wallet in wallets) { swapsToWatch.addAll(wallet.swaps); } - // swapsToWatch.removeWhere((_) => _.failed()); if (swapsToWatch.isEmpty) return; - // print('Listening to Swaps: ${swapsToWatch.map((_) => _.id).toList()}'); + _disposeAll(); __watchSwapStatus( @@ -155,15 +148,13 @@ class WatchTxsBloc extends Bloc { required String swapId, required SwapStreamStatus status, }) async { - // print('----swapstatus : $swapId - ${status.status}'); - for (final walletBloc in _homeCubit.state.walletBlocs!) { - if (walletBloc.state.wallet!.hasOngoingSwap(swapId)) { - // print('SwapStatusUpdate: $id - ${status.status}'); + for (final wallet in _appWalletsRepository.allWallets) { + if (wallet.hasOngoingSwap(swapId)) { if (!state.isListeningId(swapId)) return; - // final swapTx = walletBloc.state.wallet!.getOngoingSwap(swapId); + add( ProcessSwapTx( - walletId: walletBloc.state.wallet!.id, + walletId: wallet.id, status: status, swapTxId: swapId, ), @@ -174,11 +165,11 @@ class WatchTxsBloc extends Bloc { Future __updateWalletTxs( SwapTx swapTx, - WalletBloc walletBloc, + Wallet wallet, Emitter emit, ) async { final (resp, err) = _walletTx.updateSwapTxs( - wallet: walletBloc.state.wallet!, + wallet: wallet, swapTx: swapTx, ); if (err != null) { @@ -187,25 +178,22 @@ class WatchTxsBloc extends Bloc { } final updatedWallet = resp!.wallet; - walletBloc.add( - UpdateWallet( - updatedWallet, - syncAfter: swapTx.syncWallet(), - delaySync: 200, - updateTypes: [ - UpdateWalletTypes.swaps, - UpdateWalletTypes.transactions, - ], - ), + await _appWalletsRepository.getWalletServiceById(wallet.id)?.updateWallet( + updatedWallet, + syncAfter: swapTx.syncWallet(), + delaySync: 200, + updateTypes: [ + UpdateWalletTypes.swaps, + UpdateWalletTypes.transactions, + ], ); - await Future.delayed(400.ms); return updatedWallet; } Future __refundSwap( SwapTx swapTx, - WalletBloc walletBloc, + Wallet wallet, Emitter emit, ) async { if (state.swapRefunded(swapTx.id) || (state.isRefunding(swapTx.id))) { @@ -224,14 +212,12 @@ class WatchTxsBloc extends Bloc { ), ); - // await Future.delayed(10.seconds); - - final broadcastViaBoltz = _networkCubit.state.selectedLiquidNetwork != + final broadcastViaBoltz = _networkRepository.data.selectedLiquidNetwork != LiquidElectrumTypes.bullbitcoin; final (txid, err) = await _swapBoltz.refundSubmarineSwap( swapTx: swapTx, - wallet: walletBloc.state.wallet!, + wallet: wallet, tryCooperate: true, broadcastViaBoltz: broadcastViaBoltz, ); @@ -274,7 +260,6 @@ class WatchTxsBloc extends Bloc { refundedSwapTxs: [...state.refundedSwapTxs, updatedSwap.id], refundingSwapTxIds: state.removeRefunding(updatedSwap.id), refundingSwap: false, - // syncWallet: walletBloc.state.wallet, ), ); @@ -283,7 +268,7 @@ class WatchTxsBloc extends Bloc { Future __claimSwap( SwapTx swapTx, - WalletBloc walletBloc, + Wallet wallet, Emitter emit, ) async { if (state.swapClaimed(swapTx.id) || (state.isClaiming(swapTx.id))) { @@ -302,13 +287,12 @@ class WatchTxsBloc extends Bloc { ), ); - // await Future.delayed(5.seconds); - final broadcastViaBoltz = _networkCubit.state.selectedLiquidNetwork != + final broadcastViaBoltz = _networkRepository.data.selectedLiquidNetwork != LiquidElectrumTypes.bullbitcoin; final (txid, err) = await _swapBoltz.claimReverseSwap( swapTx: swapTx, - wallet: walletBloc.state.wallet!, + wallet: wallet, tryCooperate: true, broadcastViaBoltz: broadcastViaBoltz, ); @@ -339,7 +323,6 @@ class WatchTxsBloc extends Bloc { claimedSwapTxs: [...state.claimedSwapTxs, updatedSwap.id], claimingSwapTxIds: state.removeClaiming(updatedSwap.id), claimingSwap: false, - // syncWallet: walletBloc.state.wallet, ), ); @@ -348,7 +331,7 @@ class WatchTxsBloc extends Bloc { Future __coopCloseSwap( SwapTx swapTx, - WalletBloc walletBloc, + Wallet wallet, Emitter emit, ) async { if (state.swapClaimed(swapTx.id) || (state.isClaiming(swapTx.id))) { @@ -371,14 +354,11 @@ class WatchTxsBloc extends Bloc { ), ); - // await Future.delayed(7.seconds); - final err = await _swapBoltz.cooperativeSubmarineClose( swapTx: swapTx, - wallet: walletBloc.state.wallet!, + wallet: wallet, ); if (err != null) { - // print(err); locator() .log('Error Coop Closing Submarine Swap ${swapTx.id}: $err'); @@ -397,7 +377,6 @@ class WatchTxsBloc extends Bloc { claimedSwapTxs: [...state.claimedSwapTxs, swapTx.id], claimingSwapTxIds: state.removeClaiming(swapTx.id), claimingSwap: false, - // syncWallet: walletBloc.state.wallet, ), ); return null; @@ -410,14 +389,11 @@ class WatchTxsBloc extends Bloc { emit( state.copyWith(listeningTxs: state.removeListeningTx(swapTx.id)), ); - // await Future.delayed(1500.ms); - // final isTestnet = swapTx.network == BBNetwork.Testnet; - // add(WatchWallets(isTestnet: isTestnet)); } Future __onChainclaimSwap( SwapTx swapTx, - WalletBloc walletBloc, + Wallet wallet, Emitter emit, ) async { if (state.swapClaimed(swapTx.id) || (state.isClaiming(swapTx.id))) { @@ -438,7 +414,7 @@ class WatchTxsBloc extends Bloc { final (txid, err) = await _swapBoltz.claimChainSwap( swapTx: swapTx, - wallet: walletBloc.state.wallet!, + wallet: wallet, tryCooperate: true, ); @@ -467,7 +443,6 @@ class WatchTxsBloc extends Bloc { claimedSwapTxs: [...state.claimedSwapTxs, updatedSwap.id], claimingSwapTxIds: state.removeClaiming(updatedSwap.id), claimingSwap: false, - // syncWallet: walletBloc.state.wallet, ), ); @@ -476,7 +451,7 @@ class WatchTxsBloc extends Bloc { Future __onchainRefund( SwapTx swapTx, - WalletBloc walletBloc, + Wallet wallet, Emitter emit, ) async { if (state.swapRefunded(swapTx.id) || (state.isRefunding(swapTx.id))) { @@ -495,13 +470,10 @@ class WatchTxsBloc extends Bloc { ), ); - // await Future.delayed(10.seconds); - final (txid, err) = await _swapBoltz.refundChainSwap( swapTx: swapTx, - wallet: walletBloc.state.wallet!, + wallet: wallet, tryCooperate: true, - // broadcastViaBoltz: broadcastViaBoltz, ); if (err != null) { locator().log('Error Refunding Chain Swap ${swapTx.id}: $err'); @@ -514,19 +486,10 @@ class WatchTxsBloc extends Bloc { ), ); return null; - - // final updatedSwap = swapTx.copyWith( - // status: SwapStreamStatus( - // id: swapTx.id, - // status: SwapStatus.swapRefunded, - // ), - // ); - // return updatedSwap; } SwapTx updatedSwap; - // try { - // final json = jsonDecode(txid!) as Map; + updatedSwap = swapTx.copyWith( claimTxid: txid, status: SwapStreamStatus( @@ -534,16 +497,12 @@ class WatchTxsBloc extends Bloc { status: SwapStatus.swapRefunded, ), ); - // } catch (e) { - // updatedSwap = swapTx.copyWith(txid: txid); - // } emit( state.copyWith( refundedSwapTxs: [...state.refundedSwapTxs, updatedSwap.id], refundingSwapTxIds: state.removeRefunding(updatedSwap.id), refundingSwap: false, - // syncWallet: walletBloc.state.wallet, ), ); @@ -554,16 +513,11 @@ class WatchTxsBloc extends Bloc { ProcessSwapTx event, Emitter emit, ) async { - final walletBloc = _homeCubit.state.getWalletBlocById(event.walletId); - final wallet = walletBloc?.state.wallet; - if (walletBloc == null || wallet == null) return; - final SwapTx? swapFromWallet = - walletBloc.state.wallet!.getOngoingSwap(event.swapTxId); - // if swapFromWallet == null - // then look for swaps from the wallet.transactions - // only go ahead with processing it if its claimable or refundable - // OR - // pass the swapTx via the event ONLY IF ITS FROM SWAP HISTORY PAGE + final wallet = _appWalletsRepository.getWalletById(event.walletId); + + if (wallet == null) return; + final SwapTx? swapFromWallet = wallet.getOngoingSwap(event.swapTxId); + if (swapFromWallet == null) return; final swapTx = event.status != null @@ -576,153 +530,140 @@ class WatchTxsBloc extends Bloc { ); emit(state.copyWith(updatedSwapTx: swapTx)); - // await Future.delayed(100.ms); - // emit(state.copyWith(updatedSwapTx: null)); - - // final liquidElectrum = _networkCubit.state.selectedLiquidNetwork; if (swapTx.isReverse()) { switch (swapTx.reverseSwapAction()) { case ReverseSwapActions.created: - await __updateWalletTxs(swapTx, walletBloc, emit); + await __updateWalletTxs(swapTx, wallet, emit); case ReverseSwapActions.failed: - await __updateWalletTxs(swapTx, walletBloc, emit); + await __updateWalletTxs(swapTx, wallet, emit); await __closeSwap(swapTx, emit); case ReverseSwapActions.paid: if (wallet.isLiquid()) { - final swap = await __claimSwap(swapTx, walletBloc, emit); - if (swap != null) await __updateWalletTxs(swap, walletBloc, emit); + final swap = await __claimSwap(swapTx, wallet, emit); + if (swap != null) await __updateWalletTxs(swap, wallet, emit); break; } - await __updateWalletTxs(swapTx, walletBloc, emit); + await __updateWalletTxs(swapTx, wallet, emit); case ReverseSwapActions.claimable: - final swap = await __claimSwap(swapTx, walletBloc, emit); + final swap = await __claimSwap(swapTx, wallet, emit); if (swap != null) { - await __updateWalletTxs(swap, walletBloc, emit); + await __updateWalletTxs(swap, wallet, emit); } else { - await __updateWalletTxs(swapTx, walletBloc, emit); + await __updateWalletTxs(swapTx, wallet, emit); } case ReverseSwapActions.settled: final updatedSwapTx = swapTx.copyWith(completionTime: DateTime.now()); - final w = await __updateWalletTxs(updatedSwapTx, walletBloc, emit); + final w = await __updateWalletTxs(updatedSwapTx, wallet, emit); if (w == null) break; await __closeSwap(updatedSwapTx, emit); } } else if (swapTx.isSubmarine()) { switch (swapTx.submarineSwapAction()) { case SubmarineSwapActions.created: - await __updateWalletTxs(swapTx, walletBloc, emit); + await __updateWalletTxs(swapTx, wallet, emit); case SubmarineSwapActions.failed: - await __updateWalletTxs(swapTx, walletBloc, emit); + await __updateWalletTxs(swapTx, wallet, emit); await __closeSwap(swapTx, emit); case SubmarineSwapActions.paid: if (swapTx.isLiquid()) { - final swap = await __coopCloseSwap(swapTx, walletBloc, emit); - if (swap != null) await __updateWalletTxs(swap, walletBloc, emit); + final swap = await __coopCloseSwap(swapTx, wallet, emit); + if (swap != null) await __updateWalletTxs(swap, wallet, emit); break; } else { - await __updateWalletTxs(swapTx, walletBloc, emit); + await __updateWalletTxs(swapTx, wallet, emit); } case SubmarineSwapActions.claimable: - final swap = await __coopCloseSwap(swapTx, walletBloc, emit); + final swap = await __coopCloseSwap(swapTx, wallet, emit); if (swap != null) { - await __updateWalletTxs(swap, walletBloc, emit); + await __updateWalletTxs(swap, wallet, emit); } else { - await __updateWalletTxs(swapTx, walletBloc, emit); + await __updateWalletTxs(swapTx, wallet, emit); } case SubmarineSwapActions.refundable: - // TODO: Delays are introduced so wallet update actually happens. - // Without the delays, swap.status and swap.claimTxId doesn't get updated. - await __updateWalletTxs(swapTx, walletBloc, emit); - // await Future.delayed(const Duration(milliseconds: 1000)); - final swap = await __refundSwap(swapTx, walletBloc, emit); + await __updateWalletTxs(swapTx, wallet, emit); + + final swap = await __refundSwap(swapTx, wallet, emit); if (swap != null) { - // await Future.delayed(const Duration(milliseconds: 1000)); await __updateWalletTxs( swap, - walletBloc, + wallet, emit, ); } - // await Future.delayed(const Duration(milliseconds: 1000)); case SubmarineSwapActions.settled: final updatedSwapTx = swapTx.copyWith(completionTime: DateTime.now()); final w = await __updateWalletTxs( updatedSwapTx, - walletBloc, + wallet, emit, ); if (w == null) break; await __closeSwap(updatedSwapTx, emit); } } else if (swapTx.isChainSwap()) { - // print('process Chain Swap ${swapTx.id}: ${swapTx.status!.status}'); - switch (swapTx.chainSwapAction()) { case ChainSwapActions.created: - await __updateWalletTxs(swapTx, walletBloc, emit); + await __updateWalletTxs(swapTx, wallet, emit); case ChainSwapActions.paid: if (swapTx.isChainReceive() && swapTx.lockupTxid == null) { final (txid, err) = await _swapBoltz.chainUserLockup( swapTx: swapTx, - wallet: walletBloc.state.wallet!, + wallet: wallet, ); if (err != null) { - await __updateWalletTxs(swapTx, walletBloc, emit); + await __updateWalletTxs(swapTx, wallet, emit); } else { await __updateWalletTxs( swapTx.copyWith(lockupTxid: txid), - walletBloc, + wallet, emit, ); } } else { - await __updateWalletTxs(swapTx, walletBloc, emit); + await __updateWalletTxs(swapTx, wallet, emit); } case ChainSwapActions.claimable: - // await Future.delayed(const Duration(milliseconds: 100)); - final swap = await __onChainclaimSwap(swapTx, walletBloc, emit); + final swap = await __onChainclaimSwap(swapTx, wallet, emit); if (swap != null) { - await __updateWalletTxs(swap, walletBloc, emit); + await __updateWalletTxs(swap, wallet, emit); } else { - await __updateWalletTxs(swapTx, walletBloc, emit); + await __updateWalletTxs(swapTx, wallet, emit); } case ChainSwapActions.settled: - // await Future.delayed(const Duration(milliseconds: 200)); final updatedSwapTx = swapTx.copyWith(completionTime: DateTime.now()); - await __updateWalletTxs(updatedSwapTx, walletBloc, emit); - // await Future.delayed(const Duration(milliseconds: 100)); + await __updateWalletTxs(updatedSwapTx, wallet, emit); + await __closeSwap(swapTx, emit); - // await Future.delayed(const Duration(milliseconds: 200)); - final toWalletBloc = _homeCubit.state - .getWalletBlocById(swapTx.chainSwapDetails!.toWalletId); - toWalletBloc?.add(SyncWallet()); - // TODO: Better way to sync `to` wallet + + await _appWalletsRepository + .getWalletServiceById( + swapTx.chainSwapDetails!.toWalletId, + ) + ?.syncWallet(); + case ChainSwapActions.refundable: - // TODO: Delays are introduced so wallet update actually happens. - // Without the delays, swap.status and swap.claimTxId doesn't get updated. - // await Future.delayed(const Duration(milliseconds: 1000)); - final swap = await __onchainRefund(swapTx, walletBloc, emit); - // await Future.delayed(const Duration(milliseconds: 1200)); - if (swap != null) await __updateWalletTxs(swap, walletBloc, emit); - // await Future.delayed(const Duration(milliseconds: 1000)); + final swap = await __onchainRefund(swapTx, wallet, emit); + + if (swap != null) await __updateWalletTxs(swap, wallet, emit); + default: - await __updateWalletTxs(swapTx, walletBloc, emit); + await __updateWalletTxs(swapTx, wallet, emit); } } - // Give time for the walletBloc to update + await Future.delayed( const Duration( milliseconds: 1000, diff --git a/lib/testground.dart b/lib/testground.dart index c32058cb..1a3a8633 100644 --- a/lib/testground.dart +++ b/lib/testground.dart @@ -1,38 +1,38 @@ -import 'package:bb_mobile/_ui/app_bar.dart'; -import 'package:bb_mobile/_ui/organisms/swap_widget.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:go_router/go_router.dart'; +// import 'package:bb_mobile/_ui/app_bar.dart'; +// import 'package:bb_mobile/home/bloc/home_cubit.dart'; +// import 'package:bb_mobile/network/bloc/network_cubit.dart'; +// import 'package:bb_mobile/swap/ui_swapwidget/swap_widget.dart'; +// import 'package:flutter/material.dart'; +// import 'package:flutter_bloc/flutter_bloc.dart'; +// import 'package:go_router/go_router.dart'; -class Testground extends StatelessWidget { - const Testground({super.key}); +// class Testground extends StatelessWidget { +// const Testground({super.key}); - @override - Widget build(BuildContext context) { - final network = context.select((NetworkCubit x) => x.state.getBBNetwork()); +// @override +// Widget build(BuildContext context) { +// final network = context.select((NetworkCubit x) => x.state.getBBNetwork()); - final walletBlocs = context.select( - (HomeCubit x) => x.state.walletBlocsFromNetwork(network), - ); +// final walletBlocs = context.select( +// (HomeBloc x) => x.state.walletBlocsFromNetwork(network), +// ); - final wallets = walletBlocs.map((e) => e.state.wallet!).toList(); +// final wallets = walletBlocs.map((e) => e.state.wallet!).toList(); - return Scaffold( - appBar: AppBar( - automaticallyImplyLeading: false, - flexibleSpace: BBAppBar( - text: 'Swap', - onBack: () { - context.pop(); - }, - ), - ), - body: Padding( - padding: const EdgeInsets.all(32.0), - child: SwapWidget(wallets: wallets), - ), - ); - } -} +// return Scaffold( +// appBar: AppBar( +// automaticallyImplyLeading: false, +// flexibleSpace: BBAppBar( +// text: 'Swap', +// onBack: () { +// context.pop(); +// }, +// ), +// ), +// body: Padding( +// padding: const EdgeInsets.all(32.0), +// child: SwapWidget(wallets: wallets), +// ), +// ); +// } +// } diff --git a/lib/transaction/bloc/transaction_cubit.dart b/lib/transaction/bloc/transaction_cubit.dart index e5196d28..fdddc6aa 100644 --- a/lib/transaction/bloc/transaction_cubit.dart +++ b/lib/transaction/bloc/transaction_cubit.dart @@ -2,41 +2,42 @@ import 'dart:async'; import 'package:bb_mobile/_model/address.dart'; import 'package:bb_mobile/_model/transaction.dart'; +import 'package:bb_mobile/_model/wallet.dart'; import 'package:bb_mobile/_pkg/wallet/address.dart'; import 'package:bb_mobile/_pkg/wallet/bdk/sensitive_create.dart'; import 'package:bb_mobile/_pkg/wallet/bdk/transaction.dart'; import 'package:bb_mobile/_pkg/wallet/bdk/utxo.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/sensitive_storage.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/wallets.dart'; import 'package:bb_mobile/_pkg/wallet/transaction.dart'; import 'package:bb_mobile/_pkg/wallet/update.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; +import 'package:bb_mobile/_repository/wallet/internal_wallets.dart'; +import 'package:bb_mobile/_repository/wallet/sensitive_wallet_storage.dart'; +import 'package:bb_mobile/_repository/wallet_service.dart'; import 'package:bb_mobile/transaction/bloc/state.dart'; -import 'package:bb_mobile/wallet/bloc/event.dart'; -import 'package:bb_mobile/wallet/bloc/wallet_bloc.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class TransactionCubit extends Cubit { TransactionCubit({ required Transaction tx, - required WalletBloc walletBloc, + required Wallet wallet, required WalletTx walletTx, required BDKTransactions bdkTx, - // required HomeCubit homeCubit, required WalletSensitiveStorageRepository walletSensRepository, required WalletAddress walletAddress, required WalletUpdate walletUpdate, - required WalletsRepository walletsRepository, + required InternalWalletsRepository walletsRepository, required BDKSensitiveCreate bdkSensitiveCreate, + required AppWalletsRepository appWalletsRepository, }) : _bdkTx = bdkTx, _bdkSensitiveCreate = bdkSensitiveCreate, _walletAddress = walletAddress, _walletSensRepository = walletSensRepository, _walletsRepository = walletsRepository, _walletUpdate = walletUpdate, + _appWalletsRepository = appWalletsRepository, _walletTx = walletTx, - _walletBloc = walletBloc, - // _homeCubit = homeCubit, + _wallet = wallet, super(TransactionState(tx: tx)) { if (tx.isReceived()) { loadReceiveLabel(); @@ -49,65 +50,28 @@ class TransactionCubit extends Cubit { final WalletTx _walletTx; final WalletUpdate _walletUpdate; - final WalletsRepository _walletsRepository; + final InternalWalletsRepository _walletsRepository; final WalletSensitiveStorageRepository _walletSensRepository; final WalletAddress _walletAddress; final BDKSensitiveCreate _bdkSensitiveCreate; final BDKTransactions _bdkTx; + final AppWalletsRepository _appWalletsRepository; - final WalletBloc _walletBloc; - // final HomeCubit _homeCubit; + final Wallet _wallet; Future loadTx() async { emit(state.copyWith(loadingAddresses: true, errLoadingAddresses: '')); - Future.wait([ - // loadInAddresses(), - // loadOutAddresses(), - ]); + Future.wait([]); emit(state.copyWith(loadingAddresses: false)); } - // Future loadInAddresses() async { - // final (tx, err) = await walletTx.updateTxInputAddresses( - // tx: state.tx, - // wallet: walletBloc.state.wallet!, - // mempoolAPI: mempoolAPI, - // ); - // if (err != null) { - // emit( - // state.copyWith( - // errLoadingAddresses: err.toString(), - // ), - // ); - // return; - // } - - // emit(state.copyWith(tx: tx!)); - // } - - // Future loadOutAddresses() async { - // final (tx, err) = await walletTx.updateTxOutputAddresses( - // tx: state.tx, - // wallet: walletBloc.state.wallet!, - // ); - // if (err != null) { - // emit( - // state.copyWith( - // errLoadingAddresses: err.toString(), - // ), - // ); - // return; - // } - // emit(state.copyWith(tx: tx!)); - // } - void loadReceiveLabel() { final tx = state.tx; final txid = tx.txid; - final address = _walletBloc.state.wallet!.getAddressFromAddresses(txid); + final address = _wallet.getAddressFromAddresses(txid); if (address == null || address.label == null) return; @@ -121,14 +85,6 @@ class TransactionCubit extends Cubit { void loadSentLabel() {} - // void loadLabel() async { - // final tx = (walletBloc.state.wallet?.transactions ?? []).firstWhere( - // (t) => t.txid == state.tx.txid, - // orElse: () => state.tx, - // ); - // emit(state.copyWith(label: tx.label ?? '')); - // } - void labelChanged(String label) { emit(state.copyWith(label: label)); } @@ -149,10 +105,9 @@ class TransactionCubit extends Cubit { .toList(), ); - final updateWallet = _walletBloc.state.wallet!.copyWith( + final updateWallet = _wallet.copyWith( transactions: [ - for (final t - in _walletBloc.state.wallet?.transactions ?? []) + for (final t in _wallet.transactions) if (t.txid == tx.txid) tx else t, ], ); @@ -174,42 +129,37 @@ class TransactionCubit extends Cubit { label: state.label, ); if (updatedWallet != null) { - _walletBloc.add( - UpdateWallet( - updatedWallet, - updateTypes: [ - UpdateWalletTypes.transactions, - UpdateWalletTypes.addresses, - UpdateWalletTypes.utxos, - ], - ), + await _appWalletsRepository + .getWalletServiceById(updatedWallet.id) + ?.updateWallet( + updatedWallet, + updateTypes: [ + UpdateWalletTypes.transactions, + UpdateWalletTypes.addresses, + UpdateWalletTypes.utxos, + ], ); } else { - _walletBloc.add( - UpdateWallet( - w, - updateTypes: [ - UpdateWalletTypes.transactions, - UpdateWalletTypes.addresses, - ], - ), + await _appWalletsRepository.getWalletServiceById(w.id)?.updateWallet( + w, + updateTypes: [ + UpdateWalletTypes.transactions, + UpdateWalletTypes.addresses, + ], ); } } catch (_) {} } else { - _walletBloc.add( - UpdateWallet( - w, - updateTypes: [ - UpdateWalletTypes.transactions, - UpdateWalletTypes.addresses, - ], - ), + await _appWalletsRepository.getWalletServiceById(w.id)?.updateWallet( + w, + updateTypes: [ + UpdateWalletTypes.transactions, + UpdateWalletTypes.addresses, + ], ); } - await Future.delayed(const Duration(seconds: 1)); - _walletBloc.add(ListTransactions()); + await _appWalletsRepository.getWalletServiceById(w.id)?.listTransactions(); emit( state.copyWith( @@ -219,25 +169,9 @@ class TransactionCubit extends Cubit { ); } - // void updateFeeRate(String feeRate) { - // final amt = int.tryParse(feeRate) ?? 0; - // emit(state.copyWith(feeRate: amt)); - // } - - // void updateFeeRateInt(int feeRate) { - // emit(state.copyWith(feeRate: feeRate)); - // } - - // SENSITIVE FX Future buildRbfTx(int fee) async { emit(state.copyWith(buildingTx: true, errBuildingTx: '')); - // final isManualFees = _networkFeesCubit.state.feeOption() == 4; - // int fees = 0; - // if (!isManualFees) - // fees = _networkFeesCubit.state.feesList?[_networkFeesCubit.state.feeOption()] ?? 0; - // else - // fees = _networkFeesCubit.state.fee(); final fees = fee; if (fees == 0) { @@ -259,18 +193,8 @@ class TransactionCubit extends Cubit { return; } - // final walletBloc = _homeCubit.state.getWalletBlocById(state.tx.walletId!); - final wallet = _walletBloc.state.wallet!; - // final (wallet, err) = await _walletsStorageRepository.readWallet( - // walletHashId: _walletBloc.state.wallet!.getWalletStorageString(), - // ); - // if (err != null) { - // emit(state.copyWith(errBuildingTx: err.toString(), buildingTx: false)); - // return; - // } - final (seed, errRead) = await _walletSensRepository.readSeed( - fingerprintIndex: wallet.getRelatedSeedStorageString(), + fingerprintIndex: _wallet.getRelatedSeedStorageString(), ); if (errRead != null) { @@ -284,7 +208,7 @@ class TransactionCubit extends Cubit { } final (bdkSignerWallet, errLoad) = - await _bdkSensitiveCreate.loadPrivateBdkWallet(wallet, seed!); + await _bdkSensitiveCreate.loadPrivateBdkWallet(_wallet, seed!); if (errLoad != null) { emit( state.copyWith( @@ -295,7 +219,7 @@ class TransactionCubit extends Cubit { return; } - final (pubBdkWallet, errGet) = _walletsRepository.getBdkWallet(wallet.id); + final (pubBdkWallet, errGet) = _walletsRepository.getBdkWallet(_wallet.id); if (errGet != null) { emit( state.copyWith( @@ -334,7 +258,6 @@ class TransactionCubit extends Cubit { emit( state.copyWith( - // buildingTx: false, updatedTx: newTx, ), ); @@ -348,7 +271,7 @@ class TransactionCubit extends Cubit { final tx = state.tx.swapTx != null ? state.updatedTx!.copyWith(swapTx: state.tx.swapTx, isSwap: true) : state.updatedTx!; - final wallet = _walletBloc.state.wallet!; + final wallet = _wallet; final (wtxid, err) = await _walletTx.broadcastTxWithWallet( address: tx.toAddress!, @@ -376,16 +299,6 @@ class TransactionCubit extends Cubit { state: AddressStatus.used, ); - // final txs = walletBloc.state.wallet!.transactions.toList(); - // final idx = txs.indexWhere((element) => element.txid == tx.txid); - // if (idx != -1) { - // txs.removeAt(idx); - // txs.insert(idx, state.tx.copyWith(oldTx: true)); - // } else - // txs.add(state.tx.copyWith(oldTx: true)); - - // updatedWallet = updatedWallet.copyWith(transactions: txs); - final (updatedWallet2, err2) = await _walletUpdate.removePrevTxofRbf(updatedWallet, state.tx, tx); if (err2 != null) { @@ -398,17 +311,19 @@ class TransactionCubit extends Cubit { return; } - _walletBloc.add( - UpdateWallet( - updatedWallet2!, - updateTypes: [ - UpdateWalletTypes.transactions, - UpdateWalletTypes.addresses, - ], - ), + await _appWalletsRepository + .getWalletServiceById(updatedWallet2!.id) + ?.updateWallet( + updatedWallet2, + updateTypes: [ + UpdateWalletTypes.transactions, + UpdateWalletTypes.addresses, + ], ); - _walletBloc.add(SyncWallet()); + await _appWalletsRepository + .getWalletServiceById(updatedWallet2.id) + ?.syncWallet(); emit( state.copyWith( diff --git a/lib/transaction/bump_fees.dart b/lib/transaction/bump_fees.dart index aff1054f..f0a9b5e5 100644 --- a/lib/transaction/bump_fees.dart +++ b/lib/transaction/bump_fees.dart @@ -10,18 +10,19 @@ import 'package:bb_mobile/_pkg/storage/hive.dart'; import 'package:bb_mobile/_pkg/wallet/address.dart'; import 'package:bb_mobile/_pkg/wallet/bdk/sensitive_create.dart'; import 'package:bb_mobile/_pkg/wallet/bdk/transaction.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/sensitive_storage.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/wallets.dart'; import 'package:bb_mobile/_pkg/wallet/transaction.dart'; import 'package:bb_mobile/_pkg/wallet/update.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; +import 'package:bb_mobile/_repository/wallet/internal_wallets.dart'; +import 'package:bb_mobile/_repository/wallet/sensitive_wallet_storage.dart'; import 'package:bb_mobile/_ui/app_bar.dart'; import 'package:bb_mobile/_ui/components/button.dart'; import 'package:bb_mobile/_ui/components/text.dart'; import 'package:bb_mobile/_ui/page_template.dart'; import 'package:bb_mobile/currency/bloc/currency_cubit.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; import 'package:bb_mobile/locator.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/network/bloc/network_bloc.dart'; import 'package:bb_mobile/network_fees/bloc/networkfees_cubit.dart'; import 'package:bb_mobile/send/bloc/send_cubit.dart'; import 'package:bb_mobile/send/send_page.dart'; @@ -113,23 +114,26 @@ class _BumpFeesPageState extends State { @override void initState() { - walletBloc = context.read().state.getWalletBlocFromTx(widget.tx); - - if (walletBloc == null) return; + final wallet = + context.read().getWalletFromTx(widget.tx); + if (wallet == null) return; + walletBloc = createOrRetreiveWalletBloc(wallet.id); swap = CreateSwapCubit( walletSensitiveRepository: locator(), swapBoltz: locator(), walletTx: locator(), - homeCubit: context.read(), + appWalletsRepository: locator(), + // homeCubit: context.read(), watchTxsBloc: context.read(), - networkCubit: context.read(), - )..fetchFees(context.read().state.testnet); + // networkCubit: context.read(), + networkRepository: locator(), + )..fetchFees(context.read().state.networkData.testnet); networkFees = NetworkFeesCubit( hiveStorage: locator(), mempoolAPI: locator(), - networkCubit: locator(), + networkRepository: locator(), defaultNetworkFeesCubit: context.read(), ); networkFees.showOnlyFastest(true); @@ -137,17 +141,14 @@ class _BumpFeesPageState extends State { txCubit = TransactionCubit( tx: widget.tx, - walletBloc: walletBloc!, + wallet: wallet, walletUpdate: locator(), - + appWalletsRepository: locator(), walletTx: locator(), bdkTx: locator(), - // walletSensTx: locator(), - // walletsStorageRepository: locator(), walletSensRepository: locator(), walletAddress: locator(), - - walletsRepository: locator(), + walletsRepository: locator(), bdkSensitiveCreate: locator(), ); @@ -162,14 +163,11 @@ class _BumpFeesPageState extends State { barcode: locator(), defaultRBF: locator().state.defaultRBF, fileStorage: locator(), - networkCubit: locator(), - networkFeesCubit: locator(), - homeCubit: locator(), + networkRepository: locator(), + appWalletsRepository: locator(), payjoinManager: locator(), swapBoltz: locator(), - currencyCubit: currency, openScanner: false, - walletBloc: walletBloc, swapCubit: swap, ); super.initState(); @@ -237,7 +235,7 @@ class _Screen extends StatelessWidget { @override Widget build(BuildContext context) { final tx = context.select((TransactionCubit _) => _.state.tx); - // final swap = tx.swapTx; + final isSwapPending = tx.swapIdisTxid(); final txid = tx.txid; @@ -258,11 +256,6 @@ class _Screen extends StatelessWidget { final feeRate = (tx.feeRate ?? 1).toStringAsFixed(2); - // final size = await tx.bdkTx.transaction.size(); // cant do await here. - // final feesPetByte = fees / size; - - // final statuss = tx.height == null || tx.height == 0 || tx.timestamp == 0; - final er = context.select((TransactionCubit x) => x.state.errSendingTx); final err = context.select((TransactionCubit x) => x.state.errBuildingTx); @@ -311,11 +304,10 @@ class _Screen extends StatelessWidget { const Gap(4), InkWell( onTap: () { - final url = - context.read().state.explorerTxUrl( - txid, - isLiquid: tx.isLiquid, - ); + final url = context.read().state.explorerTxUrl( + txid, + isLiquid: tx.isLiquid, + ); locator().launchApp(url); }, child: BBText.body(txid, isBlue: true), @@ -347,10 +339,8 @@ class _Screen extends StatelessWidget { ), const Gap(24), const NetworkFees(label: 'Set new fee rate'), - const Gap(24), if (errr.isNotEmpty) BBText.errorSmall(errr), - // const Gap(100), ], ), ), diff --git a/lib/transaction/transaction_page.dart b/lib/transaction/transaction_page.dart index c172cb5f..0f7d2691 100644 --- a/lib/transaction/transaction_page.dart +++ b/lib/transaction/transaction_page.dart @@ -8,16 +8,18 @@ import 'package:bb_mobile/_pkg/storage/hive.dart'; import 'package:bb_mobile/_pkg/wallet/address.dart'; import 'package:bb_mobile/_pkg/wallet/bdk/sensitive_create.dart'; import 'package:bb_mobile/_pkg/wallet/bdk/transaction.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/sensitive_storage.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/wallets.dart'; import 'package:bb_mobile/_pkg/wallet/transaction.dart'; import 'package:bb_mobile/_pkg/wallet/update.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; +import 'package:bb_mobile/_repository/wallet/internal_wallets.dart'; +import 'package:bb_mobile/_repository/wallet/sensitive_wallet_storage.dart'; import 'package:bb_mobile/_ui/app_bar.dart'; import 'package:bb_mobile/_ui/components/text.dart'; import 'package:bb_mobile/currency/bloc/currency_cubit.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; +import 'package:bb_mobile/home/bloc/home_bloc.dart'; import 'package:bb_mobile/locator.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/network/bloc/network_bloc.dart'; import 'package:bb_mobile/network_fees/bloc/networkfees_cubit.dart'; import 'package:bb_mobile/styles.dart'; import 'package:bb_mobile/swap/fee_popup.dart'; @@ -26,6 +28,7 @@ import 'package:bb_mobile/transaction/bloc/state.dart'; import 'package:bb_mobile/transaction/bloc/transaction_cubit.dart'; import 'package:bb_mobile/transaction/bump_fees.dart'; import 'package:bb_mobile/transaction/rename_label.dart'; +import 'package:bb_mobile/wallet/bloc/wallet_bloc.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -33,47 +36,61 @@ import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:timeago/timeago.dart' as timeago; -class TxPage extends StatelessWidget { +class TxPage extends StatefulWidget { const TxPage({super.key, required this.tx, this.showOnchainSwap = false}); final Transaction tx; final bool showOnchainSwap; @override - Widget build(BuildContext context) { - // final home = context.read(); - // final wallet = home.state.selectedWalletCubit!; - // final wallet = ; - final walletBloc = context.read().state.getWalletBlocFromTx(tx); - if (walletBloc == null) { + State createState() => _TxPageState(); +} + +class _TxPageState extends State { + late TransactionCubit txCubit; + late WalletBloc walletBloc; + late NetworkFeesCubit networkFees; + + @override + void initState() { + final wallet = + context.read().getWalletFromTx(widget.tx); + if (wallet == null) { context.pop(); - return const SizedBox.shrink(); + return; } - final networkFees = NetworkFeesCubit( + + walletBloc = createOrRetreiveWalletBloc(wallet.id); + + networkFees = NetworkFeesCubit( hiveStorage: locator(), mempoolAPI: locator(), - networkCubit: locator(), + networkRepository: locator(), defaultNetworkFeesCubit: context.read(), ); - final txCubit = TransactionCubit( - tx: tx, - walletBloc: walletBloc, + txCubit = TransactionCubit( + tx: widget.tx, + wallet: wallet, + appWalletsRepository: locator(), walletUpdate: locator(), - walletTx: locator(), bdkTx: locator(), - // walletSensTx: locator(), - // walletsStorageRepository: locator(), walletSensRepository: locator(), walletAddress: locator(), - - walletsRepository: locator(), + walletsRepository: locator(), bdkSensitiveCreate: locator(), - - // networkFeesCubit: networkFees, ); + super.initState(); + } + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { return MultiBlocProvider( providers: [ BlocProvider.value(value: txCubit), @@ -83,15 +100,13 @@ class TxPage extends StatelessWidget { ], child: BlocListener( listenWhen: (previous, current) => previous.tx != current.tx, - listener: (context, state) async { - // home.updateSelectedWallet(walletBloc); - }, + listener: (context, state) async {}, child: Scaffold( appBar: AppBar( automaticallyImplyLeading: false, flexibleSpace: const _TxAppBar(), ), - body: _Screen(showOnchainSwap: showOnchainSwap), + body: _Screen(showOnchainSwap: widget.showOnchainSwap), ), ), ); @@ -103,9 +118,11 @@ class _TxAppBar extends StatelessWidget { @override Widget build(BuildContext context) { - final label = + var label = context.select((TransactionCubit cubit) => cubit.state.tx.label ?? ''); + label = (label.length > 20) ? '${label.substring(0, 20)}...' : label; + return BBAppBar( text: label.isNotEmpty ? label : 'Transaction', onBack: () { @@ -132,14 +149,6 @@ class _Screen extends StatelessWidget { return const _CombinedTxAndOnchainSwapPage(); } return const _OnlyTxPage(); - - // final page = context.select((TransactionCubit _) => _.state.tx.pageLayout); - // if() - // return switch (page) { - // TxLayout.onlyTx => const _OnlyTxPage(), - // TxLayout.onlySwapTx => const _OnlySwapTxPage(), - // TxLayout.both => const _CombinedTxAndSwapPage(), - // }; } } @@ -152,15 +161,6 @@ class _OnlyTxPage extends StatelessWidget { } } -// class _OnlySwapTxPage extends StatelessWidget { -// const _OnlySwapTxPage(); - -// @override -// Widget build(BuildContext context) { -// return const SingleChildScrollView(child: _SwapDetails()); -// } -// } - class _CombinedTxAndSwapPage extends StatelessWidget { const _CombinedTxAndSwapPage(); @@ -222,8 +222,6 @@ class _TxDetails extends StatelessWidget { final isLiq = tx.isLiquid; final isSwapPending = tx.swapIdisTxid(); - // final toAddresses = tx.outAddresses ?? []; - final err = context .select((TransactionCubit cubit) => cubit.state.errLoadingAddresses); @@ -294,11 +292,10 @@ class _TxDetails extends StatelessWidget { if (recipients.isNotEmpty && recipientAddress.address.isNotEmpty) ...[ const BBText.title('Recipient Bitcoin Address'), - // const Gap(4), InkWell( onTap: () { final url = - context.read().state.explorerAddressUrl( + context.read().state.explorerAddressUrl( recipientAddress.address, isLiquid: tx.isLiquid, ); @@ -306,7 +303,6 @@ class _TxDetails extends StatelessWidget { }, child: BBText.body(recipientAddress.address, isBlue: true), ), - const Gap(24), ], if (status.isNotEmpty && !isSwapPending) ...[ @@ -379,7 +375,6 @@ class _TxDetails extends StatelessWidget { err, ), ], - // const Gap(100), ], ), ), @@ -403,7 +398,7 @@ class TxLink extends StatelessWidget { Widget build(BuildContext context) { return InkWell( onTap: () { - final url = context.read().state.explorerTxUrl( + final url = context.read().state.explorerTxUrl( txid, isLiquid: tx.isLiquid, unblindedUrl: unblindedUrl, @@ -466,7 +461,6 @@ class _SwapDetails extends StatelessWidget { (TransactionCubit cubit) => cubit.state.tx.swapTx?.status?.status, ); final isLiq = tx.isLiquid; - // final showQr = status?.showQR ?? true; // may not be required final swap = tx.swapTx; if (swap == null) return const SizedBox.shrink(); @@ -474,8 +468,6 @@ class _SwapDetails extends StatelessWidget { ? status.getOnChainStr(swap.chainSwapDetails!.onChainType) : status.getStr(swap.isSubmarine()); - // final _ = tx.swapTx?.txid?.isNotEmpty ?? false; - final amt = swap.amountForDisplay() ?? 0; final amount = context.select( (CurrencyCubit x) => x.state.getAmountInUnits(amt, removeText: true), @@ -483,20 +475,19 @@ class _SwapDetails extends StatelessWidget { final isReceive = swap.isReverse(); final date = tx.getDateTimeStr(); - // swap. + final id = swap.id; final fees = swap.totalFees() ?? 0; final feesAmount = context.select( (CurrencyCubit x) => x.state.getAmountInUnits(fees, removeText: true), ); - // final invoice = swap.invoice; + final units = context.select( (CurrencyCubit cubit) => cubit.state.getUnitString(isLiquid: isLiq), ); final isRefundedSend = swap.isSubmarine() && swap.refundedAny(); - // Is this needed? final refundChildren = [ const Gap(24), const BBText.title('Refund Tx ID'), @@ -518,7 +509,6 @@ class _SwapDetails extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - // const Gap(24), const BBText.title( 'Swap Amount', ), @@ -564,7 +554,6 @@ class _SwapDetails extends StatelessWidget { claimFee ?? 0, swap.boltzFees ?? 0, ); - // show popup }, ), ], @@ -605,7 +594,6 @@ class _SwapDetails extends StatelessWidget { ), ], ), - // const Gap(24), ], const Gap(16), if (statusStr != null) ...[ @@ -620,7 +608,6 @@ class _SwapDetails extends StatelessWidget { statusStr.$2, ), ], - // const Gap(4), if (isRefundedSend) ...refundChildren, const Gap(24), if (date.isNotEmpty) ...[ @@ -631,21 +618,6 @@ class _SwapDetails extends StatelessWidget { BBText.titleLarge(date, isBold: true), const Gap(32), ], - // if (showQr) - // Center( - // child: SizedBox( - // width: 300, - // child: Column( - // children: [ - // ReceiveQRDisplay(address: invoice), - // ReceiveDisplayAddress( - // addressQr: invoice, - // fontSize: 10, - // ), - // ], - // ), - // ), - // ), const Gap(24), ], ), @@ -664,7 +636,6 @@ class _OnchainSwapDetails extends StatelessWidget { (TransactionCubit cubit) => cubit.state.tx.swapTx?.status?.status, ); final isLiq = tx.isLiquid; - // final showQr = status?.showQR ?? true; // may not be required final swap = tx.swapTx; if (swap == null) return const SizedBox.shrink(); @@ -672,8 +643,6 @@ class _OnchainSwapDetails extends StatelessWidget { ? status.getOnChainStr(swap.chainSwapDetails!.onChainType) : status.getStr(swap.isSubmarine()); - // final _ = tx.swapTx?.txid?.isNotEmpty ?? false; - final amt = swap.amountForDisplay() ?? 0; context.select( (CurrencyCubit x) => x.state.getAmountInUnits(amt, removeText: true), @@ -681,20 +650,19 @@ class _OnchainSwapDetails extends StatelessWidget { swap.isReverse(); final date = tx.getDateTimeStr(); - // swap. + final id = swap.id; final fees = swap.totalFees() ?? 0; final feesAmount = context.select( (CurrencyCubit x) => x.state.getAmountInUnits(fees, removeText: true), ); - // final invoice = swap.invoice; + final units = context.select( (CurrencyCubit cubit) => cubit.state.getUnitString(isLiquid: isLiq), ); - // status of swap should be read from WalletBloc.state.wallet.transactions - // final status = context.select((WatchTxsBloc _) => _.state.showStatus(swap)); + final fromWallet = - context.select((HomeCubit cubit) => cubit.state.getWalletFromTx(tx)); + context.select((HomeBloc cubit) => cubit.state.getWalletFromTx(tx)); final fromStatus = tx.height == null || tx.height == 0 || tx.timestamp == 0; final fromStatusStr = fromStatus ? 'Pending' : 'Confirmed'; final fromAmtStr = context.select( @@ -706,13 +674,11 @@ class _OnchainSwapDetails extends StatelessWidget { ); final toWallet = context.select( - (HomeCubit cubit) => cubit.state - .getWalletBlocById(swap.chainSwapDetails!.toWalletId) - ?.state - .wallet, + (HomeBloc cubit) => + cubit.state.getWalletById(swap.chainSwapDetails!.toWalletId), ); final isRefundedReceive = swap.isChainReceive() && swap.refundedOnchain(); - // swap.baseWallet is based on direction. btc->lbtc will have base wallet as instant, although receiving in secure + final walletReceiveRefundedTo = isRefundedReceive && swap.isLiquid() ? 'Instant' : 'Secure'; final isRefundedChainSend = @@ -742,25 +708,6 @@ class _OnchainSwapDetails extends StatelessWidget { toStatusStr = toStatus ? 'Pending' : 'Confirmed'; } } - // if (swap.claimTxid != null && isRefunded) { - // receiveTx = .getTxWithId(swap.claimTxid!); - // if (receiveTx != null) { - // toAmtStr = context.select( - // (CurrencyCubit cubit) => cubit.state.getAmountInUnits( - // receiveTx!.getAmount(sentAsTotal: true), - // removeText: true, - // ), - // ); - // toUnits = context.select( - // (CurrencyCubit cubit) => cubit.state.getUnitString(isLiquid: isLiq), - // ); - - // final toStatus = receiveTx.height == null || - // receiveTx.height == 0 || - // receiveTx.timestamp == 0; - // toStatusStr = toStatus ? 'Pending' : 'Confirmed'; - // } - // } final selfFromWalletChildren = [ const BBText.body( @@ -838,7 +785,6 @@ class _OnchainSwapDetails extends StatelessWidget { ), ]; - // Is this needed? final refundedSendChildren = [ const Gap(24), const BBText.title('Refund Tx ID'), @@ -965,7 +911,7 @@ class _OnchainSwapDetails extends StatelessWidget { ), const Gap(4), BBText.titleLarge( - date, // swap.creationTime?.toIso8601String() ?? 'In progress', + date, isBold: true, ), const Gap(24), @@ -985,7 +931,6 @@ class _OnchainSwapDetails extends StatelessWidget { claimFee ?? 0, swap.boltzFees ?? 0, ); - // show popup }, ), ], diff --git a/lib/wallet/bloc/event.dart b/lib/wallet/bloc/event.dart index 0cfe0a48..1ef9169c 100644 --- a/lib/wallet/bloc/event.dart +++ b/lib/wallet/bloc/event.dart @@ -1,11 +1,15 @@ -import 'package:bb_mobile/_model/wallet.dart'; - class WalletEvent {} -class LoadWallet extends WalletEvent { - LoadWallet(this.saveDir); +// class LoadWallet extends WalletEvent { +// LoadWallet(this.saveDir); + +// final String saveDir; +// } + +class WalletSubscribe extends WalletEvent { + WalletSubscribe(this.walletId); - final String saveDir; + final String walletId; } class SyncWallet extends WalletEvent { @@ -18,37 +22,37 @@ class RemoveInternalWallet extends WalletEvent {} class KillSync extends WalletEvent {} -class UpdateWallet extends WalletEvent { - UpdateWallet( - this.wallet, { - this.saveToStorage = true, - required this.updateTypes, - this.syncAfter = false, - this.delaySync = 500, - }); - final Wallet wallet; - final bool saveToStorage; - final bool syncAfter; - final int delaySync; - final List updateTypes; -} - -class GetBalance extends WalletEvent {} +// class UpdateWallet extends WalletEvent { +// UpdateWallet( +// this.wallet, { +// this.saveToStorage = true, +// required this.updateTypes, +// this.syncAfter = false, +// this.delaySync = 500, +// }); +// final Wallet wallet; +// final bool saveToStorage; +// final bool syncAfter; +// final int delaySync; +// final List updateTypes; +// } + +// class GetBalance extends WalletEvent {} // class GetAddresses extends WalletEvent {} -class ListTransactions extends WalletEvent {} +// class ListTransactions extends WalletEvent {} -class GetFirstAddress extends WalletEvent {} +// class GetFirstAddress extends WalletEvent {} -class GetNewAddress extends WalletEvent {} +// class GetNewAddress extends WalletEvent {} -enum UpdateWalletTypes { - load, - balance, - transactions, - swaps, - addresses, - settings, - utxos -} +// enum UpdateWalletTypess { +// load, +// balance, +// transactions, +// swaps, +// addresses, +// settings, +// utxos +// } diff --git a/lib/wallet/bloc/state.dart b/lib/wallet/bloc/state.dart index 5a5023b9..6151164f 100644 --- a/lib/wallet/bloc/state.dart +++ b/lib/wallet/bloc/state.dart @@ -1,4 +1,3 @@ -import 'package:bb_mobile/_model/address.dart'; import 'package:bb_mobile/_model/wallet.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; @@ -7,49 +6,9 @@ part 'state.freezed.dart'; @freezed class WalletState with _$WalletState { const factory WalletState({ - Wallet? wallet, - // bdk.Wallet? bdkWallet, - // List? txs, - // Balance? balance, - @Default('') String name, - @Default(true) bool loadingWallet, - @Default('') String errLoadingWallet, - @Default(false) bool loadingTxs, - @Default('') String errLoadingTxs, - @Default(false) bool loadingBalance, - @Default('') String errLoadingBalance, + required Wallet wallet, @Default(false) bool syncing, @Default('') String errSyncing, - @Default(false) bool syncingAddresses, - @Default('') String errSyncingAddresses, - @Default(false) bool savingName, - @Default('') String errSavingName, - @Default(0) int syncErrCount, - // Address? newAddress, - Address? firstAddress, - @Default(3) int loadingAttepmtsLeft, - // required WalletCreate walletCreate, }) = _WalletState; const WalletState._(); - - String balanceStr() => - ((wallet?.balance ?? 0) / 100000000).toStringAsFixed(8); - - int balanceSats() => wallet?.balance ?? 0; - - bool loading() => syncing || loadingTxs || loadingBalance; - - bool enoughBalance(int amount) => (wallet?.balance ?? 0) >= amount; - - bool isLiq() => wallet?.isLiquid() ?? false; - - // (bdk.Wallet?, Err?) getBdkWallet() { - // if (wallet!.baseWalletType != BaseWalletType.Bitcoin) return (null, Err('Invalid Wallet Type')); - // return walletCreate.getBdkWallet(wallet!); - // } - - // (lwk.Wallet?, Err?) getLwkWallet() { - // if (wallet!.baseWalletType != BaseWalletType.Liquid) return (null, Err('Invalid Wallet Type')); - // return walletCreate.getLwkWallet(wallet!); - // } } diff --git a/lib/wallet/bloc/wallet_bloc.dart b/lib/wallet/bloc/wallet_bloc.dart index 5ff30329..a1a6daae 100644 --- a/lib/wallet/bloc/wallet_bloc.dart +++ b/lib/wallet/bloc/wallet_bloc.dart @@ -1,132 +1,79 @@ import 'dart:async'; -import 'package:bb_mobile/_model/address.dart'; import 'package:bb_mobile/_model/wallet.dart'; -import 'package:bb_mobile/_pkg/logger.dart'; -import 'package:bb_mobile/_pkg/wallet/address.dart'; -import 'package:bb_mobile/_pkg/wallet/balance.dart'; -import 'package:bb_mobile/_pkg/wallet/create.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/network.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/storage.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/wallets.dart'; import 'package:bb_mobile/_pkg/wallet/sync.dart'; -import 'package:bb_mobile/_pkg/wallet/transaction.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; +import 'package:bb_mobile/_repository/wallet/internal_wallets.dart'; +import 'package:bb_mobile/_repository/wallet_service.dart'; +import 'package:bb_mobile/home/home_page.dart'; import 'package:bb_mobile/locator.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; import 'package:bb_mobile/wallet/bloc/event.dart'; import 'package:bb_mobile/wallet/bloc/state.dart'; import 'package:bloc_concurrency/bloc_concurrency.dart'; -import 'package:flutter_animate/flutter_animate.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class WalletBloc extends Bloc { WalletBloc({ - required String saveDir, required WalletSync walletSync, - required WalletsStorageRepository walletsStorageRepository, - required WalletBalance walletBalance, - required WalletAddress walletAddress, - required NetworkCubit networkCubit, - // required WatchTxsBloc swapBloc, - required NetworkRepository networkRepository, - required WalletsRepository walletsRepository, - required WalletTx walletTransactionn, - required WalletCreate walletCreatee, - bool fromStorage = true, - Wallet? wallet, - }) : _fromStorage = fromStorage, - // _swapBloc = swapBloc, - _networkCubit = networkCubit, - _walletTransactionn = walletTransactionn, - _walletCreate = walletCreatee, - _walletAddress = walletAddress, - _walletBalance = walletBalance, - _walletSync = walletSync, + required InternalWalletsRepository walletsRepository, + required Wallet wallet, + required AppWalletsRepository appWalletsRepository, + }) : _walletSync = walletSync, _walletsRepository = walletsRepository, - _networkRepository = networkRepository, - _walletsStorageRepository = walletsStorageRepository, + _appWalletsRepository = appWalletsRepository, super(WalletState(wallet: wallet)) { - on(_loadWallet); on(_syncWallet, transformer: droppable()); on(_removeInternalWallet); on(_killSync); - on(_updateWallet, transformer: sequential()); - on(_getBalance); - on(_listTransactions); - on(_getFirstAddress); + on( + (event, emit) async { + final walletService = _appWalletsRepository.getWalletServiceById( + event.walletId, + ); + if (walletService == null) { + _walletServiceFromTempWallets = createWalletService(wallet: wallet) + ..loadWallet(syncAfter: true); + + await emit.forEach( + _walletServiceFromTempWallets!.dataStream, + onData: (WalletServiceData data) { + return state.copyWith( + wallet: data.wallet, + syncing: data.syncing, + ); + }, + ); + return; + } + + await emit.forEach( + walletService.dataStream, + onData: (WalletServiceData data) { + return state.copyWith( + wallet: data.wallet, + syncing: data.syncing, + ); + }, + onError: (error, stackTrace) { + return state.copyWith(errSyncing: error.toString()); + }, + ); + }, + ); - add(LoadWallet(saveDir)); + add(WalletSubscribe(wallet.id)); } - final WalletsStorageRepository _walletsStorageRepository; - final NetworkRepository _networkRepository; - final WalletsRepository _walletsRepository; - + final InternalWalletsRepository _walletsRepository; final WalletSync _walletSync; - final WalletBalance _walletBalance; - final WalletAddress _walletAddress; - final WalletCreate _walletCreate; - final WalletTx _walletTransactionn; - - final NetworkCubit _networkCubit; - // final WatchTxsBloc _swapBloc; - - final bool _fromStorage; - - @override - Future close() { - _walletsRepository.removeWallet( - state.wallet!.baseWalletType, - state.wallet!.id, - ); - return super.close(); - } - - Future _loadWallet(LoadWallet event, Emitter emit) async { - emit(state.copyWith(loadingWallet: true, errLoadingWallet: '')); - - final (wallet, err) = await _walletCreate.loadPublicWallet( - saveDir: event.saveDir, - wallet: state.wallet, - network: _networkCubit.state.getBBNetwork(), - ); - if (err != null) { - emit( - state.copyWith( - loadingWallet: false, - errLoadingWallet: err.toString(), - ), - ); - return; - } - - emit( - state.copyWith( - loadingWallet: false, - errLoadingWallet: '', - name: wallet!.name ?? '', - loadingAttepmtsLeft: 3, - ), - ); - - add( - UpdateWallet( - wallet, - saveToStorage: _fromStorage, - updateTypes: [UpdateWalletTypes.load], - ), - ); - await Future.delayed(50.ms); - add(GetFirstAddress()); - await Future.delayed(200.ms); - add(SyncWallet()); - } + final AppWalletsRepository _appWalletsRepository; + WalletService? _walletServiceFromTempWallets; FutureOr _removeInternalWallet( RemoveInternalWallet event, Emitter emit, ) { - _walletsRepository.removeBdkWallet(state.wallet?.id ?? ''); + _walletsRepository.removeBdkWallet(state.wallet.id); } FutureOr _killSync(KillSync event, Emitter emit) { @@ -135,295 +82,26 @@ class WalletBloc extends Bloc { } Future _syncWallet(SyncWallet event, Emitter emit) async { - if (state.wallet == null) return; if (state.syncing) return; - if (state.errLoadingWallet.isNotEmpty && state.loadingAttepmtsLeft > 0) { - emit(state.copyWith(loadingAttepmtsLeft: state.loadingAttepmtsLeft - 1)); - add(LoadWallet(state.wallet!.getWalletStorageString())); - return; - } - // if (walletIsLoaded) - // final (wallet, _) = await _walletsStorageRepository.readWallet( - // walletHashId: state.wallet!.id, - // ); - // if (wallet != null) - // emit( - // state.copyWith( - // wallet: wallet, - // ), - // ); - - emit( - state.copyWith( - syncing: true, - errSyncing: '', - ), - ); - final errNetwork = _networkRepository.checkNetworks(); - if (errNetwork != null) { - await _networkCubit.loadNetworks(); - await Future.delayed(const Duration(milliseconds: 300)); - final errNetwork2 = _networkRepository.checkNetworks(); - if (errNetwork2 != null) { - emit(state.copyWith(syncing: false)); - return; - } - } - - await Future.delayed(100.ms); - final isLiq = state.isLiq() ? 'Instant' : 'Secure'; - locator().log( - 'Start $isLiq Wallet Sync for ${state.wallet?.id ?? ''}', - printToConsole: true, - ); - final err = await _walletSync.syncWallet(state.wallet!); - locator().log( - 'End $isLiq Wallet Sync for ${state.wallet?.id ?? ''}', - printToConsole: true, - ); - emit( - state.copyWith( - errSyncing: err.toString(), - syncing: false, - ), - ); - if (err != null) { - if (err.message.toLowerCase().contains('panic') && - state.syncErrCount < 5) { - await _networkCubit.loadNetworks(); - await Future.delayed(const Duration(milliseconds: 300)); - emit(state.copyWith(syncErrCount: state.syncErrCount + 1)); - add(SyncWallet()); - return; - } - - locator().log(err.toString()); - return; - } - - emit(state.copyWith(syncing: false, syncErrCount: 0)); - await Future.delayed(100.ms); - - if (!_fromStorage) add(GetFirstAddress()); - add(GetBalance()); - - // emit(state.copyWith(syncing: false)); - } - - Future _getBalance(GetBalance event, Emitter emit) async { - if (state.wallet == null) return; - - emit(state.copyWith(loadingBalance: true, errLoadingBalance: '')); - - final (w, err) = await _walletBalance.getBalance(state.wallet!); - if (err != null) { - emit( - state.copyWith( - errLoadingBalance: err.toString(), - loadingBalance: false, - ), - ); - return; - } - - final (wallet, _) = w!; - - add( - UpdateWallet( - wallet, - saveToStorage: _fromStorage, - updateTypes: [UpdateWalletTypes.balance], - ), - ); - - emit( - state.copyWith( - loadingBalance: false, - ), - ); - - add(ListTransactions()); + await _appWalletsRepository + .getWalletServiceById(state.wallet.id) + ?.syncWallet(); } +} - Future _listTransactions( - ListTransactions event, - Emitter emit, - ) async { - if (state.wallet == null) return; - - emit(state.copyWith(loadingTxs: true, errLoadingWallet: '')); - - final (wallet, errTxs) = - await _walletTransactionn.getTransactions(state.wallet!); - - if (errTxs != null) { - emit( - state.copyWith( - errLoadingWallet: errTxs.toString(), - loadingTxs: false, - ), - ); - return; - } - - add( - UpdateWallet( - wallet!, - saveToStorage: _fromStorage, - updateTypes: [ - UpdateWalletTypes.addresses, - UpdateWalletTypes.transactions, - UpdateWalletTypes.utxos, - ], - ), - ); - emit( - state.copyWith( - loadingTxs: false, - ), - ); - - await Future.delayed(100.ms); - - // _swapBloc.add(WatchWallets(isTestnet: state.wallet!)); - } - - Future _getFirstAddress( - GetFirstAddress event, - Emitter emit, - ) async { - if (state.wallet == null) return; - - final (address, err) = - await _walletAddress.peekIndex(wallet: state.wallet!, idx: 0); - if (err != null) { - emit(state.copyWith(errSyncingAddresses: err.toString())); - return; - } - - emit( - state.copyWith( - firstAddress: Address( - address: address!, - index: 0, - kind: AddressKind.deposit, - state: AddressStatus.unused, - ), - ), - ); - } - - Future _updateWallet( - UpdateWallet event, - Emitter emit, - ) async { - if (!event.saveToStorage) { - emit(state.copyWith(wallet: event.wallet)); - return; - } - - if (event.updateTypes.contains(UpdateWalletTypes.load)) { - final err = await _walletsStorageRepository.updateWallet( - event.wallet, - ); - if (err != null) locator().log(err.toString()); - - emit(state.copyWith(wallet: event.wallet)); - return; - } - - final eventWallet = event.wallet; - var (storageWallet, errr) = await _walletsStorageRepository.readWallet( - walletHashId: state.wallet!.getWalletStorageString(), - ); - if (errr != null) locator().log(errr.toString()); - if (storageWallet == null) return; - - for (final eventType in event.updateTypes) { - switch (eventType) { - case UpdateWalletTypes.load: - break; - case UpdateWalletTypes.balance: - if (eventWallet.balance != null) { - storageWallet = storageWallet!.copyWith( - balance: eventWallet.balance, - fullBalance: eventWallet.fullBalance, - ); - } - case UpdateWalletTypes.transactions: - if (eventWallet.transactions.isNotEmpty) { - storageWallet = storageWallet!.copyWith( - transactions: eventWallet.transactions, - ); - } - - if (eventWallet.unsignedTxs.isNotEmpty) { - storageWallet = storageWallet!.copyWith( - unsignedTxs: eventWallet.unsignedTxs, - ); - } - - case UpdateWalletTypes.swaps: - storageWallet = storageWallet!.copyWith( - swaps: eventWallet.swaps, - revKeyIndex: eventWallet.revKeyIndex, - subKeyIndex: eventWallet.subKeyIndex, - ); - - case UpdateWalletTypes.addresses: - if (eventWallet.myAddressBook.isNotEmpty) { - storageWallet = storageWallet!.copyWith( - myAddressBook: eventWallet.myAddressBook, - ); - } - - if (eventWallet.externalAddressBook != null && - eventWallet.externalAddressBook!.isNotEmpty) { - storageWallet = storageWallet!.copyWith( - externalAddressBook: eventWallet.externalAddressBook, - ); - } - - if (eventWallet.lastGeneratedAddress != null) { - storageWallet = storageWallet!.copyWith( - lastGeneratedAddress: eventWallet.lastGeneratedAddress, - ); - } - - case UpdateWalletTypes.utxos: - storageWallet = storageWallet!.copyWith(utxos: eventWallet.utxos); - - case UpdateWalletTypes.settings: - if (eventWallet.backupTested != storageWallet!.backupTested) { - storageWallet = storageWallet.copyWith( - backupTested: eventWallet.backupTested, - ); - } - - if (eventWallet.name != storageWallet.name) { - storageWallet = storageWallet.copyWith( - name: eventWallet.name, - ); - } +WalletBloc createOrRetreiveWalletBloc(String walletId, {Wallet? wallet}) { + final existIdx = locator() + .state + .indexWhere((_) => _.state.wallet.id == walletId); - if (eventWallet.lastBackupTested != null && - eventWallet.lastBackupTested != storageWallet.lastBackupTested) { - storageWallet = storageWallet.copyWith( - lastBackupTested: eventWallet.lastBackupTested, - ); - } - } - } + if (existIdx != -1) return locator().state[existIdx]; - final err = await _walletsStorageRepository.updateWallet( - storageWallet!, - ); - if (err != null) { - locator().log(err.toString(), printToConsole: true); - } - emit(state.copyWith(wallet: storageWallet)); - await Future.delayed(event.delaySync.ms); - if (event.syncAfter) add(SyncWallet()); - } + final w = wallet ?? locator().getWalletById(walletId); + return WalletBloc( + walletSync: locator(), + walletsRepository: locator(), + appWalletsRepository: locator(), + wallet: w!, + ); } diff --git a/lib/wallet/details.dart b/lib/wallet/details.dart index 780b2dac..43dcd8a9 100644 --- a/lib/wallet/details.dart +++ b/lib/wallet/details.dart @@ -12,14 +12,14 @@ import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; class WalletDetailsPage extends StatelessWidget { - const WalletDetailsPage({super.key, required this.walletBloc}); + const WalletDetailsPage({super.key, required this.wallet}); - final WalletBloc walletBloc; + final String wallet; @override Widget build(BuildContext context) { return BlocProvider.value( - value: walletBloc, + value: createOrRetreiveWalletBloc(wallet), child: Scaffold( appBar: AppBar( automaticallyImplyLeading: false, @@ -50,23 +50,23 @@ class _Screen extends StatelessWidget { @override Widget build(BuildContext context) { - final fingerPrint = context - .select((WalletBloc _) => _.state.wallet?.sourceFingerprint ?? ''); + final fingerPrint = + context.select((WalletBloc _) => _.state.wallet.sourceFingerprint); final descriptorCombined = context.select( - (WalletBloc _) => _.state.wallet?.getDescriptorCombined() ?? '', + (WalletBloc _) => _.state.wallet.getDescriptorCombined(), ); final descriptor = context.select( - (WalletBloc _) => _.state.wallet?.externalPublicDescriptor ?? '', + (WalletBloc _) => _.state.wallet.externalPublicDescriptor, ); final pub = keyFromDescriptor(descriptor); final scriptType = - context.select((WalletBloc _) => _.state.wallet!.scriptType); + context.select((WalletBloc _) => _.state.wallet.scriptType); final addressTypeStr = scriptTypeString(scriptType); - final network = context.select((WalletBloc _) => _.state.wallet!.network); + final network = context.select((WalletBloc _) => _.state.wallet.network); - final derivationPath = context - .select((WalletBloc _) => _.state.wallet?.derivationPathString() ?? ''); + final derivationPath = + context.select((WalletBloc _) => _.state.wallet.derivationPathString()); final slipKey = convertToSlipPub(scriptType, network, pub); final showFingerprint = !fingerPrint.toLowerCase().contains('unknown'); diff --git a/lib/wallet/wallet_card.dart b/lib/wallet/wallet_card.dart index 44c5033e..206f9637 100644 --- a/lib/wallet/wallet_card.dart +++ b/lib/wallet/wallet_card.dart @@ -1,7 +1,7 @@ import 'package:bb_mobile/_model/wallet.dart'; import 'package:bb_mobile/_ui/components/text.dart'; import 'package:bb_mobile/currency/bloc/currency_cubit.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; +import 'package:bb_mobile/network/bloc/network_bloc.dart'; import 'package:bb_mobile/settings/bloc/lighting_cubit.dart'; import 'package:bb_mobile/styles.dart'; import 'package:bb_mobile/wallet/bloc/wallet_bloc.dart'; @@ -21,7 +21,7 @@ class HomeCard extends StatelessWidget { @override Widget build(BuildContext context) { final wallet = context.select((WalletBloc x) => x.state.wallet); - if (wallet == null) return const SizedBox.shrink(); + // if (wallet == null) return const SizedBox.shrink(); final (_, info) = WalletCardDetails.cardDetails(context, wallet); final keyName = 'home_card_$info'; @@ -77,17 +77,17 @@ class WalletCardDetails extends StatelessWidget { @override Widget build(BuildContext context) { final wallet = context.select((WalletBloc x) => x.state.wallet); - if (wallet == null) return const SizedBox.shrink(); + // if (wallet == null) return const SizedBox.shrink(); final (color, _) = cardDetails(context, wallet); - final name = context.select((WalletBloc x) => x.state.wallet?.name); - final fingerprint = context - .select((WalletBloc x) => x.state.wallet?.sourceFingerprint ?? ''); + final name = context.select((WalletBloc x) => x.state.wallet.name); + final fingerprint = + context.select((WalletBloc x) => x.state.wallet.sourceFingerprint); final walletStr = - context.select((WalletBloc x) => x.state.wallet?.getWalletTypeStr()); + context.select((WalletBloc x) => x.state.wallet.getWalletTypeStr()); - final sats = context.select((WalletBloc x) => x.state.balanceSats()); + final sats = context.select((WalletBloc x) => x.state.wallet.balanceSats()); final balance = context.select( (CurrencyCubit x) => x.state.getAmountInUnits(sats, removeText: true), @@ -100,7 +100,7 @@ class WalletCardDetails extends StatelessWidget { context.select((CurrencyCubit x) => x.state.defaultFiatCurrency); final fiatAmt = context - .select((NetworkCubit x) => x.state.calculatePrice(sats, fiatCurrency)); + .select((NetworkBloc x) => x.state.calculatePrice(sats, fiatCurrency)); return DecoratedBox( decoration: BoxDecoration( @@ -128,8 +128,8 @@ class WalletCardDetails extends StatelessWidget { padding: const EdgeInsets.only(top: 4), child: IconButton( onPressed: () { - final walletBloc = context.read(); - context.push('/wallet-settings', extra: walletBloc); + final walletBloc = context.read(); + context.push('/wallet-settings', extra: walletBloc.id); }, color: context.colour.onPrimary, icon: const FaIcon( @@ -208,7 +208,7 @@ class WalletCardDetails extends StatelessWidget { child: Opacity( opacity: 0.7, child: BBText.bodySmall( - walletStr ?? '', + walletStr, onSurface: true, isBold: true, ), diff --git a/lib/wallet/wallet_page.dart b/lib/wallet/wallet_page.dart index 372e1ffa..deb21b42 100644 --- a/lib/wallet/wallet_page.dart +++ b/lib/wallet/wallet_page.dart @@ -13,10 +13,29 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; -class WalletPage extends StatelessWidget { - const WalletPage({super.key, required this.walletBloc}); +class WalletPage extends StatefulWidget { + const WalletPage({super.key, required this.wallet}); - final WalletBloc walletBloc; + final String wallet; + + @override + State createState() => _WalletPageState(); +} + +class _WalletPageState extends State { + late WalletBloc walletBloc; + + @override + void initState() { + walletBloc = createOrRetreiveWalletBloc(widget.wallet); + super.initState(); + } + + @override + void dispose() { + // walletBloc.close(); + super.dispose(); + } @override Widget build(BuildContext context) { @@ -44,7 +63,7 @@ class _Screen extends StatelessWidget { @override Widget build(BuildContext context) { final backupTested = - context.select((WalletBloc x) => x.state.wallet?.backupTested ?? false); + context.select((WalletBloc x) => x.state.wallet.backupTested); return RefreshIndicator( onRefresh: () async { @@ -69,7 +88,7 @@ class _Screen extends StatelessWidget { ), BottomCenter( child: WalletActionButtons( - walletBloc: context.read(), + wallet: context.read().state.wallet, ), ), ], @@ -96,11 +115,11 @@ class ActionsRow extends StatelessWidget { @override Widget build(BuildContext context) { final backupTested = - context.select((WalletBloc x) => x.state.wallet?.backupTested ?? false); + context.select((WalletBloc x) => x.state.wallet.backupTested); final watchonly = - context.select((WalletBloc x) => x.state.wallet?.watchOnly() ?? false); + context.select((WalletBloc x) => x.state.wallet.watchOnly()); final isInstant = - context.select((WalletBloc x) => x.state.wallet?.isInstant() ?? false); + context.select((WalletBloc x) => x.state.wallet.isInstant()); final isdarkMode = context.select( (Lighting x) => x.state == ThemeLighting.dark, @@ -120,7 +139,10 @@ class ActionsRow extends StatelessWidget { isRed: !backupTested, onPressed: () { final walletBloc = context.read(); - context.push('/wallet-settings/open-backup', extra: walletBloc); + context.push( + '/wallet-settings/open-backup', + extra: walletBloc.state.wallet.id, + ); }, ), BBButton.text( @@ -131,7 +153,7 @@ class ActionsRow extends StatelessWidget { ? context.push('/information') : context.push( '/wallet/details', - extra: context.read(), + extra: context.read().state.wallet.id, ); }, ), @@ -139,8 +161,8 @@ class ActionsRow extends StatelessWidget { label: 'Settings', isBlue: false, onPressed: () { - final walletBloc = context.read(); - context.push('/wallet-settings', extra: walletBloc); + final wallet = context.read().state.wallet; + context.push('/wallet-settings', extra: wallet.id); }, ), ], diff --git a/lib/wallet/wallet_txs.dart b/lib/wallet/wallet_txs.dart index 581eccfa..b83b33a5 100644 --- a/lib/wallet/wallet_txs.dart +++ b/lib/wallet/wallet_txs.dart @@ -1,13 +1,12 @@ import 'package:bb_mobile/_model/transaction.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; +import 'package:bb_mobile/_repository/network_repository.dart'; import 'package:bb_mobile/_ui/components/button.dart'; import 'package:bb_mobile/_ui/components/indicators.dart'; import 'package:bb_mobile/_ui/components/text.dart'; import 'package:bb_mobile/_ui/warning.dart'; import 'package:bb_mobile/currency/bloc/currency_cubit.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; -import 'package:bb_mobile/network/bloc/network_cubit.dart'; import 'package:bb_mobile/settings/bloc/lighting_cubit.dart'; -import 'package:bb_mobile/wallet/bloc/event.dart'; import 'package:bb_mobile/wallet/bloc/wallet_bloc.dart'; import 'package:extra_alignments/extra_alignments.dart'; import 'package:flutter/material.dart'; @@ -25,12 +24,12 @@ class WalletTxList extends StatelessWidget { // final syncing = context.select((WalletBloc x) => x.state.syncing); // final loading = context.select((WalletBloc x) => x.state.loadingTxs); // final loadingBal = context.select((WalletBloc x) => x.state.loadingBalance); - final loading = context.select((WalletBloc x) => x.state.loading()); + final loading = context.select((WalletBloc x) => x.state.syncing); - final confirmedTXs = context - .select((WalletBloc x) => x.state.wallet?.getConfirmedTxs() ?? []); + final confirmedTXs = + context.select((WalletBloc x) => x.state.wallet.getConfirmedTxs()); final pendingTXs = - context.select((WalletBloc x) => x.state.wallet?.getPendingTxs() ?? []); + context.select((WalletBloc x) => x.state.wallet.getPendingTxs()); final zeroPending = pendingTXs.isEmpty; if (loading && confirmedTXs.isEmpty && pendingTXs.isEmpty) { @@ -69,13 +68,12 @@ class WalletTxList extends StatelessWidget { fontSize: 11, onPressed: () { final network = - context.read().state.getBBNetwork(); + context.read().getBBNetwork; final wallets = context - .read() - .state - .walletBlocsFromNetwork(network); + .read() + .walletServiceFromNetwork(network); for (final wallet in wallets) { - wallet.add(SyncWallet()); + wallet.syncWallet(); } }, ), @@ -134,7 +132,10 @@ class HomeTxItem extends StatelessWidget { @override Widget build(BuildContext context) { - final label = tx.label ?? ''; + // final label = tx.label ?? ''; + final label = (tx.label != null && tx.label!.length > 20) + ? '${tx.label!.substring(0, 20)}...' + : tx.label ?? ''; final isReceive = tx.isReceived(); var amount = context.select( @@ -228,9 +229,9 @@ class BackupAlertBanner extends StatelessWidget { @override Widget build(BuildContext context) { - final _ = context.select((WalletBloc x) => x.state.wallet); + final wallet = context.select((WalletBloc x) => x.state.wallet); final backupTested = - context.select((WalletBloc x) => x.state.wallet?.backupTested ?? true); + context.select((WalletBloc x) => x.state.wallet.backupTested); if (backupTested) return const SizedBox.shrink(); @@ -238,7 +239,7 @@ class BackupAlertBanner extends StatelessWidget { onTap: () { context.push( '/wallet-settings/open-backup', - extra: context.read(), + extra: wallet.id, ); }, info: 'Back up your wallet! Tap to test backup.', diff --git a/lib/wallet_settings/accounting.dart b/lib/wallet_settings/accounting.dart index 932c8743..589a86b6 100644 --- a/lib/wallet_settings/accounting.dart +++ b/lib/wallet_settings/accounting.dart @@ -8,14 +8,14 @@ import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; class AccountingPage extends StatelessWidget { - const AccountingPage({super.key, required this.walletBloc}); + const AccountingPage({super.key, required this.wallet}); - final WalletBloc walletBloc; + final String wallet; @override Widget build(BuildContext context) { return BlocProvider.value( - value: walletBloc, + value: createOrRetreiveWalletBloc(wallet), child: Scaffold( appBar: AppBar( automaticallyImplyLeading: false, @@ -37,38 +37,45 @@ class _Screen extends StatelessWidget { @override Widget build(BuildContext context) { - final walletName = context.select((WalletBloc _) => _.state.wallet?.name ?? ''); - final totalBalance = context.select((WalletBloc _) => _.state.wallet?.fullBalance?.total ?? 0); + final walletName = + context.select((WalletBloc _) => _.state.wallet.name ?? ''); + final totalBalance = context + .select((WalletBloc _) => _.state.wallet.fullBalance?.total ?? 0); final totalStr = context.select( - (CurrencyCubit _) => _.state.getAmountInUnits(totalBalance, removeText: true), + (CurrencyCubit _) => + _.state.getAmountInUnits(totalBalance, removeText: true), ); - final confirmedBalance = - context.select((WalletBloc _) => _.state.wallet?.fullBalance?.confirmed ?? 0); + final confirmedBalance = context + .select((WalletBloc _) => _.state.wallet.fullBalance?.confirmed ?? 0); final confirmedStr = context.select( - (CurrencyCubit _) => _.state.getAmountInUnits(confirmedBalance, removeText: true), + (CurrencyCubit _) => + _.state.getAmountInUnits(confirmedBalance, removeText: true), + ); + final unconfirmedBalance = context.select( + (WalletBloc _) => _.state.wallet.fullBalance?.untrustedPending ?? 0, ); - final unconfirmedBalance = - context.select((WalletBloc _) => _.state.wallet?.fullBalance?.untrustedPending ?? 0); final unconfirmedStr = context.select( - (CurrencyCubit _) => _.state.getAmountInUnits(unconfirmedBalance, removeText: true), + (CurrencyCubit _) => + _.state.getAmountInUnits(unconfirmedBalance, removeText: true), ); final amtSent = context.select( - (WalletBloc cubit) => cubit.state.wallet!.totalSent(), + (WalletBloc cubit) => cubit.state.wallet.totalSent(), ); final sentStr = context.select( (CurrencyCubit _) => _.state.getAmountInUnits(amtSent, removeText: true), ); final amtReceived = context.select( - (WalletBloc cubit) => cubit.state.wallet!.totalReceived(), + (WalletBloc cubit) => cubit.state.wallet.totalReceived(), ); final receivedStr = context.select( - (CurrencyCubit _) => _.state.getAmountInUnits(amtReceived, removeText: true), + (CurrencyCubit _) => + _.state.getAmountInUnits(amtReceived, removeText: true), ); final txsReceivedCount = context.select( - (WalletBloc _) => _.state.wallet?.txReceivedCount() ?? 0, + (WalletBloc _) => _.state.wallet.txReceivedCount(), ); final txsSentCount = context.select( - (WalletBloc _) => _.state.wallet?.txSentCount() ?? 0, + (WalletBloc _) => _.state.wallet.txSentCount(), ); final units = context.select((CurrencyCubit x) => x.state.getUnitString()); @@ -78,7 +85,7 @@ class _Screen extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - const BBText.title('Wallet Name'), + const BBText.title('WalletBloc Name'), BBText.body(walletName, isBold: true), const Gap(16), const BBText.title('Total Balance'), @@ -109,10 +116,20 @@ class _Screen extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ - const BBText.title('Total amount sent', textAlign: TextAlign.right), - BBText.body('$sentStr $units', isBold: true, textAlign: TextAlign.right), + const BBText.title( + 'Total amount sent', + textAlign: TextAlign.right, + ), + BBText.body( + '$sentStr $units', + isBold: true, + textAlign: TextAlign.right, + ), const Gap(16), - const BBText.title('Transactions Sent', textAlign: TextAlign.right), + const BBText.title( + 'Transactions Sent', + textAlign: TextAlign.right, + ), BBText.body( '$txsSentCount', isBold: true, diff --git a/lib/wallet_settings/addresses.dart b/lib/wallet_settings/addresses.dart index 924173de..315bb834 100644 --- a/lib/wallet_settings/addresses.dart +++ b/lib/wallet_settings/addresses.dart @@ -39,7 +39,7 @@ class AddressesScreen extends HookWidget { final selectedOption = useState(0); var addresses = - context.select((WalletBloc cubit) => cubit.state.wallet!.myAddressBook); + context.select((WalletBloc cubit) => cubit.state.wallet.myAddressBook); addresses = addresses.toList() ..sort((a, b) => (b.index ?? 0).compareTo(a.index ?? 0)); diff --git a/lib/wallet_settings/backup.dart b/lib/wallet_settings/backup.dart index eb45f24b..754aa526 100644 --- a/lib/wallet_settings/backup.dart +++ b/lib/wallet_settings/backup.dart @@ -17,24 +17,37 @@ class InfoRead extends Cubit { void unread() => emit(false); } -class BackupPage extends StatelessWidget { +class BackupPage extends StatefulWidget { const BackupPage({ super.key, - required this.walletBloc, - required this.walletSettings, + required this.wallet, }); - final WalletBloc walletBloc; - final WalletSettingsCubit walletSettings; + final String wallet; @override - Widget build(BuildContext context) { + State createState() => _BackupPageState(); +} + +class _BackupPageState extends State { + late WalletBloc walletBloc; + late WalletSettingsCubit walletSettings; + @override + void initState() { + walletBloc = createOrRetreiveWalletBloc(widget.wallet); + walletSettings = createWalletSettingsCubit(widget.wallet); + walletSettings.loadBackupClicked(); + super.initState(); + } + + @override + Widget build(BuildContext context) { return MultiBlocProvider( providers: [ BlocProvider.value(value: walletBloc), - BlocProvider.value(value: walletSettings), + BlocProvider(create: (BuildContext context) => walletSettings), BlocProvider.value(value: InfoRead()), ], child: const _Screen(), @@ -91,10 +104,10 @@ class BackUpInfoScreen extends StatelessWidget { @override Widget build(BuildContext context) { final lastBackupTested = context - .select((WalletBloc cubit) => cubit.state.wallet!.lastBackupTested); + .select((WalletBloc cubit) => cubit.state.wallet.lastBackupTested); final hasPassphrase = context - .select((WalletBloc cubit) => cubit.state.wallet!.hasPassphrase()); + .select((WalletBloc cubit) => cubit.state.wallet.hasPassphrase()); final instructions = backupInstructions(hasPassphrase); return SingleChildScrollView( child: Padding( @@ -224,10 +237,11 @@ class BackupScreen extends StatelessWidget { // ..pop() .push( '/wallet-settings/test-backup', - extra: ( - context.read(), - context.read(), - ), + extra: context.read().state.wallet.id, + // ( + // context.read(), + // context.read(), + // ), ); // context.pop(); // TestBackupScreen.openPopup(context); diff --git a/lib/wallet_settings/bloc/state.dart b/lib/wallet_settings/bloc/state.dart index 61630433..d87946bb 100644 --- a/lib/wallet_settings/bloc/state.dart +++ b/lib/wallet_settings/bloc/state.dart @@ -1,4 +1,3 @@ -import 'package:bb_mobile/_model/wallet.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'state.freezed.dart'; @@ -6,7 +5,7 @@ part 'state.freezed.dart'; @freezed class WalletSettingsState with _$WalletSettingsState { const factory WalletSettingsState({ - required Wallet wallet, + // required Wallet wallet, @Default('') String name, /** * diff --git a/lib/wallet_settings/bloc/wallet_settings_cubit.dart b/lib/wallet_settings/bloc/wallet_settings_cubit.dart index cbea47c8..67ce583f 100644 --- a/lib/wallet_settings/bloc/wallet_settings_cubit.dart +++ b/lib/wallet_settings/bloc/wallet_settings_cubit.dart @@ -4,38 +4,54 @@ import 'package:bb_mobile/_model/bip329_label.dart'; import 'package:bb_mobile/_model/wallet.dart'; import 'package:bb_mobile/_pkg/file_storage.dart'; import 'package:bb_mobile/_pkg/wallet/labels.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/sensitive_storage.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/storage.dart'; import 'package:bb_mobile/_pkg/wallet/update.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; +import 'package:bb_mobile/_repository/wallet/sensitive_wallet_storage.dart'; +import 'package:bb_mobile/_repository/wallet/wallet_storage.dart'; +import 'package:bb_mobile/_repository/wallet_service.dart'; import 'package:bb_mobile/_ui/alert.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; -import 'package:bb_mobile/wallet/bloc/event.dart'; -import 'package:bb_mobile/wallet/bloc/wallet_bloc.dart'; +import 'package:bb_mobile/locator.dart'; import 'package:bb_mobile/wallet_settings/bloc/state.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:path_provider/path_provider.dart'; +WalletSettingsCubit createWalletSettingsCubit( + String wallet, +) { + final w = locator().getWalletById(wallet); + return WalletSettingsCubit( + wallet: w!, + appWalletsRepository: locator(), + walletsStorageRepository: locator(), + walletSensRepository: locator(), + fileStorage: locator(), + ); +} + class WalletSettingsCubit extends Cubit { WalletSettingsCubit({ required Wallet wallet, - required WalletBloc walletBloc, - required HomeCubit homeCubit, + required AppWalletsRepository appWalletsRepository, required WalletsStorageRepository walletsStorageRepository, required WalletSensitiveStorageRepository walletSensRepository, required FileStorage fileStorage, }) : _fileStorage = fileStorage, _walletSensRepository = walletSensRepository, - _homeCubit = homeCubit, _walletsStorageRepository = walletsStorageRepository, - _walletBloc = walletBloc, - super(WalletSettingsState(wallet: wallet)); + _appWalletsRepository = appWalletsRepository, + _wallet = wallet, + super( + const WalletSettingsState(), + ); - final WalletBloc _walletBloc; final WalletsStorageRepository _walletsStorageRepository; - final HomeCubit _homeCubit; + final WalletSensitiveStorageRepository _walletSensRepository; final FileStorage _fileStorage; + final AppWalletsRepository _appWalletsRepository; + + final Wallet _wallet; void changeName(String name) { emit(state.copyWith(name: name)); @@ -44,27 +60,15 @@ class WalletSettingsCubit extends Cubit { Future saveNameClicked() async { emit(state.copyWith(savingName: true, errSavingName: '')); - final wallet = state.wallet.copyWith(name: state.name); - // final err = await walletsStorageRepository.updateWallet( - // wallet: wallet, - // hiveStore: hiveStorage, - // ); - // if (err != null) { - // emit( - // state.copyWith( - // errSavingName: err.toString(), - // savingName: false, - // ), - // ); - // return; - // } - _walletBloc - .add(UpdateWallet(wallet, updateTypes: [UpdateWalletTypes.settings])); + final wallet = _wallet.copyWith(name: state.name); + + await _appWalletsRepository + .getWalletServiceById(wallet.id) + ?.updateWallet(wallet, updateTypes: [UpdateWalletTypes.settings]); emit( state.copyWith( savingName: false, - wallet: wallet, savedName: true, ), ); @@ -74,20 +78,9 @@ class WalletSettingsCubit extends Cubit { emit(state.copyWith(savedName: false)); } - // void wordChanged(int index, String word) { - // final words = state.mnemonic.toList(); - // words[index] = word; - // emit( - // state.copyWith( - // mnemonic: words, - // errTestingBackup: '', - // ), - // ); - // } - Future loadBackupClicked() async { final (seed, err) = await _walletSensRepository.readSeed( - fingerprintIndex: state.wallet.getRelatedSeedStorageString(), + fingerprintIndex: _wallet.getRelatedSeedStorageString(), ); if (err != null) { emit(state.copyWith(errTestingBackup: err.toString())); @@ -102,9 +95,8 @@ class WalletSettingsCubit extends Cubit { testMnemonicOrder: [], mnemonic: words, errTestingBackup: '', - password: seed - .getPassphraseFromIndex(state.wallet.sourceFingerprint) - .passphrase, + password: + seed.getPassphraseFromIndex(_wallet.sourceFingerprint).passphrase, shuffledMnemonic: shuffled, ), ); @@ -131,8 +123,6 @@ class WalletSettingsCubit extends Cubit { ); emit(state.copyWith(testMnemonicOrder: testMnemonic)); - - // if (testMnemonic.length == 12) testBackupClicked(); } void word24Clicked(int shuffledIdx) { @@ -156,8 +146,6 @@ class WalletSettingsCubit extends Cubit { ); emit(state.copyWith(testMnemonicOrder: testMnemonic)); - - // if (testMnemonic.length == 24) testBackupClicked(); } Future invalidTestOrderClicked() async { @@ -182,45 +170,6 @@ class WalletSettingsCubit extends Cubit { ); } - // void testingOrderCompleted() async { - // final words = state.testMneString(); - // final mne = state.mnemonic.join(' '); - // if (words != mne) { - // return; - // } - // if (state.password != state.testBackupPassword) { - // return; - // } - // emit(state.copyWith(testingBackup: true, errTestingBackup: '')); - - // final wallet = state.wallet.copyWith(backupTested: true); - - // // final updateErr = await walletsStorageRepository.updateWallet( - // // wallet: wallet, - // // hiveStore: hiveStorage, - // // ); - // // if (updateErr != null) { - // // emit( - // // state.copyWith( - // // errTestingBackup: updateErr.toString(), - // // testingBackup: false, - // // ), - // // ); - // // return; - // // } - - // walletBloc.add(UpdateWallet(wallet, updateTypes: [UpdateWalletTypes.settings])); - - // emit( - // state.copyWith( - // backupTested: true, - // testingBackup: false, - // wallet: wallet, - // ), - // ); - // clearSensitive(); - // } - void changePassword(String password) { emit( state.copyWith( @@ -235,7 +184,7 @@ class WalletSettingsCubit extends Cubit { final words = state.testMneString(); final password = state.testBackupPassword; final (seed, err) = await _walletSensRepository.readSeed( - fingerprintIndex: state.wallet.getRelatedSeedStorageString(), + fingerprintIndex: _wallet.getRelatedSeedStorageString(), ); if (err != null) { @@ -249,11 +198,10 @@ class WalletSettingsCubit extends Cubit { } final mne = seed!.mnemonic == words; - // final pp = seed.getPassphraseFromIndex(state.wallet.sourceFingerprint).passphrase; - final psd = seed - .getPassphraseFromIndex(state.wallet.sourceFingerprint) - .passphrase == - password; + + final psd = + seed.getPassphraseFromIndex(_wallet.sourceFingerprint).passphrase == + password; if (!mne) { { emit( @@ -275,34 +223,17 @@ class WalletSettingsCubit extends Cubit { return; } - final wallet = state.wallet - .copyWith(backupTested: true, lastBackupTested: DateTime.now()); - - // final updateErr = await walletsStorageRepository.updateWallet( - // wallet: wallet, - // hiveStore: hiveStorage, - // ); - - // if (updateErr != null) { - // emit( - // state.copyWith( - // errTestingBackup: updateErr.toString(), - // testingBackup: false, - // ), - // ); - // return; - // } + final wallet = + _wallet.copyWith(backupTested: true, lastBackupTested: DateTime.now()); - _walletBloc - .add(UpdateWallet(wallet, updateTypes: [UpdateWalletTypes.settings])); - - await Future.delayed(100.ms); + await _appWalletsRepository + .getWalletServiceById(wallet.id) + ?.updateWallet(wallet, updateTypes: [UpdateWalletTypes.settings]); emit( state.copyWith( backupTested: true, testingBackup: false, - wallet: wallet, ), ); clearSensitive(); @@ -327,7 +258,7 @@ class WalletSettingsCubit extends Cubit { Future backupToSD() async { emit(state.copyWith(savingFile: true, errSavingFile: '')); final (seed, err) = await _walletSensRepository.readSeed( - fingerprintIndex: state.wallet.getRelatedSeedStorageString(), + fingerprintIndex: _wallet.getRelatedSeedStorageString(), ); if (err != null) { @@ -375,9 +306,12 @@ class WalletSettingsCubit extends Cubit { Future deleteWalletClicked() async { emit(state.copyWith(deleting: true, errDeleting: '')); - _walletBloc.add(KillSync()); - await Future.delayed(200.ms); - if (_walletBloc.state.wallet!.type == BBWalletType.main) { + final walletService = + _appWalletsRepository.getWalletServiceById(_wallet.id); + + walletService?.killSync(); + + if (walletService!.wallet.type == BBWalletType.main) { emit( state.copyWith( deleting: false, @@ -386,11 +320,11 @@ class WalletSettingsCubit extends Cubit { ); return; } - final mnemonicFingerprint = state.wallet.getRelatedSeedStorageString(); - final sourceFingerprint = state.wallet.sourceFingerprint; - final hasPassphrase = state.wallet.hasPassphrase(); + final mnemonicFingerprint = _wallet.getRelatedSeedStorageString(); + final sourceFingerprint = _wallet.sourceFingerprint; + final hasPassphrase = _wallet.hasPassphrase(); final err = await _walletsStorageRepository.deleteWallet( - walletHashId: state.wallet.getWalletStorageString(), + walletHashId: _wallet.getWalletStorageString(), ); if (err != null) { @@ -406,7 +340,7 @@ class WalletSettingsCubit extends Cubit { await Future.delayed(const Duration(seconds: 1)); final appDocDir = await getApplicationDocumentsDirectory(); - final dbDir = '${appDocDir.path}/${state.wallet.getWalletStorageString()}'; + final dbDir = '${appDocDir.path}/${_wallet.getWalletStorageString()}'; final errDeleting = await _fileStorage.deleteFile(dbDir); if (errDeleting != null) { @@ -445,18 +379,16 @@ class WalletSettingsCubit extends Cubit { } final List networkSpecificWallets = (wallets != null) - ? wallets - .where((wallet) => wallet.network == state.wallet.network) - .toList() + ? wallets.where((wallet) => wallet.network == _wallet.network).toList() : []; - // check if a wallet exists for this seed, if it does we should not delete the seed + final walletInUse = await WalletUpdate().walletExists( mnemonicFingerprint, networkSpecificWallets, ); if (!walletInUse) { final errr = await _walletSensRepository.deleteSeed( - fingerprint: state.wallet.getRelatedSeedStorageString(), + fingerprint: _wallet.getRelatedSeedStorageString(), ); if (errr != null) { emit( @@ -467,8 +399,7 @@ class WalletSettingsCubit extends Cubit { ); } } - _homeCubit.removeWallet(_walletBloc); - // homeCubit.removeWalletPostDelete(state.wallet.id); + _appWalletsRepository.deleteWallet(_wallet.id); emit( state.copyWith( @@ -481,17 +412,17 @@ class WalletSettingsCubit extends Cubit { Future exportLabelsClicked() async { try { emit(state.copyWith(exporting: true, errExporting: '', errImporting: '')); - final key = state.wallet.generateBIP329Key(); - final fileName = state.wallet.id; + final key = _wallet.generateBIP329Key(); + final fileName = _wallet.id; final walletLabels = WalletLabels(); final labelsToExport = await walletLabels.txsToBip329( - state.wallet.transactions, - state.wallet.originString(), + _wallet.transactions, + _wallet.originString(), ) ..addAll( await walletLabels.addressesToBip329( - state.wallet.myAddressBook, - state.wallet.originString(), + _wallet.myAddressBook, + _wallet.originString(), ), ); final err = await Bip329LabelHelpers.encryptWrite( @@ -514,7 +445,7 @@ class WalletSettingsCubit extends Cubit { Future importLabelsClicked() async { try { emit(state.copyWith(importing: true, errImporting: '', errExporting: '')); - final wallet = state.wallet; + final wallet = _wallet; final key = wallet.generateBIP329Key(); final fileName = wallet.id; final walletLabels = WalletLabels(); @@ -542,14 +473,14 @@ class WalletSettingsCubit extends Cubit { return; } - _walletBloc.add( - UpdateWallet( - updatedWallet!, - updateTypes: [ - UpdateWalletTypes.addresses, - UpdateWalletTypes.transactions, - ], - ), + await _appWalletsRepository + .getWalletServiceById(updatedWallet!.id) + ?.updateWallet( + updatedWallet, + updateTypes: [ + UpdateWalletTypes.addresses, + UpdateWalletTypes.transactions, + ], ); emit(state.copyWith(importing: false, imported: true)); diff --git a/lib/wallet_settings/descriptors.dart b/lib/wallet_settings/descriptors.dart index 28f4c31e..9379b9d1 100644 --- a/lib/wallet_settings/descriptors.dart +++ b/lib/wallet_settings/descriptors.dart @@ -19,7 +19,7 @@ class PublicDescriptorButton extends StatelessWidget { @override Widget build(BuildContext context) { final desc = context.select( - (WalletBloc cubit) => cubit.state.wallet!.externalPublicDescriptor, + (WalletBloc cubit) => cubit.state.wallet.externalPublicDescriptor, ); if (desc.isEmpty) return const SizedBox(); @@ -43,7 +43,7 @@ class ExtendedPublicKeyButton extends StatelessWidget { @override Widget build(BuildContext context) { final desc = context.select( - (WalletBloc cubit) => cubit.state.wallet!.externalPublicDescriptor, + (WalletBloc cubit) => cubit.state.wallet.externalPublicDescriptor, ); if (desc.isEmpty) return const SizedBox(); diff --git a/lib/wallet_settings/listeners.dart b/lib/wallet_settings/listeners.dart index 50745679..93972301 100644 --- a/lib/wallet_settings/listeners.dart +++ b/lib/wallet_settings/listeners.dart @@ -1,5 +1,8 @@ -import 'package:bb_mobile/home/bloc/home_cubit.dart'; -import 'package:bb_mobile/wallet/bloc/event.dart'; +import 'package:bb_mobile/_repository/app_wallets_repository.dart'; +import 'package:bb_mobile/_repository/wallet_service.dart'; +import 'package:bb_mobile/home/bloc/home_bloc.dart'; +import 'package:bb_mobile/home/bloc/home_event.dart'; +import 'package:bb_mobile/wallet/bloc/wallet_bloc.dart'; import 'package:bb_mobile/wallet_settings/bloc/state.dart'; import 'package:bb_mobile/wallet_settings/bloc/wallet_settings_cubit.dart'; import 'package:flutter/material.dart'; @@ -16,13 +19,15 @@ class WalletSettingsListeners extends StatelessWidget { return MultiBlocListener( listeners: [ BlocListener( - listenWhen: (previous, current) => previous.wallet != current.wallet, + listenWhen: (previous, current) => + previous.deleted != current.deleted, listener: (context, state) async { if (!state.deleted) return; // home.updateSelectedWallet(walletBloc); - await context.read().getWalletsFromStorage(); + // await context.read().getWalletsFromStorage(); + context.read().add(LoadWalletsFromStorage()); if (!context.mounted) return; context.pop(); }, @@ -54,25 +59,34 @@ class TestBackupListener extends StatelessWidget { listener: (context, state) { if (!state.backupTested) return; - final walletBloc = context - .read() - .state - .findWalletBlocWithSameFngr(state.wallet); - if (walletBloc == null) return; + final wallet = context.read().state.wallet; + + final walletService = context + .read() + .findWalletServiceWithSameFngr(wallet); + // .findWalletBlocWithSameFngr(state.wallet); + if (walletService == null) return; - final w = walletBloc.state.wallet!.copyWith( + final w = walletService.wallet.copyWith( backupTested: true, lastBackupTested: DateTime.now(), ); - walletBloc.add( - UpdateWallet( - w, - updateTypes: [ - UpdateWalletTypes.settings, - ], - ), + walletService.updateWallet( + w, + updateTypes: [ + UpdateWalletTypes.settings, + ], ); + + // walletService.add( + // UpdateWallet( + // w, + // updateTypes: [ + // UpdateWalletTypes.settings, + // ], + // ), + // ); }, child: child, ); diff --git a/lib/wallet_settings/test_backup.dart b/lib/wallet_settings/test_backup.dart index 8fdd53be..502958dd 100644 --- a/lib/wallet_settings/test_backup.dart +++ b/lib/wallet_settings/test_backup.dart @@ -14,24 +14,37 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; -class TestBackupPage extends StatelessWidget { +class TestBackupPage extends StatefulWidget { const TestBackupPage({ super.key, - required this.walletBloc, - required this.walletSettings, + required this.wallet, }); - final WalletBloc walletBloc; - final WalletSettingsCubit walletSettings; + final String wallet; @override - Widget build(BuildContext context) { + State createState() => _TestBackupPageState(); +} + +class _TestBackupPageState extends State { + late WalletSettingsCubit walletSettings; + late WalletBloc walletBloc; + @override + void initState() { + walletBloc = createOrRetreiveWalletBloc(widget.wallet); + walletSettings = createWalletSettingsCubit(widget.wallet); + walletSettings.loadBackupClicked(); + super.initState(); + } + + @override + Widget build(BuildContext context) { return MultiBlocProvider( providers: [ BlocProvider.value(value: walletBloc), - BlocProvider.value(value: walletSettings), + BlocProvider(create: (BuildContext context) => walletSettings), ], child: TestBackupListener( child: Builder( @@ -253,7 +266,7 @@ class TestBackupPassField extends HookWidget { if (tested) return const SizedBox.shrink(); final hasPassphrase = - context.select((WalletBloc x) => x.state.wallet!.hasPassphrase()); + context.select((WalletBloc x) => x.state.wallet.hasPassphrase()); if (!hasPassphrase) return const SizedBox.shrink(); @@ -326,7 +339,6 @@ class TestBackupConfirmButton extends StatelessWidget { } } - // class BackupTestTextField extends StatefulWidget { // const BackupTestTextField({super.key, required this.index}); diff --git a/lib/wallet_settings/wallet_settings_page.dart b/lib/wallet_settings/wallet_settings_page.dart index 296868bd..8418457a 100644 --- a/lib/wallet_settings/wallet_settings_page.dart +++ b/lib/wallet_settings/wallet_settings_page.dart @@ -1,9 +1,6 @@ import 'dart:async'; import 'package:bb_mobile/_model/wallet.dart'; -import 'package:bb_mobile/_pkg/file_storage.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/sensitive_storage.dart'; -import 'package:bb_mobile/_pkg/wallet/repository/storage.dart'; import 'package:bb_mobile/_ui/app_bar.dart'; import 'package:bb_mobile/_ui/bottom_sheet.dart'; import 'package:bb_mobile/_ui/components/button.dart'; @@ -11,8 +8,6 @@ import 'package:bb_mobile/_ui/components/text.dart'; import 'package:bb_mobile/_ui/components/text_input.dart'; import 'package:bb_mobile/_ui/headers.dart'; import 'package:bb_mobile/currency/bloc/currency_cubit.dart'; -import 'package:bb_mobile/home/bloc/home_cubit.dart'; -import 'package:bb_mobile/locator.dart'; import 'package:bb_mobile/wallet/bloc/wallet_bloc.dart'; import 'package:bb_mobile/wallet_settings/addresses.dart'; import 'package:bb_mobile/wallet_settings/bloc/state.dart'; @@ -29,32 +24,38 @@ import 'package:go_router/go_router.dart'; class WalletSettingsPage extends StatelessWidget { const WalletSettingsPage({ super.key, - required this.walletBloc, + required this.wallet, // this.openTestBackup = false, this.openBackup = false, }); // final bool openTestBackup; final bool openBackup; - final WalletBloc walletBloc; + final String wallet; @override Widget build(BuildContext context) { // final wallet = home.state.selectedWalletCubit!; - final walletSettings = WalletSettingsCubit( - wallet: walletBloc.state.wallet!, - // walletRead: locator(), - walletBloc: walletBloc, - fileStorage: locator(), - walletsStorageRepository: locator(), - walletSensRepository: locator(), - homeCubit: locator(), - ); + // final walletSettings = WalletSettingsCubit( + // // wallet: wallet.state.wallet!, + // // walletRead: locator(), + // wallet: wallet, + // appWalletsRepository: locator(), + // // walletBloc: wallet, + // fileStorage: locator(), + // walletsStorageRepository: locator(), + // walletSensRepository: locator(), + // // homeCubit: locator(), + // ); return MultiBlocProvider( providers: [ - BlocProvider.value(value: walletBloc), - BlocProvider.value(value: walletSettings), + BlocProvider.value( + value: createOrRetreiveWalletBloc(wallet), + ), + BlocProvider( + create: (BuildContext context) => createWalletSettingsCubit(wallet), + ), ], child: WalletSettingsListeners( child: _Screen( @@ -90,10 +91,11 @@ class _ScreenState extends State<_Screen> { // await Future.delayed(const Duration(milliseconds: 300)); await context.push( '/wallet-settings/backup', - extra: ( - context.read(), - context.read(), - ), + extra: context.read().state.wallet.id, + // ( + // context.read(), + // context.read(), + // ), ); } else { // showPage = true; @@ -103,13 +105,11 @@ class _ScreenState extends State<_Screen> { @override Widget build(BuildContext context) { - final watchOnly = context - .select((WalletSettingsCubit cubit) => cubit.state.wallet.watchOnly()); + final watchOnly = + context.select((WalletBloc cubit) => cubit.state.wallet.watchOnly()); - final isInstant = - context.read().state.wallet?.isInstant() ?? false; - final isSecure = - context.read().state.wallet?.isSecure() ?? false; + final isInstant = context.read().state.wallet.isInstant(); + final isSecure = context.read().state.wallet.isSecure(); // if (!showPage) return const Scaffold(body: SizedBox.shrink()); return Scaffold( @@ -177,11 +177,11 @@ class ApppBar extends StatelessWidget { @override Widget build(BuildContext context) { final walletName = context.select( - (WalletSettingsCubit cubit) => cubit.state.wallet.name, + (WalletBloc cubit) => cubit.state.wallet.name, ); final fingerPrint = context.select( - (WalletSettingsCubit cubit) => cubit.state.wallet.sourceFingerprint, + (WalletBloc cubit) => cubit.state.wallet.sourceFingerprint, ); return BBAppBar( @@ -206,7 +206,7 @@ class _WalletNameState extends State { @override Widget build(BuildContext context) { final mainWallet = - context.select((WalletBloc x) => x.state.wallet!.mainWallet); + context.select((WalletBloc x) => x.state.wallet.mainWallet); if (mainWallet) return const SizedBox.shrink(); final saving = context.select( @@ -219,7 +219,7 @@ class _WalletNameState extends State { if (text != _controller.text) _controller.text = text; final name = context.select( - (WalletSettingsCubit x) => x.state.wallet.name, + (WalletBloc x) => x.state.wallet.name, ); final showSave = text.isNotEmpty && name != text; @@ -266,10 +266,10 @@ class WalletType extends StatelessWidget { @override Widget build(BuildContext context) { final type = context.select( - (WalletSettingsCubit x) => x.state.wallet.getWalletTypeString(), + (WalletBloc x) => x.state.wallet.getWalletTypeString(), ); final scriptType = - context.select((WalletSettingsCubit x) => x.state.wallet.scriptType); + context.select((WalletBloc x) => x.state.wallet.scriptType); final _ = scriptTypeString(scriptType); return Column( @@ -291,7 +291,7 @@ class Balances extends StatelessWidget { @override Widget build(BuildContext context) { - final wallet = context.select((WalletBloc cubit) => cubit.state.wallet!); + final wallet = context.select((WalletBloc cubit) => cubit.state.wallet); final amtSent = wallet.totalSent(); @@ -411,8 +411,8 @@ class AccountingButton extends StatelessWidget { return BBButton.textWithStatusAndRightArrow( label: 'Accounting', onPressed: () { - final walletBloc = context.read(); - context.push('/wallet-settings/accounting', extra: walletBloc); + final wallet = context.read().state.wallet; + context.push('/wallet-settings/accounting', extra: wallet.id); }, ); } @@ -426,8 +426,8 @@ class WalletDetailsButton extends StatelessWidget { return BBButton.textWithStatusAndRightArrow( label: 'Wallet Details', onPressed: () { - final walletBloc = context.read(); - context.push('/wallet/details', extra: walletBloc); + final wallet = context.read().state.wallet; + context.push('/wallet/details', extra: wallet.id); }, ); } @@ -439,7 +439,7 @@ class TestBackupButton extends StatelessWidget { @override Widget build(BuildContext context) { final isTested = - context.select((WalletBloc x) => x.state.wallet!.backupTested); + context.select((WalletBloc x) => x.state.wallet.backupTested); // if (isTested) return const SizedBox.shrink(); return BBButton.textWithStatusAndRightArrow( @@ -449,10 +449,11 @@ class TestBackupButton extends StatelessWidget { onPressed: () async { context.push( '/wallet-settings/test-backup', - extra: ( - context.read(), - context.read(), - ), + extra: context.read().state.wallet.id, + // ( + // context.read(), + // context.read(), + // ), ); // await TestBackupScreen.openPopup(context); }, @@ -482,16 +483,17 @@ class BackupButton extends StatelessWidget { @override Widget build(BuildContext context) { final isTested = - context.select((WalletBloc x) => x.state.wallet!.backupTested); + context.select((WalletBloc x) => x.state.wallet.backupTested); return BBButton.textWithStatusAndRightArrow( onPressed: () async { context.push( '/wallet-settings/backup', - extra: ( - context.read(), - context.read(), - ), + extra: context.read().state.wallet.id, + // ( + // context.read(), + // context.read(), + // ), ); // await BackupScreen.openPopup(context); }, @@ -537,7 +539,7 @@ class DeletePopUp extends StatelessWidget { listener: (context, state) { if (state.deleted) { // final walletBloc = settings.walletBloc; - // context.read().clearWallet(walletBloc); + // context.read().clearWallet(walletBloc); context.go('/home'); } }, diff --git a/pubspec.lock b/pubspec.lock index dd0c2670..37c48486 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1207,6 +1207,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.0" + rxdart: + dependency: "direct main" + description: + name: rxdart + sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" + url: "https://pub.dev" + source: hosted + version: "0.28.0" share_plus: dependency: "direct main" description: @@ -1348,6 +1356,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.1" + synchronized: + dependency: "direct main" + description: + name: synchronized + sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225" + url: "https://pub.dev" + source: hosted + version: "3.3.0+3" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 2f2be29b..1c96ec71 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -90,6 +90,8 @@ dependencies: convert: ^3.1.2 crypto: ^3.0.6 permission_handler: ^11.3.1 + rxdart: ^0.28.0 + synchronized: ^3.3.0+3 dev_dependencies: build_runner: ^2.4.9