From b6a9ce58dbd1718ad168ef5993fccaf65201f34b Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Wed, 24 Apr 2024 21:37:22 +0200 Subject: [PATCH 001/100] monero.dart clean --- .fvmrc | 3 +++ .gitignore | 3 +++ .vscode/settings.json | 1 + android/app/build.gradle | 7 ++++++- android/app/src/main/AndroidManifest.xml | 3 ++- .../arm64-v8a/libmonero_libwallet2_api_c.so | 1 + .../armeabi-v7a/libmonero_libwallet2_api_c.so | 1 + .../x86_64/libmonero_libwallet2_api_c.so | 1 + lib/wallets/crypto_currency/coins/monero.dart | 3 ++- .../crypto_currency/coins/wownero.dart | 2 +- lib/wallets/wallet/impl/wownero_wallet.dart | 6 ++++-- macos/Flutter/GeneratedPluginRegistrant.swift | 6 ------ macos/Podfile.lock | 4 ++-- macos/Runner.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- pubspec.lock | 20 ++++++++++--------- pubspec.yaml | 2 +- scripts/macos/build_all.sh | 6 +++--- 18 files changed, 44 insertions(+), 29 deletions(-) create mode 100644 .fvmrc create mode 120000 android/app/src/main/jniLibs/arm64-v8a/libmonero_libwallet2_api_c.so create mode 120000 android/app/src/main/jniLibs/armeabi-v7a/libmonero_libwallet2_api_c.so create mode 120000 android/app/src/main/jniLibs/x86_64/libmonero_libwallet2_api_c.so diff --git a/.fvmrc b/.fvmrc new file mode 100644 index 000000000..5fb0c142c --- /dev/null +++ b/.fvmrc @@ -0,0 +1,3 @@ +{ + "flutter": "3.16.0" +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index ec9131840..144745e55 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,6 @@ libtor_ffi.dll flutter_libsparkmobile.dll secp256k1.dll /libisar.so + +# FVM Version Cache +.fvm/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index eba0d6cd7..a86b03d61 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,4 @@ { "dart.lineLength": 80, + "dart.flutterSdkPath": ".fvm/versions/3.16.0" } \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index ec8747bff..cd56f57ff 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -45,7 +45,12 @@ android { lintOptions { disable 'InvalidPackage' } - + packagingOptions { + pickFirst 'lib/x86/libc++_shared.so' + pickFirst 'lib/x86_64/libc++_shared.so' + pickFirst 'lib/armeabi-v7a/libc++_shared.so' + pickFirst 'lib/arm64-v8a/libc++_shared.so' + } defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.cypherstack.stackwallet" diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 3f1a56aa8..786db2728 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -23,7 +23,8 @@ android:requestLegacyExternalStorage="true" android:icon="@mipmap/ic_launcher" android:allowBackup="false" - android:fullBackupContent="false"> + android:fullBackupContent="false" + android:extractNativeLibs="true"> Date: Fri, 26 Apr 2024 00:44:11 +0200 Subject: [PATCH 002/100] fix build issues --- linux/CMakeLists.txt | 4 ++-- scripts/android/build_all.sh | 6 +++--- scripts/linux/build_all.sh | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index 53391e7ad..8fed408de 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -134,10 +134,10 @@ install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR} install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/linux/build/libcw_monero.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/monero/x86_64-linux-gnu_libwallet2_api_c.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "monero_libwallet2_api_c.so" COMPONENT Runtime) -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/linux/build/libcw_wownero.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/wownero/x86_64-linux-gnu_libwallet2_api_c.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "wownero_libwallet2_api_c.so" COMPONENT Runtime) install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libepiccash/scripts/linux/build/rust/target/x86_64-unknown-linux-gnu/release/libepic_cash_wallet.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" diff --git a/scripts/android/build_all.sh b/scripts/android/build_all.sh index 3f499e3f9..13438cf7d 100755 --- a/scripts/android/build_all.sh +++ b/scripts/android/build_all.sh @@ -10,9 +10,9 @@ mkdir -p build . ./config.sh ./install_ndk.sh -(cd ../../crypto_plugins/flutter_liblelantus/scripts/android && ./build_all.sh ) & -(cd ../../crypto_plugins/flutter_libepiccash/scripts/android && ./install_ndk.sh && ./build_opensll.sh && ./build_all.sh ) & -(cd ../../crypto_plugins/flutter_libmonero/scripts/android/ && ./build_all.sh ) & +(cd ../../crypto_plugins/flutter_liblelantus/scripts/android && ./build_all.sh ) +(cd ../../crypto_plugins/flutter_libepiccash/scripts/android && ./install_ndk.sh && ./build_openssl.sh && ./build_all.sh ) +(cd ../../crypto_plugins/flutter_libmonero/scripts/android/ && ./build_all.sh ) wait echo "Done building" diff --git a/scripts/linux/build_all.sh b/scripts/linux/build_all.sh index 672668c13..408a2c6b4 100755 --- a/scripts/linux/build_all.sh +++ b/scripts/linux/build_all.sh @@ -11,10 +11,10 @@ set_rust_to_1671 # flutter-elinux pub get # flutter-elinux build linux --dart-define="IS_ARM=true" mkdir -p build -./build_secure_storage_deps.sh & -(cd ../../crypto_plugins/flutter_liblelantus/scripts/linux && ./build_all.sh ) & -(cd ../../crypto_plugins/flutter_libepiccash/scripts/linux && ./build_all.sh ) & -(cd ../../crypto_plugins/flutter_libmonero/scripts/linux && ./build_monero_all.sh && ./build_sharedfile.sh ) & +./build_secure_storage_deps.sh +(cd ../../crypto_plugins/flutter_liblelantus/scripts/linux && ./build_all.sh ) +(cd ../../crypto_plugins/flutter_libepiccash/scripts/linux && ./build_all.sh ) +(cd ../../crypto_plugins/flutter_libmonero/scripts/linux && ./build_all.sh ) wait echo "Done building" From fb0b87942a8c3da4a3ad8ea168237f6920814761 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Fri, 26 Apr 2024 14:21:21 +0200 Subject: [PATCH 003/100] wownero support --- .../add_wallet_views/add_wallet_view/add_wallet_view.dart | 2 +- .../settings/settings_menu/nodes_settings.dart | 2 +- pubspec.lock | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart b/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart index 32e1b618c..54b94efb3 100644 --- a/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart +++ b/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart @@ -131,7 +131,7 @@ class _AddWalletViewState extends ConsumerState { _coins.remove(Coin.monero); _coins.remove(Coin.wownero); } else if (Platform.isLinux) { - _coins.remove(Coin.wownero); + // _coins.remove(Coin.wownero); } coinEntities.addAll(_coins.map((e) => CoinEntity(e))); diff --git a/lib/pages_desktop_specific/settings/settings_menu/nodes_settings.dart b/lib/pages_desktop_specific/settings/settings_menu/nodes_settings.dart index eec6e1a5f..1edeac7a6 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/nodes_settings.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/nodes_settings.dart @@ -69,7 +69,7 @@ class _NodesSettings extends ConsumerState { _coins.remove(Coin.monero); _coins.remove(Coin.wownero); } else if (Platform.isLinux) { - _coins.remove(Coin.wownero); + // _coins.remove(Coin.wownero); } searchNodeController = TextEditingController(); diff --git a/pubspec.lock b/pubspec.lock index 111adbb54..428c81a90 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1144,8 +1144,8 @@ packages: dependency: transitive description: path: "." - ref: "342bad5d91d8476f0e32a57ae944f2cfe34d4241" - resolved-ref: "342bad5d91d8476f0e32a57ae944f2cfe34d4241" + ref: "2e1d13765c1403a933c77972511a661f26d4af2e" + resolved-ref: "2e1d13765c1403a933c77972511a661f26d4af2e" url: "https://git.mrcyjanek.net/mrcyjanek/monero.dart" source: git version: "0.0.0" From dc699c6551c6e647c593227460042398fcafdfe8 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Fri, 26 Apr 2024 17:51:51 +0200 Subject: [PATCH 004/100] additionalInfo backwards compat --- lib/wallets/wallet/impl/monero_wallet.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/wallets/wallet/impl/monero_wallet.dart b/lib/wallets/wallet/impl/monero_wallet.dart index e0d70db36..5f681c774 100644 --- a/lib/wallets/wallet/impl/monero_wallet.dart +++ b/lib/wallets/wallet/impl/monero_wallet.dart @@ -218,8 +218,8 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { final addressString = (CwBasedInterface.cwWalletBase as MoneroWalletBase?) ?.getTransactionAddress( - addressInfo!['accountIndex'] as int, - addressInfo['addressIndex'] as int, + addressInfo?['accountIndex'] as int? ?? 0, + addressInfo?['addressIndex'] as int? ?? 0, ); if (addressString != null) { From 01b5bc479d2988e9283a2db171041a016da88034 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Sat, 27 Apr 2024 18:16:31 +0200 Subject: [PATCH 005/100] macos support --- macos/Podfile.lock | 54 -------------------------- macos/Runner.xcodeproj/project.pbxproj | 18 +++++++++ macos/monero_libwallet2_api_c.dylib | 1 + macos/wownero_libwallet2_api_c.dylib | 1 + pubspec.lock | 4 +- 5 files changed, 22 insertions(+), 56 deletions(-) create mode 120000 macos/monero_libwallet2_api_c.dylib create mode 120000 macos/wownero_libwallet2_api_c.dylib diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 3c7eac5bb..87053ba83 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -5,48 +5,6 @@ PODS: - connectivity_plus (0.0.1): - FlutterMacOS - ReachabilitySwift - - cw_monero (0.0.1): - - cw_monero/Boost (= 0.0.1) - - cw_monero/Monero (= 0.0.1) - - cw_monero/OpenSSL (= 0.0.1) - - cw_monero/Sodium (= 0.0.1) - - cw_monero/Unbound (= 0.0.1) - - FlutterMacOS - - cw_monero/Boost (0.0.1): - - FlutterMacOS - - cw_monero/Monero (0.0.1): - - FlutterMacOS - - cw_monero/OpenSSL (0.0.1): - - FlutterMacOS - - cw_monero/Sodium (0.0.1): - - FlutterMacOS - - cw_monero/Unbound (0.0.1): - - FlutterMacOS - - cw_shared_external (0.0.1): - - cw_shared_external/Boost (= 0.0.1) - - cw_shared_external/OpenSSL (= 0.0.1) - - FlutterMacOS - - cw_shared_external/Boost (0.0.1): - - FlutterMacOS - - cw_shared_external/OpenSSL (0.0.1): - - FlutterMacOS - - cw_wownero (0.0.1): - - cw_wownero/Boost (= 0.0.1) - - cw_wownero/OpenSSL (= 0.0.1) - - cw_wownero/Sodium (= 0.0.1) - - cw_wownero/Unbound (= 0.0.1) - - cw_wownero/Wownero (= 0.0.1) - - FlutterMacOS - - cw_wownero/Boost (0.0.1): - - FlutterMacOS - - cw_wownero/OpenSSL (0.0.1): - - FlutterMacOS - - cw_wownero/Sodium (0.0.1): - - FlutterMacOS - - cw_wownero/Unbound (0.0.1): - - FlutterMacOS - - cw_wownero/Wownero (0.0.1): - - FlutterMacOS - desktop_drop (0.0.1): - FlutterMacOS - device_info_plus (0.0.1): @@ -87,9 +45,6 @@ PODS: DEPENDENCIES: - coinlib_flutter (from `Flutter/ephemeral/.symlinks/plugins/coinlib_flutter/darwin`) - connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`) - - cw_monero (from `Flutter/ephemeral/.symlinks/plugins/cw_monero/macos`) - - cw_shared_external (from `Flutter/ephemeral/.symlinks/plugins/cw_shared_external/macos`) - - cw_wownero (from `Flutter/ephemeral/.symlinks/plugins/cw_wownero/macos`) - desktop_drop (from `Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos`) - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) - devicelocale (from `Flutter/ephemeral/.symlinks/plugins/devicelocale/macos`) @@ -118,12 +73,6 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/coinlib_flutter/darwin connectivity_plus: :path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos - cw_monero: - :path: Flutter/ephemeral/.symlinks/plugins/cw_monero/macos - cw_shared_external: - :path: Flutter/ephemeral/.symlinks/plugins/cw_shared_external/macos - cw_wownero: - :path: Flutter/ephemeral/.symlinks/plugins/cw_wownero/macos desktop_drop: :path: Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos device_info_plus: @@ -164,9 +113,6 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: coinlib_flutter: 6abec900d67762a6e7ccfd567a3cd3ae00bbee35 connectivity_plus: 18d3c32514c886e046de60e9c13895109866c747 - cw_monero: 7acce7238d217e3993ecac6ec2dec07be728769a - cw_shared_external: c6adfd29c9be4d64f84e1fa9c541ccbcbdb6b457 - cw_wownero: bcd7f2ad6c0a3e8e2a51756fb14f0579b6f8b4ff desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898 device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f devicelocale: 9f0f36ac651cabae2c33f32dcff4f32b61c38225 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index c7d39b845..ea15e0c92 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -31,6 +31,8 @@ B98151822A67402A009D013C /* mobileliblelantus.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = B98151802A674022009D013C /* mobileliblelantus.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; B98151842A674143009D013C /* libsqlite3.0.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = B98151832A674143009D013C /* libsqlite3.0.tbd */; }; BFD0376C00E1FFD46376BB9D /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9206484E84CB0AD93E3E68CA /* Pods_RunnerTests.framework */; }; + CEA2021D2BDD4D7100FE1D27 /* wownero_libwallet2_api_c.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = CEA2021C2BDD4D7100FE1D27 /* wownero_libwallet2_api_c.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + CEA202202BDD4F0B00FE1D27 /* monero_libwallet2_api_c.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = CEA2021F2BDD4F0B00FE1D27 /* monero_libwallet2_api_c.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; F653CA022D33E8B60E11A9F3 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E6036BF01BF05EA773C76D22 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ @@ -63,6 +65,17 @@ name = "Bundle Framework"; runOnlyForDeploymentPostprocessing = 0; }; + CEA202192BDD4C3500FE1D27 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 12; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + CEA202202BDD4F0B00FE1D27 /* monero_libwallet2_api_c.dylib in CopyFiles */, + CEA2021D2BDD4D7100FE1D27 /* wownero_libwallet2_api_c.dylib in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -93,6 +106,8 @@ B98151802A674022009D013C /* mobileliblelantus.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = mobileliblelantus.framework; path = ../crypto_plugins/flutter_liblelantus/scripts/macos/mobileliblelantus/mobileliblelantus.framework; sourceTree = ""; }; B98151832A674143009D013C /* libsqlite3.0.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.0.tbd; path = usr/lib/libsqlite3.0.tbd; sourceTree = SDKROOT; }; BF5E76865ACB46314AC27D8F /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + CEA2021C2BDD4D7100FE1D27 /* wownero_libwallet2_api_c.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = wownero_libwallet2_api_c.dylib; sourceTree = ""; }; + CEA2021F2BDD4F0B00FE1D27 /* monero_libwallet2_api_c.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = monero_libwallet2_api_c.dylib; sourceTree = ""; }; E6036BF01BF05EA773C76D22 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -140,6 +155,8 @@ 33CC10E42044A3C60003C045 = { isa = PBXGroup; children = ( + CEA2021F2BDD4F0B00FE1D27 /* monero_libwallet2_api_c.dylib */, + CEA2021C2BDD4D7100FE1D27 /* wownero_libwallet2_api_c.dylib */, 33FAB671232836740065AC1E /* Runner */, 33CEB47122A05771004F2AC0 /* Flutter */, 331C80D6294CF71000263BE5 /* RunnerTests */, @@ -250,6 +267,7 @@ 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, 529691D83C3BADE14E2EAC03 /* [CP] Embed Pods Frameworks */, + CEA202192BDD4C3500FE1D27 /* CopyFiles */, ); buildRules = ( ); diff --git a/macos/monero_libwallet2_api_c.dylib b/macos/monero_libwallet2_api_c.dylib new file mode 120000 index 000000000..da01b591e --- /dev/null +++ b/macos/monero_libwallet2_api_c.dylib @@ -0,0 +1 @@ +../crypto_plugins/flutter_libmonero/scripts/monero_c/release/monero/host-apple-darwin_libwallet2_api_c.dylib \ No newline at end of file diff --git a/macos/wownero_libwallet2_api_c.dylib b/macos/wownero_libwallet2_api_c.dylib new file mode 120000 index 000000000..f0a776dc1 --- /dev/null +++ b/macos/wownero_libwallet2_api_c.dylib @@ -0,0 +1 @@ +../crypto_plugins/flutter_libmonero/scripts/monero_c/release/wownero/host-apple-darwin_libwallet2_api_c.dylib \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 428c81a90..7d2bdde86 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1144,8 +1144,8 @@ packages: dependency: transitive description: path: "." - ref: "2e1d13765c1403a933c77972511a661f26d4af2e" - resolved-ref: "2e1d13765c1403a933c77972511a661f26d4af2e" + ref: "7ba955fd9d975c7228ee43a744294399b48f9993" + resolved-ref: "7ba955fd9d975c7228ee43a744294399b48f9993" url: "https://git.mrcyjanek.net/mrcyjanek/monero.dart" source: git version: "0.0.0" From c78fba7b364bc69ecedc6c71405506f9c02fcc6b Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Wed, 24 Apr 2024 21:37:22 +0200 Subject: [PATCH 006/100] monero.dart clean --- .fvmrc | 3 +++ .gitignore | 3 +++ .vscode/settings.json | 1 + android/app/build.gradle | 7 ++++++- android/app/src/main/AndroidManifest.xml | 3 ++- .../arm64-v8a/libmonero_libwallet2_api_c.so | 1 + .../armeabi-v7a/libmonero_libwallet2_api_c.so | 1 + .../jniLibs/x86_64/libmonero_libwallet2_api_c.so | 1 + crypto_plugins/flutter_libmonero | 2 +- lib/wallets/crypto_currency/coins/monero.dart | 3 ++- lib/wallets/crypto_currency/coins/wownero.dart | 2 +- lib/wallets/wallet/impl/wownero_wallet.dart | 6 ++++-- macos/Flutter/GeneratedPluginRegistrant.swift | 6 ------ pubspec.lock | 16 +++++++++------- scripts/macos/build_all.sh | 10 +++++----- 15 files changed, 40 insertions(+), 25 deletions(-) create mode 100644 .fvmrc create mode 120000 android/app/src/main/jniLibs/arm64-v8a/libmonero_libwallet2_api_c.so create mode 120000 android/app/src/main/jniLibs/armeabi-v7a/libmonero_libwallet2_api_c.so create mode 120000 android/app/src/main/jniLibs/x86_64/libmonero_libwallet2_api_c.so diff --git a/.fvmrc b/.fvmrc new file mode 100644 index 000000000..5fb0c142c --- /dev/null +++ b/.fvmrc @@ -0,0 +1,3 @@ +{ + "flutter": "3.16.0" +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index ec9131840..144745e55 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,6 @@ libtor_ffi.dll flutter_libsparkmobile.dll secp256k1.dll /libisar.so + +# FVM Version Cache +.fvm/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index eba0d6cd7..a86b03d61 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,4 @@ { "dart.lineLength": 80, + "dart.flutterSdkPath": ".fvm/versions/3.16.0" } \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index ae9570217..5272e9456 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -45,7 +45,12 @@ android { lintOptions { disable 'InvalidPackage' } - + packagingOptions { + pickFirst 'lib/x86/libc++_shared.so' + pickFirst 'lib/x86_64/libc++_shared.so' + pickFirst 'lib/armeabi-v7a/libc++_shared.so' + pickFirst 'lib/arm64-v8a/libc++_shared.so' + } defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.cypherstack.stackwallet" diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 3f1a56aa8..786db2728 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -23,7 +23,8 @@ android:requestLegacyExternalStorage="true" android:icon="@mipmap/ic_launcher" android:allowBackup="false" - android:fullBackupContent="false"> + android:fullBackupContent="false" + android:extractNativeLibs="true"> Date: Fri, 26 Apr 2024 00:44:11 +0200 Subject: [PATCH 007/100] fix build issues --- linux/CMakeLists.txt | 4 ++-- scripts/android/build_all.sh | 2 +- scripts/linux/build_all.sh | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index 53391e7ad..8fed408de 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -134,10 +134,10 @@ install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR} install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/linux/build/libcw_monero.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/monero/x86_64-linux-gnu_libwallet2_api_c.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "monero_libwallet2_api_c.so" COMPONENT Runtime) -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/linux/build/libcw_wownero.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/wownero/x86_64-linux-gnu_libwallet2_api_c.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "wownero_libwallet2_api_c.so" COMPONENT Runtime) install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libepiccash/scripts/linux/build/rust/target/x86_64-unknown-linux-gnu/release/libepic_cash_wallet.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" diff --git a/scripts/android/build_all.sh b/scripts/android/build_all.sh index 5da88e0e3..987c7df57 100755 --- a/scripts/android/build_all.sh +++ b/scripts/android/build_all.sh @@ -13,7 +13,7 @@ mkdir -p build PLUGINS_DIR=../../crypto_plugins (cd "${PLUGINS_DIR}"/flutter_liblelantus/scripts/android && ./build_all.sh ) & -(cd "${PLUGINS_DIR}"/flutter_libepiccash/scripts/android && ./build_all.sh ) & +(cd "${PLUGINS_DIR}"/flutter_libepiccash/scripts/android && ./install_ndk.sh && ./build_openssl.sh && ./build_all.sh ) & (cd "${PLUGINS_DIR}"/flutter_libmonero/scripts/android/ && ./build_all.sh ) && set_rust_to_1720 && (cd "${PLUGINS_DIR}"/frostdart/scripts/android && ./build_all.sh ) & diff --git a/scripts/linux/build_all.sh b/scripts/linux/build_all.sh index 2b6bd1ffd..5d07aa450 100755 --- a/scripts/linux/build_all.sh +++ b/scripts/linux/build_all.sh @@ -11,12 +11,12 @@ set_rust_to_1671 # flutter-elinux pub get # flutter-elinux build linux --dart-define="IS_ARM=true" mkdir -p build -./build_secure_storage_deps.sh & -(cd ../../crypto_plugins/flutter_liblelantus/scripts/linux && ./build_all.sh ) & -(cd ../../crypto_plugins/flutter_libepiccash/scripts/linux && ./build_all.sh ) & -(cd ../../crypto_plugins/flutter_libmonero/scripts/linux && ./build_monero_all.sh && ./build_sharedfile.sh ) & -set_rust_to_1720 & -(cd ../../crypto_plugins/frostdart/scripts/linux && ./build_all.sh ) & +./build_secure_storage_deps.sh +(cd ../../crypto_plugins/flutter_liblelantus/scripts/linux && ./build_all.sh ) +(cd ../../crypto_plugins/flutter_libepiccash/scripts/linux && ./build_all.sh ) +(cd ../../crypto_plugins/flutter_libmonero/scripts/linux && ./build_monero_all.sh && ./build_sharedfile.sh ) +set_rust_to_1720 +(cd ../../crypto_plugins/frostdart/scripts/linux && ./build_all.sh ) wait echo "Done building" From f614cfff5f9aa372fc8eb5b06f97985a20e6f2fb Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Fri, 26 Apr 2024 14:21:21 +0200 Subject: [PATCH 008/100] wownero support --- .../add_wallet_views/add_wallet_view/add_wallet_view.dart | 2 +- .../settings/settings_menu/nodes_settings.dart | 2 +- pubspec.lock | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart b/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart index d84bfc708..76e1ee0a2 100644 --- a/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart +++ b/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart @@ -131,7 +131,7 @@ class _AddWalletViewState extends ConsumerState { _coins.remove(Coin.monero); _coins.remove(Coin.wownero); } else if (Platform.isLinux) { - _coins.remove(Coin.wownero); + // _coins.remove(Coin.wownero); } // Remove FROST from the list of coins based on our frostEnabled preference. diff --git a/lib/pages_desktop_specific/settings/settings_menu/nodes_settings.dart b/lib/pages_desktop_specific/settings/settings_menu/nodes_settings.dart index eec6e1a5f..1edeac7a6 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/nodes_settings.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/nodes_settings.dart @@ -69,7 +69,7 @@ class _NodesSettings extends ConsumerState { _coins.remove(Coin.monero); _coins.remove(Coin.wownero); } else if (Platform.isLinux) { - _coins.remove(Coin.wownero); + // _coins.remove(Coin.wownero); } searchNodeController = TextEditingController(); diff --git a/pubspec.lock b/pubspec.lock index 479d5c45d..0845fd66c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1197,8 +1197,8 @@ packages: dependency: transitive description: path: "." - ref: "342bad5d91d8476f0e32a57ae944f2cfe34d4241" - resolved-ref: "342bad5d91d8476f0e32a57ae944f2cfe34d4241" + ref: "2e1d13765c1403a933c77972511a661f26d4af2e" + resolved-ref: "2e1d13765c1403a933c77972511a661f26d4af2e" url: "https://git.mrcyjanek.net/mrcyjanek/monero.dart" source: git version: "0.0.0" From ffa4c85d532b7a9251395583bfdd36525cecc64a Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Fri, 26 Apr 2024 17:51:51 +0200 Subject: [PATCH 009/100] additionalInfo backwards compat --- lib/wallets/wallet/impl/monero_wallet.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/wallets/wallet/impl/monero_wallet.dart b/lib/wallets/wallet/impl/monero_wallet.dart index e0d70db36..5f681c774 100644 --- a/lib/wallets/wallet/impl/monero_wallet.dart +++ b/lib/wallets/wallet/impl/monero_wallet.dart @@ -218,8 +218,8 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { final addressString = (CwBasedInterface.cwWalletBase as MoneroWalletBase?) ?.getTransactionAddress( - addressInfo!['accountIndex'] as int, - addressInfo['addressIndex'] as int, + addressInfo?['accountIndex'] as int? ?? 0, + addressInfo?['addressIndex'] as int? ?? 0, ); if (addressString != null) { From 19de471b85e9adede9a9c2c152a17c3a726b8c52 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Sat, 27 Apr 2024 18:16:31 +0200 Subject: [PATCH 010/100] macos support --- macos/Podfile.lock | 54 -------------------------- macos/Runner.xcodeproj/project.pbxproj | 18 +++++++++ macos/monero_libwallet2_api_c.dylib | 1 + macos/wownero_libwallet2_api_c.dylib | 1 + pubspec.lock | 4 +- 5 files changed, 22 insertions(+), 56 deletions(-) create mode 120000 macos/monero_libwallet2_api_c.dylib create mode 120000 macos/wownero_libwallet2_api_c.dylib diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 35c7171cb..57c4d3468 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -5,48 +5,6 @@ PODS: - connectivity_plus (0.0.1): - FlutterMacOS - ReachabilitySwift - - cw_monero (0.0.1): - - cw_monero/Boost (= 0.0.1) - - cw_monero/Monero (= 0.0.1) - - cw_monero/OpenSSL (= 0.0.1) - - cw_monero/Sodium (= 0.0.1) - - cw_monero/Unbound (= 0.0.1) - - FlutterMacOS - - cw_monero/Boost (0.0.1): - - FlutterMacOS - - cw_monero/Monero (0.0.1): - - FlutterMacOS - - cw_monero/OpenSSL (0.0.1): - - FlutterMacOS - - cw_monero/Sodium (0.0.1): - - FlutterMacOS - - cw_monero/Unbound (0.0.1): - - FlutterMacOS - - cw_shared_external (0.0.1): - - cw_shared_external/Boost (= 0.0.1) - - cw_shared_external/OpenSSL (= 0.0.1) - - FlutterMacOS - - cw_shared_external/Boost (0.0.1): - - FlutterMacOS - - cw_shared_external/OpenSSL (0.0.1): - - FlutterMacOS - - cw_wownero (0.0.1): - - cw_wownero/Boost (= 0.0.1) - - cw_wownero/OpenSSL (= 0.0.1) - - cw_wownero/Sodium (= 0.0.1) - - cw_wownero/Unbound (= 0.0.1) - - cw_wownero/Wownero (= 0.0.1) - - FlutterMacOS - - cw_wownero/Boost (0.0.1): - - FlutterMacOS - - cw_wownero/OpenSSL (0.0.1): - - FlutterMacOS - - cw_wownero/Sodium (0.0.1): - - FlutterMacOS - - cw_wownero/Unbound (0.0.1): - - FlutterMacOS - - cw_wownero/Wownero (0.0.1): - - FlutterMacOS - desktop_drop (0.0.1): - FlutterMacOS - device_info_plus (0.0.1): @@ -89,9 +47,6 @@ PODS: DEPENDENCIES: - coinlib_flutter (from `Flutter/ephemeral/.symlinks/plugins/coinlib_flutter/darwin`) - connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`) - - cw_monero (from `Flutter/ephemeral/.symlinks/plugins/cw_monero/macos`) - - cw_shared_external (from `Flutter/ephemeral/.symlinks/plugins/cw_shared_external/macos`) - - cw_wownero (from `Flutter/ephemeral/.symlinks/plugins/cw_wownero/macos`) - desktop_drop (from `Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos`) - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) - devicelocale (from `Flutter/ephemeral/.symlinks/plugins/devicelocale/macos`) @@ -121,12 +76,6 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/coinlib_flutter/darwin connectivity_plus: :path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos - cw_monero: - :path: Flutter/ephemeral/.symlinks/plugins/cw_monero/macos - cw_shared_external: - :path: Flutter/ephemeral/.symlinks/plugins/cw_shared_external/macos - cw_wownero: - :path: Flutter/ephemeral/.symlinks/plugins/cw_wownero/macos desktop_drop: :path: Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos device_info_plus: @@ -169,9 +118,6 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: coinlib_flutter: 6abec900d67762a6e7ccfd567a3cd3ae00bbee35 connectivity_plus: 18d3c32514c886e046de60e9c13895109866c747 - cw_monero: 7acce7238d217e3993ecac6ec2dec07be728769a - cw_shared_external: c6adfd29c9be4d64f84e1fa9c541ccbcbdb6b457 - cw_wownero: bcd7f2ad6c0a3e8e2a51756fb14f0579b6f8b4ff desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898 device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f devicelocale: 9f0f36ac651cabae2c33f32dcff4f32b61c38225 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index eccafe6ba..cf58de9bd 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -31,6 +31,8 @@ B98151822A67402A009D013C /* mobileliblelantus.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = B98151802A674022009D013C /* mobileliblelantus.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; B98151842A674143009D013C /* libsqlite3.0.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = B98151832A674143009D013C /* libsqlite3.0.tbd */; }; BFD0376C00E1FFD46376BB9D /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9206484E84CB0AD93E3E68CA /* Pods_RunnerTests.framework */; }; + CEA2021D2BDD4D7100FE1D27 /* wownero_libwallet2_api_c.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = CEA2021C2BDD4D7100FE1D27 /* wownero_libwallet2_api_c.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + CEA202202BDD4F0B00FE1D27 /* monero_libwallet2_api_c.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = CEA2021F2BDD4F0B00FE1D27 /* monero_libwallet2_api_c.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; F1FA2C4E2BA4B49F00BDA1BB /* frostdart.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F1FA2C4D2BA4B49F00BDA1BB /* frostdart.dylib */; settings = {ATTRIBUTES = (Weak, ); }; }; F1FA2C502BA4B4CA00BDA1BB /* frostdart.dylib in Resources */ = {isa = PBXBuildFile; fileRef = F1FA2C4F2BA4B4CA00BDA1BB /* frostdart.dylib */; }; F1FA2C512BA4B51E00BDA1BB /* frostdart.dylib in Bundle Framework */ = {isa = PBXBuildFile; fileRef = F1FA2C4D2BA4B49F00BDA1BB /* frostdart.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; @@ -67,6 +69,17 @@ name = "Bundle Framework"; runOnlyForDeploymentPostprocessing = 0; }; + CEA202192BDD4C3500FE1D27 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 12; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + CEA202202BDD4F0B00FE1D27 /* monero_libwallet2_api_c.dylib in CopyFiles */, + CEA2021D2BDD4D7100FE1D27 /* wownero_libwallet2_api_c.dylib in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -97,6 +110,8 @@ B98151802A674022009D013C /* mobileliblelantus.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = mobileliblelantus.framework; path = ../crypto_plugins/flutter_liblelantus/scripts/macos/mobileliblelantus/mobileliblelantus.framework; sourceTree = ""; }; B98151832A674143009D013C /* libsqlite3.0.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.0.tbd; path = usr/lib/libsqlite3.0.tbd; sourceTree = SDKROOT; }; BF5E76865ACB46314AC27D8F /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + CEA2021C2BDD4D7100FE1D27 /* wownero_libwallet2_api_c.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = wownero_libwallet2_api_c.dylib; sourceTree = ""; }; + CEA2021F2BDD4F0B00FE1D27 /* monero_libwallet2_api_c.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = monero_libwallet2_api_c.dylib; sourceTree = ""; }; E6036BF01BF05EA773C76D22 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; F1FA2C4D2BA4B49F00BDA1BB /* frostdart.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = frostdart.dylib; path = ../crypto_plugins/frostdart/macos/frostdart.dylib; sourceTree = ""; }; F1FA2C4F2BA4B4CA00BDA1BB /* frostdart.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = frostdart.dylib; path = ../crypto_plugins/frostdart/macos/frostdart.dylib; sourceTree = ""; }; @@ -147,6 +162,8 @@ 33CC10E42044A3C60003C045 = { isa = PBXGroup; children = ( + CEA2021F2BDD4F0B00FE1D27 /* monero_libwallet2_api_c.dylib */, + CEA2021C2BDD4D7100FE1D27 /* wownero_libwallet2_api_c.dylib */, F1FA2C4F2BA4B4CA00BDA1BB /* frostdart.dylib */, 33FAB671232836740065AC1E /* Runner */, 33CEB47122A05771004F2AC0 /* Flutter */, @@ -259,6 +276,7 @@ 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, 529691D83C3BADE14E2EAC03 /* [CP] Embed Pods Frameworks */, + CEA202192BDD4C3500FE1D27 /* CopyFiles */, ); buildRules = ( ); diff --git a/macos/monero_libwallet2_api_c.dylib b/macos/monero_libwallet2_api_c.dylib new file mode 120000 index 000000000..da01b591e --- /dev/null +++ b/macos/monero_libwallet2_api_c.dylib @@ -0,0 +1 @@ +../crypto_plugins/flutter_libmonero/scripts/monero_c/release/monero/host-apple-darwin_libwallet2_api_c.dylib \ No newline at end of file diff --git a/macos/wownero_libwallet2_api_c.dylib b/macos/wownero_libwallet2_api_c.dylib new file mode 120000 index 000000000..f0a776dc1 --- /dev/null +++ b/macos/wownero_libwallet2_api_c.dylib @@ -0,0 +1 @@ +../crypto_plugins/flutter_libmonero/scripts/monero_c/release/wownero/host-apple-darwin_libwallet2_api_c.dylib \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 0845fd66c..e6c510034 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1197,8 +1197,8 @@ packages: dependency: transitive description: path: "." - ref: "2e1d13765c1403a933c77972511a661f26d4af2e" - resolved-ref: "2e1d13765c1403a933c77972511a661f26d4af2e" + ref: "7ba955fd9d975c7228ee43a744294399b48f9993" + resolved-ref: "7ba955fd9d975c7228ee43a744294399b48f9993" url: "https://git.mrcyjanek.net/mrcyjanek/monero.dart" source: git version: "0.0.0" From 09cbcc7b661393f27076bea209eacdcb8b8a0ed2 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Sun, 28 Apr 2024 23:11:10 -0500 Subject: [PATCH 011/100] add 16 word polyseeds as an option for monero --- lib/utilities/constants.dart | 2 +- lib/wallets/wallet/impl/monero_wallet.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/utilities/constants.dart b/lib/utilities/constants.dart index f60a0fe1e..77c034be1 100644 --- a/lib/utilities/constants.dart +++ b/lib/utilities/constants.dart @@ -197,7 +197,7 @@ abstract class Constants { values.addAll([24, 12]); case Coin.monero: - values.addAll([25]); + values.addAll([16, 25]); break; case Coin.wownero: values.addAll([14, 25]); diff --git a/lib/wallets/wallet/impl/monero_wallet.dart b/lib/wallets/wallet/impl/monero_wallet.dart index 5f681c774..92da26d0d 100644 --- a/lib/wallets/wallet/impl/monero_wallet.dart +++ b/lib/wallets/wallet/impl/monero_wallet.dart @@ -392,7 +392,7 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { final mnemonic = await getMnemonic(); final seedLength = mnemonic.trim().split(" ").length; - if (seedLength != 25) { + if (seedLength != 25 && seedLength != 16) { throw Exception("Invalid monero mnemonic length found: $seedLength"); } From 7929d49e27d24eb294076c2118b9b83c6278621d Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Tue, 30 Apr 2024 09:55:24 +0200 Subject: [PATCH 012/100] polyseed support --- .../restore_options_view.dart | 28 ++++++++++++++----- lib/utilities/constants.dart | 2 +- lib/wallets/wallet/impl/monero_wallet.dart | 3 +- lib/wallets/wallet/impl/wownero_wallet.dart | 4 ++- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart index 44ac51aac..8b28a0466 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart @@ -311,7 +311,9 @@ class _RestoreOptionsViewState extends ConsumerState { SizedBox( height: isDesktop ? 40 : 24, ), - if (coin == Coin.monero || + if ((coin == Coin.monero && + ref.watch(mnemonicWordCountStateProvider.state).state == + 25) || coin == Coin.epicCash || (coin == Coin.wownero && ref.watch(mnemonicWordCountStateProvider.state).state == @@ -327,7 +329,9 @@ class _RestoreOptionsViewState extends ConsumerState { : STextStyles.smallMed12(context), textAlign: TextAlign.left, ), - if (coin == Coin.monero || + if ((coin == Coin.monero && + ref.watch(mnemonicWordCountStateProvider.state).state == + 25) || coin == Coin.epicCash || (coin == Coin.wownero && ref.watch(mnemonicWordCountStateProvider.state).state == @@ -335,7 +339,9 @@ class _RestoreOptionsViewState extends ConsumerState { SizedBox( height: isDesktop ? 16 : 8, ), - if (coin == Coin.monero || + if ((coin == Coin.monero && + ref.watch(mnemonicWordCountStateProvider.state).state == + 25) || coin == Coin.epicCash || (coin == Coin.wownero && ref.watch(mnemonicWordCountStateProvider.state).state == @@ -345,7 +351,9 @@ class _RestoreOptionsViewState extends ConsumerState { onTap: chooseDate, controller: _dateController, ), - if (coin == Coin.monero || + if ((coin == Coin.monero && + ref.watch(mnemonicWordCountStateProvider.state).state == + 25) || coin == Coin.epicCash || (coin == Coin.wownero && ref.watch(mnemonicWordCountStateProvider.state).state == @@ -356,7 +364,9 @@ class _RestoreOptionsViewState extends ConsumerState { onTap: chooseDesktopDate, controller: _dateController, ), - if (coin == Coin.monero || + if ((coin == Coin.monero && + ref.watch(mnemonicWordCountStateProvider.state).state == + 25) || coin == Coin.epicCash || (coin == Coin.wownero && ref.watch(mnemonicWordCountStateProvider.state).state == @@ -364,7 +374,9 @@ class _RestoreOptionsViewState extends ConsumerState { const SizedBox( height: 8, ), - if (coin == Coin.monero || + if ((coin == Coin.monero && + ref.watch(mnemonicWordCountStateProvider.state).state == + 25) || coin == Coin.epicCash || (coin == Coin.wownero && ref.watch(mnemonicWordCountStateProvider.state).state == @@ -385,7 +397,9 @@ class _RestoreOptionsViewState extends ConsumerState { ), ), ), - if (coin == Coin.monero || + if ((coin == Coin.monero && + ref.watch(mnemonicWordCountStateProvider.state).state == + 25) || coin == Coin.epicCash || (coin == Coin.wownero && ref.watch(mnemonicWordCountStateProvider.state).state == diff --git a/lib/utilities/constants.dart b/lib/utilities/constants.dart index db0543044..e0fd5e3df 100644 --- a/lib/utilities/constants.dart +++ b/lib/utilities/constants.dart @@ -184,7 +184,7 @@ abstract class Constants { values.addAll([24, 12]); case Coin.monero: - values.addAll([25]); + values.addAll([16, 25]); break; case Coin.wownero: values.addAll([14, 25]); diff --git a/lib/wallets/wallet/impl/monero_wallet.dart b/lib/wallets/wallet/impl/monero_wallet.dart index 5f681c774..9ed4b0da7 100644 --- a/lib/wallets/wallet/impl/monero_wallet.dart +++ b/lib/wallets/wallet/impl/monero_wallet.dart @@ -159,6 +159,7 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { type: WalletType.monero, trusted: node.trusted ?? false, ), + socksProxyAddress: null, ); } @@ -392,7 +393,7 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { final mnemonic = await getMnemonic(); final seedLength = mnemonic.trim().split(" ").length; - if (seedLength != 25) { + if (seedLength != 25 && seedLength != 16) { throw Exception("Invalid monero mnemonic length found: $seedLength"); } diff --git a/lib/wallets/wallet/impl/wownero_wallet.dart b/lib/wallets/wallet/impl/wownero_wallet.dart index 514f9e244..46ad5726a 100644 --- a/lib/wallets/wallet/impl/wownero_wallet.dart +++ b/lib/wallets/wallet/impl/wownero_wallet.dart @@ -149,6 +149,7 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { type: WalletType.wownero, trusted: node.trusted ?? false, ), + socksProxyAddress: null, ); } @@ -331,7 +332,8 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { // days: // 2))); // subtract a couple days to ensure we have a buffer for SWB // TODO(mrcyjanek): implement - final bufferedCreateHeight = 1; //getSeedHeightSync(wallet!.seed.trim()); + final bufferedCreateHeight = + 1; //getSeedHeightSync(wallet!.seed.trim()); await info.updateRestoreHeight( newRestoreHeight: bufferedCreateHeight, From 90fca36b779c56d56cf9cb28050d07bb3d50bcb9 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Tue, 30 Apr 2024 16:12:46 +0200 Subject: [PATCH 013/100] tor proxy support --- lib/wallets/wallet/impl/monero_wallet.dart | 9 ++++++++- lib/wallets/wallet/impl/wownero_wallet.dart | 12 +++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/lib/wallets/wallet/impl/monero_wallet.dart b/lib/wallets/wallet/impl/monero_wallet.dart index 9ed4b0da7..ce81d1552 100644 --- a/lib/wallets/wallet/impl/monero_wallet.dart +++ b/lib/wallets/wallet/impl/monero_wallet.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:io'; import 'dart:math'; import 'package:cw_core/monero_transaction_priority.dart'; @@ -21,6 +22,7 @@ import 'package:isar/isar.dart'; import 'package:stackwallet/db/hive/db.dart'; import 'package:stackwallet/models/isar/models/blockchain_data/address.dart'; import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart'; +import 'package:stackwallet/services/tor_service.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart'; import 'package:stackwallet/utilities/logger.dart'; @@ -153,13 +155,18 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { final node = getCurrentNode(); final host = Uri.parse(node.host).host; + ({InternetAddress host, int port})? proxy; + if (prefs.useTor) { + proxy = TorService.sharedInstance.getProxyInfo(); + } await CwBasedInterface.cwWalletBase?.connectToNode( node: Node( uri: "$host:${node.port}", type: WalletType.monero, trusted: node.trusted ?? false, ), - socksProxyAddress: null, + socksProxyAddress: + proxy == null ? null : "${proxy.host.address}:${proxy.port}", ); } diff --git a/lib/wallets/wallet/impl/wownero_wallet.dart b/lib/wallets/wallet/impl/wownero_wallet.dart index 46ad5726a..5c9e481a2 100644 --- a/lib/wallets/wallet/impl/wownero_wallet.dart +++ b/lib/wallets/wallet/impl/wownero_wallet.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:io'; import 'dart:math'; import 'package:cw_core/monero_transaction_priority.dart'; @@ -11,7 +12,6 @@ import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:cw_monero/api/exceptions/creation_transaction_exception.dart'; -import 'package:cw_wownero/api/wallet.dart'; import 'package:cw_wownero/pending_wownero_transaction.dart'; import 'package:cw_wownero/wownero_wallet.dart'; import 'package:decimal/decimal.dart'; @@ -23,6 +23,7 @@ import 'package:isar/isar.dart'; import 'package:stackwallet/db/hive/db.dart'; import 'package:stackwallet/models/isar/models/blockchain_data/address.dart'; import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart'; +import 'package:stackwallet/services/tor_service.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart'; import 'package:stackwallet/utilities/logger.dart'; @@ -143,13 +144,18 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { final node = getCurrentNode(); final host = Uri.parse(node.host).host; + ({InternetAddress host, int port})? proxy; + if (prefs.useTor) { + proxy = TorService.sharedInstance.getProxyInfo(); + } await CwBasedInterface.cwWalletBase?.connectToNode( node: Node( uri: "$host:${node.port}", type: WalletType.wownero, trusted: node.trusted ?? false, ), - socksProxyAddress: null, + socksProxyAddress: + proxy == null ? null : "${proxy.host.address}:${proxy.port}", ); } @@ -332,7 +338,7 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { // days: // 2))); // subtract a couple days to ensure we have a buffer for SWB // TODO(mrcyjanek): implement - final bufferedCreateHeight = + const bufferedCreateHeight = 1; //getSeedHeightSync(wallet!.seed.trim()); await info.updateRestoreHeight( From 491bc2475be902ed6eba8e8926f679a37876f702 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Tue, 30 Apr 2024 17:24:24 +0200 Subject: [PATCH 014/100] initial windows build support --- scripts/windows/build_all.sh | 6 +++--- scripts/windows/deps.sh | 4 ++-- windows/flutter/CMakeLists.txt | 7 ++++++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/scripts/windows/build_all.sh b/scripts/windows/build_all.sh index ee3c1b558..06c80bd54 100755 --- a/scripts/windows/build_all.sh +++ b/scripts/windows/build_all.sh @@ -7,9 +7,9 @@ source ../rust_version.sh set_rust_to_1671 mkdir -p build -(cd ../../crypto_plugins/flutter_libepiccash/scripts/windows && ./build_all.sh ) & -(cd ../../crypto_plugins/flutter_liblelantus/scripts/windows && ./build_all.sh ) & -(cd ../../crypto_plugins/flutter_libmonero/scripts/windows && ./build_all.sh) & +(cd ../../crypto_plugins/flutter_libepiccash/scripts/windows && ./build_all.sh ) +(cd ../../crypto_plugins/flutter_liblelantus/scripts/windows && ./build_all.sh ) +(cd ../../crypto_plugins/flutter_libmonero/scripts/windows && ./build_all.sh) wait echo "Done building" diff --git a/scripts/windows/deps.sh b/scripts/windows/deps.sh index 0f6642068..802ca8f8a 100644 --- a/scripts/windows/deps.sh +++ b/scripts/windows/deps.sh @@ -1,7 +1,7 @@ #!/bin/bash -(cd ../../crypto_plugins/flutter_libepiccash/scripts/windows && ./deps.sh ) & -(cd ../../crypto_plugins/flutter_liblelantus/scripts/windows && ./mxedeps.sh ) & +(cd ../../crypto_plugins/flutter_libepiccash/scripts/windows && ./deps.sh ) +(cd ../../crypto_plugins/flutter_liblelantus/scripts/windows && ./mxedeps.sh ) # (cd ../../crypto_plugins/flutter_libmonero/scripts/windows && ./monerodeps.sh && ./mxedeps.sh) & wait diff --git a/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt index 930d2071a..903f4899d 100644 --- a/windows/flutter/CMakeLists.txt +++ b/windows/flutter/CMakeLists.txt @@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -92,7 +97,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS From 76f0d20d2697bfe502c2b75be5588d867bc22026 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Tue, 30 Apr 2024 10:36:26 -0500 Subject: [PATCH 015/100] update flutter_libmonero to latest monerodart --- crypto_plugins/flutter_libmonero | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero index b25aa4845..e2168de1d 160000 --- a/crypto_plugins/flutter_libmonero +++ b/crypto_plugins/flutter_libmonero @@ -1 +1 @@ -Subproject commit b25aa48451aeb6f2ea4e9b3831789f6bdbfb90e1 +Subproject commit e2168de1d8f2cc3ab91dd031aa8f1580a1920358 From 25688a696e1d2c11f78cb52cba44c67eb65a6943 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Tue, 30 Apr 2024 11:18:08 -0500 Subject: [PATCH 016/100] pass nettype in flutter_libmonero and update its pubspec.lock --- crypto_plugins/flutter_libmonero | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero index e2168de1d..4be1ce1a4 160000 --- a/crypto_plugins/flutter_libmonero +++ b/crypto_plugins/flutter_libmonero @@ -1 +1 @@ -Subproject commit e2168de1d8f2cc3ab91dd031aa8f1580a1920358 +Subproject commit 4be1ce1a404b8488f03da77ae8d826e39d0cb846 From ef7afcb63402173a50e24ac377f07fafca7b5ed4 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Tue, 30 Apr 2024 13:14:03 -0500 Subject: [PATCH 017/100] update default monero seed length for ui display purposes --- lib/utilities/constants.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utilities/constants.dart b/lib/utilities/constants.dart index 77c034be1..8ea74f707 100644 --- a/lib/utilities/constants.dart +++ b/lib/utilities/constants.dart @@ -297,7 +297,7 @@ abstract class Constants { return 24; case Coin.monero: - return 25; + return 16; case Coin.bitcoinFrost: case Coin.bitcoinFrostTestNet: From 8ea347cacf50f4750e695c1ef4d4cec2912fcb4e Mon Sep 17 00:00:00 2001 From: sneurlax Date: Tue, 30 Apr 2024 14:44:00 -0500 Subject: [PATCH 018/100] initial flutter_libmonero windows support --- crypto_plugins/flutter_libmonero | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero index 4be1ce1a4..216c1a3ea 160000 --- a/crypto_plugins/flutter_libmonero +++ b/crypto_plugins/flutter_libmonero @@ -1 +1 @@ -Subproject commit 4be1ce1a404b8488f03da77ae8d826e39d0cb846 +Subproject commit 216c1a3ea1be8c97bd9148c5e47ef87db52b8515 From ed840ac2357b304770ed44ee19da428b5846fb9e Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Wed, 1 May 2024 16:42:54 +0200 Subject: [PATCH 019/100] windows support --- lib/main.dart | 8 ++----- .../add_wallet_view/add_wallet_view.dart | 6 ------ .../settings_menu/nodes_settings.dart | 6 ------ pubspec.lock | 4 ++-- windows/CMakeLists.txt | 21 +++++++++++++++++++ 5 files changed, 25 insertions(+), 20 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 54ccf3866..1d6a7128b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -212,12 +212,8 @@ void main(List args) async { } } - if (!Platform.isWindows) { - monero.onStartup(); - } - if (!Platform.isLinux && !Platform.isWindows) { - wownero.onStartup(); - } + monero.onStartup(); + wownero.onStartup(); // SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, // overlays: [SystemUiOverlay.bottom]); diff --git a/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart b/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart index 54b94efb3..88bf55833 100644 --- a/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart +++ b/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart @@ -127,12 +127,6 @@ class _AddWalletViewState extends ConsumerState { _searchFieldController = TextEditingController(); _searchFocusNode = FocusNode(); // _coinsTestnet.remove(Coin.firoTestNet); - if (Platform.isWindows) { - _coins.remove(Coin.monero); - _coins.remove(Coin.wownero); - } else if (Platform.isLinux) { - // _coins.remove(Coin.wownero); - } coinEntities.addAll(_coins.map((e) => CoinEntity(e))); diff --git a/lib/pages_desktop_specific/settings/settings_menu/nodes_settings.dart b/lib/pages_desktop_specific/settings/settings_menu/nodes_settings.dart index 1edeac7a6..20e8a4696 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/nodes_settings.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/nodes_settings.dart @@ -65,12 +65,6 @@ class _NodesSettings extends ConsumerState { void initState() { _coins = _coins.toList(); _coins.remove(Coin.firoTestNet); - if (Platform.isWindows) { - _coins.remove(Coin.monero); - _coins.remove(Coin.wownero); - } else if (Platform.isLinux) { - // _coins.remove(Coin.wownero); - } searchNodeController = TextEditingController(); searchNodeFocusNode = FocusNode(); diff --git a/pubspec.lock b/pubspec.lock index 7d2bdde86..e52c01710 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1144,8 +1144,8 @@ packages: dependency: transitive description: path: "." - ref: "7ba955fd9d975c7228ee43a744294399b48f9993" - resolved-ref: "7ba955fd9d975c7228ee43a744294399b48f9993" + ref: "6a17a405a1a260fa228b2f4fc94044088a4335ac" + resolved-ref: "6a17a405a1a260fa228b2f4fc94044088a4335ac" url: "https://git.mrcyjanek.net/mrcyjanek/monero.dart" source: git version: "0.0.0" diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index eddd46e4d..610881fcc 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -86,6 +86,27 @@ install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libepiccash install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_liblelantus/scripts/windows/build/libmobileliblelantus.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/monero/x86_64-w64-mingw32_libwallet2_api_c.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "monero_libwallet2_api_c.dll" + COMPONENT Runtime) + +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/wownero/x86_64-w64-mingw32_libwallet2_api_c.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "wownero_libwallet2_api_c.dll" + COMPONENT Runtime) + +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/wownero/x86_64-w64-mingw32_libgcc_s_seh-1.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "libgcc_s_seh-1.dll" + COMPONENT Runtime) + +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/wownero/x86_64-w64-mingw32_libpolyseed.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "libpolyseed.dll" + COMPONENT Runtime) + +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/wownero/x86_64-w64-mingw32_libssp-0.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "libssp-0.dll" + COMPONENT Runtime) + +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/wownero/x86_64-w64-mingw32_libstdc++-6.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "libstdc++-6.dll" + COMPONENT Runtime) + +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/wownero/x86_64-w64-mingw32_libwinpthread-1.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "libwinpthread-1.dll" + COMPONENT Runtime) + if(PLUGIN_BUNDLED_LIBRARIES) install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" From 53bccd20d15e7677c5ca7776157d660363e7c443 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 1 May 2024 19:28:05 -0500 Subject: [PATCH 020/100] update flutter_libmonero ref --- crypto_plugins/flutter_libmonero | 2 +- pubspec.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero index 216c1a3ea..a4a66396b 160000 --- a/crypto_plugins/flutter_libmonero +++ b/crypto_plugins/flutter_libmonero @@ -1 +1 @@ -Subproject commit 216c1a3ea1be8c97bd9148c5e47ef87db52b8515 +Subproject commit a4a66396bbfab7ac61a04eab0c0858f6900c83c3 diff --git a/pubspec.lock b/pubspec.lock index 5fbea128b..e6c510034 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1197,8 +1197,8 @@ packages: dependency: transitive description: path: "." - ref: "6a17a405a1a260fa228b2f4fc94044088a4335ac" - resolved-ref: "6a17a405a1a260fa228b2f4fc94044088a4335ac" + ref: "7ba955fd9d975c7228ee43a744294399b48f9993" + resolved-ref: "7ba955fd9d975c7228ee43a744294399b48f9993" url: "https://git.mrcyjanek.net/mrcyjanek/monero.dart" source: git version: "0.0.0" From 395fb2410047db238d49f6cf7f5ac98ec363d85f Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 3 May 2024 16:24:23 -0500 Subject: [PATCH 021/100] monerodart windows updates and submodule-pinning --- crypto_plugins/flutter_libmonero | 2 +- pubspec.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero index a4a66396b..e06c5b92a 160000 --- a/crypto_plugins/flutter_libmonero +++ b/crypto_plugins/flutter_libmonero @@ -1 +1 @@ -Subproject commit a4a66396bbfab7ac61a04eab0c0858f6900c83c3 +Subproject commit e06c5b92a2a98b78d5506e6472a0fac1fbba9a10 diff --git a/pubspec.lock b/pubspec.lock index e6c510034..5fbea128b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1197,8 +1197,8 @@ packages: dependency: transitive description: path: "." - ref: "7ba955fd9d975c7228ee43a744294399b48f9993" - resolved-ref: "7ba955fd9d975c7228ee43a744294399b48f9993" + ref: "6a17a405a1a260fa228b2f4fc94044088a4335ac" + resolved-ref: "6a17a405a1a260fa228b2f4fc94044088a4335ac" url: "https://git.mrcyjanek.net/mrcyjanek/monero.dart" source: git version: "0.0.0" From e94793cb4650f27c3e91e12db697c2c161504957 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 3 May 2024 16:33:26 -0500 Subject: [PATCH 022/100] Update building.md Remove duplicate and join into one command --- docs/building.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/building.md b/docs/building.md index d4d3ad6f4..100367ce0 100644 --- a/docs/building.md +++ b/docs/building.md @@ -46,7 +46,7 @@ Install [Rust](https://www.rust-lang.org/tools/install) with command: ``` curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh source ~/.bashrc -rustup install 1.67.1 +rustup install 1.67.1 1.72.0 1.73.0 rustup default 1.67.1 ``` From d5d5f1bcd2a035d562a94ee09b2115920e0a54dd Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 3 May 2024 16:41:56 -0500 Subject: [PATCH 023/100] Remove newline in building.md --- docs/building.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/building.md b/docs/building.md index 100367ce0..48943eba3 100644 --- a/docs/building.md +++ b/docs/building.md @@ -70,7 +70,6 @@ After installing the prerequisites listed above, download the code and init the git clone https://github.com/cypherstack/stack_wallet.git cd stack_wallet git submodule update --init --recursive - ``` Remove pre-installed system libraries for the following packages built by cryptography plugins in the crypto_plugins folder: `boost iconv libjson-dev libsecret openssl sodium unbound zmq`. You can use From 9035e2311d327f09d86c9f41da6cde016062c6f1 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 3 May 2024 17:06:43 -0500 Subject: [PATCH 024/100] Add g++ gcc gperf deps for monerodart See notes on https://github.com/cypherstack/stack_wallet/pull/818 --- docs/building.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/building.md b/docs/building.md index 48943eba3..af8c38c23 100644 --- a/docs/building.md +++ b/docs/building.md @@ -34,7 +34,7 @@ Make a Pixel 4 (API 30) x86_64 emulator with 2GB of storage space for emulation Install basic dependencies ``` -sudo apt-get install libssl-dev curl unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake libgit2-dev clang libncurses5-dev libncursesw5-dev zlib1g-dev llvm python3-distutils +sudo apt-get install libssl-dev curl unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake libgit2-dev clang libncurses5-dev libncursesw5-dev zlib1g-dev llvm python3-distutils g++ gcc gperf ``` The following *may* be needed for Android studio: From c4e1c3f23e3a00508ed06bb991b9b50aeb089b6e Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 3 May 2024 17:14:19 -0500 Subject: [PATCH 025/100] Remove redundant build script calls --- scripts/android/build_all.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/android/build_all.sh b/scripts/android/build_all.sh index 3bc005cf1..4fb525ab7 100755 --- a/scripts/android/build_all.sh +++ b/scripts/android/build_all.sh @@ -13,7 +13,7 @@ mkdir -p build PLUGINS_DIR=../../crypto_plugins (cd "${PLUGINS_DIR}"/flutter_liblelantus/scripts/android && ./build_all.sh ) -(cd "${PLUGINS_DIR}"/flutter_libepiccash/scripts/android && ./install_ndk.sh && ./build_openssl.sh && ./build_all.sh ) +(cd "${PLUGINS_DIR}"/flutter_libepiccash/scripts/android && ./build_all.sh ) (cd "${PLUGINS_DIR}"/flutter_libmonero/scripts/android/ && ./build_all.sh ) set_rust_to_1720 (cd "${PLUGINS_DIR}"/frostdart/scripts/android && ./build_all.sh ) From ea0d380bc2d12a6839ffc00ff699acd90beb2860 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 3 May 2024 17:15:45 -0500 Subject: [PATCH 026/100] Delete .fvmrc Otherwise we'd have to update it. Only I use fvm on macos --- .fvmrc | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 .fvmrc diff --git a/.fvmrc b/.fvmrc deleted file mode 100644 index 5fb0c142c..000000000 --- a/.fvmrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "flutter": "3.16.0" -} \ No newline at end of file From 89a687b9715a8a69590f01da76b83c2a8424c4ee Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 3 May 2024 17:18:02 -0500 Subject: [PATCH 027/100] partial reversion of c78fba7b364bc69ecedc6c71405506f9c02fcc6b otherwise we will need to maintain it and I'm the only one using fvm on macos --- .vscode/settings.json | 1 - 1 file changed, 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index a86b03d61..eba0d6cd7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,3 @@ { "dart.lineLength": 80, - "dart.flutterSdkPath": ".fvm/versions/3.16.0" } \ No newline at end of file From bcb39c39065d04c3907855e1139358732b3c184c Mon Sep 17 00:00:00 2001 From: julian Date: Fri, 3 May 2024 15:42:21 -0600 Subject: [PATCH 028/100] no need for class wide htt client property, and close the client before assigning a new one --- lib/wallets/wallet/impl/stellar_wallet.dart | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/wallets/wallet/impl/stellar_wallet.dart b/lib/wallets/wallet/impl/stellar_wallet.dart index edea60fe5..a2ad93083 100644 --- a/lib/wallets/wallet/impl/stellar_wallet.dart +++ b/lib/wallets/wallet/impl/stellar_wallet.dart @@ -46,7 +46,6 @@ class StellarWallet extends Bip39Wallet { // ============== Private ==================================================== stellar.StellarSDK? _stellarSdk; - HttpClient? _httpClient; Future _getBaseFee() async { final fees = await stellarSdk.feeStats.execute(); @@ -55,6 +54,7 @@ class StellarWallet extends Bip39Wallet { void _updateSdk() { final currentNode = getCurrentNode(); + HttpClient? _httpClient; // TODO [prio=med]: refactor out and call before requests in case Tor is enabled/disabled, listen to prefs change, or similar. if (prefs.useTor) { @@ -63,13 +63,21 @@ class StellarWallet extends Bip39Wallet { _httpClient = HttpClient(); SocksTCPClient.assignToHttpClient( - _httpClient!, [ProxySettings(proxyInfo.host, proxyInfo.port)]); - } else { - _httpClient = null; + _httpClient, + [ + ProxySettings( + proxyInfo.host, + proxyInfo.port, + ), + ], + ); } - _stellarSdk = stellar.StellarSDK("${currentNode.host}:${currentNode.port}", - httpClient: _httpClient); + _stellarSdk?.httpClient.close(); + _stellarSdk = stellar.StellarSDK( + "${currentNode.host}:${currentNode.port}", + httpClient: _httpClient, + ); } Future _accountExists(String accountId) async { From 8c082f3ed43d51cd66319fb3e6bb42d7a7044c1e Mon Sep 17 00:00:00 2001 From: julian Date: Fri, 3 May 2024 18:04:57 -0600 Subject: [PATCH 029/100] untested stellar tor listener --- lib/wallets/wallet/impl/stellar_wallet.dart | 118 +++++++++++++++----- 1 file changed, 90 insertions(+), 28 deletions(-) diff --git a/lib/wallets/wallet/impl/stellar_wallet.dart b/lib/wallets/wallet/impl/stellar_wallet.dart index a2ad93083..657fd7676 100644 --- a/lib/wallets/wallet/impl/stellar_wallet.dart +++ b/lib/wallets/wallet/impl/stellar_wallet.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:isar/isar.dart'; +import 'package:mutex/mutex.dart'; import 'package:socks5_proxy/socks.dart'; import 'package:stackwallet/models/balance.dart'; import 'package:stackwallet/models/isar/models/blockchain_data/address.dart'; @@ -11,6 +12,9 @@ import 'package:stackwallet/models/isar/models/blockchain_data/v2/input_v2.dart' import 'package:stackwallet/models/isar/models/blockchain_data/v2/output_v2.dart'; import 'package:stackwallet/models/isar/models/blockchain_data/v2/transaction_v2.dart'; import 'package:stackwallet/models/paymint/fee_object_model.dart'; +import 'package:stackwallet/services/event_bus/events/global/tor_connection_status_changed_event.dart'; +import 'package:stackwallet/services/event_bus/events/global/tor_status_changed_event.dart'; +import 'package:stackwallet/services/event_bus/global_event_bus.dart'; import 'package:stackwallet/services/tor_service.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart'; @@ -23,11 +27,47 @@ import 'package:stackwallet/wallets/wallet/intermediate/bip39_wallet.dart'; import 'package:stellar_flutter_sdk/stellar_flutter_sdk.dart' as stellar; class StellarWallet extends Bip39Wallet { - StellarWallet(CryptoCurrencyNetwork network) : super(Stellar(network)); + StellarWallet(CryptoCurrencyNetwork network) : super(Stellar(network)) { + final bus = GlobalEventBus.instance; - stellar.StellarSDK get stellarSdk { - if (_stellarSdk == null) { - _updateSdk(); + // Listen for tor status changes. + _torStatusListener = bus.on().listen( + (event) async { + switch (event.newStatus) { + case TorConnectionStatus.connecting: + if (!_torConnectingLock.isLocked) { + await _torConnectingLock.acquire(); + } + _requireMutex = true; + break; + + case TorConnectionStatus.connected: + case TorConnectionStatus.disconnected: + if (_torConnectingLock.isLocked) { + _torConnectingLock.release(); + } + _requireMutex = false; + break; + } + }, + ); + + // Listen for tor preference changes. + _torPreferenceListener = bus.on().listen( + (event) async { + _stellarSdk?.httpClient.close(); + _stellarSdk = null; + }, + ); + } + + Future get stellarSdk async { + if (_requireMutex) { + await _torConnectingLock.protect(() async { + _stellarSdk ??= _getFreshSdk(); + }); + } else { + _stellarSdk ??= _getFreshSdk(); } return _stellarSdk!; } @@ -44,19 +84,32 @@ class StellarWallet extends Bip39Wallet { } // ============== Private ==================================================== + // add finalizer to cancel stream subscription when all references to an + // instance of this becomes inaccessible + final _ = Finalizer( + (p0) { + p0._torPreferenceListener?.cancel(); + p0._torStatusListener?.cancel(); + }, + ); + + StreamSubscription? _torStatusListener; + StreamSubscription? _torPreferenceListener; + + final Mutex _torConnectingLock = Mutex(); + bool _requireMutex = false; stellar.StellarSDK? _stellarSdk; Future _getBaseFee() async { - final fees = await stellarSdk.feeStats.execute(); + final fees = await (await stellarSdk).feeStats.execute(); return int.parse(fees.lastLedgerBaseFee); } - void _updateSdk() { + stellar.StellarSDK _getFreshSdk() { final currentNode = getCurrentNode(); HttpClient? _httpClient; - // TODO [prio=med]: refactor out and call before requests in case Tor is enabled/disabled, listen to prefs change, or similar. if (prefs.useTor) { final ({InternetAddress host, int port}) proxyInfo = TorService.sharedInstance.getProxyInfo(); @@ -73,8 +126,7 @@ class StellarWallet extends Bip39Wallet { ); } - _stellarSdk?.httpClient.close(); - _stellarSdk = stellar.StellarSDK( + return stellar.StellarSDK( "${currentNode.host}:${currentNode.port}", httpClient: _httpClient, ); @@ -84,7 +136,8 @@ class StellarWallet extends Bip39Wallet { bool exists = false; try { - final receiverAccount = await stellarSdk.accounts.account(accountId); + final receiverAccount = + await (await stellarSdk).accounts.account(accountId); if (receiverAccount.accountId != "") { exists = true; } @@ -191,7 +244,8 @@ class StellarWallet extends Bip39Wallet { @override Future confirmSend({required TxData txData}) async { final senderKeyPair = await _getSenderKeyPair(index: 0); - final sender = await stellarSdk.accounts.account(senderKeyPair.accountId); + final sender = + await (await stellarSdk).accounts.account(senderKeyPair.accountId); final address = txData.recipients!.first.address; final amountToSend = txData.recipients!.first.amount; @@ -229,7 +283,7 @@ class StellarWallet extends Bip39Wallet { transaction.sign(senderKeyPair, stellarNetwork); try { - final response = await stellarSdk.submitTransaction(transaction); + final response = await (await stellarSdk).submitTransaction(transaction); if (!response.success) { throw Exception("${response.extras?.resultCodes?.transactionResultCode}" " ::: ${response.extras?.resultCodes?.operationsResultCodes}"); @@ -256,7 +310,7 @@ class StellarWallet extends Bip39Wallet { @override Future get fees async { - int fee = await _getBaseFee(); + final int fee = await _getBaseFee(); return FeeObject( numberOfBlocksFast: 1, numberOfBlocksAverage: 1, @@ -294,7 +348,8 @@ class StellarWallet extends Bip39Wallet { stellar.AccountResponse accountResponse; try { - accountResponse = await stellarSdk.accounts + accountResponse = await (await stellarSdk) + .accounts .account((await getCurrentReceivingAddress())!.value) .onError((error, stackTrace) => throw error!); } catch (e) { @@ -315,7 +370,7 @@ class StellarWallet extends Bip39Wallet { } } - for (stellar.Balance balance in accountResponse.balances) { + for (final stellar.Balance balance in accountResponse.balances) { switch (balance.assetType) { case stellar.Asset.TYPE_NATIVE: final swBalance = Balance( @@ -352,7 +407,8 @@ class StellarWallet extends Bip39Wallet { @override Future updateChainHeight() async { try { - final height = await stellarSdk.ledgers + final height = await (await stellarSdk) + .ledgers .order(stellar.RequestBuilderOrder.DESC) .limit(1) .execute() @@ -370,7 +426,8 @@ class StellarWallet extends Bip39Wallet { @override Future updateNode() async { - _updateSdk(); + _stellarSdk?.httpClient.close(); + _stellarSdk = _getFreshSdk(); } @override @@ -378,10 +435,11 @@ class StellarWallet extends Bip39Wallet { try { final myAddress = (await getCurrentReceivingAddress())!; - List transactionList = []; + final List transactionList = []; stellar.Page payments; try { - payments = await stellarSdk.payments + payments = await (await stellarSdk) + .payments .forAccount(myAddress.value) .order(stellar.RequestBuilderOrder.DESC) .execute(); @@ -401,7 +459,7 @@ class StellarWallet extends Bip39Wallet { rethrow; } } - for (stellar.OperationResponse response in payments.records!) { + for (final stellar.OperationResponse response in payments.records!) { // PaymentOperationResponse por; if (response is stellar.PaymentOperationResponse) { final por = response; @@ -431,7 +489,8 @@ class StellarWallet extends Bip39Wallet { final List outputs = []; final List inputs = []; - OutputV2 output = OutputV2.isarCantDoRequiredInDefaultConstructor( + final OutputV2 output = + OutputV2.isarCantDoRequiredInDefaultConstructor( scriptPubKeyHex: "00", valueStringSats: amount.raw.toString(), addresses: [ @@ -439,7 +498,7 @@ class StellarWallet extends Bip39Wallet { ], walletOwns: addressTo == myAddress.value, ); - InputV2 input = InputV2.isarCantDoRequiredInDefaultConstructor( + final InputV2 input = InputV2.isarCantDoRequiredInDefaultConstructor( scriptSigHex: null, scriptSigAsm: null, sequence: null, @@ -459,8 +518,9 @@ class StellarWallet extends Bip39Wallet { int height = 0; //Query the transaction linked to the payment, // por.transaction returns a null sometimes - stellar.TransactionResponse tx = - await stellarSdk.transactions.transaction(por.transactionHash!); + final stellar.TransactionResponse tx = await (await stellarSdk) + .transactions + .transaction(por.transactionHash!); if (tx.hash.isNotEmpty) { fee = tx.feeCharged!; @@ -511,7 +571,8 @@ class StellarWallet extends Bip39Wallet { final List outputs = []; final List inputs = []; - OutputV2 output = OutputV2.isarCantDoRequiredInDefaultConstructor( + final OutputV2 output = + OutputV2.isarCantDoRequiredInDefaultConstructor( scriptPubKeyHex: "00", valueStringSats: amount.raw.toString(), addresses: [ @@ -520,7 +581,7 @@ class StellarWallet extends Bip39Wallet { ], walletOwns: caor.sourceAccount! == myAddress.value, ); - InputV2 input = InputV2.isarCantDoRequiredInDefaultConstructor( + final InputV2 input = InputV2.isarCantDoRequiredInDefaultConstructor( scriptSigHex: null, scriptSigAsm: null, sequence: null, @@ -541,8 +602,9 @@ class StellarWallet extends Bip39Wallet { int fee = 0; int height = 0; - final tx = - await stellarSdk.transactions.transaction(caor.transactionHash!); + final tx = await (await stellarSdk) + .transactions + .transaction(caor.transactionHash!); if (tx.hash.isNotEmpty) { fee = tx.feeCharged!; height = tx.ledger; From 6b6e2097127f725445ef011eb784411f2de6ad99 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 3 May 2024 19:24:38 -0500 Subject: [PATCH 030/100] revert to official package with tor support merged --- pubspec.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index fb5a9f31c..86531ddad 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -156,10 +156,7 @@ dependencies: desktop_drop: ^0.4.1 nanodart: ^2.0.0 basic_utils: ^5.5.4 - stellar_flutter_sdk: # ^1.5.3 - git: # TODO [prio=low]: Revert to official package once Tor support is merged upstream. - url: https://github.com/cypherstack/stellar_flutter_sdk.git - ref: eca1d730e952cf6a6d64502f977cfc03876b75d4 # tor-backport branch (based on 1.5.3). + stellar_flutter_sdk: ^1.7.8 bip340: ^0.2.0 # tezart: ^2.0.5 tezart: From 0221841ee3d8769ef83a1462346d293cb6247efa Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 3 May 2024 19:56:27 -0500 Subject: [PATCH 031/100] listen to Tor status changes and update node accordingly --- lib/wallets/wallet/impl/monero_wallet.dart | 79 ++++++++++++++++++--- lib/wallets/wallet/impl/wownero_wallet.dart | 79 ++++++++++++++++++--- 2 files changed, 138 insertions(+), 20 deletions(-) diff --git a/lib/wallets/wallet/impl/monero_wallet.dart b/lib/wallets/wallet/impl/monero_wallet.dart index ce81d1552..9b83b150b 100644 --- a/lib/wallets/wallet/impl/monero_wallet.dart +++ b/lib/wallets/wallet/impl/monero_wallet.dart @@ -19,9 +19,13 @@ import 'package:flutter_libmonero/core/wallet_creation_service.dart'; import 'package:flutter_libmonero/monero/monero.dart' as xmr_dart; import 'package:flutter_libmonero/view_model/send/output.dart' as monero_output; import 'package:isar/isar.dart'; +import 'package:mutex/mutex.dart'; import 'package:stackwallet/db/hive/db.dart'; import 'package:stackwallet/models/isar/models/blockchain_data/address.dart'; import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart'; +import 'package:stackwallet/services/event_bus/events/global/tor_connection_status_changed_event.dart'; +import 'package:stackwallet/services/event_bus/events/global/tor_status_changed_event.dart'; +import 'package:stackwallet/services/event_bus/global_event_bus.dart'; import 'package:stackwallet/services/tor_service.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart'; @@ -35,7 +39,38 @@ import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/cw_based_inte import 'package:tuple/tuple.dart'; class MoneroWallet extends CryptonoteWallet with CwBasedInterface { - MoneroWallet(CryptoCurrencyNetwork network) : super(Monero(network)); + MoneroWallet(CryptoCurrencyNetwork network) : super(Monero(network)) { + final bus = GlobalEventBus.instance; + + // Listen for tor status changes. + _torStatusListener = bus.on().listen( + (event) async { + switch (event.newStatus) { + case TorConnectionStatus.connecting: + if (!_torConnectingLock.isLocked) { + await _torConnectingLock.acquire(); + } + _requireMutex = true; + break; + + case TorConnectionStatus.connected: + case TorConnectionStatus.disconnected: + if (_torConnectingLock.isLocked) { + _torConnectingLock.release(); + } + _requireMutex = false; + break; + } + }, + ); + + // Listen for tor preference changes. + _torPreferenceListener = bus.on().listen( + (event) async { + await updateNode(); + }, + ); + } @override Address addressFor({required int index, int account = 0}) { @@ -159,15 +194,31 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { if (prefs.useTor) { proxy = TorService.sharedInstance.getProxyInfo(); } - await CwBasedInterface.cwWalletBase?.connectToNode( - node: Node( - uri: "$host:${node.port}", - type: WalletType.monero, - trusted: node.trusted ?? false, - ), - socksProxyAddress: - proxy == null ? null : "${proxy.host.address}:${proxy.port}", - ); + if (_requireMutex) { + await _torConnectingLock.protect(() async { + await CwBasedInterface.cwWalletBase?.connectToNode( + node: Node( + uri: "$host:${node.port}", + type: WalletType.monero, + trusted: node.trusted ?? false, + ), + socksProxyAddress: + proxy == null ? null : "${proxy.host.address}:${proxy.port}", + ); + }); + } else { + await CwBasedInterface.cwWalletBase?.connectToNode( + node: Node( + uri: "$host:${node.port}", + type: WalletType.monero, + trusted: node.trusted ?? false, + ), + socksProxyAddress: + proxy == null ? null : "${proxy.host.address}:${proxy.port}", + ); + } + + return; } @override @@ -655,4 +706,12 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { return info.cachedBalance.total; } } + + // ============== Private ==================================================== + + StreamSubscription? _torStatusListener; + StreamSubscription? _torPreferenceListener; + + final Mutex _torConnectingLock = Mutex(); + bool _requireMutex = false; } diff --git a/lib/wallets/wallet/impl/wownero_wallet.dart b/lib/wallets/wallet/impl/wownero_wallet.dart index 5c9e481a2..51c9c159c 100644 --- a/lib/wallets/wallet/impl/wownero_wallet.dart +++ b/lib/wallets/wallet/impl/wownero_wallet.dart @@ -20,9 +20,13 @@ import 'package:flutter_libmonero/view_model/send/output.dart' as wownero_output; import 'package:flutter_libmonero/wownero/wownero.dart' as wow_dart; import 'package:isar/isar.dart'; +import 'package:mutex/mutex.dart'; import 'package:stackwallet/db/hive/db.dart'; import 'package:stackwallet/models/isar/models/blockchain_data/address.dart'; import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart'; +import 'package:stackwallet/services/event_bus/events/global/tor_connection_status_changed_event.dart'; +import 'package:stackwallet/services/event_bus/events/global/tor_status_changed_event.dart'; +import 'package:stackwallet/services/event_bus/global_event_bus.dart'; import 'package:stackwallet/services/tor_service.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart'; @@ -36,7 +40,38 @@ import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/cw_based_inte import 'package:tuple/tuple.dart'; class WowneroWallet extends CryptonoteWallet with CwBasedInterface { - WowneroWallet(CryptoCurrencyNetwork network) : super(Wownero(network)); + WowneroWallet(CryptoCurrencyNetwork network) : super(Wownero(network)) { + final bus = GlobalEventBus.instance; + + // Listen for tor status changes. + _torStatusListener = bus.on().listen( + (event) async { + switch (event.newStatus) { + case TorConnectionStatus.connecting: + if (!_torConnectingLock.isLocked) { + await _torConnectingLock.acquire(); + } + _requireMutex = true; + break; + + case TorConnectionStatus.connected: + case TorConnectionStatus.disconnected: + if (_torConnectingLock.isLocked) { + _torConnectingLock.release(); + } + _requireMutex = false; + break; + } + }, + ); + + // Listen for tor preference changes. + _torPreferenceListener = bus.on().listen( + (event) async { + await updateNode(); + }, + ); + } @override Address addressFor({required int index, int account = 0}) { @@ -148,15 +183,31 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { if (prefs.useTor) { proxy = TorService.sharedInstance.getProxyInfo(); } - await CwBasedInterface.cwWalletBase?.connectToNode( - node: Node( - uri: "$host:${node.port}", - type: WalletType.wownero, - trusted: node.trusted ?? false, - ), - socksProxyAddress: - proxy == null ? null : "${proxy.host.address}:${proxy.port}", - ); + if (_requireMutex) { + await _torConnectingLock.protect(() async { + await CwBasedInterface.cwWalletBase?.connectToNode( + node: Node( + uri: "$host:${node.port}", + type: WalletType.wownero, + trusted: node.trusted ?? false, + ), + socksProxyAddress: + proxy == null ? null : "${proxy.host.address}:${proxy.port}", + ); + }); + } else { + await CwBasedInterface.cwWalletBase?.connectToNode( + node: Node( + uri: "$host:${node.port}", + type: WalletType.wownero, + trusted: node.trusted ?? false, + ), + socksProxyAddress: + proxy == null ? null : "${proxy.host.address}:${proxy.port}", + ); + } + + return; } @override @@ -703,4 +754,12 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { return info.cachedBalance.total; } } + + // ============== Private ==================================================== + + StreamSubscription? _torStatusListener; + StreamSubscription? _torPreferenceListener; + + final Mutex _torConnectingLock = Mutex(); + bool _requireMutex = false; } From 6883f3d0931e5019d999c76f7c112c07d6ccab17 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 3 May 2024 19:56:39 -0500 Subject: [PATCH 032/100] warnings and formatting --- lib/wallets/wallet/impl/monero_wallet.dart | 16 ++++++++-------- lib/wallets/wallet/impl/wownero_wallet.dart | 18 +++++++++--------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/wallets/wallet/impl/monero_wallet.dart b/lib/wallets/wallet/impl/monero_wallet.dart index 9b83b150b..3f02954d8 100644 --- a/lib/wallets/wallet/impl/monero_wallet.dart +++ b/lib/wallets/wallet/impl/monero_wallet.dart @@ -74,7 +74,7 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { @override Address addressFor({required int index, int account = 0}) { - String address = (CwBasedInterface.cwWalletBase as MoneroWalletBase) + final String address = (CwBasedInterface.cwWalletBase as MoneroWalletBase) .getTransactionAddress(account, index); final newReceivingAddress = Address( @@ -438,7 +438,7 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { // clear blockchain info await mainDB.deleteWalletBlockchainData(walletId); - var restoreHeight = + final restoreHeight = CwBasedInterface.cwWalletBase?.walletInfo.restoreHeight; highestPercentCached = 0; await CwBasedInterface.cwWalletBase?.rescan(height: restoreHeight ?? 0); @@ -467,7 +467,7 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { .createMoneroWalletService(DB.instance.moneroWalletInfoBox); WalletInfo walletInfo; WalletCredentials credentials; - String name = walletId; + final String name = walletId; final dirPath = await pathForWalletDir(name: name, type: WalletType.monero); final path = await pathForWallet(name: name, type: WalletType.monero); @@ -569,12 +569,12 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { isSendAll = true; } - List outputs = []; + final List outputs = []; for (final recipient in txData.recipients!) { final output = monero_output.Output(CwBasedInterface.cwWalletBase!); output.address = recipient.address; output.sendAll = isSendAll; - String amountToSend = recipient.amount.decimal.toString(); + final String amountToSend = recipient.amount.decimal.toString(); output.setCryptoAmount(amountToSend); outputs.add(output); } @@ -594,7 +594,7 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { level: LogLevel.Warning); } - PendingMoneroTransaction pendingMoneroTransaction = + final PendingMoneroTransaction pendingMoneroTransaction = await (awaitPendingTransaction!) as PendingMoneroTransaction; final realFee = Amount.fromDecimal( Decimal.parse(pendingMoneroTransaction.feeFormatted), @@ -676,7 +676,7 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { ?.entries; if (balanceEntries != null) { int bal = 0; - for (var element in balanceEntries) { + for (final element in balanceEntries) { bal = bal + element.value.fullBalance; } return Amount( @@ -689,7 +689,7 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { .transactionHistory! .transactions; int transactionBalance = 0; - for (var tx in transactions!.entries) { + for (final tx in transactions!.entries) { if (tx.value.direction == TransactionDirection.incoming) { transactionBalance += tx.value.amount!; } else { diff --git a/lib/wallets/wallet/impl/wownero_wallet.dart b/lib/wallets/wallet/impl/wownero_wallet.dart index 51c9c159c..b9ec28b8d 100644 --- a/lib/wallets/wallet/impl/wownero_wallet.dart +++ b/lib/wallets/wallet/impl/wownero_wallet.dart @@ -75,7 +75,7 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { @override Address addressFor({required int index, int account = 0}) { - String address = (CwBasedInterface.cwWalletBase as WowneroWalletBase) + final String address = (CwBasedInterface.cwWalletBase as WowneroWalletBase) .getTransactionAddress(account, index); final newReceivingAddress = Address( @@ -257,7 +257,7 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { final List> txnsData = []; if (transactions != null) { - for (var tx in transactions.entries) { + for (final tx in transactions.entries) { Address? address; TransactionType type; if (tx.value.direction == TransactionDirection.incoming) { @@ -479,7 +479,7 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { // clear blockchain info await mainDB.deleteWalletBlockchainData(walletId); - var restoreHeight = + final restoreHeight = CwBasedInterface.cwWalletBase?.walletInfo.restoreHeight; highestPercentCached = 0; await CwBasedInterface.cwWalletBase?.rescan(height: restoreHeight ?? 0); @@ -515,7 +515,7 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { .createWowneroWalletService(DB.instance.moneroWalletInfoBox); WalletInfo walletInfo; WalletCredentials credentials; - String name = walletId; + final String name = walletId; final dirPath = await pathForWalletDir(name: name, type: WalletType.wownero); final path = await pathForWallet(name: name, type: WalletType.wownero); @@ -617,13 +617,13 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { isSendAll = true; } - List outputs = []; + final List outputs = []; for (final recipient in txData.recipients!) { final output = wownero_output.Output(CwBasedInterface.cwWalletBase!); output.address = recipient.address; output.sendAll = isSendAll; - String amountToSend = recipient.amount.decimal.toString(); + final String amountToSend = recipient.amount.decimal.toString(); output.setCryptoAmount(amountToSend); outputs.add(output); } @@ -643,7 +643,7 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { level: LogLevel.Warning); } - PendingWowneroTransaction pendingWowneroTransaction = + final PendingWowneroTransaction pendingWowneroTransaction = await (awaitPendingTransaction!) as PendingWowneroTransaction; final realFee = Amount.fromDecimal( Decimal.parse(pendingWowneroTransaction.feeFormatted), @@ -726,7 +726,7 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { ?.entries; if (balanceEntries != null) { int bal = 0; - for (var element in balanceEntries) { + for (final element in balanceEntries) { bal = bal + element.value.fullBalance; } return Amount( @@ -737,7 +737,7 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { final transactions = CwBasedInterface.cwWalletBase!.transactionHistory!.transactions; int transactionBalance = 0; - for (var tx in transactions!.entries) { + for (final tx in transactions!.entries) { if (tx.value.direction == TransactionDirection.incoming) { transactionBalance += tx.value.amount!; } else { From e472f9d6a9e4b864d1560dcd6debd2354cc164aa Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 3 May 2024 20:04:31 -0500 Subject: [PATCH 033/100] update tezart's dio dep --- pubspec.lock | 23 +++++++++++------------ pubspec.yaml | 2 +- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index e81aae914..4baa4c14c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -495,10 +495,10 @@ packages: dependency: transitive description: name: dio - sha256: "7d328c4d898a61efc3cd93655a0955858e29a0aa647f0f9e02d59b3bb275e2e8" + sha256: "11e40df547d418cc0c4900a9318b26304e665da6fa4755399a9ff9efd09034b5" url: "https://pub.dev" source: hosted - version: "4.0.6" + version: "5.4.3+1" dropdown_button2: dependency: "direct main" description: @@ -1414,10 +1414,10 @@ packages: dependency: transitive description: name: pretty_dio_logger - sha256: "948f7eeb36e7aa0760b51c1a8e3331d4b21e36fabd39efca81f585ed93893544" + sha256: "00b80053063935cf9a6190da344c5373b9d0e92da4c944c878ff2fbef0ef6dc2" url: "https://pub.dev" source: hosted - version: "1.2.0-beta-1" + version: "1.3.1" process: dependency: transitive description: @@ -1661,12 +1661,11 @@ packages: stellar_flutter_sdk: dependency: "direct main" description: - path: "." - ref: eca1d730e952cf6a6d64502f977cfc03876b75d4 - resolved-ref: eca1d730e952cf6a6d64502f977cfc03876b75d4 - url: "https://github.com/cypherstack/stellar_flutter_sdk.git" - source: git - version: "1.5.3" + name: stellar_flutter_sdk + sha256: "574e8f40a1a1a9b18a735272196c8d3c8168a669efc8460a4d5d6f45151e8dce" + url: "https://pub.dev" + source: hosted + version: "1.7.8" stream_channel: dependency: "direct main" description: @@ -1743,8 +1742,8 @@ packages: dependency: "direct main" description: path: "." - ref: "1fb2669e2b530367a449217e952f220d5e667043" - resolved-ref: "1fb2669e2b530367a449217e952f220d5e667043" + ref: f31f8f857665d85338824ae171aba4c629c3ba6f + resolved-ref: f31f8f857665d85338824ae171aba4c629c3ba6f url: "https://github.com/cypherstack/tezart.git" source: git version: "2.0.5" diff --git a/pubspec.yaml b/pubspec.yaml index 86531ddad..5985ffdf0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -162,7 +162,7 @@ dependencies: tezart: git: url: https://github.com/cypherstack/tezart.git - ref: 1fb2669e2b530367a449217e952f220d5e667043 + ref: f31f8f857665d85338824ae171aba4c629c3ba6f socks5_proxy: ^1.0.3+dev.3 convert: ^3.1.1 flutter_hooks: ^0.20.3 From 1e946a76b9de126faa6828e49cb39698ad086ef6 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 3 May 2024 20:18:47 -0500 Subject: [PATCH 034/100] bump build number --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 5985ffdf0..6f613420c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: Stack Wallet # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 2.0.0+219 +version: 2.0.0+220 environment: sdk: ">=3.3.3 <4.0.0" From a3950c54781fb4b8956167ceb5cf504b3ff5bc4e Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 3 May 2024 20:31:04 -0500 Subject: [PATCH 035/100] tezart dio fix --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 6f613420c..bfa794af0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -162,7 +162,7 @@ dependencies: tezart: git: url: https://github.com/cypherstack/tezart.git - ref: f31f8f857665d85338824ae171aba4c629c3ba6f + ref: 13fa937ea9a9fc34caf047e068df9535f65c27ad socks5_proxy: ^1.0.3+dev.3 convert: ^3.1.1 flutter_hooks: ^0.20.3 From 85fc443f2ce3d2bfe8887fcbf0c76f6bc2b1208f Mon Sep 17 00:00:00 2001 From: julian Date: Fri, 3 May 2024 20:08:29 -0600 Subject: [PATCH 036/100] tagged libsecret version --- scripts/linux/build_secure_storage_deps.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/linux/build_secure_storage_deps.sh b/scripts/linux/build_secure_storage_deps.sh index e63e38665..aff3097dc 100755 --- a/scripts/linux/build_secure_storage_deps.sh +++ b/scripts/linux/build_secure_storage_deps.sh @@ -1,6 +1,7 @@ #!/bin/bash LINUX_DIRECTORY=$(pwd) JSONCPP_TAG=1.7.4 +LIBSECRET_TAG=0.21.4 mkdir -p build # Build JsonCPP @@ -24,8 +25,9 @@ cd "$LINUX_DIRECTORY" || exit 1 #pip3 install --user meson markdown tomli --upgrade # pip3 install --user gi-docgen cd build || exit 1 -git -C libsecret pull || git clone https://gitlab.gnome.org/GNOME/libsecret.git libsecret +git -C libsecret pull origin $LIBSECRET_TAG || git clone https://gitlab.gnome.org/GNOME/libsecret.git libsecret cd libsecret || exit 1 +git checkout $LIBSECRET_TAG if ! [ -x "$(command -v meson)" ]; then echo 'Error: meson is not installed.' >&2 exit 1 From 5cf153b9a14a705b7b969141ab44399ad370bc66 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 3 May 2024 23:00:03 -0500 Subject: [PATCH 037/100] brew sodium->libsodium --- docs/building.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/building.md b/docs/building.md index 3154526df..33509a8cd 100644 --- a/docs/building.md +++ b/docs/building.md @@ -182,7 +182,7 @@ Download and install [Homebrew](https://brew.sh/). The following command can in After installing Homebrew, install the following packages: ``` -brew install autoconf automake boost berkeley-db ca-certificates cbindgen cmake cmake cocoapods curl git libssh2 make openssl@1.1 openssl@3 perl pkg-config rustup-init sodium unbound unzip xz zmq +brew install autoconf automake boost berkeley-db ca-certificates cbindgen cmake cocoapods curl git libssh2 libsodium make openssl@1.1 openssl@3 perl pkg-config rustup-init unbound unzip xz zmq ``` The following brew formula *may* be needed: From ab234b72e697fcbd500290f4f6f9fa4f124538c9 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 3 May 2024 23:02:11 -0500 Subject: [PATCH 038/100] flutter 3.19 beta->3.19.5 --- docs/building.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/building.md b/docs/building.md index 33509a8cd..dc65fe6cc 100644 --- a/docs/building.md +++ b/docs/building.md @@ -12,7 +12,7 @@ Here you will find instructions on how to install the necessary tools for buildi The following instructions are for building and running on a Linux host. Alternatively, see the [Mac](#mac-host) and/or [Windows](#windows-host) section. This entire section (except for the Android Studio section) needs to be completed in WSL if building on a Windows host. ### Flutter -Install Flutter 3.19 beta (3.19.0-0.1.pre) by following these instructions: https://docs.flutter.dev/get-started/install/linux/desktop?tab=download#install-the-flutter-sdk. You can also clone https://github.com/flutter/flutter, check out the `3.19.0-0.1.pre` tag, and add its `flutter/bin` folder to your PATH. Run `flutter doctor` in a terminal to confirm its installation. +Install Flutter 3.19.5 by following these instructions: https://docs.flutter.dev/get-started/install/linux/desktop?tab=download#install-the-flutter-sdk. You can also clone https://github.com/flutter/flutter, check out the `3.19.5` tag, and add its `flutter/bin` folder to your PATH. Run `flutter doctor` in a terminal to confirm its installation. ### Android Studio Install Android Studio. Follow instructions here [https://developer.android.com/studio/install#linux](https://developer.android.com/studio/install#linux) or install via snap: From dd935fec9b869950cca1d41e2fd4fd9aa5cc1fcb Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Mon, 6 May 2024 10:13:37 +0200 Subject: [PATCH 039/100] remove android symlinks --- .gitignore | 8 +++++++- android/app/src/main/jniLibs/arm64-v8a/.gitkeep | 0 .../main/jniLibs/arm64-v8a/libmonero_libwallet2_api_c.so | 1 - android/app/src/main/jniLibs/armeabi-v7a/.gitkeep | 0 .../jniLibs/armeabi-v7a/libmonero_libwallet2_api_c.so | 1 - android/app/src/main/jniLibs/x86_64/.gitkeep | 0 .../src/main/jniLibs/x86_64/libmonero_libwallet2_api_c.so | 1 - 7 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 android/app/src/main/jniLibs/arm64-v8a/.gitkeep delete mode 120000 android/app/src/main/jniLibs/arm64-v8a/libmonero_libwallet2_api_c.so create mode 100644 android/app/src/main/jniLibs/armeabi-v7a/.gitkeep delete mode 120000 android/app/src/main/jniLibs/armeabi-v7a/libmonero_libwallet2_api_c.so create mode 100644 android/app/src/main/jniLibs/x86_64/.gitkeep delete mode 120000 android/app/src/main/jniLibs/x86_64/libmonero_libwallet2_api_c.so diff --git a/.gitignore b/.gitignore index 144745e55..8377786de 100644 --- a/.gitignore +++ b/.gitignore @@ -61,4 +61,10 @@ secp256k1.dll /libisar.so # FVM Version Cache -.fvm/ \ No newline at end of file +.fvm/ +android/app/src/main/jniLibs/arm64-v8a/libwownero_wallet2_api_c.so +android/app/src/main/jniLibs/arm64-v8a/libmonero_wallet2_api_c.so +android/app/src/main/jniLibs/armeabi-v7a/libmonero_wallet2_api_c.so +android/app/src/main/jniLibs/armeabi-v7a/libwownero_wallet2_api_c.so +android/app/src/main/jniLibs/x86_64/libmonero_wallet2_api_c.so +android/app/src/main/jniLibs/x86_64/libwownero_wallet2_api_c.so diff --git a/android/app/src/main/jniLibs/arm64-v8a/.gitkeep b/android/app/src/main/jniLibs/arm64-v8a/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/android/app/src/main/jniLibs/arm64-v8a/libmonero_libwallet2_api_c.so b/android/app/src/main/jniLibs/arm64-v8a/libmonero_libwallet2_api_c.so deleted file mode 120000 index 49289969b..000000000 --- a/android/app/src/main/jniLibs/arm64-v8a/libmonero_libwallet2_api_c.so +++ /dev/null @@ -1 +0,0 @@ -../../../../../../crypto_plugins/flutter_libmonero/scripts/monero_c/release/monero/aarch64-linux-android_libwallet2_api_c.so \ No newline at end of file diff --git a/android/app/src/main/jniLibs/armeabi-v7a/.gitkeep b/android/app/src/main/jniLibs/armeabi-v7a/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/android/app/src/main/jniLibs/armeabi-v7a/libmonero_libwallet2_api_c.so b/android/app/src/main/jniLibs/armeabi-v7a/libmonero_libwallet2_api_c.so deleted file mode 120000 index a69eec096..000000000 --- a/android/app/src/main/jniLibs/armeabi-v7a/libmonero_libwallet2_api_c.so +++ /dev/null @@ -1 +0,0 @@ -../../../../../../crypto_plugins/flutter_libmonero/scripts/monero_c/release/monero/arm-linux-androideabi_libwallet2_api_c.so \ No newline at end of file diff --git a/android/app/src/main/jniLibs/x86_64/.gitkeep b/android/app/src/main/jniLibs/x86_64/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/android/app/src/main/jniLibs/x86_64/libmonero_libwallet2_api_c.so b/android/app/src/main/jniLibs/x86_64/libmonero_libwallet2_api_c.so deleted file mode 120000 index 5c18d1cee..000000000 --- a/android/app/src/main/jniLibs/x86_64/libmonero_libwallet2_api_c.so +++ /dev/null @@ -1 +0,0 @@ -../../../../../../crypto_plugins/flutter_libmonero/scripts/monero_c/release/monero/x86_64-linux-android_libwallet2_api_c.so \ No newline at end of file From 143a1997c956925b1d69f871287e546b67c2d93c Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Tue, 7 May 2024 16:00:30 +0200 Subject: [PATCH 040/100] update build scripts for macos --- .gitignore | 2 ++ macos/monero_libwallet2_api_c.dylib | 1 - macos/wownero_libwallet2_api_c.dylib | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) delete mode 120000 macos/monero_libwallet2_api_c.dylib delete mode 120000 macos/wownero_libwallet2_api_c.dylib diff --git a/.gitignore b/.gitignore index 8377786de..90ad3f2d6 100644 --- a/.gitignore +++ b/.gitignore @@ -68,3 +68,5 @@ android/app/src/main/jniLibs/armeabi-v7a/libmonero_wallet2_api_c.so android/app/src/main/jniLibs/armeabi-v7a/libwownero_wallet2_api_c.so android/app/src/main/jniLibs/x86_64/libmonero_wallet2_api_c.so android/app/src/main/jniLibs/x86_64/libwownero_wallet2_api_c.so +macos/monero_wallet2_api_c.dylib +macos/wownero_wallet2_api_c.dylib diff --git a/macos/monero_libwallet2_api_c.dylib b/macos/monero_libwallet2_api_c.dylib deleted file mode 120000 index da01b591e..000000000 --- a/macos/monero_libwallet2_api_c.dylib +++ /dev/null @@ -1 +0,0 @@ -../crypto_plugins/flutter_libmonero/scripts/monero_c/release/monero/host-apple-darwin_libwallet2_api_c.dylib \ No newline at end of file diff --git a/macos/wownero_libwallet2_api_c.dylib b/macos/wownero_libwallet2_api_c.dylib deleted file mode 120000 index f0a776dc1..000000000 --- a/macos/wownero_libwallet2_api_c.dylib +++ /dev/null @@ -1 +0,0 @@ -../crypto_plugins/flutter_libmonero/scripts/monero_c/release/wownero/host-apple-darwin_libwallet2_api_c.dylib \ No newline at end of file From e33712f3db98cad526c0f59bc05d417ef0ae4194 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Tue, 7 May 2024 16:03:10 +0200 Subject: [PATCH 041/100] other things from review --- .fvmrc | 3 --- .vscode/settings.json | 1 - 2 files changed, 4 deletions(-) delete mode 100644 .fvmrc diff --git a/.fvmrc b/.fvmrc deleted file mode 100644 index 5fb0c142c..000000000 --- a/.fvmrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "flutter": "3.16.0" -} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index a86b03d61..eba0d6cd7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,3 @@ { "dart.lineLength": 80, - "dart.flutterSdkPath": ".fvm/versions/3.16.0" } \ No newline at end of file From 039a723af3f2b0362b260e28e3ba0ea29b76a6ed Mon Sep 17 00:00:00 2001 From: sneurlax Date: Tue, 7 May 2024 11:26:51 -0500 Subject: [PATCH 042/100] flutter_libmonero updates --- crypto_plugins/flutter_libmonero | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero index e06c5b92a..88a5d9f91 160000 --- a/crypto_plugins/flutter_libmonero +++ b/crypto_plugins/flutter_libmonero @@ -1 +1 @@ -Subproject commit e06c5b92a2a98b78d5506e6472a0fac1fbba9a10 +Subproject commit 88a5d9f91a27f87a9c44238f1ddfaa619d2fc7b6 From aaded93d1f1812a52fadd934e109a1d72cfc2d66 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 27 May 2024 10:12:36 -0600 Subject: [PATCH 043/100] desktop save logs implementation --- .../advanced_settings/debug_info_dialog.dart | 120 ++++++++++++++---- 1 file changed, 97 insertions(+), 23 deletions(-) diff --git a/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/debug_info_dialog.dart b/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/debug_info_dialog.dart index 9ce77873e..177898180 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/debug_info_dialog.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/debug_info_dialog.dart @@ -10,16 +10,20 @@ import 'dart:async'; +import 'package:event_bus/event_bus.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../../../models/isar/models/log.dart'; import '../../../../notifications/show_flush_bar.dart'; import '../../../../providers/global/debug_service_provider.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/assets.dart'; import '../../../../utilities/constants.dart'; -import '../../../../utilities/enums/log_level_enum.dart'; +import '../../../../utilities/logger.dart'; +import '../../../../utilities/show_loading.dart'; import '../../../../utilities/text_styles.dart'; import '../../../../utilities/util.dart'; import '../../../../widgets/desktop/desktop_dialog.dart'; @@ -28,6 +32,7 @@ import '../../../../widgets/desktop/primary_button.dart'; import '../../../../widgets/desktop/secondary_button.dart'; import '../../../../widgets/icon_widgets/x_icon.dart'; import '../../../../widgets/rounded_container.dart'; +import '../../../../widgets/stack_dialog.dart'; import '../../../../widgets/stack_text_field.dart'; import '../../../../widgets/textfield_icon_button.dart'; @@ -52,7 +57,8 @@ class _DebugInfoDialog extends ConsumerState { } return unfiltered .where( - (e) => (e.toString().toLowerCase().contains(filter.toLowerCase()))) + (e) => (e.toString().toLowerCase().contains(filter.toLowerCase())), + ) .toList(); } @@ -77,6 +83,29 @@ class _DebugInfoDialog extends ConsumerState { return null; } + bool _lock = false; + Future<(String?, bool)?> _saveFile() async { + final path = await FilePicker.platform.getDirectoryPath( + dialogTitle: "Choose Log Save Location", + lockParentWindow: true, + ); + + if (path == null) { + return null; + } + + bool logsSaved = true; + String? filename; + try { + filename = + await ref.read(debugServiceProvider).exportToFile(path, EventBus()); + } catch (e, s) { + logsSaved = false; + Logging.instance.log("$e $s", level: LogLevel.Error); + } + return (filename, logsSaved); + } + @override void initState() { searchDebugController = TextEditingController(); @@ -182,13 +211,16 @@ class _DebugInfoDialog extends ConsumerState { return [ SliverOverlapAbsorber( handle: NestedScrollView.sliverOverlapAbsorberHandleFor( - context), - sliver: SliverToBoxAdapter( + context, + ), + sliver: const SliverToBoxAdapter( child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 16, horizontal: 32), + padding: EdgeInsets.symmetric( + vertical: 16, + horizontal: 32, + ), child: Column( - children: const [], + children: [], ), ), ), @@ -198,11 +230,11 @@ class _DebugInfoDialog extends ConsumerState { body: Builder( builder: (context) { final logs = filtered( - ref.watch(debugServiceProvider - .select((value) => value.recentLogs)), - _searchTerm) - .reversed - .toList(growable: false); + ref.watch( + debugServiceProvider.select((value) => value.recentLogs), + ), + _searchTerm, + ).reversed.toList(growable: false); return CustomScrollView( reverse: true, // shrinkWrap: true, @@ -220,7 +252,8 @@ class _DebugInfoDialog extends ConsumerState { return Container( key: Key( - "log_${log.id}_${log.timestampInMillisUTC}"), + "log_${log.id}_${log.timestampInMillisUTC}", + ), decoration: BoxDecoration( color: Theme.of(context) .extension()! @@ -231,7 +264,8 @@ class _DebugInfoDialog extends ConsumerState { padding: const EdgeInsets.all(4), child: Padding( padding: const EdgeInsets.symmetric( - horizontal: 32), + horizontal: 32, + ), child: RoundedContainer( padding: const EdgeInsets.all(0), color: Theme.of(context) @@ -296,9 +330,10 @@ class _DebugInfoDialog extends ConsumerState { SelectableText( log.message, style: STextStyles.baseXS( - context) - .copyWith( - fontSize: 11.5), + context, + ).copyWith( + fontSize: 11.5, + ), ), ], ), @@ -333,12 +368,15 @@ class _DebugInfoDialog extends ConsumerState { await ref.read(debugServiceProvider).deleteAllLogs(); setState(() {}); - if (mounted) { + if (context.mounted) { Navigator.pop(context); - unawaited(showFloatingFlushBar( + unawaited( + showFloatingFlushBar( type: FlushBarType.info, context: context, - message: 'Logs cleared!')); + message: 'Logs cleared!', + ), + ); } }, ), @@ -349,11 +387,47 @@ class _DebugInfoDialog extends ConsumerState { Expanded( child: PrimaryButton( label: "Save logs to file", - onPressed: () { - // TODO: save file dialog + onPressed: () async { + if (_lock) { + return; + } + _lock = true; + try { + final results = await showLoading<(String?, bool)?>( + whileFuture: _saveFile(), + context: context, + message: "Generating logs file...", + ); + + if (results != null) { + if (results.$2) { + unawaited( + showDialog( + context: context, + builder: (context) => StackOkDialog( + title: "Logs saved to", + message: results.$1, + ), + ), + ); + } else { + unawaited( + showDialog( + context: context, + builder: (context) => StackOkDialog( + title: "Error Saving Logs", + message: results.$1, + ), + ), + ); + } + } + } finally { + _lock = false; + } }, ), - ) + ), ], ), ), From 15b39097bc30713f2d21f30778dc41c0f5a4bcd7 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 27 May 2024 11:23:56 -0600 Subject: [PATCH 044/100] update monero lib ref --- crypto_plugins/flutter_libmonero | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero index 6e71b956c..81a4f74ea 160000 --- a/crypto_plugins/flutter_libmonero +++ b/crypto_plugins/flutter_libmonero @@ -1 +1 @@ -Subproject commit 6e71b956c3801f65a662c7f140e871c246166db3 +Subproject commit 81a4f74ea068d3d1026c8e564ee9b0b28cee20c4 From aac27636ee5ae629e73b456601b201575a6cb86e Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 27 May 2024 11:24:11 -0600 Subject: [PATCH 045/100] some clean up and imports fixing from merge --- .../add_wallet_view/add_wallet_view.dart | 115 +++++++++--------- .../restore_options_view.dart | 64 +++++----- .../settings_menu/nodes_settings.dart | 3 +- lib/wallets/wallet/impl/monero_wallet.dart | 43 ++++--- lib/wallets/wallet/impl/wownero_wallet.dart | 62 ++++++---- pubspec.lock | 4 +- 6 files changed, 161 insertions(+), 130 deletions(-) diff --git a/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart b/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart index 337447ba5..57f707700 100644 --- a/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart +++ b/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart @@ -9,7 +9,6 @@ */ import 'dart:async'; -import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -149,8 +148,10 @@ class _AddWalletViewState extends ConsumerState { if (contracts.isEmpty) { contracts.addAll(DefaultTokens.list); - MainDB.instance.putEthContracts(contracts).then((value) => - ref.read(priceAnd24hChangeNotifierProvider).updatePrice()); + MainDB.instance.putEthContracts(contracts).then( + (value) => + ref.read(priceAnd24hChangeNotifierProvider).updatePrice(), + ); } tokenEntities.addAll(contracts.map((e) => EthTokenEntity(e))); @@ -349,63 +350,63 @@ class _AddWalletViewState extends ConsumerState { height: 16, ), ClipRRect( - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, - ), - child: Semantics( - label: - "Search Text Field. Inputs Text To Search In Wallets.", - excludeSemantics: true, - child: TextField( - autofocus: isDesktop, - autocorrect: !isDesktop, - enableSuggestions: !isDesktop, - controller: _searchFieldController, - focusNode: _searchFocusNode, - onChanged: (value) => - setState(() => _searchTerm = value), - style: STextStyles.field(context), - decoration: standardInputDecoration( - "Search", - _searchFocusNode, - context, - desktopMed: isDesktop, - ).copyWith( - prefixIcon: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10, - vertical: 16, - ), - child: SvgPicture.asset( - Assets.svg.search, - width: 16, - height: 16, - ), + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: Semantics( + label: + "Search Text Field. Inputs Text To Search In Wallets.", + excludeSemantics: true, + child: TextField( + autofocus: isDesktop, + autocorrect: !isDesktop, + enableSuggestions: !isDesktop, + controller: _searchFieldController, + focusNode: _searchFocusNode, + onChanged: (value) => + setState(() => _searchTerm = value), + style: STextStyles.field(context), + decoration: standardInputDecoration( + "Search", + _searchFocusNode, + context, + desktopMed: isDesktop, + ).copyWith( + prefixIcon: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 16, + ), + child: SvgPicture.asset( + Assets.svg.search, + width: 16, + height: 16, ), - suffixIcon: _searchFieldController.text.isNotEmpty - ? Padding( - padding: const EdgeInsets.only(right: 0), - child: UnconstrainedBox( - child: Row( - children: [ - TextFieldIconButton( - child: const XIcon(), - onTap: () async { - setState(() { - _searchFieldController.text = - ""; - _searchTerm = ""; - }); - }, - ), - ], - ), - ), - ) - : null, ), + suffixIcon: _searchFieldController.text.isNotEmpty + ? Padding( + padding: const EdgeInsets.only(right: 0), + child: UnconstrainedBox( + child: Row( + children: [ + TextFieldIconButton( + child: const XIcon(), + onTap: () async { + setState(() { + _searchFieldController.text = ""; + _searchTerm = ""; + }); + }, + ), + ], + ), + ), + ) + : null, ), - )), + ), + ), + ), const SizedBox( height: 10, ), diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart index 18af8ce55..e04ed9959 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart @@ -12,13 +12,8 @@ import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; -import '../../create_or_restore_wallet_view/sub_widgets/coin_image.dart'; -import 'sub_widgets/mobile_mnemonic_length_selector.dart'; -import 'sub_widgets/restore_from_date_picker.dart'; -import 'sub_widgets/restore_options_next_button.dart'; -import 'sub_widgets/restore_options_platform_layout.dart'; -import '../restore_wallet_view.dart'; -import '../sub_widgets/mnemonic_word_count_select_sheet.dart'; +import 'package:tuple/tuple.dart'; + import '../../../../pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart'; import '../../../../providers/ui/verify_recovery_phrase/mnemonic_word_count_state_provider.dart'; import '../../../../themes/stack_colors.dart'; @@ -27,9 +22,6 @@ import '../../../../utilities/constants.dart'; import '../../../../utilities/format.dart'; import '../../../../utilities/text_styles.dart'; import '../../../../utilities/util.dart'; -import '../../../../wallets/crypto_currency/coins/epiccash.dart'; -import '../../../../wallets/crypto_currency/coins/monero.dart'; -import '../../../../wallets/crypto_currency/coins/wownero.dart'; import '../../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../../widgets/conditional_parent.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; @@ -39,7 +31,13 @@ import '../../../../widgets/desktop/desktop_scaffold.dart'; import '../../../../widgets/expandable.dart'; import '../../../../widgets/rounded_white_container.dart'; import '../../../../widgets/stack_text_field.dart'; -import 'package:tuple/tuple.dart'; +import '../../create_or_restore_wallet_view/sub_widgets/coin_image.dart'; +import '../restore_wallet_view.dart'; +import '../sub_widgets/mnemonic_word_count_select_sheet.dart'; +import 'sub_widgets/mobile_mnemonic_length_selector.dart'; +import 'sub_widgets/restore_from_date_picker.dart'; +import 'sub_widgets/restore_options_next_button.dart'; +import 'sub_widgets/restore_options_platform_layout.dart'; class RestoreOptionsView extends ConsumerStatefulWidget { const RestoreOptionsView({ @@ -220,8 +218,8 @@ class _RestoreOptionsViewState extends ConsumerState { height: isDesktop ? 40 : 24, ), if ((coin is Monero && - ref.watch(mnemonicWordCountStateProvider.state).state == - 25) || + ref.watch(mnemonicWordCountStateProvider.state).state == + 25) || coin is Epiccash || (coin is Wownero && ref.watch(mnemonicWordCountStateProvider.state).state == @@ -237,9 +235,9 @@ class _RestoreOptionsViewState extends ConsumerState { : STextStyles.smallMed12(context), textAlign: TextAlign.left, ), - if ((coin is Monero && - ref.watch(mnemonicWordCountStateProvider.state).state == - 25) || + if ((coin is Monero && + ref.watch(mnemonicWordCountStateProvider.state).state == + 25) || coin is Epiccash || (coin is Wownero && ref.watch(mnemonicWordCountStateProvider.state).state == @@ -247,9 +245,9 @@ class _RestoreOptionsViewState extends ConsumerState { SizedBox( height: isDesktop ? 16 : 8, ), - if ((coin is Monero && - ref.watch(mnemonicWordCountStateProvider.state).state == - 25) || + if ((coin is Monero && + ref.watch(mnemonicWordCountStateProvider.state).state == + 25) || coin is Epiccash || (coin is Wownero && ref.watch(mnemonicWordCountStateProvider.state).state == @@ -259,8 +257,9 @@ class _RestoreOptionsViewState extends ConsumerState { onTap: chooseDate, controller: _dateController, ), - if ((coin is Monero && - ref.watch(mnemonicWordCountStateProvider.state).state == 25) || + if ((coin is Monero && + ref.watch(mnemonicWordCountStateProvider.state).state == + 25) || coin is Epiccash || (coin is Wownero && ref.watch(mnemonicWordCountStateProvider.state).state == @@ -272,8 +271,8 @@ class _RestoreOptionsViewState extends ConsumerState { controller: _dateController, ), if ((coin is Monero && - ref.watch(mnemonicWordCountStateProvider.state).state == - 25) || + ref.watch(mnemonicWordCountStateProvider.state).state == + 25) || coin is Epiccash || (coin is Wownero && ref.watch(mnemonicWordCountStateProvider.state).state == @@ -281,9 +280,9 @@ class _RestoreOptionsViewState extends ConsumerState { const SizedBox( height: 8, ), - if ((coin is Monero && - ref.watch(mnemonicWordCountStateProvider.state).state == - 25) || + if ((coin is Monero && + ref.watch(mnemonicWordCountStateProvider.state).state == + 25) || coin is Epiccash || (coin is Wownero && ref.watch(mnemonicWordCountStateProvider.state).state == @@ -304,9 +303,9 @@ class _RestoreOptionsViewState extends ConsumerState { ), ), ), - if ((coin is Monero && - ref.watch(mnemonicWordCountStateProvider.state).state == - 25) || + if ((coin is Monero && + ref.watch(mnemonicWordCountStateProvider.state).state == + 25) || coin is Epiccash || (coin is Wownero && ref.watch(mnemonicWordCountStateProvider.state).state == @@ -411,8 +410,8 @@ class _RestoreOptionsViewState extends ConsumerState { "Advanced", style: isDesktop ? STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( + context, + ).copyWith( color: Theme.of(context) .extension()! .textDark3, @@ -474,7 +473,8 @@ class _RestoreOptionsViewState extends ConsumerState { ), GestureDetector( key: const Key( - "mnemonicPassphraseFieldShowPasswordButtonKey"), + "mnemonicPassphraseFieldShowPasswordButtonKey", + ), onTap: () async { setState(() { hidePassword = !hidePassword; diff --git a/lib/pages_desktop_specific/settings/settings_menu/nodes_settings.dart b/lib/pages_desktop_specific/settings/settings_menu/nodes_settings.dart index 366111014..517073f4c 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/nodes_settings.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/nodes_settings.dart @@ -13,10 +13,11 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + +import '../../../app_config.dart'; import '../../../pages/settings_views/global_settings_view/manage_nodes_views/coin_nodes_view.dart'; import '../../../providers/providers.dart'; import '../../../route_generator.dart'; -import '../../../app_config.dart'; import '../../../themes/coin_icon_provider.dart'; import '../../../themes/stack_colors.dart'; import '../../../utilities/assets.dart'; diff --git a/lib/wallets/wallet/impl/monero_wallet.dart b/lib/wallets/wallet/impl/monero_wallet.dart index cc1360292..757c553f1 100644 --- a/lib/wallets/wallet/impl/monero_wallet.dart +++ b/lib/wallets/wallet/impl/monero_wallet.dart @@ -19,15 +19,19 @@ import 'package:flutter_libmonero/core/wallet_creation_service.dart'; import 'package:flutter_libmonero/monero/monero.dart' as xmr_dart; import 'package:flutter_libmonero/view_model/send/output.dart' as monero_output; import 'package:isar/isar.dart'; +import 'package:mutex/mutex.dart'; import 'package:tuple/tuple.dart'; import '../../../db/hive/db.dart'; import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/isar/models/blockchain_data/transaction.dart'; +import '../../../services/event_bus/events/global/tor_connection_status_changed_event.dart'; +import '../../../services/event_bus/events/global/tor_status_changed_event.dart'; +import '../../../services/event_bus/global_event_bus.dart'; +import '../../../services/tor_service.dart'; import '../../../utilities/amount/amount.dart'; import '../../../utilities/enums/fee_rate_type_enum.dart'; import '../../../utilities/logger.dart'; -import '../../crypto_currency/coins/monero.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../models/tx_data.dart'; import '../intermediate/cryptonote_wallet.dart'; @@ -390,7 +394,8 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { // subtract a couple days to ensure we have a buffer for SWB final bufferedCreateHeight = xmr_dart.monero.getHeigthByDate( - date: DateTime.now().subtract(const Duration(days: 2))); + date: DateTime.now().subtract(const Duration(days: 2)), + ); await info.updateRestoreHeight( newRestoreHeight: bufferedCreateHeight, @@ -528,8 +533,9 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { CwBasedInterface.cwWalletBase?.close(); } catch (e, s) { Logging.instance.log( - "Exception rethrown from recoverFromMnemonic(): $e\n$s", - level: LogLevel.Error); + "Exception rethrown from recoverFromMnemonic(): $e\n$s", + level: LogLevel.Error, + ); rethrow; } }); @@ -586,8 +592,10 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { CwBasedInterface.cwWalletBase!.createTransaction(tmp); }); } catch (e, s) { - Logging.instance.log("Exception rethrown from prepareSend(): $e\n$s", - level: LogLevel.Warning); + Logging.instance.log( + "Exception rethrown from prepareSend(): $e\n$s", + level: LogLevel.Warning, + ); } final PendingMoneroTransaction pendingMoneroTransaction = @@ -605,8 +613,10 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { throw ArgumentError("Invalid fee rate argument provided!"); } } catch (e, s) { - Logging.instance.log("Exception rethrown from prepare send(): $e\n$s", - level: LogLevel.Info); + Logging.instance.log( + "Exception rethrown from prepare send(): $e\n$s", + level: LogLevel.Info, + ); if (e.toString().contains("Incorrect unlocked balance")) { throw Exception("Insufficient balance!"); @@ -624,17 +634,22 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { try { await txData.pendingMoneroTransaction!.commit(); Logging.instance.log( - "transaction ${txData.pendingMoneroTransaction!.id} has been sent", - level: LogLevel.Info); + "transaction ${txData.pendingMoneroTransaction!.id} has been sent", + level: LogLevel.Info, + ); return txData.copyWith(txid: txData.pendingMoneroTransaction!.id); } catch (e, s) { - Logging.instance.log("${info.name} monero confirmSend: $e\n$s", - level: LogLevel.Error); + Logging.instance.log( + "${info.name} monero confirmSend: $e\n$s", + level: LogLevel.Error, + ); rethrow; } } catch (e, s) { - Logging.instance.log("Exception rethrown from confirmSend(): $e\n$s", - level: LogLevel.Info); + Logging.instance.log( + "Exception rethrown from confirmSend(): $e\n$s", + level: LogLevel.Info, + ); rethrow; } } diff --git a/lib/wallets/wallet/impl/wownero_wallet.dart b/lib/wallets/wallet/impl/wownero_wallet.dart index 95dceb31b..847b8b811 100644 --- a/lib/wallets/wallet/impl/wownero_wallet.dart +++ b/lib/wallets/wallet/impl/wownero_wallet.dart @@ -20,21 +20,24 @@ import 'package:flutter_libmonero/view_model/send/output.dart' as wownero_output; import 'package:flutter_libmonero/wownero/wownero.dart' as wow_dart; import 'package:isar/isar.dart'; +import 'package:mutex/mutex.dart'; +import 'package:tuple/tuple.dart'; + import '../../../db/hive/db.dart'; import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/isar/models/blockchain_data/transaction.dart'; import '../../../services/event_bus/events/global/tor_connection_status_changed_event.dart'; +import '../../../services/event_bus/events/global/tor_status_changed_event.dart'; import '../../../services/event_bus/global_event_bus.dart'; +import '../../../services/tor_service.dart'; import '../../../utilities/amount/amount.dart'; import '../../../utilities/enums/fee_rate_type_enum.dart'; import '../../../utilities/logger.dart'; -import '../../crypto_currency/coins/wownero.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../models/tx_data.dart'; import '../intermediate/cryptonote_wallet.dart'; import '../wallet.dart'; import '../wallet_mixin_interfaces/cw_based_interface.dart'; -import 'package:tuple/tuple.dart'; class WowneroWallet extends CryptonoteWallet with CwBasedInterface { WowneroWallet(CryptoCurrencyNetwork network) : super(Wownero(network)) { @@ -524,16 +527,17 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { ); try { walletInfo = WalletInfo.external( - id: WalletBase.idFor(name, WalletType.wownero), - name: name, - type: WalletType.wownero, - isRecovery: false, - restoreHeight: credentials.height ?? 0, - date: DateTime.now(), - path: path, - dirPath: dirPath, - // TODO: find out what to put for address - address: ''); + id: WalletBase.idFor(name, WalletType.wownero), + name: name, + type: WalletType.wownero, + isRecovery: false, + restoreHeight: credentials.height ?? 0, + date: DateTime.now(), + path: path, + dirPath: dirPath, + // TODO: find out what to put for address + address: '', + ); credentials.walletInfo = walletInfo; final cwWalletCreationService = WalletCreationService( @@ -577,8 +581,9 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { CwBasedInterface.cwWalletBase?.close(); } catch (e, s) { Logging.instance.log( - "Exception rethrown from recoverFromMnemonic(): $e\n$s", - level: LogLevel.Error); + "Exception rethrown from recoverFromMnemonic(): $e\n$s", + level: LogLevel.Error, + ); rethrow; } }); @@ -636,8 +641,10 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { CwBasedInterface.cwWalletBase!.createTransaction(tmp); }); } catch (e, s) { - Logging.instance.log("Exception rethrown from prepareSend(): $e\n$s", - level: LogLevel.Warning); + Logging.instance.log( + "Exception rethrown from prepareSend(): $e\n$s", + level: LogLevel.Warning, + ); } final PendingWowneroTransaction pendingWowneroTransaction = @@ -655,8 +662,10 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { throw ArgumentError("Invalid fee rate argument provided!"); } } catch (e, s) { - Logging.instance.log("Exception rethrown from prepare send(): $e\n$s", - level: LogLevel.Info); + Logging.instance.log( + "Exception rethrown from prepare send(): $e\n$s", + level: LogLevel.Info, + ); if (e.toString().contains("Incorrect unlocked balance")) { throw Exception("Insufficient balance!"); @@ -674,17 +683,22 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { try { await txData.pendingWowneroTransaction!.commit(); Logging.instance.log( - "transaction ${txData.pendingWowneroTransaction!.id} has been sent", - level: LogLevel.Info); + "transaction ${txData.pendingWowneroTransaction!.id} has been sent", + level: LogLevel.Info, + ); return txData.copyWith(txid: txData.pendingWowneroTransaction!.id); } catch (e, s) { - Logging.instance.log("${info.name} wownero confirmSend: $e\n$s", - level: LogLevel.Error); + Logging.instance.log( + "${info.name} wownero confirmSend: $e\n$s", + level: LogLevel.Error, + ); rethrow; } } catch (e, s) { - Logging.instance.log("Exception rethrown from confirmSend(): $e\n$s", - level: LogLevel.Info); + Logging.instance.log( + "Exception rethrown from confirmSend(): $e\n$s", + level: LogLevel.Info, + ); rethrow; } } diff --git a/pubspec.lock b/pubspec.lock index 9e5440f27..169fd94fb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1742,8 +1742,8 @@ packages: dependency: "direct main" description: path: "." - ref: f31f8f857665d85338824ae171aba4c629c3ba6f - resolved-ref: f31f8f857665d85338824ae171aba4c629c3ba6f + ref: "13fa937ea9a9fc34caf047e068df9535f65c27ad" + resolved-ref: "13fa937ea9a9fc34caf047e068df9535f65c27ad" url: "https://github.com/cypherstack/tezart.git" source: git version: "2.0.5" From 5862ed1ed77f7b6ed60b553220bcdeb6f55595c8 Mon Sep 17 00:00:00 2001 From: Julian Date: Mon, 27 May 2024 13:03:41 -0600 Subject: [PATCH 046/100] update gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 7dcc2903e..365c35736 100644 --- a/.gitignore +++ b/.gitignore @@ -107,3 +107,7 @@ android/app/src/main/jniLibs/x86_64/libmonero_wallet2_api_c.so android/app/src/main/jniLibs/x86_64/libwownero_wallet2_api_c.so macos/monero_wallet2_api_c.dylib macos/wownero_wallet2_api_c.dylib +/macos/monero_libwallet2_api_c.dylib +/macos/wownero_libwallet2_api_c.dylib +/ios/monero_libwallet2_api_c.dylib +/ios/wownero_libwallet2_api_c.dylib From 88bda91d1b0ee2743d51687b4d8466c9e5763106 Mon Sep 17 00:00:00 2001 From: Julian Date: Mon, 27 May 2024 13:40:42 -0600 Subject: [PATCH 047/100] add package monero and do network check in validate address --- lib/wallets/crypto_currency/coins/monero.dart | 8 ++++++-- scripts/app_config/templates/pubspec.template | 5 +++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/wallets/crypto_currency/coins/monero.dart b/lib/wallets/crypto_currency/coins/monero.dart index 8d55cff65..fa7e32886 100644 --- a/lib/wallets/crypto_currency/coins/monero.dart +++ b/lib/wallets/crypto_currency/coins/monero.dart @@ -1,4 +1,3 @@ -import 'package:cw_monero/api/wallet.dart' as monero_wallet; import 'package:monero/monero.dart' as monero; import '../../../models/node_model.dart'; @@ -46,7 +45,12 @@ class Monero extends CryptonoteCurrency { @override bool validateAddress(String address) { - return monero.Wallet_addressValid(address, 0); + switch (network) { + case CryptoCurrencyNetwork.main: + return monero.Wallet_addressValid(address, 0); + default: + throw Exception("Unsupported network: $network"); + } } @override diff --git a/scripts/app_config/templates/pubspec.template b/scripts/app_config/templates/pubspec.template index d7e07cc1a..a77535133 100644 --- a/scripts/app_config/templates/pubspec.template +++ b/scripts/app_config/templates/pubspec.template @@ -47,6 +47,11 @@ dependencies: cw_core: path: ./crypto_plugins/flutter_libmonero/cw_core + monero: + git: + url: https://git.mrcyjanek.net/mrcyjanek/monero.dart + ref: 6a17a405a1a260fa228b2f4fc94044088a4335ac + flutter_libepiccash: path: ./crypto_plugins/flutter_libepiccash From 1542bcb4d12b60c39b759027521c06c6d8a0d0b3 Mon Sep 17 00:00:00 2001 From: Julian Date: Mon, 27 May 2024 14:06:33 -0600 Subject: [PATCH 048/100] enable wownero wallet creation --- ios/Podfile.lock | 71 +------------------ .../create_wallet_button_group.dart | 64 ++++++++--------- macos/Podfile.lock | 2 +- pubspec.lock | 6 +- 4 files changed, 35 insertions(+), 108 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index fd6ae37e5..198e44f2d 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -9,63 +9,6 @@ PODS: - connectivity_plus (0.0.1): - Flutter - ReachabilitySwift - - cw_monero (0.0.2): - - cw_monero/Boost (= 0.0.2) - - cw_monero/Monero (= 0.0.2) - - cw_monero/OpenSSL (= 0.0.2) - - cw_monero/Sodium (= 0.0.2) - - cw_monero/Unbound (= 0.0.2) - - cw_shared_external - - Flutter - - cw_monero/Boost (0.0.2): - - cw_shared_external - - Flutter - - cw_monero/Monero (0.0.2): - - cw_shared_external - - Flutter - - cw_monero/OpenSSL (0.0.2): - - cw_shared_external - - Flutter - - cw_monero/Sodium (0.0.2): - - cw_shared_external - - Flutter - - cw_monero/Unbound (0.0.2): - - cw_shared_external - - Flutter - - cw_shared_external (0.0.1): - - cw_shared_external/Boost (= 0.0.1) - - cw_shared_external/OpenSSL (= 0.0.1) - - cw_shared_external/Sodium (= 0.0.1) - - Flutter - - cw_shared_external/Boost (0.0.1): - - Flutter - - cw_shared_external/OpenSSL (0.0.1): - - Flutter - - cw_shared_external/Sodium (0.0.1): - - Flutter - - cw_wownero (0.0.2): - - cw_shared_external - - cw_wownero/Boost (= 0.0.2) - - cw_wownero/OpenSSL (= 0.0.2) - - cw_wownero/Sodium (= 0.0.2) - - cw_wownero/Unbound (= 0.0.2) - - cw_wownero/Wownero (= 0.0.2) - - Flutter - - cw_wownero/Boost (0.0.2): - - cw_shared_external - - Flutter - - cw_wownero/OpenSSL (0.0.2): - - cw_shared_external - - Flutter - - cw_wownero/Sodium (0.0.2): - - cw_shared_external - - Flutter - - cw_wownero/Unbound (0.0.2): - - cw_shared_external - - Flutter - - cw_wownero/Wownero (0.0.2): - - cw_shared_external - - Flutter - device_info_plus (0.0.1): - Flutter - devicelocale (0.0.1): @@ -155,9 +98,6 @@ DEPENDENCIES: - barcode_scan2 (from `.symlinks/plugins/barcode_scan2/ios`) - coinlib_flutter (from `.symlinks/plugins/coinlib_flutter/darwin`) - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - - cw_monero (from `.symlinks/plugins/cw_monero/ios`) - - cw_shared_external (from `.symlinks/plugins/cw_shared_external/ios`) - - cw_wownero (from `.symlinks/plugins/cw_wownero/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - devicelocale (from `.symlinks/plugins/devicelocale/ios`) - file_picker (from `.symlinks/plugins/file_picker/ios`) @@ -199,12 +139,6 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/coinlib_flutter/darwin" connectivity_plus: :path: ".symlinks/plugins/connectivity_plus/ios" - cw_monero: - :path: ".symlinks/plugins/cw_monero/ios" - cw_shared_external: - :path: ".symlinks/plugins/cw_shared_external/ios" - cw_wownero: - :path: ".symlinks/plugins/cw_wownero/ios" device_info_plus: :path: ".symlinks/plugins/device_info_plus/ios" devicelocale: @@ -256,9 +190,6 @@ SPEC CHECKSUMS: barcode_scan2: 0af2bb63c81b4565aab6cd78278e4c0fa136dbb0 coinlib_flutter: 6abec900d67762a6e7ccfd567a3cd3ae00bbee35 connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a - cw_monero: 9816991daff0e3ad0a8be140e31933b5526babd4 - cw_shared_external: 2972d872b8917603478117c9957dfca611845a92 - cw_wownero: ac53899fa5c6ff46b3fb490aa3b7ca36301fa832 device_info_plus: 7545d84d8d1b896cb16a4ff98c19f07ec4b298ea devicelocale: b22617f40038496deffba44747101255cee005b0 DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac @@ -272,7 +203,7 @@ SPEC CHECKSUMS: flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be frostdart: 4c72b69ccac2f13ede744107db046a125acce597 - integration_test: 13825b8a9334a850581300559b8839134b124670 + integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4 isar_flutter_libs: b69f437aeab9c521821c3f376198c4371fa21073 lelantus: 417f0221260013dfc052cae9cf4b741b6479edba local_auth: 1740f55d7af0a2e2a8684ce225fe79d8931e808c diff --git a/lib/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/create_wallet_button_group.dart b/lib/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/create_wallet_button_group.dart index 1dabb24d3..895aeb533 100644 --- a/lib/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/create_wallet_button_group.dart +++ b/lib/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/create_wallet_button_group.dart @@ -8,16 +8,14 @@ * */ -import 'dart:io'; - import 'package:flutter/material.dart'; -import '../../name_your_wallet_view/name_your_wallet_view.dart'; +import 'package:tuple/tuple.dart'; + import '../../../../themes/stack_colors.dart'; import '../../../../utilities/enums/add_wallet_type_enum.dart'; import '../../../../utilities/text_styles.dart'; -import '../../../../wallets/crypto_currency/coins/wownero.dart'; import '../../../../wallets/crypto_currency/crypto_currency.dart'; -import 'package:tuple/tuple.dart'; +import '../../name_your_wallet_view/name_your_wallet_view.dart'; class CreateWalletButtonGroup extends StatelessWidget { const CreateWalletButtonGroup({ @@ -35,37 +33,35 @@ class CreateWalletButtonGroup extends StatelessWidget { crossAxisAlignment: isDesktop ? CrossAxisAlignment.center : CrossAxisAlignment.stretch, children: [ - if (Platform.isAndroid || coin is! Wownero) - ConstrainedBox( - constraints: BoxConstraints( - minHeight: isDesktop ? 70 : 0, - minWidth: isDesktop ? 480 : 0, - ), - child: TextButton( - style: Theme.of(context) - .extension()! - .getPrimaryEnabledButtonStyle(context), - onPressed: () { - Navigator.of(context).pushNamed( - NameYourWalletView.routeName, - arguments: Tuple2( - AddWalletType.New, - coin, - ), - ); - }, - child: Text( - "Create new wallet", - style: isDesktop - ? STextStyles.desktopButtonEnabled(context) - : STextStyles.button(context), - ), + ConstrainedBox( + constraints: BoxConstraints( + minHeight: isDesktop ? 70 : 0, + minWidth: isDesktop ? 480 : 0, + ), + child: TextButton( + style: Theme.of(context) + .extension()! + .getPrimaryEnabledButtonStyle(context), + onPressed: () { + Navigator.of(context).pushNamed( + NameYourWalletView.routeName, + arguments: Tuple2( + AddWalletType.New, + coin, + ), + ); + }, + child: Text( + "Create new wallet", + style: isDesktop + ? STextStyles.desktopButtonEnabled(context) + : STextStyles.button(context), ), ), - if (Platform.isAndroid || coin is! Wownero) - SizedBox( - height: isDesktop ? 16 : 12, - ), + ), + SizedBox( + height: isDesktop ? 16 : 12, + ), ConstrainedBox( constraints: BoxConstraints( minHeight: isDesktop ? 70 : 0, diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 57c4d3468..04bb85e54 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -141,4 +141,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 -COCOAPODS: 1.14.3 +COCOAPODS: 1.15.2 diff --git a/pubspec.lock b/pubspec.lock index 169fd94fb..a9cfa08cf 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1181,10 +1181,10 @@ packages: dependency: "direct dev" description: name: mockito - sha256: "7d5b53bcd556c1bc7ffbe4e4d5a19c3e112b7e925e9e172dd7c6ad0630812616" + sha256: "6841eed20a7befac0ce07df8116c8b8233ed1f4486a7647c7fc5a02ae6163917" url: "https://pub.dev" source: hosted - version: "5.4.2" + version: "5.4.4" mocktail: dependency: transitive description: @@ -1194,7 +1194,7 @@ packages: source: hosted version: "0.2.0" monero: - dependency: transitive + dependency: "direct main" description: path: "." ref: "6a17a405a1a260fa228b2f4fc94044088a4335ac" From 96966a78a178d3093d40b00d86496aea7c78881b Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 27 May 2024 14:15:25 -0600 Subject: [PATCH 049/100] update gitignore --- .gitignore | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.gitignore b/.gitignore index 365c35736..35282800f 100644 --- a/.gitignore +++ b/.gitignore @@ -99,6 +99,7 @@ pubspec.yaml # FVM Version Cache .fvm/ + android/app/src/main/jniLibs/arm64-v8a/libwownero_wallet2_api_c.so android/app/src/main/jniLibs/arm64-v8a/libmonero_wallet2_api_c.so android/app/src/main/jniLibs/armeabi-v7a/libmonero_wallet2_api_c.so @@ -111,3 +112,9 @@ macos/wownero_wallet2_api_c.dylib /macos/wownero_libwallet2_api_c.dylib /ios/monero_libwallet2_api_c.dylib /ios/wownero_libwallet2_api_c.dylib +/android/app/src/main/jniLibs/arm64-v8a/libmonero_libwallet2_api_c.so +/android/app/src/main/jniLibs/armeabi-v7a/libmonero_libwallet2_api_c.so +/android/app/src/main/jniLibs/x86_64/libmonero_libwallet2_api_c.so +/android/app/src/main/jniLibs/arm64-v8a/libwownero_libwallet2_api_c.so +/android/app/src/main/jniLibs/armeabi-v7a/libwownero_libwallet2_api_c.so +/android/app/src/main/jniLibs/x86_64/libwownero_libwallet2_api_c.so From 2ba0dc576e8a662a1c29359dfc8eb8a00649eb69 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 27 May 2024 14:15:45 -0600 Subject: [PATCH 050/100] fix variable read --- .../exchange_step_views/step_4_view.dart | 17 +++++++++-------- .../restore_from_file_view.dart | 11 ++++++----- .../sub_views/stack_restore_progress_view.dart | 17 +++++++++-------- .../exchange_steps/step_scaffold.dart | 2 +- .../forgotten_passphrase_restore_from_swb.dart | 7 ++++--- 5 files changed, 29 insertions(+), 25 deletions(-) diff --git a/lib/pages/exchange_view/exchange_step_views/step_4_view.dart b/lib/pages/exchange_view/exchange_step_views/step_4_view.dart index 3d0730ee4..4700d12b1 100644 --- a/lib/pages/exchange_view/exchange_step_views/step_4_view.dart +++ b/lib/pages/exchange_view/exchange_step_views/step_4_view.dart @@ -15,15 +15,11 @@ import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:qr_flutter/qr_flutter.dart'; +import 'package:tuple/tuple.dart'; + import '../../../app_config.dart'; import '../../../models/exchange/incomplete_exchange.dart'; import '../../../notifications/show_flush_bar.dart'; -import '../confirm_change_now_send.dart'; -import '../send_from_view.dart'; -import '../sub_widgets/step_row.dart'; -import '../../home_view/home_view.dart'; -import '../../send_view/sub_widgets/building_transaction_dialog.dart'; -import '../../wallet_view/wallet_view.dart'; import '../../../providers/providers.dart'; import '../../../route_generator.dart'; import '../../../themes/stack_colors.dart'; @@ -44,7 +40,12 @@ import '../../../widgets/desktop/secondary_button.dart'; import '../../../widgets/rounded_container.dart'; import '../../../widgets/rounded_white_container.dart'; import '../../../widgets/stack_dialog.dart'; -import 'package:tuple/tuple.dart'; +import '../../home_view/home_view.dart'; +import '../../send_view/sub_widgets/building_transaction_dialog.dart'; +import '../../wallet_view/wallet_view.dart'; +import '../confirm_change_now_send.dart'; +import '../send_from_view.dart'; +import '../sub_widgets/step_row.dart'; class Step4View extends ConsumerStatefulWidget { const Step4View({ @@ -816,7 +817,7 @@ class _Step4ViewState extends ConsumerState { Builder( builder: (context) { String buttonTitle = - "Send from {$AppConfig.appName}"; + "Send from ${AppConfig.appName}"; final tuple = ref .read( diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/restore_from_file_view.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/restore_from_file_view.dart index 5a609acb4..d7a659ac2 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/restore_from_file_view.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/restore_from_file_view.dart @@ -15,11 +15,10 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:tuple/tuple.dart'; + import '../../../../app_config.dart'; import '../../../../notifications/show_flush_bar.dart'; -import 'helpers/restore_create_backup.dart'; -import 'helpers/swb_file_system.dart'; -import 'sub_views/stack_restore_progress_view.dart'; import '../../../../route_generator.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/assets.dart'; @@ -36,7 +35,9 @@ import '../../../../widgets/desktop/primary_button.dart'; import '../../../../widgets/desktop/secondary_button.dart'; import '../../../../widgets/loading_indicator.dart'; import '../../../../widgets/stack_text_field.dart'; -import 'package:tuple/tuple.dart'; +import 'helpers/restore_create_backup.dart'; +import 'helpers/swb_file_system.dart'; +import 'sub_views/stack_restore_progress_view.dart'; class RestoreFromFileView extends ConsumerStatefulWidget { const RestoreFromFileView({Key? key}) : super(key: key); @@ -537,7 +538,7 @@ class _RestoreFromFileViewState extends ConsumerState { .all( 32), child: Text( - "Restore {$AppConfig.appName}", + "Restore ${AppConfig.appName}", style: STextStyles .desktopH3( context), diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/stack_restore_progress_view.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/stack_restore_progress_view.dart index 0cd60b29c..bc8d9a329 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/stack_restore_progress_view.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/stack_restore_progress_view.dart @@ -13,14 +13,8 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../../../../app_config.dart'; -import '../../../../home_view/home_view.dart'; -import '../dialogs/cancel_stack_restore_dialog.dart'; -import '../helpers/restore_create_backup.dart'; -import '../restore_from_encrypted_string_view.dart'; -import '../stack_backup_view.dart'; -import '../sub_widgets/restoring_item_card.dart'; -import '../sub_widgets/restoring_wallet_card.dart'; import '../../../../../pages_desktop_specific/desktop_home_view.dart'; import '../../../../../pages_desktop_specific/desktop_menu.dart'; import '../../../../../providers/desktop/current_desktop_menu_item.dart'; @@ -40,6 +34,13 @@ import '../../../../../widgets/desktop/secondary_button.dart'; import '../../../../../widgets/icon_widgets/addressbook_icon.dart'; import '../../../../../widgets/loading_indicator.dart'; import '../../../../../widgets/rounded_container.dart'; +import '../../../../home_view/home_view.dart'; +import '../dialogs/cancel_stack_restore_dialog.dart'; +import '../helpers/restore_create_backup.dart'; +import '../restore_from_encrypted_string_view.dart'; +import '../stack_backup_view.dart'; +import '../sub_widgets/restoring_item_card.dart'; +import '../sub_widgets/restoring_wallet_card.dart'; class StackRestoreProgressView extends ConsumerStatefulWidget { const StackRestoreProgressView({ @@ -264,7 +265,7 @@ class _StackRestoreProgressViewState }, ), title: Text( - "Restoring {$AppConfig.appName}", + "Restoring ${AppConfig.appName}", style: STextStyles.navBarTitle(context), ), ), diff --git a/lib/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart b/lib/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart index 103280f88..3c0688166 100644 --- a/lib/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart +++ b/lib/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart @@ -319,7 +319,7 @@ class _StepScaffoldState extends ConsumerState { onPressed: onBack, ), secondChild: SecondaryButton( - label: "Send from {$AppConfig.appName}", + label: "Send from ${AppConfig.appName}", buttonHeight: ButtonHeight.l, onPressed: sendFromStack, ), diff --git a/lib/pages_desktop_specific/password/forgotten_passphrase_restore_from_swb.dart b/lib/pages_desktop_specific/password/forgotten_passphrase_restore_from_swb.dart index b1efe30f1..bb2e6e816 100644 --- a/lib/pages_desktop_specific/password/forgotten_passphrase_restore_from_swb.dart +++ b/lib/pages_desktop_specific/password/forgotten_passphrase_restore_from_swb.dart @@ -15,13 +15,14 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:tuple/tuple.dart'; + import '../../app_config.dart'; import '../../db/hive/db.dart'; import '../../notifications/show_flush_bar.dart'; import '../../pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart'; import '../../pages/settings_views/global_settings_view/stack_backup_views/helpers/swb_file_system.dart'; import '../../pages/settings_views/global_settings_view/stack_backup_views/sub_views/stack_restore_progress_view.dart'; -import 'create_password_view.dart'; import '../../providers/desktop/storage_crypto_handler_provider.dart'; import '../../providers/global/secure_store_provider.dart'; import '../../providers/global/wallets_provider.dart'; @@ -38,7 +39,7 @@ import '../../widgets/desktop/desktop_scaffold.dart'; import '../../widgets/desktop/primary_button.dart'; import '../../widgets/loading_indicator.dart'; import '../../widgets/stack_text_field.dart'; -import 'package:tuple/tuple.dart'; +import 'create_password_view.dart'; class ForgottenPassphraseRestoreFromSWB extends ConsumerStatefulWidget { const ForgottenPassphraseRestoreFromSWB({Key? key}) : super(key: key); @@ -155,7 +156,7 @@ class _ForgottenPassphraseRestoreFromSWBState mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - "Restoring {$AppConfig.appName}", + "Restoring ${AppConfig.appName}", style: STextStyles.desktopH3(context), ), ], From 8b523739d916e744be85164f0f3cb12032934dbf Mon Sep 17 00:00:00 2001 From: Julian Date: Mon, 27 May 2024 15:18:28 -0600 Subject: [PATCH 051/100] call correct dart exe on windows --- scripts/app_config/shared/asset_generators.sh | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/scripts/app_config/shared/asset_generators.sh b/scripts/app_config/shared/asset_generators.sh index a0150866a..50d035a65 100755 --- a/scripts/app_config/shared/asset_generators.sh +++ b/scripts/app_config/shared/asset_generators.sh @@ -11,8 +11,17 @@ APP_BUILD_PLATFORM=$1 # run icon and image generators pushd "${APP_PROJECT_ROOT_DIR}" -flutter pub get -#native splash screen not used -#dart run flutter_native_splash:create -dart run flutter_launcher_icons -f "${APP_PROJECT_ROOT_DIR}/scripts/app_config/platforms/${APP_BUILD_PLATFORM}/flutter_launcher_icons.yaml" +YAML_FILE="${APP_PROJECT_ROOT_DIR}/scripts/app_config/platforms/${APP_BUILD_PLATFORM}/flutter_launcher_icons.yaml" +if [[ "${APP_BUILD_PLATFORM}" = 'windows' ]]; then + cmd.exe /c flutter pub get + WIN_PATH_VERSION=$(wslpath -w ${YAML_FILE}) + cmd.exe /c dart run flutter_launcher_icons -f "${WIN_PATH_VERSION}" + #native splash screen not used + #cmd.exe /c dart run flutter_native_splash:create +else + flutter pub get + dart run flutter_launcher_icons -f "${YAML_FILE}" + #native splash screen not used + #dart run flutter_native_splash:create +fi popd \ No newline at end of file From 9006de0f0a233b885286e51e677bd3992b8b7bc5 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 27 May 2024 17:56:22 -0600 Subject: [PATCH 052/100] code style clean up and gen mocks --- lib/db/isar/main_db.dart | 8 +- lib/db/migrate_wallets_to_isar.dart | 3 +- lib/dto/ethereum/eth_token_tx_extra_dto.dart | 1 - lib/dto/ethereum/eth_tx_dto.dart | 1 - .../address_inscription_response.dart | 12 +- .../cached_electrumx_client.dart | 10 +- lib/electrumx_rpc/electrumx_client.dart | 2 - lib/main.dart | 75 +- .../sub_classes/eth_token_entity.dart | 1 - lib/models/balance.dart | 3 +- lib/models/buy/response_objects/fiat.dart | 11 +- lib/models/buy/simplex/simplex.dart | 32 +- lib/models/contact.dart | 6 +- lib/models/epicbox_config_model.dart | 12 +- lib/models/epicbox_server_model.dart | 2 +- .../change_now/estimated_exchange_amount.dart | 7 +- .../change_now/exchange_transaction.dart | 9 +- .../exchange_transaction_status.dart | 7 +- .../exchange/majestic_bank/mb_rate.dart | 7 +- .../exchange/response_objects/trade.dart | 7 +- .../exchange/simpleswap/sp_currency.dart | 6 +- lib/models/isar/exchange_cache/currency.dart | 10 +- lib/models/isar/exchange_cache/pair.dart | 10 +- .../models/blockchain_data/transaction.dart | 41 +- .../isar/models/blockchain_data/utxo.dart | 12 +- .../models/blockchain_data/v2/output_v2.dart | 4 +- lib/models/isar/ordinal.dart | 13 +- lib/models/lelantus_coin.dart | 2 +- lib/models/node_model.dart | 2 +- lib/models/paymint/transactions_model.dart | 83 +- lib/models/paymint/utxo_model.dart | 6 +- lib/models/paynym/paynym_account.dart | 12 +- lib/models/paynym/paynym_follow.dart | 10 +- lib/models/stack_restoring_ui_state.dart | 8 +- lib/notifications/notification_card.dart | 25 +- lib/pages/TermsOfServiceView.dart | 2 +- .../add_token_view/add_custom_token_view.dart | 28 +- .../edit_wallet_tokens_view.dart | 21 +- .../add_custom_token_selector.dart | 4 +- .../sub_widgets/add_token_list.dart | 4 +- .../sub_widgets/add_token_text.dart | 4 +- .../sub_widgets/add_wallet_entity_list.dart | 4 +- .../sub_widgets/expanding_sub_list_item.dart | 10 +- .../sub_widgets/next_button.dart | 4 +- .../create_or_restore_wallet_subtitle.dart | 4 +- .../create_wallet_button_group.dart | 3 +- .../select_new_frost_import_type_view.dart | 2 +- .../new/steps/frost_create_step_1b.dart | 2 +- .../new/steps/frost_create_step_4.dart | 2 +- .../reshare/frost_reshare_step_1c.dart | 2 +- .../name_your_wallet_view.dart | 2 +- .../new_wallet_recovery_phrase_view.dart | 39 +- .../sub_widgets/mnemonic_table.dart | 4 +- .../sub_widgets/mnemonic_table_item.dart | 4 +- .../recovery_phrase_explanation_dialog.dart | 2 +- .../confirm_recovery_dialog.dart | 7 +- .../mobile_mnemonic_length_selector.dart | 6 +- .../sub_widgets/restore_from_date_picker.dart | 4 +- .../restore_options_next_button.dart | 4 +- .../restore_options_platform_layout.dart | 4 +- .../restore_wallet_view.dart | 5 - .../mnemonic_word_count_select_sheet.dart | 16 +- .../sub_widgets/restore_failed_dialog.dart | 4 +- .../sub_widgets/restore_succeeded_dialog.dart | 2 +- .../sub_widgets/restoring_dialog.dart | 4 +- .../select_wallet_for_token_view.dart | 4 +- .../sub_widgets/word_table.dart | 4 +- .../sub_widgets/word_table_item.dart | 4 +- .../verify_mnemonic_passphrase_dialog.dart | 6 +- .../address_book_views/address_book_view.dart | 1 - .../subviews/add_address_book_entry_view.dart | 263 ++-- .../add_new_contact_address_view.dart | 28 +- .../subviews/address_book_filter_view.dart | 77 +- .../subviews/coin_select_sheet.dart | 2 +- .../subviews/contact_details_view.dart | 54 +- .../subviews/contact_popup.dart | 96 +- .../subviews/edit_contact_address_view.dart | 29 +- .../edit_contact_name_emoji_view.dart | 60 +- lib/pages/buy_view/buy_form.dart | 6 +- lib/pages/buy_view/buy_order_details.dart | 26 +- lib/pages/buy_view/buy_quote_preview.dart | 10 +- .../sub_widgets/buy_warning_popup.dart | 21 +- .../sub_widgets/fiat_selection_view.dart | 37 +- lib/pages/cashfusion/cashfusion_view.dart | 15 +- .../cashfusion/fusion_progress_view.dart | 4 +- .../fusion_rounds_selection_sheet.dart | 4 +- lib/pages/coin_control/coin_control_view.dart | 71 +- lib/pages/coin_control/utxo_card.dart | 146 +-- lib/pages/coin_control/utxo_details_view.dart | 2 +- .../exchange_view/choose_from_stack_view.dart | 2 +- .../confirm_change_now_send.dart | 89 +- .../exchange_view/edit_trade_note_view.dart | 6 +- .../exchange_currency_selection_view.dart | 2 +- lib/pages/exchange_view/exchange_form.dart | 51 +- .../exchange_loading_overlay.dart | 9 +- .../exchange_step_views/step_1_view.dart | 49 +- .../exchange_step_views/step_2_view.dart | 4 +- .../exchange_step_views/step_3_view.dart | 52 +- .../exchange_step_views/step_4_view.dart | 4 +- lib/pages/exchange_view/exchange_view.dart | 130 +- lib/pages/exchange_view/send_from_view.dart | 6 +- .../sub_widgets/exchange_provider_option.dart | 9 +- .../exchange_provider_options.dart | 4 +- .../sub_widgets/rate_type_toggle.dart | 4 +- .../sub_widgets/step_indicator.dart | 4 +- .../exchange_view/sub_widgets/step_row.dart | 51 +- .../wallet_initiated_exchange_view.dart | 2 +- lib/pages/generic/single_field_edit_view.dart | 4 +- lib/pages/home_view/home_view.dart | 34 +- .../sub_widgets/home_view_button_bar.dart | 2 +- lib/pages/intro_view.dart | 12 +- lib/pages/loading_view.dart | 2 +- .../manage_favorites_view.dart | 4 +- lib/pages/monkey/monkey_view.dart | 17 +- .../sub_widgets/fetch_monkey_dialog.dart | 4 +- .../notifications_view.dart | 6 +- lib/pages/ordinals/ordinal_details_view.dart | 16 +- lib/pages/ordinals/ordinals_filter_view.dart | 25 +- lib/pages/ordinals/ordinals_view.dart | 4 +- lib/pages/ordinals/widgets/ordinal_card.dart | 4 +- lib/pages/ordinals/widgets/ordinals_list.dart | 17 +- .../paynym/add_new_paynym_follow_view.dart | 26 +- .../dialogs/claiming_paynym_dialog.dart | 4 +- .../confirm_paynym_connect_dialog.dart | 2 +- .../paynym/dialogs/paynym_details_popup.dart | 2 +- lib/pages/paynym/dialogs/paynym_qr_popup.dart | 6 +- lib/pages/paynym/paynym_claim_view.dart | 6 +- lib/pages/paynym/paynym_home_view.dart | 83 +- .../subwidgets/desktop_paynym_details.dart | 6 +- .../subwidgets/featured_paynyms_widget.dart | 4 +- lib/pages/paynym/subwidgets/paynym_bot.dart | 4 +- lib/pages/paynym/subwidgets/paynym_card.dart | 4 +- .../paynym/subwidgets/paynym_card_button.dart | 4 +- .../subwidgets/paynym_followers_list.dart | 4 +- .../subwidgets/paynym_following_list.dart | 4 +- lib/pages/pinpad_views/create_pin_view.dart | 15 +- lib/pages/pinpad_views/lock_screen_view.dart | 59 +- .../receive_view/addresses/address_card.dart | 12 +- .../addresses/address_details_view.dart | 90 +- .../addresses/address_qr_popup.dart | 12 +- .../receive_view/addresses/address_tag.dart | 2 +- .../addresses/edit_address_label_view.dart | 10 +- .../addresses/wallet_addresses_view.dart | 51 +- .../generate_receiving_uri_qr_code_view.dart | 52 +- lib/pages/receive_view/receive_view.dart | 68 +- .../send_view/frost_ms/frost_send_view.dart | 30 +- lib/pages/send_view/frost_ms/recipient.dart | 21 +- .../send_steps/frost_send_step_1b.dart | 16 +- .../send_steps/frost_send_step_2.dart | 29 +- .../send_steps/frost_send_step_3.dart | 11 +- .../send_steps/frost_send_step_4.dart | 10 +- lib/pages/send_view/send_view.dart | 24 +- .../firo_balance_selection_sheet.dart | 36 +- .../sending_transaction_dialog.dart | 4 +- .../transaction_fee_selection_sheet.dart | 4 - lib/pages/send_view/token_send_view.dart | 1 - .../advanced_settings_view.dart | 36 +- .../advanced_views/debug_view.dart | 119 +- .../edit_coin_units_view.dart | 2 +- .../advanced_views/manage_explorer_view.dart | 60 +- .../appearance_settings_view.dart | 19 +- .../appearance_settings/manage_themes.dart | 6 +- .../install_theme_from_file_dialog.dart | 4 +- .../sub_widgets/stack_theme_card.dart | 14 +- .../sub_widgets/theme_option.dart | 4 +- .../sub_widgets/theme_options_widget.dart | 2 +- ...ystem_brightness_theme_selection_view.dart | 29 +- .../global_settings_view/currency_view.dart | 32 +- .../delete_account_view.dart | 16 +- .../global_settings_view.dart | 42 +- .../global_settings_view/hidden_settings.dart | 1 - .../global_settings_view/language_view.dart | 21 +- .../add_edit_node_view.dart | 64 +- .../manage_nodes_views/manage_nodes_view.dart | 13 +- .../change_pin_view/change_pin_view.dart | 15 +- .../security_views/security_view.dart | 4 +- .../stack_backup_views/auto_backup_view.dart | 30 +- .../create_auto_backup_view.dart | 1062 +++++++++-------- .../create_backup_information_view.dart | 2 +- .../create_backup_view.dart | 699 ++++++----- .../dialogs/cancel_stack_restore_dialog.dart | 13 +- .../edit_auto_backup_view.dart | 71 +- .../restore_from_encrypted_string_view.dart | 23 +- .../restore_from_file_view.dart | 886 +++++++------- .../stack_backup_views/stack_backup_view.dart | 4 +- .../backup_frequency_type_select_sheet.dart | 15 +- .../sub_views/recovery_phrase_view.dart | 4 +- .../stack_restore_progress_view.dart | 129 +- .../sub_widgets/restoring_item_card.dart | 4 +- .../sub_widgets/restoring_wallet_card.dart | 33 +- .../startup_preferences_view.dart | 93 +- .../startup_wallet_selection_view.dart | 2 +- .../global_settings_view/support_view.dart | 10 +- .../syncing_options_view.dart | 26 +- .../syncing_preferences_view.dart | 21 +- .../wallet_syncing_options_view.dart | 300 ++--- .../tor_settings/tor_settings_view.dart | 38 +- .../frost_ms/frost_ms_options_view.dart | 34 +- .../wallet_backup_view.dart | 52 +- .../sub_widgets/confirm_full_rescan.dart | 7 +- .../sub_widgets/rescanning_dialog.dart | 4 +- .../wallet_network_settings_view.dart | 2 +- .../wallet_settings_view.dart | 73 +- .../change_representative_view.dart | 57 +- .../delete_wallet_warning_view.dart | 10 +- .../rename_wallet_view.dart | 4 +- .../wallet_settings_wallet_settings_view.dart | 4 +- .../xpub_view.dart | 23 +- .../firo_rescan_recovery_error_dialog.dart | 22 +- lib/pages/stack_privacy_calls.dart | 47 +- lib/pages/token_view/my_tokens_view.dart | 4 +- .../sub_widgets/my_token_select_item.dart | 38 +- .../sub_widgets/my_tokens_list.dart | 15 +- .../sub_widgets/no_tokens_found.dart | 2 +- .../token_view/sub_widgets/token_summary.dart | 5 +- .../token_transaction_list_widget.dart | 6 +- .../token_contract_details_view.dart | 8 +- lib/pages/token_view/token_view.dart | 27 +- .../sub_widgets/no_transactions_found.dart | 2 +- .../sub_widgets/transactions_list.dart | 21 +- .../wallet_balance_toggle_sheet.dart | 3 +- .../sub_widgets/wallet_navigation_bar.dart | 2 - .../sub_widgets/wallet_refresh_button.dart | 105 +- .../all_transactions_view.dart | 141 ++- ...ancelling_transaction_progress_dialog.dart | 2 +- .../transaction_views/edit_note_view.dart | 14 +- .../transaction_details_view.dart | 19 +- .../transaction_search_filter_view.dart | 40 +- .../tx_v2/all_transactions_v2_view.dart | 10 +- .../tx_v2/transaction_v2_card.dart | 51 +- .../tx_v2/transaction_v2_details_view.dart | 8 +- .../tx_v2/transaction_v2_list.dart | 56 +- .../tx_v2/transaction_v2_list_item.dart | 21 +- lib/pages/wallet_view/wallet_view.dart | 3 - .../wallets_view/sub_widgets/all_wallets.dart | 2 +- .../sub_widgets/empty_wallets.dart | 8 +- .../sub_widgets/favorite_card.dart | 8 +- .../sub_widgets/favorite_wallets.dart | 29 +- .../sub_widgets/wallet_list_item.dart | 17 +- lib/pages/wallets_view/wallets_overview.dart | 9 +- lib/pages/wallets_view/wallets_view.dart | 7 +- .../desktop_address_book.dart | 1 - .../desktop_address_book_scaffold.dart | 6 +- .../subwidgets/desktop_address_card.dart | 11 +- .../subwidgets/desktop_contact_details.dart | 31 +- .../desktop_contact_options_menu_popup.dart | 35 +- .../desktop_wallet_addresses_view.dart | 10 +- .../sub_widgets/desktop_address_list.dart | 43 +- .../cashfusion/desktop_cashfusion_view.dart | 24 +- .../cashfusion/sub_widgets/fusion_dialog.dart | 6 +- .../sub_widgets/fusion_progress.dart | 58 +- .../desktop_coin_control_use_dialog.dart | 26 +- .../desktop_coin_control_view.dart | 18 +- .../coin_control/freeze_button.dart | 8 +- .../coin_control/utxo_row.dart | 4 +- .../desktop_buy/desktop_buy_view.dart | 2 +- .../desktop_all_trades_view.dart | 27 +- .../desktop_exchange_view.dart | 8 +- .../subwidgets/desktop_step_1.dart | 21 +- .../subwidgets/desktop_step_2.dart | 4 +- .../subwidgets/desktop_step_3.dart | 28 +- .../subwidgets/desktop_step_4.dart | 39 +- .../subwidgets/desktop_step_item.dart | 15 +- .../subwidgets/desktop_choose_from_stack.dart | 16 +- .../desktop_exchange_steps_indicator.dart | 3 +- .../subwidgets/desktop_trade_history.dart | 18 +- .../desktop_home_view.dart | 31 +- lib/pages_desktop_specific/desktop_menu.dart | 8 +- .../desktop_menu_item.dart | 38 +- .../lelantus_coins/lelantus_coins_view.dart | 4 +- .../my_stack_view/coin_wallets_table.dart | 4 +- .../desktop_favorite_wallets.dart | 8 +- .../desktop_expanding_wallet_card.dart | 4 +- .../exit_to_my_stack_button.dart | 4 +- .../my_stack_view/my_stack_view.dart | 2 +- .../my_stack_view/my_wallets.dart | 12 +- .../paynym/desktop_paynym_send_dialog.dart | 28 +- .../my_stack_view/wallet_summary_table.dart | 4 +- .../wallet_view/desktop_token_view.dart | 4 +- .../wallet_view/desktop_wallet_view.dart | 22 +- .../address_book_address_chooser.dart | 20 +- .../sub_widgets/contact_list_item.dart | 26 +- .../sub_widgets/delete_wallet_keys_popup.dart | 8 +- .../sub_widgets/desktop_auth_send.dart | 6 +- .../desktop_balance_toggle_button.dart | 8 +- .../desktop_delete_wallet_dialog.dart | 16 +- .../sub_widgets/desktop_fee_dropdown.dart | 77 +- .../sub_widgets/desktop_receive.dart | 14 +- .../wallet_view/sub_widgets/desktop_send.dart | 7 - .../sub_widgets/desktop_token_send.dart | 1 - .../sub_widgets/desktop_wallet_features.dart | 4 +- .../sub_widgets/desktop_wallet_summary.dart | 31 +- .../more_features/more_features_dialog.dart | 4 +- .../wallet_view/sub_widgets/my_wallet.dart | 1 - .../sub_widgets/network_info_button.dart | 4 +- .../qr_code_desktop_popup_content.dart | 4 +- .../unlock_wallet_keys_desktop.dart | 16 +- .../sub_widgets/wallet_keys_button.dart | 8 +- .../wallet_keys_desktop_popup.dart | 25 +- .../sub_widgets/wallet_options_button.dart | 41 +- .../desktop_notifications_view.dart | 2 +- .../desktop_ordinal_details_view.dart | 58 +- .../ordinals/desktop_ordinals_view.dart | 24 +- .../password/create_password_view.dart | 74 +- .../password/desktop_login_view.dart | 24 +- ...forgotten_passphrase_restore_from_swb.dart | 5 +- .../settings/desktop_settings_view.dart | 4 +- .../settings/settings_menu.dart | 13 +- .../advanced_settings/advanced_settings.dart | 159 +-- .../advanced_settings/debug_info_dialog.dart | 2 +- ...desktop_manage_block_explorers_dialog.dart | 18 +- .../stack_privacy_dialog.dart | 44 +- .../sub_widgets/desktop_install_theme.dart | 4 +- .../sub_widgets/desktop_manage_themes.dart | 2 +- .../sub_widgets/desktop_themes_gallery.dart | 8 +- .../create_auto_backup.dart | 190 +-- .../enable_backup_dialog.dart | 4 +- .../currency_settings/currency_settings.dart | 2 +- .../settings_menu/desktop_about_view.dart | 3 +- .../settings_menu/desktop_support_view.dart | 10 +- .../language_settings/language_dialog.dart | 41 +- .../language_settings/language_settings.dart | 8 +- .../settings_menu/security_settings.dart | 73 +- .../syncing_preferences_settings.dart | 79 +- .../tor_settings/tor_settings.dart | 31 +- .../settings/settings_menu_item.dart | 4 +- .../spark_coins/spark_coins_view.dart | 4 +- .../exchange_form_state_provider.dart | 1 - ...trade_sent_from_stack_lookup_provider.dart | 4 +- .../global/base_currencies_provider.dart | 2 +- .../global/secure_store_provider.dart | 7 +- .../stack_restoring_ui_state_provider.dart | 4 +- .../valid_contact_state_provider.dart | 7 +- .../ui/preview_tx_button_state_provider.dart | 1 - .../wallet_balance_toggle_state_provider.dart | 4 +- lib/route_generator.dart | 10 +- lib/services/auto_swb_service.dart | 35 +- .../coins/bitcoincash/cashtokens.dart | 57 +- lib/services/coins/tezos/api/tezos_api.dart | 16 +- .../coins/tezos/api/tezos_rpc_api.dart | 2 +- .../coins/tezos/api/tezos_transaction.dart | 2 +- lib/services/debug_service.dart | 9 +- lib/services/ethereum/ethereum_api.dart | 31 +- .../events/global/blocks_remaining_event.dart | 5 +- .../global/refresh_percent_changed_event.dart | 5 +- .../tor_connection_status_changed_event.dart | 5 +- .../global/updated_in_background_event.dart | 5 +- .../exchange/change_now/change_now_api.dart | 193 ++- .../exchange_data_loading_service.dart | 39 +- .../majestic_bank/majestic_bank_api.dart | 14 +- .../majestic_bank/majestic_bank_exchange.dart | 4 +- .../exchange/simpleswap/simpleswap_api.dart | 108 +- .../simpleswap/simpleswap_exchange.dart | 4 +- lib/services/litescribe_api.dart | 26 +- lib/services/nano_api.dart | 6 +- lib/services/notifications_api.dart | 18 +- .../trade_sent_from_stack_service.dart | 12 +- lib/services/trade_service.dart | 13 +- .../transaction_notification_tracker.dart | 60 +- lib/themes/coin_icon_provider.dart | 11 - lib/themes/stack_colors.dart | 15 +- lib/utilities/address_utils.dart | 8 +- lib/utilities/amount/amount.dart | 10 +- .../amount/amount_input_formatter.dart | 7 +- lib/utilities/amount/amount_unit.dart | 3 - lib/utilities/biometrics.dart | 35 +- lib/utilities/bip47_utils.dart | 6 +- .../electrum_connection_check.dart | 4 +- lib/utilities/desktop_password_service.dart | 10 +- lib/utilities/enums/fiat_enum.dart | 5 +- lib/utilities/eth_commons.dart | 1 - lib/utilities/extensions/impl/big_int.dart | 2 +- .../extensions/impl/contract_abi.dart | 16 +- .../flutter_secure_storage_interface.dart | 9 +- lib/utilities/format.dart | 4 +- lib/utilities/logger.dart | 6 +- lib/utilities/paynym_is_api.dart | 13 +- lib/utilities/stack_file_system.dart | 6 +- lib/utilities/test_epic_box_connection.dart | 8 +- lib/utilities/test_eth_node_connection.dart | 2 +- lib/utilities/test_node_connection.dart | 6 - .../test_stellar_node_connection.dart | 15 +- lib/utilities/util.dart | 4 +- lib/wallets/api/lelantus_ffi_wrapper.dart | 96 +- lib/wallets/api/tezos/tezos_api.dart | 10 +- lib/wallets/api/tezos/tezos_rpc_api.dart | 2 +- .../crypto_currency/coins/bitcoincash.dart | 7 +- lib/wallets/crypto_currency/coins/ecash.dart | 10 +- .../crypto_currency/coins/namecoin.dart | 8 +- .../crypto_currency/coins/particl.dart | 12 +- .../crypto_currency/coins/peercoin.dart | 8 +- lib/wallets/crypto_currency/coins/solana.dart | 4 +- .../crypto_currency/coins/stellar.dart | 3 +- lib/wallets/crypto_currency/coins/tezos.dart | 20 +- lib/wallets/isar/models/wallet_info.dart | 8 +- .../providers/eth/token_balance_provider.dart | 8 +- .../isar/providers/wallet_info_provider.dart | 10 +- lib/wallets/wallet/impl/banano_wallet.dart | 1 - .../wallet/impl/bitcoin_frost_wallet.dart | 1 - lib/wallets/wallet/impl/bitcoin_wallet.dart | 7 +- .../wallet/impl/bitcoincash_wallet.dart | 40 +- lib/wallets/wallet/impl/dogecoin_wallet.dart | 25 +- lib/wallets/wallet/impl/ecash_wallet.dart | 24 +- lib/wallets/wallet/impl/epiccash_wallet.dart | 52 +- lib/wallets/wallet/impl/ethereum_wallet.dart | 18 +- lib/wallets/wallet/impl/firo_wallet.dart | 42 +- lib/wallets/wallet/impl/litecoin_wallet.dart | 20 +- lib/wallets/wallet/impl/namecoin_wallet.dart | 25 +- lib/wallets/wallet/impl/nano_wallet.dart | 1 - lib/wallets/wallet/impl/particl_wallet.dart | 40 +- lib/wallets/wallet/impl/peercoin_wallet.dart | 1 - lib/wallets/wallet/impl/solana_wallet.dart | 60 +- lib/wallets/wallet/impl/stellar_wallet.dart | 40 +- lib/wallets/wallet/impl/tezos_wallet.dart | 1 - .../intermediate/cryptonote_wallet.dart | 2 +- lib/wallets/wallet/wallet.dart | 23 +- .../bcash_interface.dart | 7 +- .../cash_fusion_interface.dart | 228 ++-- .../cw_based_interface.dart | 13 +- .../lelantus_interface.dart | 6 +- .../ordinals_interface.dart | 13 +- .../paynym_interface.dart | 1 - .../spark_interface.dart | 4 +- lib/widgets/address_book_card.dart | 6 +- lib/widgets/animated_text.dart | 4 +- lib/widgets/animated_widgets/rotate_icon.dart | 4 +- .../animated_widgets/rotating_arrows.dart | 4 +- lib/widgets/app_bar_field.dart | 4 +- lib/widgets/background.dart | 4 +- lib/widgets/choose_coin_view.dart | 2 +- lib/widgets/conditional_parent.dart | 8 +- .../custom_buttons/app_bar_icon_button.dart | 105 +- .../custom_buttons/blue_text_button.dart | 8 +- .../draggable_switch_button.dart | 12 +- .../custom_buttons/dropdown_button.dart | 27 +- .../custom_buttons/favorite_toggle.dart | 4 +- .../paynym_follow_toggle_button.dart | 33 +- .../custom_buttons/simple_copy_button.dart | 4 +- lib/widgets/custom_loading_overlay.dart | 4 +- .../custom_page_view/custom_page_view.dart | 144 ++- .../custom_sliver_fill_viewport.dart | 52 +- .../custom_pin_put/custom_pin_put.dart | 4 +- lib/widgets/custom_pin_put/pin_keyboard.dart | 154 +-- lib/widgets/custom_tab_view.dart | 6 +- lib/widgets/desktop/custom_text_button.dart | 4 +- lib/widgets/desktop/delete_button.dart | 8 +- lib/widgets/desktop/desktop_app_bar.dart | 4 +- lib/widgets/desktop/desktop_dialog.dart | 4 +- .../desktop/desktop_dialog_close_button.dart | 4 +- lib/widgets/desktop/desktop_fee_dialog.dart | 8 +- lib/widgets/desktop/desktop_scaffold.dart | 8 +- lib/widgets/desktop/outline_blue_button.dart | 4 +- lib/widgets/desktop/paynym_search_button.dart | 4 +- lib/widgets/desktop/primary_button.dart | 4 +- lib/widgets/desktop/secondary_button.dart | 4 +- .../desktop/simple_desktop_dialog.dart | 6 +- lib/widgets/dialogs/basic_dialog.dart | 6 +- .../frost/frost_step_explanation_dialog.dart | 4 +- .../dialogs/frost/frost_step_qr_dialog.dart | 14 +- lib/widgets/emoji_select_sheet.dart | 19 +- lib/widgets/eth_wallet_radio.dart | 4 +- .../exchange/trocador/trocador_kyc_icon.dart | 4 +- .../trocador/trocador_kyc_info_button.dart | 4 +- lib/widgets/expandable.dart | 4 +- lib/widgets/expandable2.dart | 4 +- lib/widgets/fee_slider.dart | 1 - lib/widgets/frost_scaffold.dart | 2 +- lib/widgets/hover_text_field.dart | 4 +- .../icon_widgets/addressbook_icon.dart | 4 +- lib/widgets/icon_widgets/clipboard_icon.dart | 4 +- lib/widgets/icon_widgets/copy_icon.dart | 4 +- lib/widgets/icon_widgets/dice_icon.dart | 4 +- lib/widgets/icon_widgets/eth_token_icon.dart | 1 - lib/widgets/icon_widgets/pencil_icon.dart | 4 +- lib/widgets/icon_widgets/qrcode_icon.dart | 4 +- lib/widgets/icon_widgets/share_icon.dart | 4 +- .../icon_widgets/utxo_status_icon.dart | 4 +- lib/widgets/icon_widgets/x_icon.dart | 4 +- lib/widgets/loading_indicator.dart | 4 +- lib/widgets/master_wallet_card.dart | 4 +- lib/widgets/node_options_sheet.dart | 6 +- .../tor_has_been_add_dialog.dart | 6 +- lib/widgets/progress_bar.dart | 6 +- lib/widgets/rounded_container.dart | 4 +- lib/widgets/rounded_white_container.dart | 4 +- lib/widgets/shake/shake.dart | 4 +- lib/widgets/stack_dialog.dart | 16 +- lib/widgets/stack_text_field.dart | 7 +- lib/widgets/table_view/table_view.dart | 6 +- lib/widgets/table_view/table_view_cell.dart | 4 +- lib/widgets/table_view/table_view_row.dart | 4 +- lib/widgets/textfield_icon_button.dart | 6 +- .../textfields/exchange_textfield.dart | 13 +- lib/widgets/textfields/frost_step_field.dart | 4 +- lib/widgets/toggle.dart | 15 +- lib/widgets/trade_card.dart | 10 +- lib/widgets/transaction_card.dart | 56 +- lib/widgets/trocador_kyc_rating_info.dart | 10 +- lib/widgets/wallet_card.dart | 11 +- .../wallet_info_row/wallet_info_row.dart | 13 +- .../components/icons/buy_nav_icon.dart | 2 +- .../icons/coin_control_nav_icon.dart | 2 +- .../components/icons/exchange_nav_icon.dart | 2 +- .../components/icons/fusion_nav_icon.dart | 2 +- .../components/icons/ordinals_nav_icon.dart | 2 +- .../components/icons/paynym_nav_icon.dart | 2 +- .../components/icons/receive_nav_icon.dart | 2 +- .../components/icons/send_nav_icon.dart | 2 +- .../components/icons/whirlpool_nav_icon.dart | 2 +- .../wallet_navigation_bar_item.dart | 8 +- .../wallet_navigation_bar.dart | 6 +- test/address_utils_test.dart | 1 - test/cached_electrumx_test.dart | 1 - test/cached_electrumx_test.mocks.dart | 271 +++-- ...r_secure_storage_interface_test.mocks.dart | 4 +- test/global_events_test.dart | 1 - .../lelantus_coin_adapter_test.dart | 10 +- .../lelantus_coin_adapter_test.mocks.dart | 16 +- .../transactions_model_adapter_test.dart | 18 +- ...transactions_model_adapter_test.mocks.dart | 16 +- .../utxo_model_adapter_test.dart | 12 +- .../utxo_model_adapter_test.mocks.dart | 16 +- .../notification_card_test.mocks.dart | 4 +- .../pages/send_view/send_view_test.mocks.dart | 101 +- test/price_test.mocks.dart | 4 +- .../sample_data/transaction_data_samples.dart | 4 +- .../address_book_view_screen_test.dart | 2 +- .../address_book_view_screen_test.mocks.dart | 8 +- .../add_address_book_view_screen_test.dart | 2 +- ...d_address_book_view_screen_test.mocks.dart | 8 +- ...s_book_entry_details_view_screen_test.dart | 4 +- ..._entry_details_view_screen_test.mocks.dart | 18 +- ...t_address_book_entry_view_screen_test.dart | 2 +- ...ess_book_entry_view_screen_test.mocks.dart | 8 +- .../exchange/exchange_view_test.mocks.dart | 355 +++--- .../lockscreen_view_screen_test.dart | 11 +- .../lockscreen_view_screen_test.mocks.dart | 12 +- .../main_view_screen_testA_test.dart | 4 +- .../main_view_screen_testA_test.mocks.dart | 18 +- .../main_view_screen_testB_test.dart | 4 +- .../main_view_screen_testB_test.mocks.dart | 18 +- .../main_view_screen_testC_test.dart | 4 +- .../main_view_screen_testC_test.mocks.dart | 18 +- .../backup_key_warning_view_screen_test.dart | 2 +- ...up_key_warning_view_screen_test.mocks.dart | 8 +- .../create_pin_view_screen_test.dart | 4 +- .../create_pin_view_screen_test.mocks.dart | 12 +- .../name_your_wallet_view_screen_test.dart | 2 +- ...me_your_wallet_view_screen_test.mocks.dart | 8 +- .../restore_wallet_view_screen_test.dart | 4 +- ...restore_wallet_view_screen_test.mocks.dart | 12 +- .../add_custom_node_view_screen_test.dart | 2 +- ...dd_custom_node_view_screen_test.mocks.dart | 8 +- .../node_details_view_screen_test.dart | 2 +- .../node_details_view_screen_test.mocks.dart | 8 +- .../network_settings_view_screen_test.dart | 2 +- ...twork_settings_view_screen_test.mocks.dart | 8 +- .../change_pin_view_screen_test.dart | 2 +- .../change_pin_view_screen_test.mocks.dart | 8 +- .../rename_wallet_view_screen_test.dart | 2 +- .../rename_wallet_view_screen_test.mocks.dart | 8 +- ...llet_delete_mnemonic_view_screen_test.dart | 2 +- ...elete_mnemonic_view_screen_test.mocks.dart | 8 +- .../wallet_settings_view_screen_test.dart | 2 +- ...allet_settings_view_screen_test.mocks.dart | 71 +- .../settings_view_screen_test.dart | 2 +- .../settings_view_screen_test.mocks.dart | 8 +- .../transaction_details_view_screen_test.dart | 4 +- ...action_details_view_screen_test.mocks.dart | 18 +- ...ction_search_results_view_screen_test.dart | 2 +- ...search_results_view_screen_test.mocks.dart | 30 +- .../transaction_search_view_screen_test.dart | 2 +- ...saction_search_view_screen_test.mocks.dart | 8 +- .../send_view_screen_test.mocks.dart | 4 +- .../wallet_view/wallet_view_screen_test.dart | 2 +- .../wallet_view_screen_test.mocks.dart | 30 +- .../change_now/change_now_test.mocks.dart | 4 +- .../bitcoin/bitcoin_wallet_test.mocks.dart | 259 ++-- .../bitcoincash_wallet_test.mocks.dart | 259 ++-- .../coins/bitcoincash/cashtokens_test.dart | 8 +- .../dogecoin/dogecoin_wallet_test.mocks.dart | 259 ++-- .../sample_data/transaction_data_samples.dart | 2 +- .../namecoin/namecoin_wallet_test.mocks.dart | 259 ++-- .../particl/particl_wallet_test.mocks.dart | 259 ++-- test/utilities/amount/amount_unit_test.dart | 2 - test/widget_tests/address_book_card_test.dart | 1 - .../address_book_card_test.mocks.dart | 4 +- .../favorite_toggle_test.mocks.dart | 4 +- .../custom_loading_overlay_test.mocks.dart | 4 +- test/widget_tests/custom_pin_put_test.dart | 2 +- .../desktop/desktop_scaffold_test.mocks.dart | 4 +- test/widget_tests/managed_favorite_test.dart | 19 +- .../managed_favorite_test.mocks.dart | 117 +- test/widget_tests/node_card_test.dart | 1 - test/widget_tests/node_card_test.mocks.dart | 4 +- .../widget_tests/node_options_sheet_test.dart | 3 +- .../node_options_sheet_test.mocks.dart | 116 +- .../table_view/table_view_row_test.mocks.dart | 4 +- test/widget_tests/trade_card_test.mocks.dart | 95 +- .../transaction_card_test.mocks.dart | 404 ++++--- test/widget_tests/wallet_card_test.mocks.dart | 52 +- .../wallet_info_row_balance_future_test.dart | 15 +- ...et_info_row_balance_future_test.mocks.dart | 8 +- .../wallet_info_row/wallet_info_row_test.dart | 18 +- .../wallet_info_row_test.mocks.dart | 8 +- 605 files changed, 9126 insertions(+), 6975 deletions(-) diff --git a/lib/db/isar/main_db.dart b/lib/db/isar/main_db.dart index 931f18263..1ea6830c5 100644 --- a/lib/db/isar/main_db.dart +++ b/lib/db/isar/main_db.dart @@ -11,6 +11,8 @@ import 'package:decimal/decimal.dart'; import 'package:flutter_native_splash/cli_commands.dart'; import 'package:isar/isar.dart'; +import 'package:tuple/tuple.dart'; + import '../../exceptions/main_db/main_db_exception.dart'; import '../../models/isar/models/block_explorer.dart'; import '../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; @@ -26,7 +28,6 @@ import '../../wallets/isar/models/spark_coin.dart'; import '../../wallets/isar/models/token_wallet_info.dart'; import '../../wallets/isar/models/wallet_info.dart'; import '../../wallets/isar/models/wallet_info_meta.dart'; -import 'package:tuple/tuple.dart'; part '../queries/queries.dart'; @@ -149,8 +150,9 @@ class MainDB { } // tx block explorers - TransactionBlockExplorer? getTransactionBlockExplorer( - {required CryptoCurrency cryptoCurrency}) { + TransactionBlockExplorer? getTransactionBlockExplorer({ + required CryptoCurrency cryptoCurrency, + }) { return isar.transactionBlockExplorers .where() .tickerEqualTo(cryptoCurrency.ticker) diff --git a/lib/db/migrate_wallets_to_isar.dart b/lib/db/migrate_wallets_to_isar.dart index f957ff6e9..474924997 100644 --- a/lib/db/migrate_wallets_to_isar.dart +++ b/lib/db/migrate_wallets_to_isar.dart @@ -207,7 +207,8 @@ Future migrateWalletsToIsar({ } await _cleanupOnSuccess( - walletIds: newInfo.map((e) => e.$1.walletId).toList()); + walletIds: newInfo.map((e) => e.$1.walletId).toList(), + ); } Future _cleanupOnSuccess({required List walletIds}) async { diff --git a/lib/dto/ethereum/eth_token_tx_extra_dto.dart b/lib/dto/ethereum/eth_token_tx_extra_dto.dart index c902d2bba..401ea7122 100644 --- a/lib/dto/ethereum/eth_token_tx_extra_dto.dart +++ b/lib/dto/ethereum/eth_token_tx_extra_dto.dart @@ -11,7 +11,6 @@ import 'dart:convert'; import '../../utilities/amount/amount.dart'; -import '../../wallets/crypto_currency/coins/ethereum.dart'; import '../../wallets/crypto_currency/crypto_currency.dart'; class EthTokenTxExtraDTO { diff --git a/lib/dto/ethereum/eth_tx_dto.dart b/lib/dto/ethereum/eth_tx_dto.dart index 2f60e9792..10a46d740 100644 --- a/lib/dto/ethereum/eth_tx_dto.dart +++ b/lib/dto/ethereum/eth_tx_dto.dart @@ -11,7 +11,6 @@ import 'dart:convert'; import '../../utilities/amount/amount.dart'; -import '../../wallets/crypto_currency/coins/ethereum.dart'; import '../../wallets/crypto_currency/crypto_currency.dart'; class EthTxDTO { diff --git a/lib/dto/ordinals/address_inscription_response.dart b/lib/dto/ordinals/address_inscription_response.dart index fa3144b57..ba9ce0747 100644 --- a/lib/dto/ordinals/address_inscription_response.dart +++ b/lib/dto/ordinals/address_inscription_response.dart @@ -1,7 +1,8 @@ -import 'litescribe_response.dart'; import 'inscription_data.dart'; +import 'litescribe_response.dart'; -class AddressInscriptionResponse extends LitescribeResponse { +class AddressInscriptionResponse + extends LitescribeResponse { final int status; final String message; final AddressInscriptionResult result; @@ -16,7 +17,8 @@ class AddressInscriptionResponse extends LitescribeResponse), + result: AddressInscriptionResult.fromJson( + json['result'] as Map), ); } } @@ -32,7 +34,9 @@ class AddressInscriptionResult { factory AddressInscriptionResult.fromJson(Map json) { return AddressInscriptionResult( - list: (json['list'] as List).map((item) => InscriptionData.fromJson(item as Map)).toList(), + list: (json['list'] as List) + .map((item) => InscriptionData.fromJson(item as Map)) + .toList(), total: json['total'] as int, ); } diff --git a/lib/electrumx_rpc/cached_electrumx_client.dart b/lib/electrumx_rpc/cached_electrumx_client.dart index 569c092fa..fb4ff4a87 100644 --- a/lib/electrumx_rpc/cached_electrumx_client.dart +++ b/lib/electrumx_rpc/cached_electrumx_client.dart @@ -11,11 +11,12 @@ import 'dart:convert'; import 'dart:math'; +import 'package:string_validator/string_validator.dart'; + import '../db/hive/db.dart'; -import 'electrumx_client.dart'; import '../utilities/logger.dart'; import '../wallets/crypto_currency/crypto_currency.dart'; -import 'package:string_validator/string_validator.dart'; +import 'electrumx_client.dart'; class CachedElectrumXClient { final ElectrumXClient electrumXClient; @@ -331,8 +332,9 @@ class CachedElectrumXClient { } /// Clear all cached transactions for the specified coin - Future clearSharedTransactionCache( - {required CryptoCurrency cryptoCurrency}) async { + Future clearSharedTransactionCache({ + required CryptoCurrency cryptoCurrency, + }) async { await DB.instance.clearSharedTransactionCache(currency: cryptoCurrency); await DB.instance.closeAnonymitySetCacheBox(currency: cryptoCurrency); } diff --git a/lib/electrumx_rpc/electrumx_client.dart b/lib/electrumx_rpc/electrumx_client.dart index c37547f92..afe44ec14 100644 --- a/lib/electrumx_rpc/electrumx_client.dart +++ b/lib/electrumx_rpc/electrumx_client.dart @@ -28,8 +28,6 @@ import '../services/event_bus/global_event_bus.dart'; import '../services/tor_service.dart'; import '../utilities/logger.dart'; import '../utilities/prefs.dart'; -import '../wallets/crypto_currency/coins/dogecoin.dart'; -import '../wallets/crypto_currency/coins/firo.dart'; import '../wallets/crypto_currency/crypto_currency.dart'; import 'package:stream_channel/stream_channel.dart'; diff --git a/lib/main.dart b/lib/main.dart index c1b10e77b..52370d13d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -180,7 +180,8 @@ void main(List args) async { Hive.registerAdapter(UnspentCoinsInfoAdapter()); await Hive.initFlutter( - (await StackFileSystem.applicationHiveDirectory()).path); + (await StackFileSystem.applicationHiveDirectory()).path, + ); await Hive.openBox(DB.boxNameDBInfo); await Hive.openBox(DB.boxNamePrefs); @@ -202,8 +203,10 @@ void main(List args) async { // Desktop migrate handled elsewhere (currently desktop_login_view.dart) if (!Util.isDesktop) { - int dbVersion = DB.instance.get( - boxName: DB.boxNameDBInfo, key: "hive_data_version") as int? ?? + final int dbVersion = DB.instance.get( + boxName: DB.boxNameDBInfo, + key: "hive_data_version", + ) as int? ?? 0; if (dbVersion < Constants.currentDataVersion) { try { @@ -215,8 +218,11 @@ void main(List args) async { ), ); } catch (e, s) { - Logging.instance.log("Cannot migrate mobile database\n$e $s", - level: LogLevel.Error, printFullLength: true); + Logging.instance.log( + "Cannot migrate mobile database\n$e $s", + level: LogLevel.Error, + printFullLength: true, + ); } } } @@ -262,7 +268,7 @@ void main(List args) async { /// MyApp initialises relevant services with a MultiProvider class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); + const MyApp({super.key}); @override Widget build(BuildContext context) { @@ -279,8 +285,8 @@ class MyApp extends StatelessWidget { class MaterialAppWithTheme extends ConsumerStatefulWidget { const MaterialAppWithTheme({ - Key? key, - }) : super(key: key); + super.key, + }); @override ConsumerState createState() => @@ -382,7 +388,8 @@ class _MaterialAppWithThemeState extends ConsumerState switch (ref.read(prefsChangeNotifierProvider).backupFrequencyType) { case BackupFrequencyType.everyTenMinutes: ref.read(autoSWBServiceProvider).startPeriodicBackupTimer( - duration: const Duration(minutes: 10)); + duration: const Duration(minutes: 10), + ); break; case BackupFrequencyType.everyAppStart: unawaited(ref.read(autoSWBServiceProvider).doBackup()); @@ -448,7 +455,8 @@ class _MaterialAppWithThemeState extends ConsumerState await loadingCompleter.future; await goToRestoreSWB( - ref.read(openedFromSWBFileStringStateProvider.state).state!); + ref.read(openedFromSWBFileStringStateProvider.state).state!, + ); ref.read(openedFromSWBFileStringStateProvider.state).state = null; } // ref.read(shouldShowLockscreenOnResumeStateProvider.state).state = false; @@ -511,7 +519,8 @@ class _MaterialAppWithThemeState extends ConsumerState if (ref.read(openedFromSWBFileStringStateProvider.state).state != null) { await goToRestoreSWB( - ref.read(openedFromSWBFileStringStateProvider.state).state!); + ref.read(openedFromSWBFileStringStateProvider.state).state!, + ); ref.read(openedFromSWBFileStringStateProvider.state).state = null; } } @@ -559,8 +568,9 @@ class _MaterialAppWithThemeState extends ConsumerState await resetOpenPath(); Logging.instance.log( - "This is the .swb content from intent: ${ref.read(openedFromSWBFileStringStateProvider.state).state}", - level: LogLevel.Info); + "This is the .swb content from intent: ${ref.read(openedFromSWBFileStringStateProvider.state).state}", + level: LogLevel.Info, + ); } /// should only be called on android currently @@ -575,27 +585,31 @@ class _MaterialAppWithThemeState extends ConsumerState .then((value) { if (value is! bool || value == false) { Navigator.of(navigatorKey.currentContext!).pushNamed( - RestoreFromEncryptedStringView.routeName, - arguments: encrypted); + RestoreFromEncryptedStringView.routeName, + arguments: encrypted, + ); } }); } else { - unawaited(Navigator.push( - navigatorKey.currentContext!, - RouteGenerator.getRoute( - shouldUseMaterialRoute: RouteGenerator.useMaterialPageRoute, - builder: (_) => LockscreenView( - showBackButton: true, - routeOnSuccess: RestoreFromEncryptedStringView.routeName, - routeOnSuccessArguments: encrypted, - biometricsCancelButtonString: "CANCEL", - biometricsLocalizedReason: - "Authenticate to restore ${AppConfig.appName} backup", - biometricsAuthenticationTitle: "Restore ${AppConfig.prefix} backup", + unawaited( + Navigator.push( + navigatorKey.currentContext!, + RouteGenerator.getRoute( + shouldUseMaterialRoute: RouteGenerator.useMaterialPageRoute, + builder: (_) => LockscreenView( + showBackButton: true, + routeOnSuccess: RestoreFromEncryptedStringView.routeName, + routeOnSuccessArguments: encrypted, + biometricsCancelButtonString: "CANCEL", + biometricsLocalizedReason: + "Authenticate to restore ${AppConfig.appName} backup", + biometricsAuthenticationTitle: + "Restore ${AppConfig.prefix} backup", + ), + settings: const RouteSettings(name: "/swbrestorelockscreen"), ), - settings: const RouteSettings(name: "/swbrestorelockscreen"), ), - )); + ); } } @@ -655,7 +669,8 @@ class _MaterialAppWithThemeState extends ConsumerState foregroundColor: MaterialStateProperty.all(colorScheme.buttonTextSecondary), backgroundColor: MaterialStateProperty.all( - colorScheme.buttonBackSecondary), + colorScheme.buttonBackSecondary, + ), shape: MaterialStateProperty.all( RoundedRectangleBorder( // 1000 to be relatively sure it keeps its pill shape diff --git a/lib/models/add_wallet_list_entity/sub_classes/eth_token_entity.dart b/lib/models/add_wallet_list_entity/sub_classes/eth_token_entity.dart index 5980b6503..ef0074dd4 100644 --- a/lib/models/add_wallet_list_entity/sub_classes/eth_token_entity.dart +++ b/lib/models/add_wallet_list_entity/sub_classes/eth_token_entity.dart @@ -10,7 +10,6 @@ import '../add_wallet_list_entity.dart'; import '../../isar/models/ethereum/eth_contract.dart'; -import '../../../wallets/crypto_currency/coins/ethereum.dart'; import '../../../wallets/crypto_currency/crypto_currency.dart'; class EthTokenEntity extends AddWalletListEntity { diff --git a/lib/models/balance.dart b/lib/models/balance.dart index 66eb6ab92..968041254 100644 --- a/lib/models/balance.dart +++ b/lib/models/balance.dart @@ -72,7 +72,8 @@ class Balance { ), pendingSpendable: decoded["pendingSpendable"] is String ? Amount.fromSerializedJsonString( - decoded["pendingSpendable"] as String) + decoded["pendingSpendable"] as String, + ) : Amount( rawValue: BigInt.from(decoded["pendingSpendable"] as int), fractionDigits: deprecatedValue, diff --git a/lib/models/buy/response_objects/fiat.dart b/lib/models/buy/response_objects/fiat.dart index f8a425836..3b8579ffa 100644 --- a/lib/models/buy/response_objects/fiat.dart +++ b/lib/models/buy/response_objects/fiat.dart @@ -23,11 +23,12 @@ class Fiat { /// Fiat name final Decimal maxAmount; - Fiat( - {required this.ticker, - required this.name, - required this.minAmount, - required this.maxAmount}); + Fiat({ + required this.ticker, + required this.name, + required this.minAmount, + required this.maxAmount, + }); factory Fiat.fromJson(Map json) { try { diff --git a/lib/models/buy/simplex/simplex.dart b/lib/models/buy/simplex/simplex.dart index 15d71eeb6..a787d2747 100644 --- a/lib/models/buy/simplex/simplex.dart +++ b/lib/models/buy/simplex/simplex.dart @@ -9,6 +9,7 @@ */ import 'package:decimal/decimal.dart'; + import '../response_objects/crypto.dart'; import '../response_objects/fiat.dart'; import '../response_objects/order.dart'; @@ -20,7 +21,8 @@ class Simplex { SimplexQuote quote = SimplexQuote( crypto: Crypto.fromJson({'ticker': 'BTC', 'name': 'Bitcoin', 'image': ''}), fiat: Fiat.fromJson( - {'ticker': 'USD', 'name': 'United States Dollar', 'image': ''}), + {'ticker': 'USD', 'name': 'United States Dollar', 'image': ''}, + ), youPayFiatPrice: Decimal.parse("100"), youReceiveCryptoAmount: Decimal.parse("1.0238917"), id: "someID", @@ -28,20 +30,22 @@ class Simplex { buyWithFiat: true, ); SimplexOrder order = SimplexOrder( - quote: SimplexQuote( - crypto: - Crypto.fromJson({'ticker': 'BTC', 'name': 'Bitcoin', 'image': ''}), - fiat: Fiat.fromJson( - {'ticker': 'USD', 'name': 'United States Dollar', 'image': ''}), - youPayFiatPrice: Decimal.parse("100"), - youReceiveCryptoAmount: Decimal.parse("1.0238917"), - id: "someID", - receivingAddress: '', - buyWithFiat: true, + quote: SimplexQuote( + crypto: + Crypto.fromJson({'ticker': 'BTC', 'name': 'Bitcoin', 'image': ''}), + fiat: Fiat.fromJson( + {'ticker': 'USD', 'name': 'United States Dollar', 'image': ''}, ), - orderId: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', - paymentId: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', - userId: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'); + youPayFiatPrice: Decimal.parse("100"), + youReceiveCryptoAmount: Decimal.parse("1.0238917"), + id: "someID", + receivingAddress: '', + buyWithFiat: true, + ), + orderId: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + paymentId: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + userId: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + ); void updateSupportedCryptos(List newCryptos) { supportedCryptos = newCryptos; diff --git a/lib/models/contact.dart b/lib/models/contact.dart index 46cea2a76..91ac1be5e 100644 --- a/lib/models/contact.dart +++ b/lib/models/contact.dart @@ -44,13 +44,13 @@ class Contact { List? addresses, bool? isFavorite, }) { - List _addresses = []; + final List _addresses = []; if (addresses == null) { - for (var e in this.addresses) { + for (final e in this.addresses) { _addresses.add(e.copyWith()); } } else { - for (var e in addresses) { + for (final e in addresses) { _addresses.add(e.copyWith()); } } diff --git a/lib/models/epicbox_config_model.dart b/lib/models/epicbox_config_model.dart index 55650f11e..4d22dadd7 100644 --- a/lib/models/epicbox_config_model.dart +++ b/lib/models/epicbox_config_model.dart @@ -11,6 +11,7 @@ import 'dart:convert'; import 'package:hive/hive.dart'; + import 'epicbox_server_model.dart'; part 'type_adaptors/epicbox_config_model.g.dart'; @@ -57,7 +58,7 @@ class EpicBoxConfigModel { } Map toMap() { - Map map = {}; + final Map map = {}; map['epicbox_domain'] = host; map['epicbox_port'] = port; map['epicbox_protocol_insecure'] = protocolInsecure; @@ -84,7 +85,7 @@ class EpicBoxConfigModel { } static EpicBoxConfigModel fromString(String epicBoxConfigString) { - dynamic _epicBox = json.decode(epicBoxConfigString); + final dynamic _epicBox = json.decode(epicBoxConfigString); // handle old epicbox config formats final oldDomain = _epicBox["domain"] ?? "empty"; @@ -117,8 +118,11 @@ class EpicBoxConfigModel { ); } - static EpicBoxConfigModel fromServer(EpicBoxServerModel server, - {bool? protocolInsecure, int? addressIndex}) { + static EpicBoxConfigModel fromServer( + EpicBoxServerModel server, { + bool? protocolInsecure, + int? addressIndex, + }) { return EpicBoxConfigModel( host: server.host, port: server.port ?? 443, diff --git a/lib/models/epicbox_server_model.dart b/lib/models/epicbox_server_model.dart index ec8f6cd8e..810168943 100644 --- a/lib/models/epicbox_server_model.dart +++ b/lib/models/epicbox_server_model.dart @@ -64,7 +64,7 @@ class EpicBoxServerModel { } Map toMap() { - Map map = {}; + final Map map = {}; map['id'] = id; map['host'] = host; map['port'] = port; diff --git a/lib/models/exchange/change_now/estimated_exchange_amount.dart b/lib/models/exchange/change_now/estimated_exchange_amount.dart index 8313e686b..550b39489 100644 --- a/lib/models/exchange/change_now/estimated_exchange_amount.dart +++ b/lib/models/exchange/change_now/estimated_exchange_amount.dart @@ -9,6 +9,7 @@ */ import 'package:decimal/decimal.dart'; + import '../../../utilities/logger.dart'; class EstimatedExchangeAmount { @@ -45,8 +46,10 @@ class EstimatedExchangeAmount { factory EstimatedExchangeAmount.fromJson(Map json) { try { return EstimatedExchangeAmount( - estimatedAmount: Decimal.parse(json["estimatedAmount"]?.toString() ?? - json["estimatedDeposit"].toString()), + estimatedAmount: Decimal.parse( + json["estimatedAmount"]?.toString() ?? + json["estimatedDeposit"].toString(), + ), transactionSpeedForecast: json["transactionSpeedForecast"] as String? ?? "", warningMessage: json["warningMessage"] as String?, diff --git a/lib/models/exchange/change_now/exchange_transaction.dart b/lib/models/exchange/change_now/exchange_transaction.dart index e19d1c9c8..6bd514546 100644 --- a/lib/models/exchange/change_now/exchange_transaction.dart +++ b/lib/models/exchange/change_now/exchange_transaction.dart @@ -10,13 +10,15 @@ import 'package:decimal/decimal.dart'; import 'package:hive/hive.dart'; -import 'exchange_transaction_status.dart'; import 'package:uuid/uuid.dart'; +import 'exchange_transaction_status.dart'; + part '../../type_adaptors/exchange_transaction.g.dart'; @Deprecated( - "Do not use. Migrated to Trade in db_version_migration to hive_data_version 2") + "Do not use. Migrated to Trade in db_version_migration to hive_data_version 2", +) // @HiveType(typeId: 13) class ExchangeTransaction { /// You can use it to get transaction status at the Transaction status API endpoint @@ -114,7 +116,8 @@ class ExchangeTransaction { statusString: json["statusString"] as String? ?? "", statusObject: json["statusObject"] is Map ? ExchangeTransactionStatus.fromJson( - json["statusObject"] as Map) + json["statusObject"] as Map, + ) : null, ); } catch (e) { diff --git a/lib/models/exchange/change_now/exchange_transaction_status.dart b/lib/models/exchange/change_now/exchange_transaction_status.dart index 679d1af13..3c3aebb7f 100644 --- a/lib/models/exchange/change_now/exchange_transaction_status.dart +++ b/lib/models/exchange/change_now/exchange_transaction_status.dart @@ -9,6 +9,7 @@ */ import 'package:hive/hive.dart'; + import '../../../utilities/logger.dart'; part '../../type_adaptors/exchange_transaction_status.g.dart'; @@ -38,7 +39,8 @@ ChangeNowTransactionStatus changeNowTransactionStatusFromStringIgnoreCase( } } throw ArgumentError( - "String value does not match any known ChangeNowTransactionStatus"); + "String value does not match any known ChangeNowTransactionStatus", + ); } @HiveType(typeId: 16) @@ -189,7 +191,8 @@ class ExchangeTransactionStatus { try { return ExchangeTransactionStatus( status: changeNowTransactionStatusFromStringIgnoreCase( - json["status"] as String), + json["status"] as String, + ), payinAddress: json["payinAddress"] as String? ?? "", payoutAddress: json["payoutAddress"] as String? ?? "", fromCurrency: json["fromCurrency"] as String? ?? "", diff --git a/lib/models/exchange/majestic_bank/mb_rate.dart b/lib/models/exchange/majestic_bank/mb_rate.dart index c4b5d2b8a..7602a1725 100644 --- a/lib/models/exchange/majestic_bank/mb_rate.dart +++ b/lib/models/exchange/majestic_bank/mb_rate.dart @@ -9,10 +9,15 @@ */ import 'package:decimal/decimal.dart'; + import 'mb_object.dart'; class MBRate extends MBObject { - MBRate({required this.fromCurrency, required this.toCurrency, required this.rate,}); + MBRate({ + required this.fromCurrency, + required this.toCurrency, + required this.rate, + }); final String fromCurrency; final String toCurrency; diff --git a/lib/models/exchange/response_objects/trade.dart b/lib/models/exchange/response_objects/trade.dart index 86adb0bee..03685a0ab 100644 --- a/lib/models/exchange/response_objects/trade.dart +++ b/lib/models/exchange/response_objects/trade.dart @@ -9,8 +9,9 @@ */ import 'package:hive/hive.dart'; -import '../change_now/exchange_transaction.dart'; + import '../../../services/exchange/change_now/change_now_exchange.dart'; +import '../change_now/exchange_transaction.dart'; part 'trade.g.dart'; @@ -213,7 +214,9 @@ class Trade { } factory Trade.fromExchangeTransaction( - ExchangeTransaction exTx, bool reversed) { + ExchangeTransaction exTx, + bool reversed, + ) { return Trade( uuid: exTx.uuid, tradeId: exTx.id, diff --git a/lib/models/exchange/simpleswap/sp_currency.dart b/lib/models/exchange/simpleswap/sp_currency.dart index 04a77de67..96905f44c 100644 --- a/lib/models/exchange/simpleswap/sp_currency.dart +++ b/lib/models/exchange/simpleswap/sp_currency.dart @@ -59,8 +59,10 @@ class SPCurrency { warningsTo: json["warnings_to"] as List, ); } catch (e, s) { - Logging.instance.log("SPCurrency.fromJson failed to parse: $e\n$s", - level: LogLevel.Error); + Logging.instance.log( + "SPCurrency.fromJson failed to parse: $e\n$s", + level: LogLevel.Error, + ); rethrow; } } diff --git a/lib/models/isar/exchange_cache/currency.dart b/lib/models/isar/exchange_cache/currency.dart index 71c7890ce..29fed140d 100644 --- a/lib/models/isar/exchange_cache/currency.dart +++ b/lib/models/isar/exchange_cache/currency.dart @@ -23,10 +23,12 @@ class Currency { final String exchangeName; /// Currency ticker - @Index(composite: [ - CompositeIndex("exchangeName"), - CompositeIndex("name"), - ]) + @Index( + composite: [ + CompositeIndex("exchangeName"), + CompositeIndex("name"), + ], + ) final String ticker; /// Currency name diff --git a/lib/models/isar/exchange_cache/pair.dart b/lib/models/isar/exchange_cache/pair.dart index 4630a192e..923aeb26e 100644 --- a/lib/models/isar/exchange_cache/pair.dart +++ b/lib/models/isar/exchange_cache/pair.dart @@ -29,10 +29,12 @@ class Pair { @Index() final String exchangeName; - @Index(composite: [ - CompositeIndex("exchangeName"), - CompositeIndex("to"), - ]) + @Index( + composite: [ + CompositeIndex("exchangeName"), + CompositeIndex("to"), + ], + ) final String from; final String to; diff --git a/lib/models/isar/models/blockchain_data/transaction.dart b/lib/models/isar/models/blockchain_data/transaction.dart index 73d935681..d5b3572fe 100644 --- a/lib/models/isar/models/blockchain_data/transaction.dart +++ b/lib/models/isar/models/blockchain_data/transaction.dart @@ -12,11 +12,12 @@ import 'dart:convert'; import 'dart:math'; import 'package:isar/isar.dart'; +import 'package:tuple/tuple.dart'; + +import '../../../../utilities/amount/amount.dart'; import 'address.dart'; import 'input.dart'; import 'output.dart'; -import '../../../../utilities/amount/amount.dart'; -import 'package:tuple/tuple.dart'; part 'transaction.g.dart'; @@ -65,24 +66,24 @@ class Transaction { }) { return Tuple2( Transaction( - walletId: walletId ?? this.walletId, - txid: txid ?? this.txid, - timestamp: timestamp ?? this.timestamp, - type: type ?? this.type, - subType: subType ?? this.subType, - amount: amount ?? this.amount, - amountString: amountString ?? this.amountString, - fee: fee ?? this.fee, - height: height ?? this.height, - isCancelled: isCancelled ?? this.isCancelled, - isLelantus: isLelantus ?? this.isLelantus, - slateId: slateId ?? this.slateId, - otherData: otherData ?? this.otherData, - nonce: nonce ?? this.nonce, - inputs: inputs ?? this.inputs, - outputs: outputs ?? this.outputs, - numberOfMessages: numberOfMessages ?? this.numberOfMessages) - ..id = id ?? this.id, + walletId: walletId ?? this.walletId, + txid: txid ?? this.txid, + timestamp: timestamp ?? this.timestamp, + type: type ?? this.type, + subType: subType ?? this.subType, + amount: amount ?? this.amount, + amountString: amountString ?? this.amountString, + fee: fee ?? this.fee, + height: height ?? this.height, + isCancelled: isCancelled ?? this.isCancelled, + isLelantus: isLelantus ?? this.isLelantus, + slateId: slateId ?? this.slateId, + otherData: otherData ?? this.otherData, + nonce: nonce ?? this.nonce, + inputs: inputs ?? this.inputs, + outputs: outputs ?? this.outputs, + numberOfMessages: numberOfMessages ?? this.numberOfMessages, + )..id = id ?? this.id, address ?? this.address.value, ); } diff --git a/lib/models/isar/models/blockchain_data/utxo.dart b/lib/models/isar/models/blockchain_data/utxo.dart index 3a87957f0..7bbf50896 100644 --- a/lib/models/isar/models/blockchain_data/utxo.dart +++ b/lib/models/isar/models/blockchain_data/utxo.dart @@ -38,10 +38,14 @@ class UTXO { @Index() late final String walletId; - @Index(unique: true, replace: true, composite: [ - CompositeIndex("walletId"), - CompositeIndex("vout"), - ]) + @Index( + unique: true, + replace: true, + composite: [ + CompositeIndex("walletId"), + CompositeIndex("vout"), + ], + ) late final String txid; late final int vout; diff --git a/lib/models/isar/models/blockchain_data/v2/output_v2.dart b/lib/models/isar/models/blockchain_data/v2/output_v2.dart index 45a8b1329..01bfcfb0e 100644 --- a/lib/models/isar/models/blockchain_data/v2/output_v2.dart +++ b/lib/models/isar/models/blockchain_data/v2/output_v2.dart @@ -54,7 +54,7 @@ class OutputV2 { bool isFullAmountNotSats = false, }) { try { - List addresses = []; + final List addresses = []; if (json["scriptPubKey"]?["addresses"] is List) { for (final e in json["scriptPubKey"]["addresses"] as List) { @@ -68,7 +68,7 @@ class OutputV2 { scriptPubKeyHex: json["scriptPubKey"]["hex"] as String, scriptPubKeyAsm: json["scriptPubKey"]["asm"] as String?, valueStringSats: parseOutputAmountString( - json["value"] != null ? json["value"].toString(): "0", + json["value"] != null ? json["value"].toString() : "0", decimalPlaces: decimalPlaces, isFullAmountNotSats: isFullAmountNotSats, ), diff --git a/lib/models/isar/ordinal.dart b/lib/models/isar/ordinal.dart index c5c3a23da..6ac3a2dad 100644 --- a/lib/models/isar/ordinal.dart +++ b/lib/models/isar/ordinal.dart @@ -1,4 +1,5 @@ import 'package:isar/isar.dart'; + import '../../db/isar/main_db.dart'; import '../../dto/ordinals/inscription_data.dart'; import 'models/isar_models.dart'; @@ -11,10 +12,14 @@ class Ordinal { final String walletId; - @Index(unique: true, replace: true, composite: [ - CompositeIndex("utxoTXID"), - CompositeIndex("utxoVOUT"), - ]) + @Index( + unique: true, + replace: true, + composite: [ + CompositeIndex("utxoTXID"), + CompositeIndex("utxoVOUT"), + ], + ) final String inscriptionId; final int inscriptionNumber; diff --git a/lib/models/lelantus_coin.dart b/lib/models/lelantus_coin.dart index 56557c1cd..6dbce0b4a 100644 --- a/lib/models/lelantus_coin.dart +++ b/lib/models/lelantus_coin.dart @@ -40,7 +40,7 @@ class LelantusCoin { @override String toString() { - String coin = + final String coin = "{index: $index, value: $value, publicCoin: $publicCoin, txId: $txId, anonymitySetId: $anonymitySetId, isUsed: $isUsed}"; return coin; } diff --git a/lib/models/node_model.dart b/lib/models/node_model.dart index 636be9aac..a07c9a2dd 100644 --- a/lib/models/node_model.dart +++ b/lib/models/node_model.dart @@ -86,7 +86,7 @@ class NodeModel { } Map toMap() { - Map map = {}; + final Map map = {}; map['host'] = host; map['port'] = port; map['name'] = name; diff --git a/lib/models/paymint/transactions_model.dart b/lib/models/paymint/transactions_model.dart index c00f31565..6cea6f1ce 100644 --- a/lib/models/paymint/transactions_model.dart +++ b/lib/models/paymint/transactions_model.dart @@ -34,29 +34,32 @@ class TransactionData { TransactionData({this.txChunks = const []}); factory TransactionData.fromJson(Map json) { - var dateTimeChunks = json['dateTimeChunks'] as List; - List chunksList = dateTimeChunks - .map((txChunk) => - TransactionChunk.fromJson(txChunk as Map)) + final dateTimeChunks = json['dateTimeChunks'] as List; + final List chunksList = dateTimeChunks + .map( + (txChunk) => + TransactionChunk.fromJson(txChunk as Map), + ) .toList(); return TransactionData(txChunks: chunksList); } factory TransactionData.fromMap(Map transactions) { - Map> chunks = {}; + final Map> chunks = {}; transactions.forEach((key, value) { - String date = extractDateFromTimestamp(value.timestamp); + final String date = extractDateFromTimestamp(value.timestamp); if (!chunks.containsKey(date)) { chunks[date] = []; } chunks[date]!.add(value); }); - List chunksList = []; + final List chunksList = []; chunks.forEach((key, value) { value.sort((a, b) => b.timestamp.compareTo(a.timestamp)); chunksList.add( - TransactionChunk(timestamp: value[0].timestamp, transactions: value)); + TransactionChunk(timestamp: value[0].timestamp, transactions: value), + ); }); chunksList.sort((a, b) => b.timestamp.compareTo(a.timestamp)); return TransactionData(txChunks: chunksList); @@ -64,9 +67,9 @@ class TransactionData { Transaction? findTransaction(String txid) { for (var i = 0; i < txChunks.length; i++) { - var txChunk = txChunks[i].transactions; + final txChunk = txChunks[i].transactions; for (var j = 0; j < txChunk.length; j++) { - var tx = txChunk[j]; + final tx = txChunk[j]; if (tx.txid == txid) { return tx; } @@ -76,11 +79,11 @@ class TransactionData { } Map getAllTransactions() { - Map transactions = {}; + final Map transactions = {}; for (var i = 0; i < txChunks.length; i++) { - var txChunk = txChunks[i].transactions; + final txChunk = txChunks[i].transactions; for (var j = 0; j < txChunk.length; j++) { - var tx = txChunk[j]; + final tx = txChunk[j]; transactions[tx.txid] = tx; } } @@ -98,14 +101,15 @@ class TransactionChunk { TransactionChunk({required this.timestamp, required this.transactions}); factory TransactionChunk.fromJson(Map json) { - var txArray = json['transactions'] as List; - List txList = txArray + final txArray = json['transactions'] as List; + final List txList = txArray .map((tx) => Transaction.fromJson(tx as Map)) .toList(); return TransactionChunk( - timestamp: int.parse(json['timestamp'].toString()), - transactions: txList); + timestamp: int.parse(json['timestamp'].toString()), + transactions: txList, + ); } @override @@ -191,15 +195,16 @@ class Transaction { }); factory Transaction.fromJson(Map json) { - var inputArray = json['inputs'] as List; - var outputArray = json['outputs'] as List; + final inputArray = json['inputs'] as List; + final outputArray = json['outputs'] as List; - List inputList = inputArray + final List inputList = inputArray .map((input) => Input.fromJson(Map.from(input as Map))) .toList(); - List outputList = outputArray - .map((output) => - Output.fromJson(Map.from(output as Map))) + final List outputList = outputArray + .map( + (output) => Output.fromJson(Map.from(output as Map)), + ) .toList(); return Transaction( @@ -304,7 +309,7 @@ class Transaction { @override String toString() { - String transaction = + final String transaction = "{txid: $txid, type: $txType, subType: $subType, value: $amount, fee: $fees, height: $height, confirm: $confirmedStatus, confirmations: $confirmations, address: $address, timestamp: $timestamp, worthNow: $worthNow, inputs: $inputs, slateid: $slateId, numberOfMessages: $numberOfMessages }"; return transaction; } @@ -344,7 +349,7 @@ class Input { }); factory Input.fromJson(Map json) { - bool iscoinBase = json['coinbase'] != null; + final bool iscoinBase = json['coinbase'] != null; return Input( txid: json['txid'] as String? ?? "", vout: json['vout'] as int? ?? -1, @@ -361,7 +366,7 @@ class Input { @override String toString() { - String transaction = "{txid: $txid}"; + final String transaction = "{txid: $txid}"; return transaction; } } @@ -383,12 +388,13 @@ class Output { // @HiveField(4) final int value; - Output( - {this.scriptpubkey, - this.scriptpubkeyAsm, - this.scriptpubkeyType, - required this.scriptpubkeyAddress, - required this.value}); + Output({ + this.scriptpubkey, + this.scriptpubkeyAsm, + this.scriptpubkeyType, + required this.scriptpubkeyAddress, + required this.value, + }); factory Output.fromJson(Map json) { // TODO determine if any of this code is needed. @@ -405,12 +411,13 @@ class Output { ); } catch (s, e) { return Output( - // Return output object with null values; allows wallet history to be built - scriptpubkey: "", - scriptpubkeyAsm: "", - scriptpubkeyType: "", - scriptpubkeyAddress: "", - value: _parse(0.toString())); + // Return output object with null values; allows wallet history to be built + scriptpubkey: "", + scriptpubkeyAsm: "", + scriptpubkeyType: "", + scriptpubkeyAddress: "", + value: _parse(0.toString()), + ); } } } diff --git a/lib/models/paymint/utxo_model.dart b/lib/models/paymint/utxo_model.dart index 07f2c0e69..5a8ccffaa 100644 --- a/lib/models/paymint/utxo_model.dart +++ b/lib/models/paymint/utxo_model.dart @@ -35,8 +35,8 @@ class UtxoData { }); factory UtxoData.fromJson(Map json) { - var outputList = json['outputArray'] as List; - List utxoList = outputList + final outputList = json['outputArray'] as List; + final List utxoList = outputList .map((output) => UtxoObject.fromJson(output as Map)) .toList(); final String totalUserCurr = json['total_user_currency'] as String? ?? ""; @@ -104,7 +104,7 @@ class UtxoObject { @override String toString() { - String utxo = + final String utxo = "{txid: $txid, vout: $vout, value: $value, fiat: $fiatWorth, blocked: $blocked, status: $status, is_coinbase: $isCoinbase}"; return utxo; diff --git a/lib/models/paynym/paynym_account.dart b/lib/models/paynym/paynym_account.dart index 184f8c8aa..4d44d43fc 100644 --- a/lib/models/paynym/paynym_account.dart +++ b/lib/models/paynym/paynym_account.dart @@ -44,12 +44,16 @@ class PaynymAccount { .map((e) => PaynymCode.fromMap(Map.from(e as Map))) .toList(), followers = (map["followers"] as List) - .map((e) => - PaynymAccountLite.fromMap(Map.from(e as Map))) + .map( + (e) => PaynymAccountLite.fromMap( + Map.from(e as Map)), + ) .toList(), following = (map["following"] as List) - .map((e) => - PaynymAccountLite.fromMap(Map.from(e as Map))) + .map( + (e) => PaynymAccountLite.fromMap( + Map.from(e as Map)), + ) .toList(); PaynymAccount copyWith({ diff --git a/lib/models/paynym/paynym_follow.dart b/lib/models/paynym/paynym_follow.dart index 73fbdb2e8..76c903bee 100644 --- a/lib/models/paynym/paynym_follow.dart +++ b/lib/models/paynym/paynym_follow.dart @@ -21,13 +21,13 @@ class PaynymFollow { token = map["token"] as String; Map toMap() => { - "follower": follower, - "following": following, - "token": token, - }; + "follower": follower, + "following": following, + "token": token, + }; @override String toString() { return toMap().toString(); } -} \ No newline at end of file +} diff --git a/lib/models/stack_restoring_ui_state.dart b/lib/models/stack_restoring_ui_state.dart index 6ee4e9e1b..39a8f4f4b 100644 --- a/lib/models/stack_restoring_ui_state.dart +++ b/lib/models/stack_restoring_ui_state.dart @@ -10,9 +10,10 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'wallet_restore_state.dart'; + import '../utilities/enums/stack_restoring_status.dart'; import '../wallets/wallet/wallet.dart'; +import 'wallet_restore_state.dart'; class StackRestoringUIState extends ChangeNotifier { bool _walletsWasSet = false; @@ -94,7 +95,7 @@ class StackRestoringUIState extends ChangeNotifier { } List get wallets { - List _wallets = []; + final List _wallets = []; for (final item in _walletStates.values) { if (item.wallet != null) { _wallets.add(item.wallet!); @@ -125,7 +126,8 @@ class StackRestoringUIState extends ChangeNotifier { } ChangeNotifierProvider getWalletRestoreStateProvider( - String walletId) { + String walletId, + ) { return _walletStateProviders[walletId]!; } diff --git a/lib/notifications/notification_card.dart b/lib/notifications/notification_card.dart index feb81ce54..217608225 100644 --- a/lib/notifications/notification_card.dart +++ b/lib/notifications/notification_card.dart @@ -13,6 +13,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../app_config.dart'; import '../models/isar/stack_theme.dart'; import '../models/notification_model.dart'; @@ -28,9 +29,9 @@ import '../widgets/rounded_white_container.dart'; class NotificationCard extends ConsumerWidget { const NotificationCard({ - Key? key, + super.key, required this.notification, - }) : super(key: key); + }); final NotificationModel notification; @@ -71,10 +72,11 @@ class NotificationCard extends ConsumerWidget { ? SvgPicture.file( File( coinIconPath( - ref.watch( - themeAssetsProvider, - ), - ref), + ref.watch( + themeAssetsProvider, + ), + ref, + ), ), width: isDesktop ? desktopIconSize : mobileIconSize, height: isDesktop ? desktopIconSize : mobileIconSize, @@ -89,10 +91,11 @@ class NotificationCard extends ConsumerWidget { child: SvgPicture.file( File( coinIconPath( - ref.watch( - themeAssetsProvider, - ), - ref), + ref.watch( + themeAssetsProvider, + ), + ref, + ), ), color: Theme.of(context) .extension()! @@ -123,7 +126,7 @@ class NotificationCard extends ConsumerWidget { .extension()! .accentColorGreen, ), - ) + ), ], ), child: Text( diff --git a/lib/pages/TermsOfServiceView.dart b/lib/pages/TermsOfServiceView.dart index 64258b24f..0cea952f7 100644 --- a/lib/pages/TermsOfServiceView.dart +++ b/lib/pages/TermsOfServiceView.dart @@ -11,7 +11,7 @@ import 'package:flutter/material.dart'; class TermsOfServiceView extends StatelessWidget { - const TermsOfServiceView({Key? key}) : super(key: key); + const TermsOfServiceView({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/add_wallet_views/add_token_view/add_custom_token_view.dart b/lib/pages/add_wallet_views/add_token_view/add_custom_token_view.dart index 155d741b7..210fc954f 100644 --- a/lib/pages/add_wallet_views/add_token_view/add_custom_token_view.dart +++ b/lib/pages/add_wallet_views/add_token_view/add_custom_token_view.dart @@ -13,6 +13,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../../models/isar/models/ethereum/eth_contract.dart'; import '../../../services/ethereum/ethereum_api.dart'; import '../../../themes/stack_colors.dart'; @@ -29,8 +30,8 @@ import '../../../widgets/stack_dialog.dart'; class AddCustomTokenView extends ConsumerStatefulWidget { const AddCustomTokenView({ - Key? key, - }) : super(key: key); + super.key, + }); static const routeName = "/addCustomToken"; @@ -138,7 +139,8 @@ class _AddCustomTokenViewState extends ConsumerState { onPressed: () async { final response = await showLoading( whileFuture: EthereumAPI.getTokenContractInfoByAddress( - contractController.text), + contractController.text, + ), context: context, message: "Looking up contract", ); @@ -212,10 +214,12 @@ class _AddCustomTokenViewState extends ConsumerState { controller: decimalsController, style: STextStyles.field(context), inputFormatters: [ - TextInputFormatter.withFunction((oldValue, newValue) => - RegExp(r'^([0-9]*)$').hasMatch(newValue.text) - ? newValue - : oldValue), + TextInputFormatter.withFunction( + (oldValue, newValue) => + RegExp(r'^([0-9]*)$').hasMatch(newValue.text) + ? newValue + : oldValue, + ), ], keyboardType: const TextInputType.numberWithOptions( signed: false, @@ -253,10 +257,12 @@ class _AddCustomTokenViewState extends ConsumerState { controller: decimalsController, style: STextStyles.field(context), inputFormatters: [ - TextInputFormatter.withFunction((oldValue, newValue) => - RegExp(r'^([0-9]*)$').hasMatch(newValue.text) - ? newValue - : oldValue), + TextInputFormatter.withFunction( + (oldValue, newValue) => + RegExp(r'^([0-9]*)$').hasMatch(newValue.text) + ? newValue + : oldValue, + ), ], keyboardType: const TextInputType.numberWithOptions( signed: false, diff --git a/lib/pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart b/lib/pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart index 4a54b9b3d..46fa6f333 100644 --- a/lib/pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart +++ b/lib/pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart @@ -14,14 +14,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:isar/isar.dart'; + import '../../../db/isar/main_db.dart'; import '../../../models/isar/models/ethereum/eth_contract.dart'; import '../../../notifications/show_flush_bar.dart'; -import 'add_custom_token_view.dart'; -import 'sub_widgets/add_token_list.dart'; -import 'sub_widgets/add_token_list_element.dart'; -import 'sub_widgets/add_token_text.dart'; -import '../../home_view/home_view.dart'; import '../../../pages_desktop_specific/desktop_home_view.dart'; import '../../../providers/global/price_provider.dart'; import '../../../providers/global/wallets_provider.dart'; @@ -46,14 +42,19 @@ import '../../../widgets/icon_widgets/x_icon.dart'; import '../../../widgets/rounded_white_container.dart'; import '../../../widgets/stack_text_field.dart'; import '../../../widgets/textfield_icon_button.dart'; +import '../../home_view/home_view.dart'; +import 'add_custom_token_view.dart'; +import 'sub_widgets/add_token_list.dart'; +import 'sub_widgets/add_token_list_element.dart'; +import 'sub_widgets/add_token_text.dart'; class EditWalletTokensView extends ConsumerStatefulWidget { const EditWalletTokensView({ - Key? key, + super.key, required this.walletId, this.contractsToMarkSelected, this.isDesktopPopup = false, - }) : super(key: key); + }); final String walletId; final List? contractsToMarkSelected; @@ -178,7 +179,8 @@ class _EditWalletTokensViewState extends ConsumerState { if (contracts.isEmpty) { contracts.addAll(DefaultTokens.list); MainDB.instance.putEthContracts(contracts).then( - (_) => ref.read(priceAnd24hChangeNotifierProvider).updatePrice()); + (_) => ref.read(priceAnd24hChangeNotifierProvider).updatePrice(), + ); } tokenEntities.addAll(contracts.map((e) => AddTokenListElementData(e))); @@ -241,7 +243,8 @@ class _EditWalletTokensViewState extends ConsumerState { "Add custom token", style: STextStyles.desktopButtonSmallSecondaryEnabled( - context), + context, + ), ), ), ), diff --git a/lib/pages/add_wallet_views/add_token_view/sub_widgets/add_custom_token_selector.dart b/lib/pages/add_wallet_views/add_token_view/sub_widgets/add_custom_token_selector.dart index 0274a99f3..cf1b48e4f 100644 --- a/lib/pages/add_wallet_views/add_token_view/sub_widgets/add_custom_token_selector.dart +++ b/lib/pages/add_wallet_views/add_token_view/sub_widgets/add_custom_token_selector.dart @@ -18,9 +18,9 @@ import '../../../../utilities/util.dart'; class AddCustomTokenSelector extends StatelessWidget { const AddCustomTokenSelector({ - Key? key, + super.key, required this.addFunction, - }) : super(key: key); + }); final VoidCallback addFunction; diff --git a/lib/pages/add_wallet_views/add_token_view/sub_widgets/add_token_list.dart b/lib/pages/add_wallet_views/add_token_view/sub_widgets/add_token_list.dart index 19e4e833a..23bfb36c2 100644 --- a/lib/pages/add_wallet_views/add_token_view/sub_widgets/add_token_list.dart +++ b/lib/pages/add_wallet_views/add_token_view/sub_widgets/add_token_list.dart @@ -15,11 +15,11 @@ import '../../../../widgets/conditional_parent.dart'; class AddTokenList extends StatelessWidget { const AddTokenList({ - Key? key, + super.key, required this.walletId, required this.items, required this.addFunction, - }) : super(key: key); + }); final String walletId; final List items; diff --git a/lib/pages/add_wallet_views/add_token_view/sub_widgets/add_token_text.dart b/lib/pages/add_wallet_views/add_token_view/sub_widgets/add_token_text.dart index d92e210f1..481c052cc 100644 --- a/lib/pages/add_wallet_views/add_token_view/sub_widgets/add_token_text.dart +++ b/lib/pages/add_wallet_views/add_token_view/sub_widgets/add_token_text.dart @@ -13,10 +13,10 @@ import '../../../../utilities/text_styles.dart'; class AddTokenText extends StatelessWidget { const AddTokenText({ - Key? key, + super.key, required this.isDesktop, this.walletName, - }) : super(key: key); + }); final String? walletName; final bool isDesktop; diff --git a/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/add_wallet_entity_list.dart b/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/add_wallet_entity_list.dart index 2be9e6e7f..5ec69997d 100644 --- a/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/add_wallet_entity_list.dart +++ b/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/add_wallet_entity_list.dart @@ -14,10 +14,10 @@ import 'coin_select_item.dart'; class AddWalletEntityList extends StatelessWidget { const AddWalletEntityList({ - Key? key, + super.key, required this.entities, this.trailing, - }) : super(key: key); + }); final List entities; final Widget? trailing; diff --git a/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/expanding_sub_list_item.dart b/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/expanding_sub_list_item.dart index 1affe8787..d934526b8 100644 --- a/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/expanding_sub_list_item.dart +++ b/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/expanding_sub_list_item.dart @@ -10,27 +10,27 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; + import '../../../../models/add_wallet_list_entity/add_wallet_list_entity.dart'; -import 'add_wallet_entity_list.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/assets.dart'; import '../../../../utilities/text_styles.dart'; import '../../../../utilities/util.dart'; import '../../../../widgets/animated_widgets/rotate_icon.dart'; import '../../../../widgets/expandable.dart'; +import 'add_wallet_entity_list.dart'; class ExpandingSubListItem extends StatefulWidget { const ExpandingSubListItem({ - Key? key, + super.key, required this.title, required this.entities, this.trailing, required this.initialState, double? animationDurationMultiplier, this.curve = Curves.easeInOutCubicEmphasized, - }) : animationDurationMultiplier = - animationDurationMultiplier ?? entities.length * 0.11, - super(key: key); + }) : animationDurationMultiplier = + animationDurationMultiplier ?? entities.length * 0.11; final String title; final List entities; diff --git a/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/next_button.dart b/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/next_button.dart index 56b5fa258..4447cc7a9 100644 --- a/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/next_button.dart +++ b/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/next_button.dart @@ -19,9 +19,9 @@ import '../../../../utilities/text_styles.dart'; class AddWalletNextButton extends ConsumerWidget { const AddWalletNextButton({ - Key? key, + super.key, required this.isDesktop, - }) : super(key: key); + }); final bool isDesktop; diff --git a/lib/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/create_or_restore_wallet_subtitle.dart b/lib/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/create_or_restore_wallet_subtitle.dart index d66ded246..ddd106a50 100644 --- a/lib/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/create_or_restore_wallet_subtitle.dart +++ b/lib/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/create_or_restore_wallet_subtitle.dart @@ -13,9 +13,9 @@ import '../../../../utilities/text_styles.dart'; class CreateRestoreWalletSubTitle extends StatelessWidget { const CreateRestoreWalletSubTitle({ - Key? key, + super.key, required this.isDesktop, - }) : super(key: key); + }); final bool isDesktop; diff --git a/lib/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/create_wallet_button_group.dart b/lib/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/create_wallet_button_group.dart index 895aeb533..e042ddcdb 100644 --- a/lib/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/create_wallet_button_group.dart +++ b/lib/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/create_wallet_button_group.dart @@ -87,7 +87,8 @@ class CreateWalletButtonGroup extends StatelessWidget { : STextStyles.button(context).copyWith( color: Theme.of(context) .extension()! - .accentColorDark), + .accentColorDark, + ), ), ), ), diff --git a/lib/pages/add_wallet_views/frost_ms/new/select_new_frost_import_type_view.dart b/lib/pages/add_wallet_views/frost_ms/new/select_new_frost_import_type_view.dart index 3546dd964..d377eff72 100644 --- a/lib/pages/add_wallet_views/frost_ms/new/select_new_frost_import_type_view.dart +++ b/lib/pages/add_wallet_views/frost_ms/new/select_new_frost_import_type_view.dart @@ -167,7 +167,7 @@ class _SelectNewFrostImportTypeViewState FrostStepScaffold.routeName, ); }, - ) + ), ], ), ), diff --git a/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_1b.dart b/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_1b.dart index f38d05234..59597c659 100644 --- a/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_1b.dart +++ b/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_1b.dart @@ -176,7 +176,7 @@ class _FrostCreateStep1bState extends ConsumerState { .routeName, ); }, - ) + ), ], ), ); diff --git a/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_4.dart b/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_4.dart index b8f7aee88..0a4615280 100644 --- a/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_4.dart +++ b/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_4.dart @@ -70,7 +70,7 @@ class _FrostCreateStep4State extends ConsumerState { .routeName, ); }, - ) + ), ], ), ); diff --git a/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_1c.dart b/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_1c.dart index 3514cc20d..962e207bf 100644 --- a/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_1c.dart +++ b/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_1c.dart @@ -222,7 +222,7 @@ class _FrostReshareStep1cState extends ConsumerState { _buttonLock = false; } }, - ) + ), ], ), ); diff --git a/lib/pages/add_wallet_views/name_your_wallet_view/name_your_wallet_view.dart b/lib/pages/add_wallet_views/name_your_wallet_view/name_your_wallet_view.dart index eb2797da6..f75328e6a 100644 --- a/lib/pages/add_wallet_views/name_your_wallet_view/name_your_wallet_view.dart +++ b/lib/pages/add_wallet_views/name_your_wallet_view/name_your_wallet_view.dart @@ -341,7 +341,7 @@ class _NameYourWalletViewState extends ConsumerState { }); } }, - ) + ), ], ), ), diff --git a/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/new_wallet_recovery_phrase_view.dart b/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/new_wallet_recovery_phrase_view.dart index 510abe67d..ee80421dc 100644 --- a/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/new_wallet_recovery_phrase_view.dart +++ b/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/new_wallet_recovery_phrase_view.dart @@ -40,11 +40,11 @@ import 'sub_widgets/mnemonic_table.dart'; class NewWalletRecoveryPhraseView extends ConsumerStatefulWidget { const NewWalletRecoveryPhraseView({ - Key? key, + super.key, required this.wallet, required this.mnemonic, this.clipboardInterface = const ClipboardWrapper(), - }) : super(key: key); + }); static const routeName = "/newWalletRecoveryPhrase"; @@ -91,12 +91,14 @@ class _NewWalletRecoveryPhraseViewState Future _copy() async { final words = _mnemonic; await _clipboardInterface.setData(ClipboardData(text: words.join(" "))); - unawaited(showFloatingFlushBar( - type: FlushBarType.info, - message: "Copied to clipboard", - iconAsset: Assets.svg.copy, - context: context, - )); + unawaited( + showFloatingFlushBar( + type: FlushBarType.info, + message: "Copied to clipboard", + iconAsset: Assets.svg.copy, + context: context, + ), + ); } @override @@ -238,7 +240,8 @@ class _NewWalletRecoveryPhraseViewState .background : Theme.of(context).extension()!.popupBG, borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius), + Constants.size.circularBorderRadius, + ), ), child: Padding( padding: isDesktop @@ -252,7 +255,8 @@ class _NewWalletRecoveryPhraseViewState : STextStyles.label(context).copyWith( color: Theme.of(context) .extension()! - .accentColorDark), + .accentColorDark, + ), ), ), ), @@ -300,8 +304,9 @@ class _NewWalletRecoveryPhraseViewState Text( "Copy to clipboard", style: STextStyles.desktopButtonSecondaryEnabled( - context), - ) + context, + ), + ), ], ), ), @@ -325,10 +330,12 @@ class _NewWalletRecoveryPhraseViewState .read(verifyMnemonicCorrectWordStateProvider.state) .update((state) => _mnemonic[next]); - unawaited(Navigator.of(context).pushNamed( - VerifyRecoveryPhraseView.routeName, - arguments: Tuple2(_wallet, _mnemonic), - )); + unawaited( + Navigator.of(context).pushNamed( + VerifyRecoveryPhraseView.routeName, + arguments: Tuple2(_wallet, _mnemonic), + ), + ); }, style: Theme.of(context) .extension()! diff --git a/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart b/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart index 4f26fcffa..f3ccdc097 100644 --- a/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart +++ b/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart @@ -13,11 +13,11 @@ import 'mnemonic_table_item.dart'; class MnemonicTable extends StatelessWidget { const MnemonicTable({ - Key? key, + super.key, required this.words, required this.isDesktop, this.itemBorderColor, - }) : super(key: key); + }); final List words; final bool isDesktop; diff --git a/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table_item.dart b/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table_item.dart index c39c496d7..bb1806fad 100644 --- a/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table_item.dart +++ b/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table_item.dart @@ -15,12 +15,12 @@ import '../../../../widgets/rounded_white_container.dart'; class MnemonicTableItem extends StatelessWidget { const MnemonicTableItem({ - Key? key, + super.key, required this.number, required this.word, required this.isDesktop, this.borderColor, - }) : super(key: key); + }); final int number; final String word; diff --git a/lib/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/recovery_phrase_explanation_dialog.dart b/lib/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/recovery_phrase_explanation_dialog.dart index bb9626c67..22c2edb8e 100644 --- a/lib/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/recovery_phrase_explanation_dialog.dart +++ b/lib/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/recovery_phrase_explanation_dialog.dart @@ -14,7 +14,7 @@ import '../../../widgets/desktop/secondary_button.dart'; import '../../../widgets/stack_dialog.dart'; class RecoveryPhraseExplanationDialog extends StatelessWidget { - const RecoveryPhraseExplanationDialog({Key? key}) : super(key: key); + const RecoveryPhraseExplanationDialog({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/add_wallet_views/restore_wallet_view/confirm_recovery_dialog.dart b/lib/pages/add_wallet_views/restore_wallet_view/confirm_recovery_dialog.dart index 70a519fad..fe11d3502 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/confirm_recovery_dialog.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/confirm_recovery_dialog.dart @@ -21,8 +21,7 @@ import '../../../widgets/desktop/secondary_button.dart'; import '../../../widgets/stack_dialog.dart'; class ConfirmRecoveryDialog extends StatelessWidget { - const ConfirmRecoveryDialog({Key? key, required this.onConfirm}) - : super(key: key); + const ConfirmRecoveryDialog({super.key, required this.onConfirm}); final VoidCallback onConfirm; @@ -85,10 +84,10 @@ class ConfirmRecoveryDialog extends StatelessWidget { onConfirm.call(); }, ), - ) + ), ], ), - ) + ), ], ), ); diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/sub_widgets/mobile_mnemonic_length_selector.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/sub_widgets/mobile_mnemonic_length_selector.dart index 7299ca476..af86c3261 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/sub_widgets/mobile_mnemonic_length_selector.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/sub_widgets/mobile_mnemonic_length_selector.dart @@ -20,9 +20,9 @@ import '../../../../../utilities/util.dart'; class MobileMnemonicLengthSelector extends ConsumerWidget { const MobileMnemonicLengthSelector({ - Key? key, + super.key, required this.chooseMnemonicLength, - }) : super(key: key); + }); final VoidCallback chooseMnemonicLength; @@ -66,7 +66,7 @@ class MobileMnemonicLengthSelector extends ConsumerWidget { ], ), ), - ) + ), ], ); } diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/sub_widgets/restore_from_date_picker.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/sub_widgets/restore_from_date_picker.dart index 58869bdf4..55aa6ff63 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/sub_widgets/restore_from_date_picker.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/sub_widgets/restore_from_date_picker.dart @@ -17,10 +17,10 @@ import '../../../../../utilities/util.dart'; class RestoreFromDatePicker extends StatefulWidget { const RestoreFromDatePicker({ - Key? key, + super.key, required this.onTap, required this.controller, - }) : super(key: key); + }); final VoidCallback onTap; final TextEditingController controller; diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/sub_widgets/restore_options_next_button.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/sub_widgets/restore_options_next_button.dart index 8ac5ed213..2ca2aeedd 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/sub_widgets/restore_options_next_button.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/sub_widgets/restore_options_next_button.dart @@ -14,10 +14,10 @@ import '../../../../../utilities/text_styles.dart'; class RestoreOptionsNextButton extends StatelessWidget { const RestoreOptionsNextButton({ - Key? key, + super.key, required this.isDesktop, this.onPressed, - }) : super(key: key); + }); final bool isDesktop; final VoidCallback? onPressed; diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/sub_widgets/restore_options_platform_layout.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/sub_widgets/restore_options_platform_layout.dart index 9f606eb66..14a7a204c 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/sub_widgets/restore_options_platform_layout.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/sub_widgets/restore_options_platform_layout.dart @@ -13,10 +13,10 @@ import '../../../../../themes/stack_colors.dart'; class RestoreOptionsPlatformLayout extends StatelessWidget { const RestoreOptionsPlatformLayout({ - Key? key, + super.key, required this.isDesktop, required this.child, - }) : super(key: key); + }); final bool isDesktop; final Widget child; diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart index 41eddad6d..9daf9d300 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart @@ -48,11 +48,6 @@ import '../../../utilities/enums/form_input_status_enum.dart'; import '../../../utilities/logger.dart'; import '../../../utilities/text_styles.dart'; import '../../../utilities/util.dart'; -import '../../../wallets/crypto_currency/coins/epiccash.dart'; -import '../../../wallets/crypto_currency/coins/ethereum.dart'; -import '../../../wallets/crypto_currency/coins/firo.dart'; -import '../../../wallets/crypto_currency/coins/monero.dart'; -import '../../../wallets/crypto_currency/coins/wownero.dart'; import '../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../wallets/isar/models/wallet_info.dart'; import '../../../wallets/wallet/impl/epiccash_wallet.dart'; diff --git a/lib/pages/add_wallet_views/restore_wallet_view/sub_widgets/mnemonic_word_count_select_sheet.dart b/lib/pages/add_wallet_views/restore_wallet_view/sub_widgets/mnemonic_word_count_select_sheet.dart index c1533bad4..933b72ba5 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/sub_widgets/mnemonic_word_count_select_sheet.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/sub_widgets/mnemonic_word_count_select_sheet.dart @@ -10,6 +10,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../../../providers/ui/verify_recovery_phrase/mnemonic_word_count_state_provider.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/constants.dart'; @@ -17,9 +18,9 @@ import '../../../../utilities/text_styles.dart'; class MnemonicWordCountSelectSheet extends ConsumerWidget { const MnemonicWordCountSelectSheet({ - Key? key, + super.key, required this.lengthOptions, - }) : super(key: key); + }); final List lengthOptions; @@ -113,13 +114,16 @@ class MnemonicWordCountSelectSheet extends ConsumerWidget { .radioButtonIconEnabled, value: lengthOptions[i], groupValue: ref - .watch(mnemonicWordCountStateProvider - .state) + .watch( + mnemonicWordCountStateProvider.state, + ) .state, onChanged: (x) { ref - .read(mnemonicWordCountStateProvider - .state) + .read( + mnemonicWordCountStateProvider + .state, + ) .state = lengthOptions[i]; Navigator.of(context).pop(); }, diff --git a/lib/pages/add_wallet_views/restore_wallet_view/sub_widgets/restore_failed_dialog.dart b/lib/pages/add_wallet_views/restore_wallet_view/sub_widgets/restore_failed_dialog.dart index fc45b372a..1ae4a5efd 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/sub_widgets/restore_failed_dialog.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/sub_widgets/restore_failed_dialog.dart @@ -20,11 +20,11 @@ import '../../../../widgets/stack_dialog.dart'; class RestoreFailedDialog extends ConsumerStatefulWidget { const RestoreFailedDialog({ - Key? key, + super.key, required this.errorMessage, required this.walletName, required this.walletId, - }) : super(key: key); + }); final String errorMessage; final String walletName; diff --git a/lib/pages/add_wallet_views/restore_wallet_view/sub_widgets/restore_succeeded_dialog.dart b/lib/pages/add_wallet_views/restore_wallet_view/sub_widgets/restore_succeeded_dialog.dart index 4c5364e44..f290c6751 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/sub_widgets/restore_succeeded_dialog.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/sub_widgets/restore_succeeded_dialog.dart @@ -20,7 +20,7 @@ import '../../../../widgets/desktop/primary_button.dart'; import '../../../../widgets/stack_dialog.dart'; class RestoreSucceededDialog extends StatelessWidget { - const RestoreSucceededDialog({Key? key}) : super(key: key); + const RestoreSucceededDialog({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/add_wallet_views/restore_wallet_view/sub_widgets/restoring_dialog.dart b/lib/pages/add_wallet_views/restore_wallet_view/sub_widgets/restoring_dialog.dart index 29a4dcf40..38004caad 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/sub_widgets/restoring_dialog.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/sub_widgets/restoring_dialog.dart @@ -20,9 +20,9 @@ import '../../../../widgets/stack_dialog.dart'; class RestoringDialog extends StatefulWidget { const RestoringDialog({ - Key? key, + super.key, required this.onCancel, - }) : super(key: key); + }); final Future Function() onCancel; diff --git a/lib/pages/add_wallet_views/select_wallet_for_token_view.dart b/lib/pages/add_wallet_views/select_wallet_for_token_view.dart index 648f6a0de..debe8731c 100644 --- a/lib/pages/add_wallet_views/select_wallet_for_token_view.dart +++ b/lib/pages/add_wallet_views/select_wallet_for_token_view.dart @@ -37,9 +37,9 @@ final newEthWalletTriggerTempUntilHiveCompletelyDeleted = class SelectWalletForTokenView extends ConsumerStatefulWidget { const SelectWalletForTokenView({ - Key? key, + super.key, required this.entity, - }) : super(key: key); + }); static const String routeName = "/selectWalletForTokenView"; diff --git a/lib/pages/add_wallet_views/verify_recovery_phrase_view/sub_widgets/word_table.dart b/lib/pages/add_wallet_views/verify_recovery_phrase_view/sub_widgets/word_table.dart index 8674f6b07..63c305a3b 100644 --- a/lib/pages/add_wallet_views/verify_recovery_phrase_view/sub_widgets/word_table.dart +++ b/lib/pages/add_wallet_views/verify_recovery_phrase_view/sub_widgets/word_table.dart @@ -14,10 +14,10 @@ import 'word_table_item.dart'; class WordTable extends ConsumerWidget { const WordTable({ - Key? key, + super.key, required this.words, required this.isDesktop, - }) : super(key: key); + }); final List words; final bool isDesktop; diff --git a/lib/pages/add_wallet_views/verify_recovery_phrase_view/sub_widgets/word_table_item.dart b/lib/pages/add_wallet_views/verify_recovery_phrase_view/sub_widgets/word_table_item.dart index 64135fe95..993f825dc 100644 --- a/lib/pages/add_wallet_views/verify_recovery_phrase_view/sub_widgets/word_table_item.dart +++ b/lib/pages/add_wallet_views/verify_recovery_phrase_view/sub_widgets/word_table_item.dart @@ -17,11 +17,11 @@ import '../../../../utilities/text_styles.dart'; class WordTableItem extends ConsumerWidget { const WordTableItem({ - Key? key, + super.key, required this.number, required this.word, required this.isDesktop, - }) : super(key: key); + }); final int number; final String word; diff --git a/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_mnemonic_passphrase_dialog.dart b/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_mnemonic_passphrase_dialog.dart index 77b650c81..d21c71706 100644 --- a/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_mnemonic_passphrase_dialog.dart +++ b/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_mnemonic_passphrase_dialog.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; + import '../../../notifications/show_flush_bar.dart'; -import '../new_wallet_options/new_wallet_options_view.dart'; import '../../../themes/stack_colors.dart'; import '../../../utilities/assets.dart'; import '../../../utilities/constants.dart'; @@ -15,6 +15,7 @@ import '../../../widgets/desktop/primary_button.dart'; import '../../../widgets/desktop/secondary_button.dart'; import '../../../widgets/stack_dialog.dart'; import '../../../widgets/stack_text_field.dart'; +import '../new_wallet_options/new_wallet_options_view.dart'; class VerifyMnemonicPassphraseDialog extends ConsumerStatefulWidget { const VerifyMnemonicPassphraseDialog({super.key}); @@ -154,7 +155,8 @@ class _VerifyMnemonicPassphraseDialogState ), GestureDetector( key: const Key( - "mnemonicPassphraseFieldShowPasswordButtonKey"), + "mnemonicPassphraseFieldShowPasswordButtonKey", + ), onTap: () async { setState(() { hidePassword = !hidePassword; diff --git a/lib/pages/address_book_views/address_book_view.dart b/lib/pages/address_book_views/address_book_view.dart index b9ed9560f..dc5cc6f99 100644 --- a/lib/pages/address_book_views/address_book_view.dart +++ b/lib/pages/address_book_views/address_book_view.dart @@ -20,7 +20,6 @@ import '../../providers/db/main_db_provider.dart'; import '../../providers/global/address_book_service_provider.dart'; import '../../providers/providers.dart'; import '../../providers/ui/address_book_providers/address_book_filter_provider.dart'; -import '../../app_config.dart'; import '../../themes/stack_colors.dart'; import '../../utilities/assets.dart'; import '../../utilities/constants.dart'; diff --git a/lib/pages/address_book_views/subviews/add_address_book_entry_view.dart b/lib/pages/address_book_views/subviews/add_address_book_entry_view.dart index bf1ee6b39..66b4b1aa9 100644 --- a/lib/pages/address_book_views/subviews/add_address_book_entry_view.dart +++ b/lib/pages/address_book_views/subviews/add_address_book_entry_view.dart @@ -12,8 +12,9 @@ import 'package:emojis/emoji.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:uuid/uuid.dart'; + import '../../../models/isar/models/contact_entry.dart'; -import 'new_contact_address_entry_form.dart'; import '../../../providers/global/address_book_service_provider.dart'; import '../../../providers/ui/address_book_providers/address_entry_data_provider.dart'; import '../../../providers/ui/address_book_providers/contact_name_is_not_empty_state_provider.dart'; @@ -37,14 +38,14 @@ import '../../../widgets/emoji_select_sheet.dart'; import '../../../widgets/icon_widgets/x_icon.dart'; import '../../../widgets/stack_text_field.dart'; import '../../../widgets/textfield_icon_button.dart'; -import 'package:uuid/uuid.dart'; +import 'new_contact_address_entry_form.dart'; class AddAddressBookEntryView extends ConsumerStatefulWidget { const AddAddressBookEntryView({ - Key? key, + super.key, this.barcodeScanner = const BarcodeScannerWrapper(), this.clipboard = const ClipboardWrapper(), - }) : super(key: key); + }); static const String routeName = "/addAddressBookEntry"; @@ -129,64 +130,66 @@ class _AddAddressBookEntryViewState builder: (child) { return Background( child: Scaffold( - backgroundColor: - Theme.of(context).extension()!.background, - appBar: AppBar( - leading: AppBarBackButton( - onPressed: () async { - if (FocusScope.of(context).hasFocus) { - FocusScope.of(context).unfocus(); - await Future.delayed( - const Duration(milliseconds: 75)); - } - if (mounted) { - Navigator.of(context).pop(); - } - }, - ), - title: Text( - "New contact", - style: STextStyles.navBarTitle(context), - ), - actions: [ - Padding( - padding: const EdgeInsets.only( - top: 10, - bottom: 10, - right: 10, - ), - child: AspectRatio( - aspectRatio: 1, - child: AppBarIconButton( - key: const Key("addAddressBookEntryFavoriteButtonKey"), - size: 36, - shadows: const [], - color: Theme.of(context) - .extension()! - .background, - icon: SvgPicture.asset( - Assets.svg.star, - color: _isFavorite - ? Theme.of(context) - .extension()! - .favoriteStarActive - : Theme.of(context) - .extension()! - .favoriteStarInactive, - width: 20, - height: 20, - ), - onPressed: () { - setState(() { - _isFavorite = !_isFavorite; - }); - }, + backgroundColor: + Theme.of(context).extension()!.background, + appBar: AppBar( + leading: AppBarBackButton( + onPressed: () async { + if (FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + await Future.delayed( + const Duration(milliseconds: 75), + ); + } + if (mounted) { + Navigator.of(context).pop(); + } + }, + ), + title: Text( + "New contact", + style: STextStyles.navBarTitle(context), + ), + actions: [ + Padding( + padding: const EdgeInsets.only( + top: 10, + bottom: 10, + right: 10, + ), + child: AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + key: const Key("addAddressBookEntryFavoriteButtonKey"), + size: 36, + shadows: const [], + color: Theme.of(context) + .extension()! + .background, + icon: SvgPicture.asset( + Assets.svg.star, + color: _isFavorite + ? Theme.of(context) + .extension()! + .favoriteStarActive + : Theme.of(context) + .extension()! + .favoriteStarInactive, + width: 20, + height: 20, ), + onPressed: () { + setState(() { + _isFavorite = !_isFavorite; + }); + }, ), ), - ], - ), - body: child), + ), + ], + ), + body: child, + ), ); }, child: ConditionalParent( @@ -267,22 +270,23 @@ class _AddAddressBookEntryViewState } showDialog( - context: context, - builder: (context) { - return const DesktopDialog( - maxHeight: 700, - maxWidth: 600, - child: Padding( - padding: EdgeInsets.only( - left: 32, - right: 20, - top: 32, - bottom: 32, - ), - child: EmojiSelectSheet(), + context: context, + builder: (context) { + return const DesktopDialog( + maxHeight: 700, + maxWidth: 600, + child: Padding( + padding: EdgeInsets.only( + left: 32, + right: 20, + top: 32, + bottom: 32, ), - ); - }).then((value) { + child: EmojiSelectSheet(), + ), + ); + }, + ).then((value) { if (value is Emoji) { setState(() { _selectedEmoji = value; @@ -313,7 +317,8 @@ class _AddAddressBookEntryViewState _selectedEmoji!.char, style: STextStyles .pageTitleH1( - context), + context, + ), ), ), ), @@ -323,19 +328,21 @@ class _AddAddressBookEntryViewState height: 14, width: 14, decoration: BoxDecoration( - borderRadius: - BorderRadius.circular( - 14), - color: Theme.of(context) - .extension< - StackColors>()! - .accentColorDark), + borderRadius: + BorderRadius.circular( + 14, + ), + color: Theme.of(context) + .extension()! + .accentColorDark, + ), child: Center( child: _selectedEmoji == null ? SvgPicture.asset( Assets.svg.plus, color: Theme.of( - context) + context, + ) .extension< StackColors>()! .textWhite, @@ -345,7 +352,8 @@ class _AddAddressBookEntryViewState : SvgPicture.asset( Assets.svg.thickX, color: Theme.of( - context) + context, + ) .extension< StackColors>()! .textWhite, @@ -354,7 +362,7 @@ class _AddAddressBookEntryViewState ), ), ), - ) + ), ], ), ), @@ -384,13 +392,15 @@ class _AddAddressBookEntryViewState STextStyles.fieldLabel(context), suffixIcon: ref .read( - contactNameIsNotEmptyStateProvider - .state) + contactNameIsNotEmptyStateProvider + .state, + ) .state ? Padding( padding: const EdgeInsets.only( - right: 0), + right: 0, + ), child: UnconstrainedBox( child: Row( children: [ @@ -412,8 +422,9 @@ class _AddAddressBookEntryViewState onChanged: (newValue) { ref .read( - contactNameIsNotEmptyStateProvider - .state) + contactNameIsNotEmptyStateProvider + .state, + ) .state = newValue.isNotEmpty; }, ), @@ -485,11 +496,12 @@ class _AddAddressBookEntryViewState height: 14, width: 14, decoration: BoxDecoration( - borderRadius: - BorderRadius.circular(14), - color: Theme.of(context) - .extension()! - .accentColorDark), + borderRadius: + BorderRadius.circular(14), + color: Theme.of(context) + .extension()! + .accentColorDark, + ), child: Center( child: _selectedEmoji == null ? SvgPicture.asset( @@ -512,7 +524,7 @@ class _AddAddressBookEntryViewState ), ), ), - ) + ), ], ), ), @@ -537,12 +549,14 @@ class _AddAddressBookEntryViewState ).copyWith( suffixIcon: ref .read( - contactNameIsNotEmptyStateProvider - .state) + contactNameIsNotEmptyStateProvider + .state, + ) .state ? Padding( padding: const EdgeInsets.only( - right: 0), + right: 0, + ), child: UnconstrainedBox( child: Row( children: [ @@ -564,8 +578,9 @@ class _AddAddressBookEntryViewState onChanged: (newValue) { ref .read( - contactNameIsNotEmptyStateProvider - .state) + contactNameIsNotEmptyStateProvider + .state, + ) .state = newValue.isNotEmpty; }, ), @@ -659,17 +674,22 @@ class _AddAddressBookEntryViewState Expanded( child: Builder( builder: (context) { - bool nameExists = ref - .watch(contactNameIsNotEmptyStateProvider - .state) + final bool nameExists = ref + .watch( + contactNameIsNotEmptyStateProvider + .state, + ) .state; - bool validForms = ref.watch( - validContactStateProvider(forms + final bool validForms = ref.watch( + validContactStateProvider( + forms .map((e) => e.id) - .toList(growable: false))); + .toList(growable: false), + ), + ); - bool shouldEnableSave = + final bool shouldEnableSave = validForms && nameExists; return PrimaryButton( @@ -684,31 +704,38 @@ class _AddAddressBookEntryViewState FocusScope.of(context).unfocus(); await Future.delayed( const Duration( - milliseconds: 75), + milliseconds: 75, + ), ); } - List entries = - []; + final List + entries = []; for (int i = 0; i < forms.length; i++) { - entries.add(ref - .read( + entries.add( + ref + .read( addressEntryDataProvider( - forms[i].id)) - .buildAddressEntry()); + forms[i].id, + ), + ) + .buildAddressEntry(), + ); } - ContactEntry contact = ContactEntry( + final ContactEntry contact = + ContactEntry( emojiChar: _selectedEmoji?.char, name: nameController.text, addresses: entries, isFavorite: _isFavorite, - customId: const Uuid().v1(), + customId: const Uuid().v1(), ); if (await ref .read( - addressBookServiceProvider) + addressBookServiceProvider, + ) .addContact(contact)) { if (mounted) { Navigator.of(context).pop(); diff --git a/lib/pages/address_book_views/subviews/add_new_contact_address_view.dart b/lib/pages/address_book_views/subviews/add_new_contact_address_view.dart index 4a2c3c497..fc59eca68 100644 --- a/lib/pages/address_book_views/subviews/add_new_contact_address_view.dart +++ b/lib/pages/address_book_views/subviews/add_new_contact_address_view.dart @@ -11,8 +11,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../../models/isar/models/contact_entry.dart'; -import 'new_contact_address_entry_form.dart'; import '../../../providers/global/address_book_service_provider.dart'; import '../../../providers/ui/address_book_providers/address_entry_data_provider.dart'; import '../../../providers/ui/address_book_providers/valid_contact_state_provider.dart'; @@ -27,14 +27,15 @@ import '../../../widgets/conditional_parent.dart'; import '../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../widgets/desktop/primary_button.dart'; import '../../../widgets/desktop/secondary_button.dart'; +import 'new_contact_address_entry_form.dart'; class AddNewContactAddressView extends ConsumerStatefulWidget { const AddNewContactAddressView({ - Key? key, + super.key, required this.contactId, this.barcodeScanner = const BarcodeScannerWrapper(), this.clipboard = const ClipboardWrapper(), - }) : super(key: key); + }); static const String routeName = "/addNewContactAddress"; @@ -66,8 +67,10 @@ class _AddNewContactAddressViewState @override Widget build(BuildContext context) { - final contact = ref.watch(addressBookServiceProvider - .select((value) => value.getContactById(contactId))); + final contact = ref.watch( + addressBookServiceProvider + .select((value) => value.getContactById(contactId)), + ); final isDesktop = Util.isDesktop; @@ -188,7 +191,8 @@ class _AddNewContactAddressViewState if (!isDesktop && FocusScope.of(context).hasFocus) { FocusScope.of(context).unfocus(); await Future.delayed( - const Duration(milliseconds: 75)); + const Duration(milliseconds: 75), + ); } if (mounted) { Navigator.of(context).pop(); @@ -211,14 +215,14 @@ class _AddNewContactAddressViewState const Duration(milliseconds: 75), ); } - List entries = + final List entries = contact.addresses.toList(); - entries.add(ref - .read(addressEntryDataProvider(0)) - .buildAddressEntry()); + entries.add( + ref.read(addressEntryDataProvider(0)).buildAddressEntry(), + ); - ContactEntry editedContact = + final ContactEntry editedContact = contact.copyWith(addresses: entries); if (await ref @@ -235,7 +239,7 @@ class _AddNewContactAddressViewState ), ), ], - ) + ), ], ), ); diff --git a/lib/pages/address_book_views/subviews/address_book_filter_view.dart b/lib/pages/address_book_views/subviews/address_book_filter_view.dart index 035628c80..124cf142f 100644 --- a/lib/pages/address_book_views/subviews/address_book_filter_view.dart +++ b/lib/pages/address_book_views/subviews/address_book_filter_view.dart @@ -10,9 +10,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../../app_config.dart'; import '../../../providers/global/prefs_provider.dart'; import '../../../providers/ui/address_book_providers/address_book_filter_provider.dart'; -import '../../../app_config.dart'; import '../../../themes/stack_colors.dart'; import '../../../utilities/text_styles.dart'; import '../../../utilities/util.dart'; @@ -82,42 +83,44 @@ class _AddressBookFilterViewState extends ConsumerState { ), body: Padding( padding: const EdgeInsets.all(12), - child: LayoutBuilder(builder: (builderContext, constraints) { - return SingleChildScrollView( - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: constraints.maxHeight, - ), - child: IntrinsicHeight( - child: Padding( - padding: const EdgeInsets.all(4), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - RoundedWhiteContainer( - child: Text( - "Only selected cryptocurrency addresses will be displayed.", - style: STextStyles.itemSubtitle(context), + child: LayoutBuilder( + builder: (builderContext, constraints) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: constraints.maxHeight, + ), + child: IntrinsicHeight( + child: Padding( + padding: const EdgeInsets.all(4), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + RoundedWhiteContainer( + child: Text( + "Only selected cryptocurrency addresses will be displayed.", + style: STextStyles.itemSubtitle(context), + ), ), - ), - const SizedBox( - height: 12, - ), - Text( - "Select cryptocurrency", - style: STextStyles.smallMed12(context), - ), - const SizedBox( - height: 12, - ), - child, - ], + const SizedBox( + height: 12, + ), + Text( + "Select cryptocurrency", + style: STextStyles.smallMed12(context), + ), + const SizedBox( + height: 12, + ), + child, + ], + ), ), ), ), - ), - ); - }), + ); + }, + ), ), ), ); @@ -230,8 +233,10 @@ class _AddressBookFilterViewState extends ConsumerState { width: 20, child: Checkbox( value: ref - .watch(addressBookFilterProvider - .select((value) => value.coins)) + .watch( + addressBookFilterProvider + .select((value) => value.coins), + ) .contains(coin), onChanged: (value) { if (value is bool) { @@ -267,7 +272,7 @@ class _AddressBookFilterViewState extends ConsumerState { style: STextStyles.itemSubtitle(context), ), ], - ) + ), ], ), ), diff --git a/lib/pages/address_book_views/subviews/coin_select_sheet.dart b/lib/pages/address_book_views/subviews/coin_select_sheet.dart index d39f7b08c..7e2cd0710 100644 --- a/lib/pages/address_book_views/subviews/coin_select_sheet.dart +++ b/lib/pages/address_book_views/subviews/coin_select_sheet.dart @@ -79,7 +79,7 @@ class CoinSelectSheet extends StatelessWidget { Flexible( child: Consumer( builder: (_, ref, __) { - bool showTestNet = ref.watch( + final bool showTestNet = ref.watch( prefsChangeNotifierProvider .select((value) => value.showTestNetCoins), ); diff --git a/lib/pages/address_book_views/subviews/contact_details_view.dart b/lib/pages/address_book_views/subviews/contact_details_view.dart index 2d12334f0..b343c3092 100644 --- a/lib/pages/address_book_views/subviews/contact_details_view.dart +++ b/lib/pages/address_book_views/subviews/contact_details_view.dart @@ -15,11 +15,10 @@ import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:isar/isar.dart'; +import 'package:tuple/tuple.dart'; + import '../../../models/isar/models/isar_models.dart'; import '../../../notifications/show_flush_bar.dart'; -import 'add_new_contact_address_view.dart'; -import 'edit_contact_address_view.dart'; -import 'edit_contact_name_emoji_view.dart'; import '../../../providers/db/main_db_provider.dart'; import '../../../providers/global/address_book_service_provider.dart'; import '../../../providers/ui/address_book_providers/address_entry_data_provider.dart'; @@ -36,7 +35,9 @@ import '../../../widgets/rounded_container.dart'; import '../../../widgets/rounded_white_container.dart'; import '../../../widgets/stack_dialog.dart'; import '../../../widgets/transaction_card.dart'; -import 'package:tuple/tuple.dart'; +import 'add_new_contact_address_view.dart'; +import 'edit_contact_address_view.dart'; +import 'edit_contact_name_emoji_view.dart'; class ContactDetailsView extends ConsumerStatefulWidget { const ContactDetailsView({ @@ -73,8 +74,10 @@ class _ContactDetailsViewState extends ConsumerState { .transactions .where() .filter() - .anyOf(contact.addresses.map((e) => e.address), - (q, String e) => q.address((q) => q.valueEqualTo(e))) + .anyOf( + contact.addresses.map((e) => e.address), + (q, String e) => q.address((q) => q.valueEqualTo(e)), + ) .sortByTimestampDesc() .findAll(); @@ -107,8 +110,10 @@ class _ContactDetailsViewState extends ConsumerState { Widget build(BuildContext context) { debugPrint("BUILD: $runtimeType"); - final _contact = ref.watch(addressBookServiceProvider - .select((value) => value.getContactById(_contactId))); + final _contact = ref.watch( + addressBookServiceProvider + .select((value) => value.getContactById(_contactId)), + ); return Background( child: Scaffold( @@ -153,7 +158,8 @@ class _ContactDetailsViewState extends ConsumerState { final bool isFavorite = _contact.isFavorite; ref.read(addressBookServiceProvider).editContact( - _contact.copyWith(isFavorite: !isFavorite)); + _contact.copyWith(isFavorite: !isFavorite), + ); }, ), ), @@ -289,18 +295,21 @@ class _ContactDetailsViewState extends ConsumerState { .getSecondaryEnabledButtonStyle(context)! .copyWith( minimumSize: MaterialStateProperty.all( - const Size(46, 32)), + const Size(46, 32), + ), ), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 12), child: Row( children: [ - SvgPicture.asset(Assets.svg.pencil, - width: 10, - height: 10, - color: Theme.of(context) - .extension()! - .accentColorDark), + SvgPicture.asset( + Assets.svg.pencil, + width: 10, + height: 10, + color: Theme.of(context) + .extension()! + .accentColorDark, + ), const SizedBox( width: 4, ), @@ -463,9 +472,10 @@ class _ContactDetailsViewState extends ConsumerState { ), FutureBuilder( future: _filteredTransactionsByContact(), - builder: (_, - AsyncSnapshot>> - snapshot) { + builder: ( + _, + AsyncSnapshot>> snapshot, + ) { if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) { _cachedTransactions = snapshot.data!; @@ -478,7 +488,8 @@ class _ContactDetailsViewState extends ConsumerState { ..._cachedTransactions.map( (e) => TransactionCard( key: Key( - "contactDetailsTransaction_${e.item1}_${e.item2.txid}_cardKey"), + "contactDetailsTransaction_${e.item1}_${e.item2.txid}_cardKey", + ), transaction: e.item2, walletId: e.item1, ), @@ -508,7 +519,8 @@ class _ContactDetailsViewState extends ConsumerState { ..._cachedTransactions.map( (e) => TransactionCard( key: Key( - "contactDetailsTransaction_${e.item1}_${e.item2.txid}_cardKey"), + "contactDetailsTransaction_${e.item1}_${e.item2.txid}_cardKey", + ), transaction: e.item2, walletId: e.item1, ), diff --git a/lib/pages/address_book_views/subviews/contact_popup.dart b/lib/pages/address_book_views/subviews/contact_popup.dart index 5411736e0..80d814323 100644 --- a/lib/pages/address_book_views/subviews/contact_popup.dart +++ b/lib/pages/address_book_views/subviews/contact_popup.dart @@ -51,8 +51,10 @@ class ContactPopUp extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final maxHeight = MediaQuery.of(context).size.height * 0.6; - final contact = ref.watch(addressBookServiceProvider - .select((value) => value.getContactById(contactId))); + final contact = ref.watch( + addressBookServiceProvider + .select((value) => value.getContactById(contactId)), + ); final active = ref.read(currentWalletIdProvider); @@ -157,7 +159,8 @@ class ContactPopUp extends ConsumerWidget { style: Theme.of(context) .extension()! .getSecondaryEnabledButtonStyle( - context)! + context, + )! .copyWith( minimumSize: MaterialStateProperty.all< @@ -165,10 +168,14 @@ class ContactPopUp extends ConsumerWidget { ), child: Padding( padding: const EdgeInsets.symmetric( - horizontal: 18), - child: Text("Details", - style: STextStyles.buttonSmall( - context)), + horizontal: 18, + ), + child: Text( + "Details", + style: STextStyles.buttonSmall( + context, + ), + ), ), ), ], @@ -186,7 +193,8 @@ class ContactPopUp extends ConsumerWidget { if (addresses.isEmpty) Padding( padding: const EdgeInsets.symmetric( - horizontal: 20), + horizontal: 20, + ), child: RoundedWhiteContainer( child: Center( child: Text( @@ -237,14 +245,16 @@ class ContactPopUp extends ConsumerWidget { e.other!, style: STextStyles.itemSubtitle12( - context), + context, + ), ), if (contact.customId != "default") Text( "${e.label} (${e.coin.ticker})", style: STextStyles.itemSubtitle12( - context), + context, + ), ), const SizedBox( height: 2, @@ -252,8 +262,8 @@ class ContactPopUp extends ConsumerWidget { Text( e.address, style: STextStyles.itemSubtitle( - context) - .copyWith( + context, + ).copyWith( fontSize: 8, ), ), @@ -286,12 +296,13 @@ class ContactPopUp extends ConsumerWidget { .textFieldDefaultBG, padding: const EdgeInsets.all(6), child: SvgPicture.asset( - Assets.svg.copy, - width: 16, - height: 16, - color: Theme.of(context) - .extension()! - .accentColorDark), + Assets.svg.copy, + width: 16, + height: 16, + color: Theme.of(context) + .extension()! + .accentColorDark, + ), ), ), ], @@ -310,12 +321,15 @@ class ContactPopUp extends ConsumerWidget { onTap: () { ref .read( - exchangeFromAddressBookAddressStateProvider - .state) + exchangeFromAddressBookAddressStateProvider + .state, + ) .state = e.address; Navigator.of(context).popUntil( - ModalRoute.withName( - Step2View.routeName)); + ModalRoute.withName( + Step2View.routeName, + ), + ); }, child: RoundedContainer( color: Theme.of(context) @@ -324,13 +338,13 @@ class ContactPopUp extends ConsumerWidget { padding: const EdgeInsets.all(6), child: SvgPicture.asset( - Assets.svg.chevronRight, - width: 16, - height: 16, - color: Theme.of(context) - .extension< - StackColors>()! - .accentColorDark), + Assets.svg.chevronRight, + width: 16, + height: 16, + color: Theme.of(context) + .extension()! + .accentColorDark, + ), ), ), ], @@ -363,7 +377,8 @@ class ContactPopUp extends ConsumerWidget { arguments: Tuple3( active, ref.read( - pWalletCoin(active)), + pWalletCoin(active), + ), SendViewAutoFillData( address: address, contactLabel: @@ -381,18 +396,15 @@ class ContactPopUp extends ConsumerWidget { Util.isDesktop ? 4 : 6, ), child: SvgPicture.asset( - Assets - .svg.circleArrowUpRight, - width: Util.isDesktop - ? 12 - : 16, - height: Util.isDesktop - ? 12 - : 16, - color: Theme.of(context) - .extension< - StackColors>()! - .accentColorDark), + Assets.svg.circleArrowUpRight, + width: + Util.isDesktop ? 12 : 16, + height: + Util.isDesktop ? 12 : 16, + color: Theme.of(context) + .extension()! + .accentColorDark, + ), ), ), ], diff --git a/lib/pages/address_book_views/subviews/edit_contact_address_view.dart b/lib/pages/address_book_views/subviews/edit_contact_address_view.dart index d9c6e584f..1d5ebd21c 100644 --- a/lib/pages/address_book_views/subviews/edit_contact_address_view.dart +++ b/lib/pages/address_book_views/subviews/edit_contact_address_view.dart @@ -8,13 +8,11 @@ * */ -import 'dart:convert'; - import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../../models/isar/models/contact_entry.dart'; -import 'new_contact_address_entry_form.dart'; import '../../../providers/global/address_book_service_provider.dart'; import '../../../providers/ui/address_book_providers/address_entry_data_provider.dart'; import '../../../providers/ui/address_book_providers/valid_contact_state_provider.dart'; @@ -29,15 +27,16 @@ import '../../../widgets/conditional_parent.dart'; import '../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../widgets/desktop/primary_button.dart'; import '../../../widgets/desktop/secondary_button.dart'; +import 'new_contact_address_entry_form.dart'; class EditContactAddressView extends ConsumerStatefulWidget { const EditContactAddressView({ - Key? key, + super.key, required this.contactId, required this.addressEntry, this.barcodeScanner = const BarcodeScannerWrapper(), this.clipboard = const ClipboardWrapper(), - }) : super(key: key); + }); static const String routeName = "/editContactAddress"; @@ -67,7 +66,7 @@ class _EditContactAddressViewState const Duration(milliseconds: 75), ); } - List entries = contact.addresses.toList(); + final List entries = contact.addresses.toList(); final entry = entries.firstWhere( (e) => @@ -79,12 +78,12 @@ class _EditContactAddressViewState final index = entries.indexOf(entry); entries.remove(entry); - ContactAddressEntry editedEntry = + final ContactAddressEntry editedEntry = ref.read(addressEntryDataProvider(0)).buildAddressEntry(); entries.insert(index, editedEntry); - ContactEntry editedContact = contact.copyWith(addresses: entries); + final ContactEntry editedContact = contact.copyWith(addresses: entries); if (await ref.read(addressBookServiceProvider).editContact(editedContact)) { if (mounted) { @@ -108,8 +107,10 @@ class _EditContactAddressViewState @override Widget build(BuildContext context) { - final contact = ref.watch(addressBookServiceProvider - .select((value) => value.getContactById(contactId))); + final contact = ref.watch( + addressBookServiceProvider + .select((value) => value.getContactById(contactId)), + ); final bool isDesktop = Util.isDesktop; @@ -239,9 +240,10 @@ class _EditContactAddressViewState //Deleting an entry directly from _addresses gives error // "Cannot remove from a fixed-length list", so we remove the // entry from a copy - var tempAddresses = List.from(_addresses); + final tempAddresses = + List.from(_addresses); tempAddresses.remove(entry); - ContactEntry editedContact = + final ContactEntry editedContact = contact.copyWith(addresses: tempAddresses); if (await ref .read(addressBookServiceProvider) @@ -272,7 +274,8 @@ class _EditContactAddressViewState if (!isDesktop && FocusScope.of(context).hasFocus) { FocusScope.of(context).unfocus(); await Future.delayed( - const Duration(milliseconds: 75)); + const Duration(milliseconds: 75), + ); } if (mounted) { Navigator.of(context).pop(); diff --git a/lib/pages/address_book_views/subviews/edit_contact_name_emoji_view.dart b/lib/pages/address_book_views/subviews/edit_contact_name_emoji_view.dart index c1d8c139e..a552aca29 100644 --- a/lib/pages/address_book_views/subviews/edit_contact_name_emoji_view.dart +++ b/lib/pages/address_book_views/subviews/edit_contact_name_emoji_view.dart @@ -14,6 +14,7 @@ import 'package:emojis/emoji.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../../providers/global/address_book_service_provider.dart'; import '../../../themes/stack_colors.dart'; import '../../../utilities/assets.dart'; @@ -33,9 +34,9 @@ import '../../../widgets/textfield_icon_button.dart'; class EditContactNameEmojiView extends ConsumerStatefulWidget { const EditContactNameEmojiView({ - Key? key, + super.key, required this.contactId, - }) : super(key: key); + }); static const String routeName = "/editContactNameEmoji"; @@ -82,8 +83,10 @@ class _EditContactNameEmojiViewState @override Widget build(BuildContext context) { - final contact = ref.watch(addressBookServiceProvider - .select((value) => value.getContactById(contactId))); + final contact = ref.watch( + addressBookServiceProvider + .select((value) => value.getContactById(contactId)), + ); final isDesktop = Util.isDesktop; final double emojiSize = isDesktop ? 56 : 48; @@ -152,23 +155,24 @@ class _EditContactNameEmojiViewState } if (isDesktop) { showDialog( - barrierColor: Colors.transparent, - context: context, - builder: (context) { - return const DesktopDialog( - maxHeight: 700, - maxWidth: 600, - child: Padding( - padding: EdgeInsets.only( - left: 32, - right: 20, - top: 32, - bottom: 32, - ), - child: EmojiSelectSheet(), + barrierColor: Colors.transparent, + context: context, + builder: (context) { + return const DesktopDialog( + maxHeight: 700, + maxWidth: 600, + child: Padding( + padding: EdgeInsets.only( + left: 32, + right: 20, + top: 32, + bottom: 32, ), - ); - }).then((value) { + child: EmojiSelectSheet(), + ), + ); + }, + ).then((value) { if (value is Emoji) { setState(() { _selectedEmoji = value; @@ -229,10 +233,11 @@ class _EditContactNameEmojiViewState height: 14, width: 14, decoration: BoxDecoration( - borderRadius: BorderRadius.circular(14), - color: Theme.of(context) - .extension()! - .accentColorDark), + borderRadius: BorderRadius.circular(14), + color: Theme.of(context) + .extension()! + .accentColorDark, + ), child: Center( child: _selectedEmoji == null ? SvgPicture.asset( @@ -253,7 +258,7 @@ class _EditContactNameEmojiViewState ), ), ), - ) + ), ], ), ), @@ -362,7 +367,8 @@ class _EditContactNameEmojiViewState if (!isDesktop && FocusScope.of(context).hasFocus) { FocusScope.of(context).unfocus(); await Future.delayed( - const Duration(milliseconds: 75)); + const Duration(milliseconds: 75), + ); } if (mounted) { Navigator.of(context).pop(); @@ -403,7 +409,7 @@ class _EditContactNameEmojiViewState ), ), ], - ) + ), ], ), ); diff --git a/lib/pages/buy_view/buy_form.dart b/lib/pages/buy_view/buy_form.dart index 1baf97db3..1b1239a27 100644 --- a/lib/pages/buy_view/buy_form.dart +++ b/lib/pages/buy_view/buy_form.dart @@ -439,7 +439,7 @@ class _BuyFormState extends ConsumerState { buyWithFiat: buyWithFiat, ); - BuyResponse quoteResponse = await _loadQuote(quote); + final BuyResponse quoteResponse = await _loadQuote(quote); shouldPop = true; if (mounted) { Navigator.of(context, rootNavigator: isDesktop).pop(); @@ -767,8 +767,8 @@ class _BuyFormState extends ConsumerState { Widget build(BuildContext context) { debugPrint("BUILD: $runtimeType"); - Locale locale = Localizations.localeOf(context); - var format = NumberFormat.simpleCurrency(locale: locale.toString()); + final Locale locale = Localizations.localeOf(context); + final format = NumberFormat.simpleCurrency(locale: locale.toString()); // See https://stackoverflow.com/a/67055685 return ConditionalParent( diff --git a/lib/pages/buy_view/buy_order_details.dart b/lib/pages/buy_view/buy_order_details.dart index 9c44f01db..4144a85ab 100644 --- a/lib/pages/buy_view/buy_order_details.dart +++ b/lib/pages/buy_view/buy_order_details.dart @@ -11,6 +11,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../models/buy/response_objects/order.dart'; import '../../themes/stack_colors.dart'; import '../../themes/theme_providers.dart'; @@ -25,9 +26,9 @@ import '../../widgets/rounded_white_container.dart'; class BuyOrderDetailsView extends ConsumerStatefulWidget { const BuyOrderDetailsView({ - Key? key, + super.key, required this.order, - }) : super(key: key); + }); final SimplexOrder order; @@ -259,22 +260,25 @@ class _BuyOrderDetailsViewState extends ConsumerState { const SizedBox( height: 24, ), - Row(mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - "This information is not saved,\nscreenshot it now for your records", - style: STextStyles.label(context).copyWith( - color: Theme.of(context).extension()!.textDark, + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "This information is not saved,\nscreenshot it now for your records", + style: STextStyles.label(context).copyWith( + color: Theme.of(context).extension()!.textDark, + ), + textAlign: TextAlign.center, ), - textAlign: TextAlign.center, - ), - ]), + ], + ), const Spacer(), PrimaryButton( label: "Dismiss", onPressed: () { Navigator.of(context, rootNavigator: isDesktop).pop(); }, - ) + ), ], ), ); diff --git a/lib/pages/buy_view/buy_quote_preview.dart b/lib/pages/buy_view/buy_quote_preview.dart index ec65de1f5..7d736e3a6 100644 --- a/lib/pages/buy_view/buy_quote_preview.dart +++ b/lib/pages/buy_view/buy_quote_preview.dart @@ -29,9 +29,9 @@ import '../../widgets/rounded_white_container.dart'; class BuyQuotePreviewView extends ConsumerStatefulWidget { const BuyQuotePreviewView({ - Key? key, + super.key, required this.quote, - }) : super(key: key); + }); final SimplexQuote quote; @@ -56,8 +56,8 @@ class _BuyQuotePreviewViewState extends ConsumerState { @override Widget build(BuildContext context) { - Locale locale = Localizations.localeOf(context); - var format = NumberFormat.simpleCurrency(locale: locale.toString()); + final Locale locale = Localizations.localeOf(context); + final format = NumberFormat.simpleCurrency(locale: locale.toString()); // See https://stackoverflow.com/a/67055685 return ConditionalParent( @@ -240,7 +240,7 @@ class _BuyQuotePreviewViewState extends ConsumerState { PrimaryButton( label: "Buy", onPressed: _buyWarning, - ) + ), ], ), ); diff --git a/lib/pages/buy_view/sub_widgets/buy_warning_popup.dart b/lib/pages/buy_view/sub_widgets/buy_warning_popup.dart index c3e1dc4e9..7bab11198 100644 --- a/lib/pages/buy_view/sub_widgets/buy_warning_popup.dart +++ b/lib/pages/buy_view/sub_widgets/buy_warning_popup.dart @@ -13,9 +13,9 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../../models/buy/response_objects/order.dart'; import '../../../models/buy/response_objects/quote.dart'; -import '../buy_order_details.dart'; import '../../../services/buy/buy_response.dart'; import '../../../services/buy/simplex/simplex_api.dart'; import '../../../themes/stack_colors.dart'; @@ -29,13 +29,14 @@ import '../../../widgets/desktop/primary_button.dart'; import '../../../widgets/desktop/secondary_button.dart'; import '../../../widgets/rounded_white_container.dart'; import '../../../widgets/stack_dialog.dart'; +import '../buy_order_details.dart'; class BuyWarningPopup extends ConsumerStatefulWidget { const BuyWarningPopup({ - Key? key, + super.key, required this.quote, this.order, - }) : super(key: key); + }); final SimplexQuote quote; final SimplexOrder? order; @override @@ -123,7 +124,8 @@ class _BuyWarningPopupState extends ConsumerState { } Future onContinue() async { - BuyResponse orderResponse = await newOrder(widget.quote); + final BuyResponse orderResponse = + await newOrder(widget.quote); if (orderResponse.exception == null) { await redirect(orderResponse.value as SimplexOrder) .then((_response) async { @@ -175,7 +177,7 @@ class _BuyWarningPopupState extends ConsumerState { ), ), ], - ) + ), ], ), ), @@ -192,9 +194,10 @@ class _BuyWarningPopupState extends ConsumerState { child: Text( "Ok", style: STextStyles.button(context).copyWith( - color: Theme.of(context) - .extension()! - .accentColorDark), + color: Theme.of(context) + .extension()! + .accentColorDark, + ), ), onPressed: () { Navigator.of(context).pop(); @@ -274,7 +277,7 @@ class _BuyWarningPopupState extends ConsumerState { ), ), ], - ) + ), ], ), ), diff --git a/lib/pages/buy_view/sub_widgets/fiat_selection_view.dart b/lib/pages/buy_view/sub_widgets/fiat_selection_view.dart index 87671494e..4fee72444 100644 --- a/lib/pages/buy_view/sub_widgets/fiat_selection_view.dart +++ b/lib/pages/buy_view/sub_widgets/fiat_selection_view.dart @@ -11,6 +11,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:intl/intl.dart'; + import '../../../models/buy/response_objects/fiat.dart'; import '../../../themes/stack_colors.dart'; import '../../../utilities/assets.dart'; @@ -28,9 +29,9 @@ import '../../../widgets/textfield_icon_button.dart'; class FiatSelectionView extends StatefulWidget { const FiatSelectionView({ - Key? key, + super.key, required this.fiats, - }) : super(key: key); + }); final List fiats; @@ -48,9 +49,11 @@ class _FiatSelectionViewState extends State { void filter(String text) { setState(() { _fiats = [ - ...fiats.where((e) => - e.name.toLowerCase().contains(text.toLowerCase()) || - e.ticker.toLowerCase().contains(text.toLowerCase())) + ...fiats.where( + (e) => + e.name.toLowerCase().contains(text.toLowerCase()) || + e.ticker.toLowerCase().contains(text.toLowerCase()), + ), ]; }); } @@ -61,10 +64,12 @@ class _FiatSelectionViewState extends State { fiats = [...widget.fiats]; fiats.sort( - (a, b) => a.ticker.toLowerCase().compareTo(b.ticker.toLowerCase())); - for (Fiats fiat in Fiats.values.reversed) { - int index = fiats.indexWhere((element) => - element.ticker.toLowerCase() == fiat.ticker.toLowerCase()); + (a, b) => a.ticker.toLowerCase().compareTo(b.ticker.toLowerCase()), + ); + for (final Fiats fiat in Fiats.values.reversed) { + final int index = fiats.indexWhere( + (element) => element.ticker.toLowerCase() == fiat.ticker.toLowerCase(), + ); if (index > 0) { final currency = fiats.removeAt(index); fiats.insert(0, currency); @@ -85,7 +90,7 @@ class _FiatSelectionViewState extends State { @override Widget build(BuildContext context) { - Locale locale = Localizations.localeOf(context); + final Locale locale = Localizations.localeOf(context); final format = NumberFormat.simpleCurrency(locale: locale.toString()); // See https://stackoverflow.com/a/67055685 @@ -104,7 +109,8 @@ class _FiatSelectionViewState extends State { if (FocusScope.of(context).hasFocus) { FocusScope.of(context).unfocus(); await Future.delayed( - const Duration(milliseconds: 50)); + const Duration(milliseconds: 50), + ); } if (mounted) { Navigator.of(context).pop(); @@ -235,17 +241,20 @@ class _FiatSelectionViewState extends State { ), child: Text( format.simpleCurrencySymbol( - e.ticker.toUpperCase()), + e.ticker.toUpperCase(), + ), style: STextStyles.subtitle(context) .apply( fontSizeFactor: (1 / format .simpleCurrencySymbol( - e.ticker.toUpperCase()) + e.ticker.toUpperCase(), + ) .length * // Couldn't get pow() working here format .simpleCurrencySymbol( - e.ticker.toUpperCase()) + e.ticker.toUpperCase(), + ) .length), ), textAlign: TextAlign.center, diff --git a/lib/pages/cashfusion/cashfusion_view.dart b/lib/pages/cashfusion/cashfusion_view.dart index e8cfaff4d..46d8a2451 100644 --- a/lib/pages/cashfusion/cashfusion_view.dart +++ b/lib/pages/cashfusion/cashfusion_view.dart @@ -15,8 +15,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_native_splash/cli_commands.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; -import 'fusion_progress_view.dart'; -import 'fusion_rounds_selection_sheet.dart'; + import '../../providers/cash_fusion/fusion_progress_ui_state_provider.dart'; import '../../providers/global/prefs_provider.dart'; import '../../providers/global/wallets_provider.dart'; @@ -34,6 +33,8 @@ import '../../widgets/desktop/primary_button.dart'; import '../../widgets/rounded_container.dart'; import '../../widgets/rounded_white_container.dart'; import '../../widgets/stack_text_field.dart'; +import 'fusion_progress_view.dart'; +import 'fusion_rounds_selection_sheet.dart'; class CashFusionView extends ConsumerStatefulWidget { const CashFusionView({ @@ -73,7 +74,8 @@ class _CashFusionViewState extends ConsumerState { ); } catch (e) { if (!e.toString().contains( - "FusionProgressUIState was already set for ${widget.walletId}")) { + "FusionProgressUIState was already set for ${widget.walletId}", + )) { rethrow; } } @@ -273,7 +275,7 @@ class _CashFusionViewState extends ConsumerState { controller: portController, focusNode: portFocusNode, inputFormatters: [ - FilteringTextInputFormatter.digitsOnly + FilteringTextInputFormatter.digitsOnly, ], keyboardType: TextInputType.number, onChanged: (value) { @@ -408,7 +410,7 @@ class _CashFusionViewState extends ConsumerState { controller: fusionRoundController, focusNode: fusionRoundFocusNode, inputFormatters: [ - FilteringTextInputFormatter.digitsOnly + FilteringTextInputFormatter.digitsOnly, ], keyboardType: TextInputType.number, onChanged: (value) { @@ -424,7 +426,8 @@ class _CashFusionViewState extends ConsumerState { fusionRoundFocusNode, context, ).copyWith( - labelText: "Enter number of fusions.."), + labelText: "Enter number of fusions..", + ), ), ), const SizedBox( diff --git a/lib/pages/cashfusion/fusion_progress_view.dart b/lib/pages/cashfusion/fusion_progress_view.dart index 6f41f07b7..fd2921c7f 100644 --- a/lib/pages/cashfusion/fusion_progress_view.dart +++ b/lib/pages/cashfusion/fusion_progress_view.dart @@ -12,6 +12,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../pages_desktop_specific/cashfusion/sub_widgets/fusion_progress.dart'; import '../../providers/cash_fusion/fusion_progress_ui_state_provider.dart'; import '../../providers/global/prefs_provider.dart'; @@ -244,7 +245,8 @@ class _FusionProgressViewState extends ConsumerState { ); } catch (e) { if (!e.toString().contains( - "FusionProgressUIState was already set for ${widget.walletId}")) { + "FusionProgressUIState was already set for ${widget.walletId}", + )) { rethrow; } } diff --git a/lib/pages/cashfusion/fusion_rounds_selection_sheet.dart b/lib/pages/cashfusion/fusion_rounds_selection_sheet.dart index 1294c0096..3994efaa9 100644 --- a/lib/pages/cashfusion/fusion_rounds_selection_sheet.dart +++ b/lib/pages/cashfusion/fusion_rounds_selection_sheet.dart @@ -22,9 +22,9 @@ enum FusionOption { class FusionRoundCountSelectSheet extends HookWidget { const FusionRoundCountSelectSheet({ - Key? key, + super.key, required this.currentOption, - }) : super(key: key); + }); final FusionOption currentOption; diff --git a/lib/pages/coin_control/coin_control_view.dart b/lib/pages/coin_control/coin_control_view.dart index 4540b6d3f..f4347a586 100644 --- a/lib/pages/coin_control/coin_control_view.dart +++ b/lib/pages/coin_control/coin_control_view.dart @@ -14,10 +14,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:isar/isar.dart'; +import 'package:tuple/tuple.dart'; + import '../../db/isar/main_db.dart'; import '../../models/isar/models/isar_models.dart'; -import 'utxo_card.dart'; -import 'utxo_details_view.dart'; import '../../providers/global/wallets_provider.dart'; import '../../themes/stack_colors.dart'; import '../../utilities/amount/amount.dart'; @@ -39,7 +39,8 @@ import '../../widgets/icon_widgets/x_icon.dart'; import '../../widgets/rounded_container.dart'; import '../../widgets/rounded_white_container.dart'; import '../../widgets/toggle.dart'; -import 'package:tuple/tuple.dart'; +import 'utxo_card.dart'; +import 'utxo_details_view.dart'; enum CoinControlViewType { manage, @@ -148,7 +149,8 @@ class _CoinControlViewState extends ConsumerState { onWillPop: () async { unawaited(_refreshBalance()); Navigator.of(context).pop( - widget.type == CoinControlViewType.use ? _selectedAvailable : null); + widget.type == CoinControlViewType.use ? _selectedAvailable : null, + ); return false; }, child: Background( @@ -179,9 +181,10 @@ class _CoinControlViewState extends ConsumerState { onPressed: () { unawaited(_refreshBalance()); Navigator.of(context).pop( - widget.type == CoinControlViewType.use - ? _selectedAvailable - : null); + widget.type == CoinControlViewType.use + ? _selectedAvailable + : null, + ); }, ), title: _isSearching @@ -336,7 +339,8 @@ class _CoinControlViewState extends ConsumerState { return UtxoCard( key: Key( - "${utxo.walletId}_${utxo.id}_$isSelected"), + "${utxo.walletId}_${utxo.id}_$isSelected", + ), walletId: widget.walletId, utxo: utxo, canSelect: widget.type == @@ -398,7 +402,8 @@ class _CoinControlViewState extends ConsumerState { return UtxoCard( key: Key( - "${utxo.walletId}_${utxo.id}_$isSelected"), + "${utxo.walletId}_${utxo.id}_$isSelected", + ), walletId: widget.walletId, utxo: utxo, canSelect: widget.type == @@ -486,7 +491,8 @@ class _CoinControlViewState extends ConsumerState { entry.key, style: STextStyles.w600_14( - context), + context, + ), ), const SizedBox( height: 2, @@ -496,8 +502,8 @@ class _CoinControlViewState extends ConsumerState { "output${entry.value.length > 1 ? "s" : ""}", style: STextStyles.w500_12( - context) - .copyWith( + context, + ).copyWith( color: Theme.of(context) .extension< StackColors>()! @@ -538,7 +544,8 @@ class _CoinControlViewState extends ConsumerState { return UtxoCard( key: Key( - "${utxo.walletId}_${utxo.id}_$isSelected"), + "${utxo.walletId}_${utxo.id}_$isSelected", + ), walletId: widget.walletId, utxo: utxo, canSelect: widget.type == @@ -615,22 +622,26 @@ class _CoinControlViewState extends ConsumerState { label: _showBlocked ? "Unfreeze" : "Freeze", onPressed: () async { if (_showBlocked) { - await MainDB.instance.putUTXOs(_selectedBlocked - .map( - (e) => e.copyWith( - isBlocked: false, - ), - ) - .toList()); + await MainDB.instance.putUTXOs( + _selectedBlocked + .map( + (e) => e.copyWith( + isBlocked: false, + ), + ) + .toList(), + ); _selectedBlocked.clear(); } else { - await MainDB.instance.putUTXOs(_selectedAvailable - .map( - (e) => e.copyWith( - isBlocked: true, - ), - ) - .toList()); + await MainDB.instance.putUTXOs( + _selectedAvailable + .map( + (e) => e.copyWith( + isBlocked: true, + ), + ) + .toList(), + ); _selectedAvailable.clear(); } setState(() {}); @@ -689,7 +700,8 @@ class _CoinControlViewState extends ConsumerState { .format(selectedSum), style: widget.requestedTotal == null ? STextStyles.w600_14(context) - : STextStyles.w600_14(context).copyWith( + : STextStyles.w600_14(context) + .copyWith( color: selectedSum >= widget .requestedTotal! @@ -700,7 +712,8 @@ class _CoinControlViewState extends ConsumerState { : Theme.of(context) .extension< StackColors>()! - .accentColorRed), + .accentColorRed, + ), ); }, ), diff --git a/lib/pages/coin_control/utxo_card.dart b/lib/pages/coin_control/utxo_card.dart index 9dc2bd900..c0a8395aa 100644 --- a/lib/pages/coin_control/utxo_card.dart +++ b/lib/pages/coin_control/utxo_card.dart @@ -10,6 +10,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../db/isar/main_db.dart'; import '../../models/isar/models/isar_models.dart'; import '../../providers/global/wallets_provider.dart'; @@ -90,83 +91,84 @@ class _UtxoCardState extends ConsumerState { ? Theme.of(context).extension()!.popupBG : Colors.transparent, child: StreamBuilder( - stream: stream, - builder: (context, snapshot) { - if (snapshot.hasData) { - utxo = snapshot.data!; - } - return Row( - children: [ - ConditionalParent( - condition: widget.canSelect, - builder: (child) => GestureDetector( - onTap: () { - _selected = !_selected; - widget.onSelectedChanged(_selected); - setState(() {}); - }, - child: child, - ), - child: UTXOStatusIcon( - blocked: utxo.isBlocked, - status: utxo.isConfirmed( - currentHeight, - ref - .watch(pWallets) - .getWallet(widget.walletId) - .cryptoCurrency - .minConfirms, - ) - ? UTXOStatusIconStatus.confirmed - : UTXOStatusIconStatus.unconfirmed, - background: - Theme.of(context).extension()!.popupBG, - selected: _selected, - width: 32, - height: 32, - ), + stream: stream, + builder: (context, snapshot) { + if (snapshot.hasData) { + utxo = snapshot.data!; + } + return Row( + children: [ + ConditionalParent( + condition: widget.canSelect, + builder: (child) => GestureDetector( + onTap: () { + _selected = !_selected; + widget.onSelectedChanged(_selected); + setState(() {}); + }, + child: child, ), - const SizedBox( - width: 10, + child: UTXOStatusIcon( + blocked: utxo.isBlocked, + status: utxo.isConfirmed( + currentHeight, + ref + .watch(pWallets) + .getWallet(widget.walletId) + .cryptoCurrency + .minConfirms, + ) + ? UTXOStatusIconStatus.confirmed + : UTXOStatusIconStatus.unconfirmed, + background: + Theme.of(context).extension()!.popupBG, + selected: _selected, + width: 32, + height: 32, ), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Text( - ref.watch(pAmountFormatter(coin)).format( - utxo.value.toAmountAsRaw( - fractionDigits: coin.fractionDigits, - ), - ), - style: STextStyles.w600_14(context), - ), - const SizedBox( - height: 2, - ), - Row( - children: [ - Flexible( - child: Text( - utxo.name.isNotEmpty - ? utxo.name - : utxo.address ?? utxo.txid, - style: STextStyles.w500_12(context).copyWith( - color: Theme.of(context) - .extension()! - .textSubtitle1, - ), + ), + const SizedBox( + width: 10, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + ref.watch(pAmountFormatter(coin)).format( + utxo.value.toAmountAsRaw( + fractionDigits: coin.fractionDigits, ), ), - ], - ), - ], - ), + style: STextStyles.w600_14(context), + ), + const SizedBox( + height: 2, + ), + Row( + children: [ + Flexible( + child: Text( + utxo.name.isNotEmpty + ? utxo.name + : utxo.address ?? utxo.txid, + style: STextStyles.w500_12(context).copyWith( + color: Theme.of(context) + .extension()! + .textSubtitle1, + ), + ), + ), + ], + ), + ], ), - ], - ); - }), + ), + ], + ); + }, + ), ), ); } diff --git a/lib/pages/coin_control/utxo_details_view.dart b/lib/pages/coin_control/utxo_details_view.dart index f27db669e..54a6f6e5a 100644 --- a/lib/pages/coin_control/utxo_details_view.dart +++ b/lib/pages/coin_control/utxo_details_view.dart @@ -550,7 +550,7 @@ class _UtxoDetailsViewState extends ConsumerState { } class _Div extends StatelessWidget { - const _Div({Key? key}) : super(key: key); + const _Div({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/exchange_view/choose_from_stack_view.dart b/lib/pages/exchange_view/choose_from_stack_view.dart index 0e6cc8627..73d79f647 100644 --- a/lib/pages/exchange_view/choose_from_stack_view.dart +++ b/lib/pages/exchange_view/choose_from_stack_view.dart @@ -131,7 +131,7 @@ class _ChooseFromStackViewState extends ConsumerState { ), ], ), - ) + ), ], ), ), diff --git a/lib/pages/exchange_view/confirm_change_now_send.dart b/lib/pages/exchange_view/confirm_change_now_send.dart index 4e99f9bcf..09aac4df9 100644 --- a/lib/pages/exchange_view/confirm_change_now_send.dart +++ b/lib/pages/exchange_view/confirm_change_now_send.dart @@ -12,12 +12,11 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:uuid/uuid.dart'; + import '../../models/exchange/response_objects/trade.dart'; import '../../models/isar/models/isar_models.dart'; import '../../models/trade_wallet_lookup.dart'; -import '../pinpad_views/lock_screen_view.dart'; -import '../send_view/sub_widgets/sending_transaction_dialog.dart'; -import '../wallet_view/wallet_view.dart'; import '../../pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_auth_send.dart'; import '../../providers/db/main_db_provider.dart'; import '../../providers/providers.dart'; @@ -42,7 +41,9 @@ import '../../widgets/desktop/secondary_button.dart'; import '../../widgets/rounded_container.dart'; import '../../widgets/rounded_white_container.dart'; import '../../widgets/stack_dialog.dart'; -import 'package:uuid/uuid.dart'; +import '../pinpad_views/lock_screen_view.dart'; +import '../send_view/sub_widgets/sending_transaction_dialog.dart'; +import '../wallet_view/wallet_view.dart'; class ConfirmChangeNowSendView extends ConsumerStatefulWidget { const ConfirmChangeNowSendView({ @@ -342,7 +343,7 @@ class _ConfirmChangeNowSendViewState Text( "Confirm ${ref.watch(pWalletCoin(walletId)).ticker} transaction", style: STextStyles.desktopH3(context), - ) + ), ], ), Padding( @@ -384,8 +385,11 @@ class _ConfirmChangeNowSendViewState children: [ Text( ref - .watch(pAmountFormatter( - ref.watch(pWalletCoin(walletId)))) + .watch( + pAmountFormatter( + ref.watch(pWalletCoin(walletId)), + ), + ) .format(widget.txData.fee!), style: STextStyles.desktopTextExtraExtraSmall(context) @@ -461,7 +465,7 @@ class _ConfirmChangeNowSendViewState ), ), ], - ) + ), ], ), ), @@ -574,41 +578,49 @@ class _ConfirmChangeNowSendViewState builder: (child) => Row( children: [ child, - Builder(builder: (context) { - final coin = ref.watch(pWalletCoin(walletId)); - final price = ref.watch( + Builder( + builder: (context) { + final coin = ref.watch(pWalletCoin(walletId)); + final price = ref.watch( priceAnd24hChangeNotifierProvider - .select((value) => value.getPrice(coin))); - final amountWithoutChange = - widget.txData.amountWithoutChange!; - final value = - (price.item1 * amountWithoutChange.decimal) - .toAmount(fractionDigits: 2); - final currency = ref.watch(prefsChangeNotifierProvider - .select((value) => value.currency)); - final locale = ref.watch( - localeServiceChangeNotifierProvider.select( - (value) => value.locale, - ), - ); + .select((value) => value.getPrice(coin)), + ); + final amountWithoutChange = + widget.txData.amountWithoutChange!; + final value = + (price.item1 * amountWithoutChange.decimal) + .toAmount(fractionDigits: 2); + final currency = ref.watch( + prefsChangeNotifierProvider + .select((value) => value.currency), + ); + final locale = ref.watch( + localeServiceChangeNotifierProvider.select( + (value) => value.locale, + ), + ); - return Text( - " | ${value.fiatString(locale: locale)} $currency", - style: - STextStyles.desktopTextExtraExtraSmall(context) - .copyWith( - color: Theme.of(context) - .extension()! - .textSubtitle2, - ), - ); - }) + return Text( + " | ${value.fiatString(locale: locale)} $currency", + style: STextStyles.desktopTextExtraExtraSmall( + context) + .copyWith( + color: Theme.of(context) + .extension()! + .textSubtitle2, + ), + ); + }, + ), ], ), child: Text( ref - .watch(pAmountFormatter( - ref.watch(pWalletCoin(walletId)))) + .watch( + pAmountFormatter( + ref.watch(pWalletCoin(walletId)), + ), + ) .format((widget.txData.amountWithoutChange!)), style: STextStyles.itemSubtitle12(context), textAlign: TextAlign.right, @@ -637,7 +649,8 @@ class _ConfirmChangeNowSendViewState Text( ref .watch( - pAmountFormatter(ref.read(pWalletCoin(walletId)))) + pAmountFormatter(ref.read(pWalletCoin(walletId))), + ) .format( widget.txData.fee!, ), diff --git a/lib/pages/exchange_view/edit_trade_note_view.dart b/lib/pages/exchange_view/edit_trade_note_view.dart index 9f6c795ec..db918be65 100644 --- a/lib/pages/exchange_view/edit_trade_note_view.dart +++ b/lib/pages/exchange_view/edit_trade_note_view.dart @@ -23,10 +23,10 @@ import '../../widgets/textfield_icon_button.dart'; class EditTradeNoteView extends ConsumerStatefulWidget { const EditTradeNoteView({ - Key? key, + super.key, required this.tradeId, required this.note, - }) : super(key: key); + }); static const String routeName = "/editTradeNote"; @@ -151,7 +151,7 @@ class _EditNoteViewState extends ConsumerState { "Save", style: STextStyles.button(context), ), - ) + ), ], ), ), diff --git a/lib/pages/exchange_view/exchange_coin_selection/exchange_currency_selection_view.dart b/lib/pages/exchange_view/exchange_coin_selection/exchange_currency_selection_view.dart index ad13cebf4..109bee3ba 100644 --- a/lib/pages/exchange_view/exchange_coin_selection/exchange_currency_selection_view.dart +++ b/lib/pages/exchange_view/exchange_coin_selection/exchange_currency_selection_view.dart @@ -110,7 +110,7 @@ class _ExchangeCurrencySelectionViewState return await _getCurrencies(); } await ExchangeDataLoadingService.instance.initDB(); - List currencies = await ExchangeDataLoadingService + final List currencies = await ExchangeDataLoadingService .instance.isar.currencies .where() .filter() diff --git a/lib/pages/exchange_view/exchange_form.dart b/lib/pages/exchange_view/exchange_form.dart index 2783e9371..9d227f231 100644 --- a/lib/pages/exchange_view/exchange_form.dart +++ b/lib/pages/exchange_view/exchange_form.dart @@ -16,6 +16,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/svg.dart'; import 'package:isar/isar.dart'; +import 'package:tuple/tuple.dart'; +import 'package:uuid/uuid.dart'; + import '../../models/exchange/aggregate_currency.dart'; import '../../models/exchange/incomplete_exchange.dart'; import '../../models/exchange/response_objects/estimate.dart'; @@ -23,11 +26,6 @@ import '../../models/exchange/response_objects/range.dart'; import '../../models/isar/exchange_cache/currency.dart'; import '../../models/isar/exchange_cache/pair.dart'; import '../../models/isar/models/ethereum/eth_contract.dart'; -import 'exchange_coin_selection/exchange_currency_selection_view.dart'; -import 'exchange_step_views/step_1_view.dart'; -import 'exchange_step_views/step_2_view.dart'; -import 'sub_widgets/exchange_provider_options.dart'; -import 'sub_widgets/rate_type_toggle.dart'; import '../../pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart'; import '../../providers/providers.dart'; import '../../services/exchange/change_now/change_now_exchange.dart'; @@ -43,7 +41,6 @@ import '../../utilities/constants.dart'; import '../../utilities/enums/exchange_rate_type_enum.dart'; import '../../utilities/text_styles.dart'; import '../../utilities/util.dart'; -import '../../wallets/crypto_currency/coins/bitcoin.dart'; import '../../wallets/crypto_currency/crypto_currency.dart'; import '../../widgets/conditional_parent.dart'; import '../../widgets/custom_loading_overlay.dart'; @@ -55,8 +52,11 @@ import '../../widgets/rounded_container.dart'; import '../../widgets/rounded_white_container.dart'; import '../../widgets/stack_dialog.dart'; import '../../widgets/textfields/exchange_textfield.dart'; -import 'package:tuple/tuple.dart'; -import 'package:uuid/uuid.dart'; +import 'exchange_coin_selection/exchange_currency_selection_view.dart'; +import 'exchange_step_views/step_1_view.dart'; +import 'exchange_step_views/step_2_view.dart'; +import 'sub_widgets/exchange_provider_options.dart'; +import 'sub_widgets/rate_type_toggle.dart'; class ExchangeForm extends ConsumerStatefulWidget { const ExchangeForm({ @@ -173,8 +173,9 @@ class _ExchangeFormState extends ConsumerState { .tryParse( value, locale: ref.read(localeServiceChangeNotifierProvider).locale, - coin: Bitcoin(CryptoCurrencyNetwork - .main), // dummy value (not used due to override) + coin: Bitcoin( + CryptoCurrencyNetwork.main, + ), // dummy value (not used due to override) overrideWithDecimalPlacesFromString: true, ) ?.decimal; @@ -184,15 +185,17 @@ class _ExchangeFormState extends ConsumerState { final rateType = ref.read(efRateTypeProvider); final currencies = await ExchangeDataLoadingService.instance.isar.currencies .filter() - .group((q) => rateType == ExchangeRateType.fixed - ? q - .rateTypeEqualTo(SupportedRateType.both) - .or() - .rateTypeEqualTo(SupportedRateType.fixed) - : q - .rateTypeEqualTo(SupportedRateType.both) - .or() - .rateTypeEqualTo(SupportedRateType.estimated)) + .group( + (q) => rateType == ExchangeRateType.fixed + ? q + .rateTypeEqualTo(SupportedRateType.both) + .or() + .rateTypeEqualTo(SupportedRateType.fixed) + : q + .rateTypeEqualTo(SupportedRateType.both) + .or() + .rateTypeEqualTo(SupportedRateType.estimated), + ) .and() .tickerEqualTo( currency.ticker, @@ -364,7 +367,8 @@ class _ExchangeFormState extends ConsumerState { ], ), ); - }) + }, + ) : await Navigator.of(context).push( MaterialPageRoute( builder: (_) => ExchangeCurrencySelectionView( @@ -489,7 +493,7 @@ class _ExchangeFormState extends ConsumerState { ), ), ], - ) + ), ], ), ); @@ -949,7 +953,8 @@ class _ExchangeFormState extends ConsumerState { ), ExchangeTextField( key: Key( - "exchangeTextFieldKeyFor1_${Theme.of(context).extension()!.themeId}"), + "exchangeTextFieldKeyFor1_${Theme.of(context).extension()!.themeId}", + ), focusNode: _receiveFocusNode, controller: _receiveController, textStyle: STextStyles.smallMed14(context).copyWith( @@ -1011,7 +1016,7 @@ class _ExchangeFormState extends ConsumerState { enabled: ref.watch(efCanExchangeProvider), onPressed: onExchangePressed, label: "Swap", - ) + ), ], ); } diff --git a/lib/pages/exchange_view/exchange_loading_overlay.dart b/lib/pages/exchange_view/exchange_loading_overlay.dart index b73d22527..5ccf12cf4 100644 --- a/lib/pages/exchange_view/exchange_loading_overlay.dart +++ b/lib/pages/exchange_view/exchange_loading_overlay.dart @@ -10,6 +10,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../providers/exchange/changenow_initial_load_status.dart'; import '../../themes/stack_colors.dart'; import '../../utilities/text_styles.dart'; @@ -18,9 +19,9 @@ import '../../widgets/stack_dialog.dart'; class ExchangeLoadingOverlayView extends ConsumerStatefulWidget { const ExchangeLoadingOverlayView({ - Key? key, + super.key, required this.unawaitedLoad, - }) : super(key: key); + }); final VoidCallback unawaitedLoad; @@ -80,7 +81,9 @@ class _ExchangeLoadingOverlayViewState .overlay .withOpacity(0.7), child: const CustomLoadingOverlay( - message: "Loading Exchange data", eventBus: null), + message: "Loading Exchange data", + eventBus: null, + ), ), if ((_statusEst == ChangeNowLoadStatus.failed || _statusFixed == ChangeNowLoadStatus.failed) && diff --git a/lib/pages/exchange_view/exchange_step_views/step_1_view.dart b/lib/pages/exchange_view/exchange_step_views/step_1_view.dart index c02647f6e..8e582dab9 100644 --- a/lib/pages/exchange_view/exchange_step_views/step_1_view.dart +++ b/lib/pages/exchange_view/exchange_step_views/step_1_view.dart @@ -9,9 +9,8 @@ */ import 'package:flutter/material.dart'; + import '../../../models/exchange/incomplete_exchange.dart'; -import 'step_2_view.dart'; -import '../sub_widgets/step_row.dart'; import '../../../themes/stack_colors.dart'; import '../../../utilities/clipboard_interface.dart'; import '../../../utilities/enums/exchange_rate_type_enum.dart'; @@ -19,13 +18,15 @@ import '../../../utilities/text_styles.dart'; import '../../../widgets/background.dart'; import '../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../widgets/rounded_white_container.dart'; +import '../sub_widgets/step_row.dart'; +import 'step_2_view.dart'; class Step1View extends StatefulWidget { const Step1View({ - Key? key, + super.key, required this.model, this.clipboard = const ClipboardWrapper(), - }) : super(key: key); + }); static const String routeName = "/exchangeStep1"; @@ -116,17 +117,19 @@ class _Step1ViewState extends State { "You send", style: STextStyles.itemSubtitle(context) .copyWith( - color: Theme.of(context) - .extension()! - .infoItemText), + color: Theme.of(context) + .extension()! + .infoItemText, + ), ), Text( "${model.sendAmount.toStringAsFixed(8)} ${model.sendTicker.toUpperCase()}", style: STextStyles.itemSubtitle12(context) .copyWith( - color: Theme.of(context) - .extension()! - .infoItemText), + color: Theme.of(context) + .extension()! + .infoItemText, + ), ), ], ), @@ -142,17 +145,19 @@ class _Step1ViewState extends State { "You receive", style: STextStyles.itemSubtitle(context) .copyWith( - color: Theme.of(context) - .extension()! - .infoItemText), + color: Theme.of(context) + .extension()! + .infoItemText, + ), ), Text( "~${model.receiveAmount.toStringAsFixed(8)} ${model.receiveTicker.toUpperCase()}", style: STextStyles.itemSubtitle12(context) .copyWith( - color: Theme.of(context) - .extension()! - .infoItemText), + color: Theme.of(context) + .extension()! + .infoItemText, + ), ), ], ), @@ -179,9 +184,10 @@ class _Step1ViewState extends State { model.rateInfo, style: STextStyles.itemSubtitle12(context) .copyWith( - color: Theme.of(context) - .extension()! - .infoItemText), + color: Theme.of(context) + .extension()! + .infoItemText, + ), ), ], ), @@ -193,8 +199,9 @@ class _Step1ViewState extends State { TextButton( onPressed: () { Navigator.of(context).pushNamed( - Step2View.routeName, - arguments: model); + Step2View.routeName, + arguments: model, + ); }, style: Theme.of(context) .extension()! diff --git a/lib/pages/exchange_view/exchange_step_views/step_2_view.dart b/lib/pages/exchange_view/exchange_step_views/step_2_view.dart index 6071c4556..433984627 100644 --- a/lib/pages/exchange_view/exchange_step_views/step_2_view.dart +++ b/lib/pages/exchange_view/exchange_step_views/step_2_view.dart @@ -42,11 +42,11 @@ import 'step_3_view.dart'; class Step2View extends ConsumerStatefulWidget { const Step2View({ - Key? key, + super.key, required this.model, this.clipboard = const ClipboardWrapper(), this.barcodeScanner = const BarcodeScannerWrapper(), - }) : super(key: key); + }); static const String routeName = "/exchangeStep2"; diff --git a/lib/pages/exchange_view/exchange_step_views/step_3_view.dart b/lib/pages/exchange_view/exchange_step_views/step_3_view.dart index 2e6b2b237..93856486e 100644 --- a/lib/pages/exchange_view/exchange_step_views/step_3_view.dart +++ b/lib/pages/exchange_view/exchange_step_views/step_3_view.dart @@ -12,10 +12,9 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../../models/exchange/incomplete_exchange.dart'; import '../../../models/exchange/response_objects/trade.dart'; -import 'step_4_view.dart'; -import '../sub_widgets/step_row.dart'; import '../../../providers/global/trades_service_provider.dart'; import '../../../providers/providers.dart'; import '../../../services/exchange/exchange_response.dart'; @@ -31,13 +30,15 @@ import '../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../widgets/custom_loading_overlay.dart'; import '../../../widgets/rounded_white_container.dart'; import '../../../widgets/stack_dialog.dart'; +import '../sub_widgets/step_row.dart'; +import 'step_4_view.dart'; class Step3View extends ConsumerStatefulWidget { const Step3View({ - Key? key, + super.key, required this.model, this.clipboard = const ClipboardWrapper(), - }) : super(key: key); + }); static const String routeName = "/exchangeStep3"; @@ -127,7 +128,7 @@ class _Step3ViewState extends ConsumerState { Text( "${model.sendAmount.toString()} ${model.sendTicker.toUpperCase()}", style: STextStyles.itemSubtitle12(context), - ) + ), ], ), ), @@ -145,7 +146,7 @@ class _Step3ViewState extends ConsumerState { Text( "${model.receiveAmount.toString()} ${model.receiveTicker.toUpperCase()}", style: STextStyles.itemSubtitle12(context), - ) + ), ], ), ), @@ -163,7 +164,7 @@ class _Step3ViewState extends ConsumerState { Text( model.rateInfo, style: STextStyles.itemSubtitle12(context), - ) + ), ], ), ), @@ -184,7 +185,7 @@ class _Step3ViewState extends ConsumerState { Text( model.recipientAddress!, style: STextStyles.itemSubtitle12(context), - ) + ), ], ), ), @@ -207,7 +208,7 @@ class _Step3ViewState extends ConsumerState { Text( model.refundAddress!, style: STextStyles.itemSubtitle12(context), - ) + ), ], ), ), @@ -321,22 +322,27 @@ class _Step3ViewState extends ConsumerState { Navigator.of(context).pop(); } - unawaited(NotificationApi.showNotification( - changeNowId: model.trade!.tradeId, - title: status, - body: "Trade ID ${model.trade!.tradeId}", - walletId: "", - iconAssetName: Assets.svg.arrowRotate, - date: model.trade!.timestamp, - shouldWatchForUpdates: true, - coinName: "coinName", - )); + unawaited( + NotificationApi.showNotification( + changeNowId: model.trade!.tradeId, + title: status, + body: + "Trade ID ${model.trade!.tradeId}", + walletId: "", + iconAssetName: Assets.svg.arrowRotate, + date: model.trade!.timestamp, + shouldWatchForUpdates: true, + coinName: "coinName", + ), + ); if (mounted) { - unawaited(Navigator.of(context).pushNamed( - Step4View.routeName, - arguments: model, - )); + unawaited( + Navigator.of(context).pushNamed( + Step4View.routeName, + arguments: model, + ), + ); } }, style: Theme.of(context) diff --git a/lib/pages/exchange_view/exchange_step_views/step_4_view.dart b/lib/pages/exchange_view/exchange_step_views/step_4_view.dart index 4700d12b1..d992451a7 100644 --- a/lib/pages/exchange_view/exchange_step_views/step_4_view.dart +++ b/lib/pages/exchange_view/exchange_step_views/step_4_view.dart @@ -49,10 +49,10 @@ import '../sub_widgets/step_row.dart'; class Step4View extends ConsumerStatefulWidget { const Step4View({ - Key? key, + super.key, required this.model, this.clipboard = const ClipboardWrapper(), - }) : super(key: key); + }); static const String routeName = "/exchangeStep4"; diff --git a/lib/pages/exchange_view/exchange_view.dart b/lib/pages/exchange_view/exchange_view.dart index b5fe193bd..1c70a64d0 100644 --- a/lib/pages/exchange_view/exchange_view.dart +++ b/lib/pages/exchange_view/exchange_view.dart @@ -13,10 +13,10 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:isar/isar.dart'; +import 'package:tuple/tuple.dart'; + import '../../db/isar/main_db.dart'; import '../../models/isar/models/blockchain_data/transaction.dart'; -import 'exchange_form.dart'; -import 'trade_details_view.dart'; import '../../providers/global/trades_service_provider.dart'; import '../../providers/providers.dart'; import '../../services/exchange/exchange_data_loading_service.dart'; @@ -26,10 +26,11 @@ import '../../utilities/text_styles.dart'; import '../../widgets/conditional_parent.dart'; import '../../widgets/custom_loading_overlay.dart'; import '../../widgets/trade_card.dart'; -import 'package:tuple/tuple.dart'; +import 'exchange_form.dart'; +import 'trade_details_view.dart'; class ExchangeView extends ConsumerStatefulWidget { - const ExchangeView({Key? key}) : super(key: key); + const ExchangeView({super.key}); @override ConsumerState createState() => _ExchangeViewState(); @@ -102,7 +103,7 @@ class _ExchangeViewState extends ConsumerState { subMessage: "This could take a few minutes", eventBus: null, ), - ) + ), ], ); }, @@ -124,7 +125,7 @@ class _ExchangeViewState extends ConsumerState { child: ExchangeForm(), ), ), - ) + ), ]; }, body: Builder( @@ -169,63 +170,78 @@ class _ExchangeViewState extends ConsumerState { ), if (hasHistory) SliverList( - delegate: SliverChildBuilderDelegate((context, index) { - return Padding( - padding: const EdgeInsets.all(4), - child: TradeCard( - key: Key("tradeCard_${trades[index].uuid}"), - trade: trades[index], - onTap: () async { - final String tradeId = trades[index].tradeId; + delegate: SliverChildBuilderDelegate( + (context, index) { + return Padding( + padding: const EdgeInsets.all(4), + child: TradeCard( + key: Key("tradeCard_${trades[index].uuid}"), + trade: trades[index], + onTap: () async { + final String tradeId = trades[index].tradeId; - final lookup = ref - .read(tradeSentFromStackLookupProvider) - .all; - - //todo: check if print needed - // debugPrint("ALL: $lookup"); - - final String? txid = ref - .read(tradeSentFromStackLookupProvider) - .getTxidForTradeId(tradeId); - final List? walletIds = ref - .read(tradeSentFromStackLookupProvider) - .getWalletIdsForTradeId(tradeId); - - if (txid != null && - walletIds != null && - walletIds.isNotEmpty) { - final wallet = ref - .read(pWallets) - .getWallet(walletIds.first); + final lookup = ref + .read(tradeSentFromStackLookupProvider) + .all; //todo: check if print needed - // debugPrint("name: ${manager.walletName}"); + // debugPrint("ALL: $lookup"); - final tx = await MainDB.instance - .getTransactions(walletIds.first) - .filter() - .txidEqualTo(txid) - .findFirst(); + final String? txid = ref + .read(tradeSentFromStackLookupProvider) + .getTxidForTradeId(tradeId); + final List? walletIds = ref + .read(tradeSentFromStackLookupProvider) + .getWalletIdsForTradeId(tradeId); - if (mounted) { - unawaited(Navigator.of(context).pushNamed( - TradeDetailsView.routeName, - arguments: Tuple4(tradeId, tx, - walletIds.first, wallet.info.name), - )); + if (txid != null && + walletIds != null && + walletIds.isNotEmpty) { + final wallet = ref + .read(pWallets) + .getWallet(walletIds.first); + + //todo: check if print needed + // debugPrint("name: ${manager.walletName}"); + + final tx = await MainDB.instance + .getTransactions(walletIds.first) + .filter() + .txidEqualTo(txid) + .findFirst(); + + if (mounted) { + unawaited( + Navigator.of(context).pushNamed( + TradeDetailsView.routeName, + arguments: Tuple4( + tradeId, + tx, + walletIds.first, + wallet.info.name, + ), + ), + ); + } + } else { + unawaited( + Navigator.of(context).pushNamed( + TradeDetailsView.routeName, + arguments: Tuple4( + tradeId, + null, + walletIds?.first, + null, + ), + ), + ); } - } else { - unawaited(Navigator.of(context).pushNamed( - TradeDetailsView.routeName, - arguments: Tuple4( - tradeId, null, walletIds?.first, null), - )); - } - }, - ), - ); - }, childCount: tradeCount), + }, + ), + ); + }, + childCount: tradeCount, + ), ), if (!hasHistory) SliverToBoxAdapter( diff --git a/lib/pages/exchange_view/send_from_view.dart b/lib/pages/exchange_view/send_from_view.dart index bf7e18df2..680330753 100644 --- a/lib/pages/exchange_view/send_from_view.dart +++ b/lib/pages/exchange_view/send_from_view.dart @@ -31,8 +31,6 @@ import '../../utilities/constants.dart'; import '../../utilities/enums/fee_rate_type_enum.dart'; import '../../utilities/text_styles.dart'; import '../../utilities/util.dart'; -import '../../wallets/crypto_currency/coins/firo.dart'; -import '../../wallets/crypto_currency/coins/stellar.dart'; import '../../wallets/crypto_currency/crypto_currency.dart'; import '../../wallets/isar/providers/wallet_info_provider.dart'; import '../../wallets/models/tx_data.dart'; @@ -48,14 +46,14 @@ import '../../widgets/stack_dialog.dart'; class SendFromView extends ConsumerStatefulWidget { const SendFromView({ - Key? key, + super.key, required this.coin, required this.trade, required this.amount, required this.address, this.shouldPopRoot = false, this.fromDesktopStep4 = false, - }) : super(key: key); + }); static const String routeName = "/sendFrom"; diff --git a/lib/pages/exchange_view/sub_widgets/exchange_provider_option.dart b/lib/pages/exchange_view/sub_widgets/exchange_provider_option.dart index 29cbd7f7f..7ba126659 100644 --- a/lib/pages/exchange_view/sub_widgets/exchange_provider_option.dart +++ b/lib/pages/exchange_view/sub_widgets/exchange_provider_option.dart @@ -132,8 +132,9 @@ class _ExchangeOptionState extends ConsumerState { localeServiceChangeNotifierProvider .select((value) => value.locale), ), - coin: Bitcoin(CryptoCurrencyNetwork - .main), // some sane default + coin: Bitcoin( + CryptoCurrencyNetwork.main, + ), // some sane default maxDecimals: 8, // some sane default ); rateString = "1 ${sendCurrency.ticker.toUpperCase()} " @@ -222,14 +223,14 @@ class _ExchangeOptionState extends ConsumerState { class _ProviderOption extends ConsumerStatefulWidget { const _ProviderOption({ - Key? key, + super.key, required this.exchange, required this.estimate, required this.rateString, this.kycRating, this.loadingString = false, this.rateColor, - }) : super(key: key); + }); final Exchange exchange; final Estimate? estimate; diff --git a/lib/pages/exchange_view/sub_widgets/exchange_provider_options.dart b/lib/pages/exchange_view/sub_widgets/exchange_provider_options.dart index dd6afa8ed..436e724ea 100644 --- a/lib/pages/exchange_view/sub_widgets/exchange_provider_options.dart +++ b/lib/pages/exchange_view/sub_widgets/exchange_provider_options.dart @@ -24,10 +24,10 @@ import '../../../widgets/rounded_white_container.dart'; class ExchangeProviderOptions extends ConsumerStatefulWidget { const ExchangeProviderOptions({ - Key? key, + super.key, required this.fixedRate, required this.reversed, - }) : super(key: key); + }); final bool fixedRate; final bool reversed; diff --git a/lib/pages/exchange_view/sub_widgets/rate_type_toggle.dart b/lib/pages/exchange_view/sub_widgets/rate_type_toggle.dart index 6840fa139..3450c7ddd 100644 --- a/lib/pages/exchange_view/sub_widgets/rate_type_toggle.dart +++ b/lib/pages/exchange_view/sub_widgets/rate_type_toggle.dart @@ -20,9 +20,9 @@ import '../../../widgets/toggle.dart'; class RateTypeToggle extends ConsumerWidget { const RateTypeToggle({ - Key? key, + super.key, this.onChanged, - }) : super(key: key); + }); final void Function(ExchangeRateType)? onChanged; diff --git a/lib/pages/exchange_view/sub_widgets/step_indicator.dart b/lib/pages/exchange_view/sub_widgets/step_indicator.dart index d1d440190..e45609035 100644 --- a/lib/pages/exchange_view/sub_widgets/step_indicator.dart +++ b/lib/pages/exchange_view/sub_widgets/step_indicator.dart @@ -18,11 +18,11 @@ enum StepIndicatorStatus { current, completed, incomplete } class StepIndicator extends StatelessWidget { const StepIndicator({ - Key? key, + super.key, required this.step, required this.status, this.size = 16, - }) : super(key: key); + }); final int step; final StepIndicatorStatus status; diff --git a/lib/pages/exchange_view/sub_widgets/step_row.dart b/lib/pages/exchange_view/sub_widgets/step_row.dart index 95a8f4a17..d555438cb 100644 --- a/lib/pages/exchange_view/sub_widgets/step_row.dart +++ b/lib/pages/exchange_view/sub_widgets/step_row.dart @@ -9,18 +9,19 @@ */ import 'package:flutter/material.dart'; -import 'step_indicator.dart'; + import '../../../themes/stack_colors.dart'; +import 'step_indicator.dart'; class StepRow extends StatelessWidget { const StepRow({ - Key? key, + super.key, required this.count, required this.current, required this.width, this.indicatorSize = 16, this.minSpacing = 4, - }) : super(key: key); + }); final int count; final int current; @@ -53,23 +54,29 @@ class StepRow extends StatelessWidget { } List _buildList(double spacerWidth, BuildContext context) { - List list = []; + final List list = []; for (int i = 0; i < count - 1; i++) { - list.add(StepIndicator( - step: i + 1, - status: getStatus(i), - )); - list.add(_SpacerRow( - width: spacerWidth, - dotSize: 1.5, - spacing: 4, - color: getColor(i, context), - )); + list.add( + StepIndicator( + step: i + 1, + status: getStatus(i), + ), + ); + list.add( + _SpacerRow( + width: spacerWidth, + dotSize: 1.5, + spacing: 4, + color: getColor(i, context), + ), + ); } - list.add(StepIndicator( - step: count, - status: getStatus(count - 1), - )); + list.add( + StepIndicator( + step: count, + status: getStatus(count - 1), + ), + ); return list; } @@ -88,12 +95,12 @@ class StepRow extends StatelessWidget { class _SpacerRow extends StatelessWidget { const _SpacerRow({ - Key? key, + super.key, required this.width, required this.dotSize, required this.spacing, required this.color, - }) : super(key: key); + }); final Color color; final double width; @@ -128,10 +135,10 @@ class _SpacerRow extends StatelessWidget { class _SpacerDot extends StatelessWidget { const _SpacerDot({ - Key? key, + super.key, required this.color, this.size = 1.5, - }) : super(key: key); + }); final double size; final Color color; diff --git a/lib/pages/exchange_view/wallet_initiated_exchange_view.dart b/lib/pages/exchange_view/wallet_initiated_exchange_view.dart index ac81190bf..bfdb0e647 100644 --- a/lib/pages/exchange_view/wallet_initiated_exchange_view.dart +++ b/lib/pages/exchange_view/wallet_initiated_exchange_view.dart @@ -119,7 +119,7 @@ class _WalletInitiatedExchangeViewState subMessage: "This could take a few minutes", eventBus: null, ), - ) + ), ], ); }, diff --git a/lib/pages/generic/single_field_edit_view.dart b/lib/pages/generic/single_field_edit_view.dart index d78f6f599..b02490db4 100644 --- a/lib/pages/generic/single_field_edit_view.dart +++ b/lib/pages/generic/single_field_edit_view.dart @@ -26,10 +26,10 @@ import '../../widgets/textfield_icon_button.dart'; class SingleFieldEditView extends StatefulWidget { const SingleFieldEditView({ - Key? key, + super.key, required this.initialValue, required this.label, - }) : super(key: key); + }); static const String routeName = "/singleFieldEdit"; diff --git a/lib/pages/home_view/home_view.dart b/lib/pages/home_view/home_view.dart index 0173064c2..f5e43eac2 100644 --- a/lib/pages/home_view/home_view.dart +++ b/lib/pages/home_view/home_view.dart @@ -200,7 +200,7 @@ class _HomeViewState extends ConsumerState { Text( "My ${AppConfig.prefix}", style: STextStyles.navBarTitle(context), - ) + ), ], ), actions: [ @@ -232,8 +232,10 @@ class _HomeViewState extends ConsumerState { color: Theme.of(context) .extension()! .backgroundAppBar, - icon: ref.watch(notificationsProvider - .select((value) => value.hasUnreadNotifications)) + icon: ref.watch( + notificationsProvider + .select((value) => value.hasUnreadNotifications), + ) ? SvgPicture.file( File( ref.watch( @@ -244,8 +246,11 @@ class _HomeViewState extends ConsumerState { ), width: 20, height: 20, - color: ref.watch(notificationsProvider.select( - (value) => value.hasUnreadNotifications)) + color: ref.watch( + notificationsProvider.select( + (value) => value.hasUnreadNotifications, + ), + ) ? null : Theme.of(context) .extension()! @@ -255,8 +260,11 @@ class _HomeViewState extends ConsumerState { Assets.svg.bell, width: 20, height: 20, - color: ref.watch(notificationsProvider.select( - (value) => value.hasUnreadNotifications)) + color: ref.watch( + notificationsProvider.select( + (value) => value.hasUnreadNotifications, + ), + ) ? null : Theme.of(context) .extension()! @@ -274,14 +282,16 @@ class _HomeViewState extends ConsumerState { .state; if (unreadNotificationIds.isEmpty) return; - List> futures = []; + final List> futures = []; for (int i = 0; i < unreadNotificationIds.length - 1; i++) { - futures.add(ref - .read(notificationsProvider) - .markAsRead( - unreadNotificationIds.elementAt(i), false)); + futures.add( + ref.read(notificationsProvider).markAsRead( + unreadNotificationIds.elementAt(i), + false, + ), + ); } // wait for multiple to update if any diff --git a/lib/pages/home_view/sub_widgets/home_view_button_bar.dart b/lib/pages/home_view/sub_widgets/home_view_button_bar.dart index e51ecacb7..8744e1980 100644 --- a/lib/pages/home_view/sub_widgets/home_view_button_bar.dart +++ b/lib/pages/home_view/sub_widgets/home_view_button_bar.dart @@ -15,7 +15,7 @@ import '../../../themes/stack_colors.dart'; import '../../../utilities/text_styles.dart'; class HomeViewButtonBar extends ConsumerStatefulWidget { - const HomeViewButtonBar({Key? key}) : super(key: key); + const HomeViewButtonBar({super.key}); @override ConsumerState createState() => _HomeViewButtonBarState(); diff --git a/lib/pages/intro_view.dart b/lib/pages/intro_view.dart index 0f99aabae..b1666cd04 100644 --- a/lib/pages/intro_view.dart +++ b/lib/pages/intro_view.dart @@ -187,7 +187,7 @@ class _IntroViewState extends ConsumerState { } class AppNameText extends StatelessWidget { - const AppNameText({Key? key, required this.isDesktop}) : super(key: key); + const AppNameText({super.key, required this.isDesktop}); final bool isDesktop; @@ -209,7 +209,7 @@ class AppNameText extends StatelessWidget { } class IntroAboutText extends StatelessWidget { - const IntroAboutText({Key? key, required this.isDesktop}) : super(key: key); + const IntroAboutText({super.key, required this.isDesktop}); final bool isDesktop; @@ -228,8 +228,7 @@ class IntroAboutText extends StatelessWidget { } class PrivacyAndTOSText extends StatelessWidget { - const PrivacyAndTOSText({Key? key, required this.isDesktop}) - : super(key: key); + const PrivacyAndTOSText({super.key, required this.isDesktop}); final bool isDesktop; @@ -241,7 +240,8 @@ class PrivacyAndTOSText extends StatelessWidget { text: TextSpan( style: STextStyles.label(context).copyWith(fontSize: fontSize), children: [ - TextSpan(text: "By using ${AppConfig.appName}, you agree to the "), + const TextSpan( + text: "By using ${AppConfig.appName}, you agree to the "), TextSpan( text: "Terms of service", style: STextStyles.richLink(context).copyWith(fontSize: fontSize), @@ -272,7 +272,7 @@ class PrivacyAndTOSText extends StatelessWidget { } class GetStartedButton extends StatelessWidget { - const GetStartedButton({Key? key, required this.isDesktop}) : super(key: key); + const GetStartedButton({super.key, required this.isDesktop}); final bool isDesktop; diff --git a/lib/pages/loading_view.dart b/lib/pages/loading_view.dart index 67598d894..e347860ec 100644 --- a/lib/pages/loading_view.dart +++ b/lib/pages/loading_view.dart @@ -22,7 +22,7 @@ import '../widgets/conditional_parent.dart'; import '../widgets/rounded_container.dart'; class LoadingView extends ConsumerWidget { - const LoadingView({Key? key}) : super(key: key); + const LoadingView({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { diff --git a/lib/pages/manage_favorites_view/manage_favorites_view.dart b/lib/pages/manage_favorites_view/manage_favorites_view.dart index a6fe24401..a3673e95a 100644 --- a/lib/pages/manage_favorites_view/manage_favorites_view.dart +++ b/lib/pages/manage_favorites_view/manage_favorites_view.dart @@ -22,7 +22,7 @@ import '../../widgets/desktop/desktop_scaffold.dart'; import '../../widgets/managed_favorite.dart'; class ManageFavoritesView extends StatelessWidget { - const ManageFavoritesView({Key? key}) : super(key: key); + const ManageFavoritesView({super.key}); static const routeName = "/manageFavorites"; @@ -219,7 +219,7 @@ class ManageFavoritesView extends StatelessWidget { ), ); }, - ) + ), ], ), ), diff --git a/lib/pages/monkey/monkey_view.dart b/lib/pages/monkey/monkey_view.dart index d2da7026a..f8f9d3f4d 100644 --- a/lib/pages/monkey/monkey_view.dart +++ b/lib/pages/monkey/monkey_view.dart @@ -6,6 +6,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; + import '../../notifications/show_flush_bar.dart'; import '../../providers/global/wallets_provider.dart'; import '../../services/monkey_service.dart'; @@ -30,9 +31,9 @@ import '../../widgets/stack_dialog.dart'; class MonkeyView extends ConsumerStatefulWidget { const MonkeyView({ - Key? key, + super.key, required this.walletId, - }) : super(key: key); + }); static const String routeName = "/monkey"; static const double navBarHeight = 65.0; @@ -89,7 +90,7 @@ class _MonkeyViewState extends ConsumerState { filePath += isPNG ? ".png" : ".svg"; - File imgFile = File(filePath); + final File imgFile = File(filePath); if (imgFile.existsSync() && !overwrite) { throw Exception("File already exists"); @@ -249,7 +250,7 @@ class _MonkeyViewState extends ConsumerState { .extension()! .customTextButtonEnabledText, ), - ) + ), ], ), ), @@ -341,8 +342,9 @@ class _MonkeyViewState extends ConsumerState { whileFuture: Future.wait([ _saveMonKeyToFile( bytes: Uint8List.fromList( - (wallet as BananoWallet) - .getMonkeyImageBytes()!), + (wallet as BananoWallet) + .getMonkeyImageBytes()!, + ), ), Future.delayed( const Duration(seconds: 2), @@ -399,7 +401,8 @@ class _MonkeyViewState extends ConsumerState { ), ), Future.delayed( - const Duration(seconds: 2)), + const Duration(seconds: 2), + ), ]), context: context, rootNavigator: Util.isDesktop, diff --git a/lib/pages/monkey/sub_widgets/fetch_monkey_dialog.dart b/lib/pages/monkey/sub_widgets/fetch_monkey_dialog.dart index ba691645c..b3b5d7e6d 100644 --- a/lib/pages/monkey/sub_widgets/fetch_monkey_dialog.dart +++ b/lib/pages/monkey/sub_widgets/fetch_monkey_dialog.dart @@ -20,9 +20,9 @@ import '../../../widgets/stack_dialog.dart'; class FetchMonkeyDialog extends StatefulWidget { const FetchMonkeyDialog({ - Key? key, + super.key, required this.onCancel, - }) : super(key: key); + }); final Future Function() onCancel; diff --git a/lib/pages/notification_views/notifications_view.dart b/lib/pages/notification_views/notifications_view.dart index 03fbeb784..7a20efffd 100644 --- a/lib/pages/notification_views/notifications_view.dart +++ b/lib/pages/notification_views/notifications_view.dart @@ -21,9 +21,9 @@ import '../../widgets/rounded_white_container.dart'; class NotificationsView extends ConsumerStatefulWidget { const NotificationsView({ - Key? key, + super.key, this.walletId, - }) : super(key: key); + }); final String? walletId; @@ -112,7 +112,7 @@ class _NotificationsViewState extends ConsumerState { ), ), ), - ) + ), ], ), ), diff --git a/lib/pages/ordinals/ordinal_details_view.dart b/lib/pages/ordinals/ordinal_details_view.dart index 5fa7cfd25..02e0ee074 100644 --- a/lib/pages/ordinals/ordinal_details_view.dart +++ b/lib/pages/ordinals/ordinal_details_view.dart @@ -7,6 +7,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; + import '../../models/isar/models/blockchain_data/utxo.dart'; import '../../models/isar/ordinal.dart'; import '../../networking/http.dart'; @@ -145,10 +146,10 @@ class _OrdinalDetailsViewState extends ConsumerState { class _DetailsItemWCopy extends StatelessWidget { const _DetailsItemWCopy({ - Key? key, + super.key, required this.title, required this.data, - }) : super(key: key); + }); final String title; final String data; @@ -219,10 +220,10 @@ class _DetailsItemWCopy extends StatelessWidget { class _OrdinalImageGroup extends ConsumerWidget { const _OrdinalImageGroup({ - Key? key, + super.key, required this.walletId, required this.ordinal, - }) : super(key: key); + }); final String walletId; final Ordinal ordinal; @@ -230,7 +231,7 @@ class _OrdinalImageGroup extends ConsumerWidget { static const _spacing = 12.0; Future _savePngToFile(WidgetRef ref) async { - HTTP client = HTTP(); + final HTTP client = HTTP(); final response = await client.get( url: Uri.parse(ordinal.content), @@ -241,7 +242,8 @@ class _OrdinalImageGroup extends ConsumerWidget { if (response.code != 200) { throw Exception( - "OrdinalDetailsView _savePngToFile statusCode=${response.code} body=${response.bodyBytes}"); + "OrdinalDetailsView _savePngToFile statusCode=${response.code} body=${response.bodyBytes}", + ); } final bytes = response.bodyBytes; @@ -257,7 +259,7 @@ class _OrdinalImageGroup extends ConsumerWidget { final docPath = dir.path; final filePath = "$docPath/ordinal_${ordinal.inscriptionNumber}.png"; - File imgFile = File(filePath); + final File imgFile = File(filePath); if (imgFile.existsSync()) { throw Exception("File already exists"); diff --git a/lib/pages/ordinals/ordinals_filter_view.dart b/lib/pages/ordinals/ordinals_filter_view.dart index 5f598708f..4c7d0b398 100644 --- a/lib/pages/ordinals/ordinals_filter_view.dart +++ b/lib/pages/ordinals/ordinals_filter_view.dart @@ -11,6 +11,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../themes/stack_colors.dart'; import '../../themes/theme_providers.dart'; import '../../utilities/assets.dart'; @@ -126,9 +127,10 @@ class _OrdinalsFilterViewState extends ConsumerState { return Text( isDateSelected ? "From..." : _fromDateString, style: STextStyles.fieldLabel(context).copyWith( - color: isDateSelected - ? Theme.of(context).extension()!.textSubtitle2 - : Theme.of(context).extension()!.accentColorDark), + color: isDateSelected + ? Theme.of(context).extension()!.textSubtitle2 + : Theme.of(context).extension()!.accentColorDark, + ), ); } @@ -137,9 +139,10 @@ class _OrdinalsFilterViewState extends ConsumerState { return Text( isDateSelected ? "To..." : _toDateString, style: STextStyles.fieldLabel(context).copyWith( - color: isDateSelected - ? Theme.of(context).extension()!.textSubtitle2 - : Theme.of(context).extension()!.accentColorDark), + color: isDateSelected + ? Theme.of(context).extension()!.textSubtitle2 + : Theme.of(context).extension()!.accentColorDark, + ), ); } @@ -182,7 +185,8 @@ class _OrdinalsFilterViewState extends ConsumerState { !_selectedFromDate!.isBefore(_selectedToDate!); if (flag) { _selectedToDate = DateTime.fromMillisecondsSinceEpoch( - _selectedFromDate!.millisecondsSinceEpoch); + _selectedFromDate!.millisecondsSinceEpoch, + ); } setState(() { @@ -236,7 +240,7 @@ class _OrdinalsFilterViewState extends ConsumerState { child: FittedBox( child: _dateFromText, ), - ) + ), ], ), ), @@ -272,7 +276,8 @@ class _OrdinalsFilterViewState extends ConsumerState { !_selectedToDate!.isAfter(_selectedFromDate!); if (flag) { _selectedFromDate = DateTime.fromMillisecondsSinceEpoch( - _selectedToDate!.millisecondsSinceEpoch); + _selectedToDate!.millisecondsSinceEpoch, + ); } setState(() { @@ -326,7 +331,7 @@ class _OrdinalsFilterViewState extends ConsumerState { child: FittedBox( child: _dateToText, ), - ) + ), ], ), ), diff --git a/lib/pages/ordinals/ordinals_view.dart b/lib/pages/ordinals/ordinals_view.dart index 844523110..17aaffb86 100644 --- a/lib/pages/ordinals/ordinals_view.dart +++ b/lib/pages/ordinals/ordinals_view.dart @@ -39,7 +39,7 @@ class _OrdinalsViewState extends ConsumerState { late final TextEditingController searchController; late final FocusNode searchFocus; - String _searchTerm = ""; + final String _searchTerm = ""; @override void initState() { @@ -91,7 +91,7 @@ class _OrdinalsViewState extends ConsumerState { Future.delayed(const Duration(seconds: 2)), (ref.read(pWallets).getWallet(widget.walletId) as OrdinalsInterface) - .refreshInscriptions() + .refreshInscriptions(), ]), context: context, message: "Refreshing...", diff --git a/lib/pages/ordinals/widgets/ordinal_card.dart b/lib/pages/ordinals/widgets/ordinal_card.dart index b5a561f48..31eeb5733 100644 --- a/lib/pages/ordinals/widgets/ordinal_card.dart +++ b/lib/pages/ordinals/widgets/ordinal_card.dart @@ -9,10 +9,10 @@ import '../../../widgets/rounded_white_container.dart'; class OrdinalCard extends StatelessWidget { const OrdinalCard({ - Key? key, + super.key, required this.walletId, required this.ordinal, - }) : super(key: key); + }); final String walletId; final Ordinal ordinal; diff --git a/lib/pages/ordinals/widgets/ordinals_list.dart b/lib/pages/ordinals/widgets/ordinals_list.dart index 0e9516127..ec67d7ef8 100644 --- a/lib/pages/ordinals/widgets/ordinals_list.dart +++ b/lib/pages/ordinals/widgets/ordinals_list.dart @@ -3,19 +3,20 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:isar/isar.dart'; + import '../../../models/isar/ordinal.dart'; -import 'ordinal_card.dart'; import '../../../providers/db/main_db_provider.dart'; import '../../../themes/stack_colors.dart'; import '../../../utilities/text_styles.dart'; import '../../../utilities/util.dart'; import '../../../widgets/rounded_white_container.dart'; +import 'ordinal_card.dart'; class OrdinalsList extends ConsumerStatefulWidget { const OrdinalsList({ - Key? key, + super.key, required this.walletId, - }) : super(key: key); + }); final String walletId; @@ -74,7 +75,8 @@ class _OrdinalsListState extends ConsumerState { ? STextStyles.w500_14(context).copyWith( color: Theme.of(context) .extension()! - .textSubtitle1) + .textSubtitle1, + ) : STextStyles.label(context), ), ), @@ -88,13 +90,16 @@ class _OrdinalsListState extends ConsumerState { spacing: _spacing, runSpacing: _spacing, children: _data - .map((e) => SizedBox( + .map( + (e) => SizedBox( width: 220, height: 270, child: OrdinalCard( walletId: widget.walletId, ordinal: e, - ))) + ), + ), + ) .toList(), ); } else { diff --git a/lib/pages/paynym/add_new_paynym_follow_view.dart b/lib/pages/paynym/add_new_paynym_follow_view.dart index f02089c05..5f9e7bb71 100644 --- a/lib/pages/paynym/add_new_paynym_follow_view.dart +++ b/lib/pages/paynym/add_new_paynym_follow_view.dart @@ -13,9 +13,8 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../models/paynym/paynym_account.dart'; -import 'subwidgets/featured_paynyms_widget.dart'; -import 'subwidgets/paynym_card.dart'; import '../../providers/global/paynym_api_provider.dart'; import '../../themes/stack_colors.dart'; import '../../utilities/barcode_scanner_interface.dart'; @@ -37,12 +36,14 @@ import '../../widgets/rounded_container.dart'; import '../../widgets/rounded_white_container.dart'; import '../../widgets/stack_text_field.dart'; import '../../widgets/textfield_icon_button.dart'; +import 'subwidgets/featured_paynyms_widget.dart'; +import 'subwidgets/paynym_card.dart'; class AddNewPaynymFollowView extends ConsumerStatefulWidget { const AddNewPaynymFollowView({ - Key? key, + super.key, required this.walletId, - }) : super(key: key); + }); final String walletId; @@ -283,8 +284,8 @@ class _AddNewPaynymFollowViewState }); }, style: STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( + context, + ).copyWith( color: Theme.of(context) .extension()! .textFieldActiveText, @@ -324,7 +325,8 @@ class _AddNewPaynymFollowViewState ) : TextFieldIconButton( key: const Key( - "paynymPasteAddressFieldButtonKey"), + "paynymPasteAddressFieldButtonKey", + ), onTap: _paste, child: RoundedContainer( padding: @@ -337,7 +339,8 @@ class _AddNewPaynymFollowViewState ), TextFieldIconButton( key: const Key( - "paynymScanQrButtonKey"), + "paynymScanQrButtonKey", + ), onTap: _scanQr, child: RoundedContainer( padding: const EdgeInsets.all(8), @@ -346,7 +349,7 @@ class _AddNewPaynymFollowViewState .buttonBackSecondary, child: const QrCodeIcon(), ), - ) + ), ], ), ), @@ -398,7 +401,8 @@ class _AddNewPaynymFollowViewState ) : TextFieldIconButton( key: const Key( - "paynymPasteAddressFieldButtonKey"), + "paynymPasteAddressFieldButtonKey", + ), onTap: _paste, child: const ClipboardIcon(), ), @@ -406,7 +410,7 @@ class _AddNewPaynymFollowViewState key: const Key("paynymScanQrButtonKey"), onTap: _scanQr, child: const QrCodeIcon(), - ) + ), ], ), ), diff --git a/lib/pages/paynym/dialogs/claiming_paynym_dialog.dart b/lib/pages/paynym/dialogs/claiming_paynym_dialog.dart index 2adad5a69..d10ad52fd 100644 --- a/lib/pages/paynym/dialogs/claiming_paynym_dialog.dart +++ b/lib/pages/paynym/dialogs/claiming_paynym_dialog.dart @@ -20,8 +20,8 @@ import '../../../widgets/stack_dialog.dart'; class ClaimingPaynymDialog extends StatefulWidget { const ClaimingPaynymDialog({ - Key? key, - }) : super(key: key); + super.key, + }); @override State createState() => _RestoringDialogState(); diff --git a/lib/pages/paynym/dialogs/confirm_paynym_connect_dialog.dart b/lib/pages/paynym/dialogs/confirm_paynym_connect_dialog.dart index ee2504d35..5024775eb 100644 --- a/lib/pages/paynym/dialogs/confirm_paynym_connect_dialog.dart +++ b/lib/pages/paynym/dialogs/confirm_paynym_connect_dialog.dart @@ -122,7 +122,7 @@ class ConfirmPaynymConnectDialog extends ConsumerWidget { ), ], ), - ) + ), ], ), ); diff --git a/lib/pages/paynym/dialogs/paynym_details_popup.dart b/lib/pages/paynym/dialogs/paynym_details_popup.dart index aa4774646..832f97422 100644 --- a/lib/pages/paynym/dialogs/paynym_details_popup.dart +++ b/lib/pages/paynym/dialogs/paynym_details_popup.dart @@ -227,7 +227,7 @@ class _PaynymDetailsPopupState extends ConsumerState { .extension()! .accentColorGreen, ), - ) + ), ], ); } else { diff --git a/lib/pages/paynym/dialogs/paynym_qr_popup.dart b/lib/pages/paynym/dialogs/paynym_qr_popup.dart index 757761396..b3cb3a1d0 100644 --- a/lib/pages/paynym/dialogs/paynym_qr_popup.dart +++ b/lib/pages/paynym/dialogs/paynym_qr_popup.dart @@ -26,9 +26,9 @@ import '../../../widgets/desktop/desktop_dialog_close_button.dart'; class PaynymQrPopup extends StatelessWidget { const PaynymQrPopup({ - Key? key, + super.key, required this.paynymAccount, - }) : super(key: key); + }); final PaynymAccount paynymAccount; @@ -166,7 +166,7 @@ class PaynymQrPopup extends StatelessWidget { ), ], ), - ) + ), ], ), ); diff --git a/lib/pages/paynym/paynym_claim_view.dart b/lib/pages/paynym/paynym_claim_view.dart index 457179655..9bf9cb3cd 100644 --- a/lib/pages/paynym/paynym_claim_view.dart +++ b/lib/pages/paynym/paynym_claim_view.dart @@ -33,9 +33,9 @@ import '../../widgets/desktop/primary_button.dart'; class PaynymClaimView extends ConsumerStatefulWidget { const PaynymClaimView({ - Key? key, + super.key, required this.walletId, - }) : super(key: key); + }); final String walletId; @@ -113,7 +113,7 @@ class _PaynymClaimViewState extends ConsumerState { Text( "PayNym", style: STextStyles.desktopH3(context), - ) + ), ], ), ) diff --git a/lib/pages/paynym/paynym_home_view.dart b/lib/pages/paynym/paynym_home_view.dart index be8e9db4c..8c3c6419d 100644 --- a/lib/pages/paynym/paynym_home_view.dart +++ b/lib/pages/paynym/paynym_home_view.dart @@ -15,13 +15,8 @@ import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:share_plus/share_plus.dart'; + import '../../notifications/show_flush_bar.dart'; -import 'add_new_paynym_follow_view.dart'; -import 'dialogs/paynym_qr_popup.dart'; -import 'subwidgets/desktop_paynym_details.dart'; -import 'subwidgets/paynym_bot.dart'; -import 'subwidgets/paynym_followers_list.dart'; -import 'subwidgets/paynym_following_list.dart'; import '../../providers/ui/selected_paynym_details_item_Provider.dart'; import '../../providers/wallet/my_paynym_account_state_provider.dart'; import '../../themes/stack_colors.dart'; @@ -41,12 +36,18 @@ import '../../widgets/icon_widgets/share_icon.dart'; import '../../widgets/rounded_container.dart'; import '../../widgets/rounded_white_container.dart'; import '../../widgets/toggle.dart'; +import 'add_new_paynym_follow_view.dart'; +import 'dialogs/paynym_qr_popup.dart'; +import 'subwidgets/desktop_paynym_details.dart'; +import 'subwidgets/paynym_bot.dart'; +import 'subwidgets/paynym_followers_list.dart'; +import 'subwidgets/paynym_following_list.dart'; class PaynymHomeView extends ConsumerStatefulWidget { const PaynymHomeView({ - Key? key, + super.key, required this.walletId, - }) : super(key: key); + }); final String walletId; @@ -117,7 +118,7 @@ class _PaynymHomeViewState extends ConsumerState { Text( "PayNym", style: STextStyles.desktopH3(context), - ) + ), ], ), trailing: Padding( @@ -169,8 +170,8 @@ class _PaynymHomeViewState extends ConsumerState { "Follow", style: STextStyles.desktopButtonSecondaryEnabled( - context) - .copyWith( + context, + ).copyWith( fontSize: 16, ), ), @@ -267,9 +268,11 @@ class _PaynymHomeViewState extends ConsumerState { secretCount++; if (secretCount > 5) { debugPrint( - "My Account: ${ref.read(myPaynymAccountStateProvider.state).state}"); + "My Account: ${ref.read(myPaynymAccountStateProvider.state).state}", + ); debugPrint( - "My Account: ${ref.read(myPaynymAccountStateProvider.state).state!.following}"); + "My Account: ${ref.read(myPaynymAccountStateProvider.state).state!.following}", + ); secretCount = 0; } @@ -304,13 +307,14 @@ class _PaynymHomeViewState extends ConsumerState { ), Text( Format.shorten( - ref - .watch(myPaynymAccountStateProvider.state) - .state! - .nonSegwitPaymentCode - .code, - 12, - 5), + ref + .watch(myPaynymAccountStateProvider.state) + .state! + .nonSegwitPaymentCode + .code, + 12, + 5, + ), style: STextStyles.label(context).copyWith( fontSize: 14, ), @@ -380,12 +384,13 @@ class _PaynymHomeViewState extends ConsumerState { } await Share.share( - ref - .read(myPaynymAccountStateProvider.state) - .state! - .nonSegwitPaymentCode - .code, - sharePositionOrigin: sharePositionOrigin); + ref + .read(myPaynymAccountStateProvider.state) + .state! + .nonSegwitPaymentCode + .code, + sharePositionOrigin: sharePositionOrigin, + ); }, ), ), @@ -435,9 +440,11 @@ class _PaynymHomeViewState extends ConsumerState { secretCount++; if (secretCount > 5) { debugPrint( - "My Account: ${ref.read(myPaynymAccountStateProvider.state).state}"); + "My Account: ${ref.read(myPaynymAccountStateProvider.state).state}", + ); debugPrint( - "My Account: ${ref.read(myPaynymAccountStateProvider.state).state!.following}"); + "My Account: ${ref.read(myPaynymAccountStateProvider.state).state!.following}", + ); secretCount = 0; } @@ -475,13 +482,14 @@ class _PaynymHomeViewState extends ConsumerState { ), Text( Format.shorten( - ref - .watch(myPaynymAccountStateProvider.state) - .state! - .nonSegwitPaymentCode - .code, - 12, - 5), + ref + .watch(myPaynymAccountStateProvider.state) + .state! + .nonSegwitPaymentCode + .code, + 12, + 5, + ), style: STextStyles.desktopTextExtraExtraSmall(context), ), @@ -619,8 +627,9 @@ class _PaynymHomeViewState extends ConsumerState { child: DesktopPaynymDetails( walletId: widget.walletId, accountLite: ref - .watch(selectedPaynymDetailsItemProvider - .state) + .watch( + selectedPaynymDetailsItemProvider.state, + ) .state!, ), ), diff --git a/lib/pages/paynym/subwidgets/desktop_paynym_details.dart b/lib/pages/paynym/subwidgets/desktop_paynym_details.dart index bc2fef85f..c33d5ed24 100644 --- a/lib/pages/paynym/subwidgets/desktop_paynym_details.dart +++ b/lib/pages/paynym/subwidgets/desktop_paynym_details.dart @@ -41,10 +41,10 @@ import '../../../widgets/rounded_white_container.dart'; class DesktopPaynymDetails extends ConsumerStatefulWidget { const DesktopPaynymDetails({ - Key? key, + super.key, required this.walletId, required this.accountLite, - }) : super(key: key); + }); final String walletId; final PaynymAccountLite accountLite; @@ -216,7 +216,7 @@ class _PaynymDetailsPopupState extends ConsumerState { .extension()! .accentColorGreen, ), - ) + ), ], ); } else { diff --git a/lib/pages/paynym/subwidgets/featured_paynyms_widget.dart b/lib/pages/paynym/subwidgets/featured_paynyms_widget.dart index 645d8cf60..e67f312c9 100644 --- a/lib/pages/paynym/subwidgets/featured_paynyms_widget.dart +++ b/lib/pages/paynym/subwidgets/featured_paynyms_widget.dart @@ -18,9 +18,9 @@ import '../../../widgets/rounded_white_container.dart'; class FeaturedPaynymsWidget extends StatelessWidget { const FeaturedPaynymsWidget({ - Key? key, + super.key, required this.walletId, - }) : super(key: key); + }); final String walletId; diff --git a/lib/pages/paynym/subwidgets/paynym_bot.dart b/lib/pages/paynym/subwidgets/paynym_bot.dart index ab72dce24..1a6d6e469 100644 --- a/lib/pages/paynym/subwidgets/paynym_bot.dart +++ b/lib/pages/paynym/subwidgets/paynym_bot.dart @@ -17,10 +17,10 @@ import '../../../utilities/prefs.dart'; class PayNymBot extends StatelessWidget { const PayNymBot({ - Key? key, + super.key, required this.paymentCodeString, this.size = 60.0, - }) : super(key: key); + }); final String paymentCodeString; final double size; diff --git a/lib/pages/paynym/subwidgets/paynym_card.dart b/lib/pages/paynym/subwidgets/paynym_card.dart index 1a81202a6..32422a740 100644 --- a/lib/pages/paynym/subwidgets/paynym_card.dart +++ b/lib/pages/paynym/subwidgets/paynym_card.dart @@ -18,11 +18,11 @@ import '../../../widgets/custom_buttons/paynym_follow_toggle_button.dart'; class PaynymCard extends StatefulWidget { const PaynymCard({ - Key? key, + super.key, required this.walletId, required this.label, required this.paymentCodeString, - }) : super(key: key); + }); final String walletId; final String label; diff --git a/lib/pages/paynym/subwidgets/paynym_card_button.dart b/lib/pages/paynym/subwidgets/paynym_card_button.dart index 869d6244d..8f7026be3 100644 --- a/lib/pages/paynym/subwidgets/paynym_card_button.dart +++ b/lib/pages/paynym/subwidgets/paynym_card_button.dart @@ -23,10 +23,10 @@ import '../../../widgets/rounded_container.dart'; class PaynymCardButton extends ConsumerStatefulWidget { const PaynymCardButton({ - Key? key, + super.key, required this.walletId, required this.accountLite, - }) : super(key: key); + }); final String walletId; final PaynymAccountLite accountLite; diff --git a/lib/pages/paynym/subwidgets/paynym_followers_list.dart b/lib/pages/paynym/subwidgets/paynym_followers_list.dart index 55ac272f7..43fdf0223 100644 --- a/lib/pages/paynym/subwidgets/paynym_followers_list.dart +++ b/lib/pages/paynym/subwidgets/paynym_followers_list.dart @@ -27,9 +27,9 @@ import '../../../widgets/rounded_white_container.dart'; class PaynymFollowersList extends ConsumerStatefulWidget { const PaynymFollowersList({ - Key? key, + super.key, required this.walletId, - }) : super(key: key); + }); final String walletId; diff --git a/lib/pages/paynym/subwidgets/paynym_following_list.dart b/lib/pages/paynym/subwidgets/paynym_following_list.dart index 3536f5760..486e138e2 100644 --- a/lib/pages/paynym/subwidgets/paynym_following_list.dart +++ b/lib/pages/paynym/subwidgets/paynym_following_list.dart @@ -27,9 +27,9 @@ import '../../../widgets/rounded_white_container.dart'; class PaynymFollowingList extends ConsumerStatefulWidget { const PaynymFollowingList({ - Key? key, + super.key, required this.walletId, - }) : super(key: key); + }); final String walletId; diff --git a/lib/pages/pinpad_views/create_pin_view.dart b/lib/pages/pinpad_views/create_pin_view.dart index 05231181d..586e6c9fd 100644 --- a/lib/pages/pinpad_views/create_pin_view.dart +++ b/lib/pages/pinpad_views/create_pin_view.dart @@ -13,8 +13,8 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../notifications/show_flush_bar.dart'; -import '../home_view/home_view.dart'; import '../../providers/global/prefs_provider.dart'; import '../../providers/global/secure_store_provider.dart'; import '../../themes/stack_colors.dart'; @@ -25,13 +25,14 @@ import '../../utilities/text_styles.dart'; import '../../widgets/background.dart'; import '../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../widgets/custom_pin_put/custom_pin_put.dart'; +import '../home_view/home_view.dart'; class CreatePinView extends ConsumerStatefulWidget { const CreatePinView({ - Key? key, + super.key, this.popOnSuccess = false, this.biometrics = const Biometrics(), - }) : super(key: key); + }); static const String routeName = "/createPin"; @@ -265,8 +266,9 @@ class _CreatePinViewState extends ConsumerState { // if (!Platform.isLinux) // assert((await _secureStore.read(key: "stack_pin")) == // null); - assert(ref.read(prefsChangeNotifierProvider).hasPin == - false); + assert( + ref.read(prefsChangeNotifierProvider).hasPin == false, + ); await _secureStore.write(key: "stack_pin", value: pin); @@ -275,7 +277,8 @@ class _CreatePinViewState extends ConsumerState { ref.read(prefsChangeNotifierProvider).hasPin = true; await Future.delayed( - const Duration(milliseconds: 200)); + const Duration(milliseconds: 200), + ); if (mounted) { if (!widget.popOnSuccess) { diff --git a/lib/pages/pinpad_views/lock_screen_view.dart b/lib/pages/pinpad_views/lock_screen_view.dart index ec4f7af76..afb7c7a1d 100644 --- a/lib/pages/pinpad_views/lock_screen_view.dart +++ b/lib/pages/pinpad_views/lock_screen_view.dart @@ -12,9 +12,8 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../notifications/show_flush_bar.dart'; -import '../home_view/home_view.dart'; -import '../wallet_view/wallet_view.dart'; // import 'package:stackwallet/providers/global/has_authenticated_start_state_provider.dart'; import '../../providers/global/prefs_provider.dart'; import '../../providers/global/secure_store_provider.dart'; @@ -23,7 +22,6 @@ import '../../themes/stack_colors.dart'; // import 'package:stackwallet/providers/global/should_show_lockscreen_on_resume_state_provider.dart'; import '../../utilities/assets.dart'; import '../../utilities/biometrics.dart'; - import '../../utilities/flutter_secure_storage_interface.dart'; import '../../utilities/show_loading.dart'; import '../../utilities/text_styles.dart'; @@ -33,10 +31,12 @@ import '../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../widgets/custom_buttons/blue_text_button.dart'; import '../../widgets/custom_pin_put/custom_pin_put.dart'; import '../../widgets/shake/shake.dart'; +import '../home_view/home_view.dart'; +import '../wallet_view/wallet_view.dart'; class LockscreenView extends ConsumerStatefulWidget { const LockscreenView({ - Key? key, + super.key, required this.routeOnSuccess, required this.biometricsAuthenticationTitle, required this.biometricsLocalizedReason, @@ -48,7 +48,7 @@ class LockscreenView extends ConsumerStatefulWidget { this.biometrics = const Biometrics(), this.onSuccess, this.customKeyLabel = "Button", - }) : super(key: key); + }); static const String routeName = "/lockscreen"; @@ -151,9 +151,10 @@ class _LockscreenViewState extends ConsumerState { if (useBiometrics) { if (await biometrics.authenticate( - title: title, - localizedReason: localizedReason, - cancelButtonText: cancelButtonText)) { + title: title, + localizedReason: localizedReason, + cancelButtonText: cancelButtonText, + )) { // check if initial log in // if (widget.routeOnSuccess == "/mainview") { // await logIn(await walletsService.networkName, currentWalletName, @@ -227,7 +228,8 @@ class _LockscreenViewState extends ConsumerState { if (FocusScope.of(context).hasFocus) { FocusScope.of(context).unfocus(); await Future.delayed( - const Duration(milliseconds: 70)); + const Duration(milliseconds: 70), + ); } if (mounted) { Navigator.of(context).pop(); @@ -360,16 +362,19 @@ class _LockscreenViewState extends ConsumerState { prettyTime += "${_timeout.inSeconds} seconds"; } - unawaited(showFloatingFlushBar( - type: FlushBarType.warning, - message: - "Incorrect PIN entered too many times. Please wait $prettyTime", - context: context, - iconAsset: Assets.svg.alertCircle, - )); + unawaited( + showFloatingFlushBar( + type: FlushBarType.warning, + message: + "Incorrect PIN entered too many times. Please wait $prettyTime", + context: context, + iconAsset: Assets.svg.alertCircle, + ), + ); await Future.delayed( - const Duration(milliseconds: 100)); + const Duration(milliseconds: 100), + ); _pinTextController.text = ''; @@ -381,19 +386,23 @@ class _LockscreenViewState extends ConsumerState { if (storedPin == pin) { await Future.delayed( - const Duration(milliseconds: 200)); + const Duration(milliseconds: 200), + ); unawaited(_onUnlock()); } else { unawaited(_shakeController.shake()); - unawaited(showFloatingFlushBar( - type: FlushBarType.warning, - message: "Incorrect PIN. Please try again", - context: context, - iconAsset: Assets.svg.alertCircle, - )); + unawaited( + showFloatingFlushBar( + type: FlushBarType.warning, + message: "Incorrect PIN. Please try again", + context: context, + iconAsset: Assets.svg.alertCircle, + ), + ); await Future.delayed( - const Duration(milliseconds: 100)); + const Duration(milliseconds: 100), + ); _pinTextController.text = ''; } diff --git a/lib/pages/receive_view/addresses/address_card.dart b/lib/pages/receive_view/addresses/address_card.dart index 9cd6fbb2f..a13ec17a7 100644 --- a/lib/pages/receive_view/addresses/address_card.dart +++ b/lib/pages/receive_view/addresses/address_card.dart @@ -22,6 +22,7 @@ import 'package:isar/isar.dart'; import 'package:path_provider/path_provider.dart'; import 'package:qr_flutter/qr_flutter.dart'; import 'package:share_plus/share_plus.dart'; + import '../../../db/isar/main_db.dart'; import '../../../models/isar/models/isar_models.dart'; import '../../../notifications/show_flush_bar.dart'; @@ -84,7 +85,8 @@ class _AddressCardState extends ConsumerState { final dir = Directory("${Platform.environment['HOME']}"); if (!dir.existsSync()) { throw Exception( - "Home dir not found while trying to open filepicker on QR image save"); + "Home dir not found while trying to open filepicker on QR image save", + ); } final path = await FilePicker.platform.saveFile( fileName: "qrcode.png", @@ -127,8 +129,10 @@ class _AddressCardState extends ConsumerState { final file = await File("${tempDir.path}/qrcode.png").create(); await file.writeAsBytes(pngBytes); - await Share.shareFiles(["${tempDir.path}/qrcode.png"], - text: "Receive URI QR Code"); + await Share.shareFiles( + ["${tempDir.path}/qrcode.png"], + text: "Receive URI QR Code", + ); } } catch (e) { //todo: comeback to this @@ -362,7 +366,7 @@ class _AddressCardState extends ConsumerState { ), ), ], - ) + ), ], ), ); diff --git a/lib/pages/receive_view/addresses/address_details_view.dart b/lib/pages/receive_view/addresses/address_details_view.dart index 268a9bca7..120bfa48b 100644 --- a/lib/pages/receive_view/addresses/address_details_view.dart +++ b/lib/pages/receive_view/addresses/address_details_view.dart @@ -12,13 +12,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:isar/isar.dart'; import 'package:qr_flutter/qr_flutter.dart'; + import '../../../db/isar/main_db.dart'; import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; import '../../../models/isar/models/isar_models.dart'; -import 'address_tag.dart'; -import '../../wallet_view/sub_widgets/no_transactions_found.dart'; -import '../../wallet_view/transaction_views/transaction_details_view.dart'; -import '../../wallet_view/transaction_views/tx_v2/transaction_v2_card.dart'; import '../../../providers/db/main_db_provider.dart'; import '../../../providers/global/wallets_provider.dart'; import '../../../themes/stack_colors.dart'; @@ -36,6 +33,10 @@ import '../../../widgets/desktop/desktop_dialog.dart'; import '../../../widgets/desktop/desktop_dialog_close_button.dart'; import '../../../widgets/rounded_white_container.dart'; import '../../../widgets/transaction_card.dart'; +import '../../wallet_view/sub_widgets/no_transactions_found.dart'; +import '../../wallet_view/transaction_views/transaction_details_view.dart'; +import '../../wallet_view/transaction_views/tx_v2/transaction_v2_card.dart'; +import 'address_tag.dart'; class AddressDetailsView extends ConsumerStatefulWidget { const AddressDetailsView({ @@ -212,8 +213,8 @@ class _AddressDetailsViewState extends ConsumerState { Text( "Address details", style: STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( + context, + ).copyWith( color: Theme.of(context) .extension()! .textSubtitle1, @@ -244,8 +245,8 @@ class _AddressDetailsViewState extends ConsumerState { Text( "Transaction history", style: STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( + context, + ).copyWith( color: Theme.of(context) .extension()! .textSubtitle1, @@ -476,10 +477,10 @@ class _AddressDetailsTxList extends StatelessWidget { class _AddressDetailsTxV2List extends ConsumerWidget { const _AddressDetailsTxV2List({ - Key? key, + super.key, required this.walletId, required this.address, - }) : super(key: key); + }); final String walletId; final Address address; @@ -491,37 +492,38 @@ class _AddressDetailsTxV2List extends ConsumerWidget { final query = ref.watch(mainDBProvider).isar.transactionV2s.buildQuery( - whereClauses: [ - IndexWhereClause.equalTo( - indexName: 'walletId', - value: [walletId], - ) - ], - filter: FilterGroup.and([ - if (walletTxFilter != null) walletTxFilter, - FilterGroup.or([ - ObjectFilter( - property: 'inputs', - filter: FilterCondition.contains( - property: "addresses", - value: address.value, - ), + whereClauses: [ + IndexWhereClause.equalTo( + indexName: 'walletId', + value: [walletId], ), - ObjectFilter( - property: 'outputs', - filter: FilterCondition.contains( - property: "addresses", - value: address.value, + ], + filter: FilterGroup.and([ + if (walletTxFilter != null) walletTxFilter, + FilterGroup.or([ + ObjectFilter( + property: 'inputs', + filter: FilterCondition.contains( + property: "addresses", + value: address.value, + ), ), - ) + ObjectFilter( + property: 'outputs', + filter: FilterCondition.contains( + property: "addresses", + value: address.value, + ), + ), + ]), ]), - ]), - sortBy: [ - const SortProperty( - property: "timestamp", - sort: Sort.desc, - ), - ]); + sortBy: [ + const SortProperty( + property: "timestamp", + sort: Sort.desc, + ), + ], + ); final count = query.countSync(); @@ -561,9 +563,9 @@ class _AddressDetailsTxV2List extends ConsumerWidget { class _Div extends StatelessWidget { const _Div({ - Key? key, + super.key, required this.height, - }) : super(key: key); + }); final double height; @@ -585,9 +587,9 @@ class _Div extends StatelessWidget { class _Tags extends StatelessWidget { const _Tags({ - Key? key, + super.key, required this.tags, - }) : super(key: key); + }); final List? tags; @@ -643,11 +645,11 @@ class _Tags extends StatelessWidget { class _Item extends StatelessWidget { const _Item({ - Key? key, + super.key, required this.title, required this.data, required this.button, - }) : super(key: key); + }); final String title; final String data; diff --git a/lib/pages/receive_view/addresses/address_qr_popup.dart b/lib/pages/receive_view/addresses/address_qr_popup.dart index 202876e26..7b25003b2 100644 --- a/lib/pages/receive_view/addresses/address_qr_popup.dart +++ b/lib/pages/receive_view/addresses/address_qr_popup.dart @@ -20,6 +20,7 @@ import 'package:flutter_svg/svg.dart'; import 'package:path_provider/path_provider.dart'; import 'package:qr_flutter/qr_flutter.dart'; import 'package:share_plus/share_plus.dart'; + import '../../../notifications/show_flush_bar.dart'; import '../../../themes/stack_colors.dart'; import '../../../utilities/address_utils.dart'; @@ -66,7 +67,8 @@ class _AddressQrPopupState extends State { final dir = Directory("${Platform.environment['HOME']}"); if (!dir.existsSync()) { throw Exception( - "Home dir not found while trying to open filepicker on QR image save"); + "Home dir not found while trying to open filepicker on QR image save", + ); } final path = await FilePicker.platform.saveFile( fileName: "qrcode.png", @@ -105,8 +107,10 @@ class _AddressQrPopupState extends State { final file = await File("${tempDir.path}/qrcode.png").create(); await file.writeAsBytes(pngBytes); - await Share.shareFiles(["${tempDir.path}/qrcode.png"], - text: "Receive URI QR Code"); + await Share.shareFiles( + ["${tempDir.path}/qrcode.png"], + text: "Receive URI QR Code", + ); } } catch (e) { //todo: comeback to this @@ -194,7 +198,7 @@ class _AddressQrPopupState extends State { ), ), ], - ) + ), ], ), ); diff --git a/lib/pages/receive_view/addresses/address_tag.dart b/lib/pages/receive_view/addresses/address_tag.dart index c2258adda..1d7ebaf5b 100644 --- a/lib/pages/receive_view/addresses/address_tag.dart +++ b/lib/pages/receive_view/addresses/address_tag.dart @@ -15,7 +15,7 @@ import '../../../utilities/text_styles.dart'; import '../../../widgets/rounded_container.dart'; class AddressTag extends StatelessWidget { - const AddressTag({Key? key, required this.tag}) : super(key: key); + const AddressTag({super.key, required this.tag}); final String tag; diff --git a/lib/pages/receive_view/addresses/edit_address_label_view.dart b/lib/pages/receive_view/addresses/edit_address_label_view.dart index 61c639877..2e6052690 100644 --- a/lib/pages/receive_view/addresses/edit_address_label_view.dart +++ b/lib/pages/receive_view/addresses/edit_address_label_view.dart @@ -11,6 +11,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:isar/isar.dart'; + import '../../../db/isar/main_db.dart'; import '../../../models/isar/models/address_label.dart'; import '../../../themes/stack_colors.dart'; @@ -28,9 +29,9 @@ import '../../../widgets/textfield_icon_button.dart'; class EditAddressLabelView extends ConsumerStatefulWidget { const EditAddressLabelView({ - Key? key, + super.key, required this.addressLabelId, - }) : super(key: key); + }); static const String routeName = "/editAddressLabel"; @@ -89,7 +90,8 @@ class _EditAddressLabelViewState extends ConsumerState { if (FocusScope.of(context).hasFocus) { FocusScope.of(context).unfocus(); await Future.delayed( - const Duration(milliseconds: 75)); + const Duration(milliseconds: 75), + ); } if (mounted) { Navigator.of(context).pop(); @@ -241,7 +243,7 @@ class _EditAddressLabelViewState extends ConsumerState { "Save", style: STextStyles.button(context), ), - ) + ), ], ), ), diff --git a/lib/pages/receive_view/addresses/wallet_addresses_view.dart b/lib/pages/receive_view/addresses/wallet_addresses_view.dart index b0970395b..620bfe244 100644 --- a/lib/pages/receive_view/addresses/wallet_addresses_view.dart +++ b/lib/pages/receive_view/addresses/wallet_addresses_view.dart @@ -11,10 +11,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:isar/isar.dart'; +import 'package:tuple/tuple.dart'; + import '../../../db/isar/main_db.dart'; import '../../../models/isar/models/isar_models.dart'; -import 'address_card.dart'; -import 'address_details_view.dart'; import '../../../themes/stack_colors.dart'; import '../../../utilities/text_styles.dart'; import '../../../utilities/util.dart'; @@ -23,13 +23,14 @@ import '../../../widgets/background.dart'; import '../../../widgets/conditional_parent.dart'; import '../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../widgets/loading_indicator.dart'; -import 'package:tuple/tuple.dart'; +import 'address_card.dart'; +import 'address_details_view.dart'; class WalletAddressesView extends ConsumerStatefulWidget { const WalletAddressesView({ - Key? key, + super.key, required this.walletId, - }) : super(key: key); + }); static const String routeName = "/walletAddressesView"; @@ -53,14 +54,16 @@ class _WalletAddressesViewState extends ConsumerState { return MainDB.instance .getAddresses(widget.walletId) .filter() - .group((q) => q - .subTypeEqualTo(AddressSubType.change) - .or() - .subTypeEqualTo(AddressSubType.receiving) - .or() - .subTypeEqualTo(AddressSubType.paynymReceive) - .or() - .subTypeEqualTo(AddressSubType.paynymNotification)) + .group( + (q) => q + .subTypeEqualTo(AddressSubType.change) + .or() + .subTypeEqualTo(AddressSubType.receiving) + .or() + .subTypeEqualTo(AddressSubType.paynymReceive) + .or() + .subTypeEqualTo(AddressSubType.paynymNotification), + ) .and() .not() .typeEqualTo(AddressType.nonWallet) @@ -95,15 +98,19 @@ class _WalletAddressesViewState extends ConsumerState { .getAddresses(widget.walletId) .filter() .anyOf( - labels, (q, e) => q.valueEqualTo(e.addressString)) - .group((q) => q - .subTypeEqualTo(AddressSubType.change) - .or() - .subTypeEqualTo(AddressSubType.receiving) - .or() - .subTypeEqualTo(AddressSubType.paynymReceive) - .or() - .subTypeEqualTo(AddressSubType.paynymNotification)) + labels, + (q, e) => q.valueEqualTo(e.addressString), + ) + .group( + (q) => q + .subTypeEqualTo(AddressSubType.change) + .or() + .subTypeEqualTo(AddressSubType.receiving) + .or() + .subTypeEqualTo(AddressSubType.paynymReceive) + .or() + .subTypeEqualTo(AddressSubType.paynymNotification), + ) .and() .not() .typeEqualTo(AddressType.nonWallet) diff --git a/lib/pages/receive_view/generate_receiving_uri_qr_code_view.dart b/lib/pages/receive_view/generate_receiving_uri_qr_code_view.dart index 25c338b96..e7d6d7e5f 100644 --- a/lib/pages/receive_view/generate_receiving_uri_qr_code_view.dart +++ b/lib/pages/receive_view/generate_receiving_uri_qr_code_view.dart @@ -22,6 +22,7 @@ import 'package:flutter_svg/svg.dart'; import 'package:path_provider/path_provider.dart'; import 'package:qr_flutter/qr_flutter.dart'; import 'package:share_plus/share_plus.dart'; + import '../../notifications/show_flush_bar.dart'; import '../../themes/stack_colors.dart'; import '../../utilities/address_utils.dart'; @@ -31,8 +32,6 @@ import '../../utilities/constants.dart'; import '../../utilities/logger.dart'; import '../../utilities/text_styles.dart'; import '../../utilities/util.dart'; -import '../../wallets/crypto_currency/coins/bitcoincash.dart'; -import '../../wallets/crypto_currency/coins/ecash.dart'; import '../../wallets/crypto_currency/crypto_currency.dart'; import '../../widgets/background.dart'; import '../../widgets/conditional_parent.dart'; @@ -90,7 +89,8 @@ class _GenerateUriQrCodeViewState extends State { final dir = Directory("${Platform.environment['HOME']}"); if (!dir.existsSync()) { throw Exception( - "Home dir not found while trying to open filepicker on QR image save"); + "Home dir not found while trying to open filepicker on QR image save", + ); } final path = await FilePicker.platform.saveFile( fileName: "qrcode.png", @@ -129,8 +129,10 @@ class _GenerateUriQrCodeViewState extends State { final file = await File("${tempDir.path}/qrcode.png").create(); await file.writeAsBytes(pngBytes); - await Share.shareFiles(["${tempDir.path}/qrcode.png"], - text: "Receive URI QR Code"); + await Share.shareFiles( + ["${tempDir.path}/qrcode.png"], + text: "Receive URI QR Code", + ); } } catch (e) { //todo: comeback to this @@ -173,8 +175,10 @@ class _GenerateUriQrCodeViewState extends State { queryParams, ); - Logging.instance.log("Generated receiving QR code for: $uriString", - level: LogLevel.Info); + Logging.instance.log( + "Generated receiving QR code for: $uriString", + level: LogLevel.Info, + ); return uriString; } @@ -212,13 +216,14 @@ class _GenerateUriQrCodeViewState extends State { width: width + 20, height: width + 20, child: QrImageView( - data: uriString, - size: width, - backgroundColor: - Theme.of(context).extension()!.popupBG, - foregroundColor: Theme.of(context) - .extension()! - .accentColorDark), + data: uriString, + size: width, + backgroundColor: + Theme.of(context).extension()!.popupBG, + foregroundColor: Theme.of(context) + .extension()! + .accentColorDark, + ), ), ), ), @@ -552,14 +557,15 @@ class _GenerateUriQrCodeViewState extends State { width: 234, height: 234, child: QrImageView( - data: _uriString, - size: 220, - backgroundColor: Theme.of(context) - .extension()! - .popupBG, - foregroundColor: Theme.of(context) - .extension()! - .accentColorDark), + data: _uriString, + size: 220, + backgroundColor: Theme.of(context) + .extension()! + .popupBG, + foregroundColor: Theme.of(context) + .extension()! + .accentColorDark, + ), ), ), ), @@ -613,7 +619,7 @@ class _GenerateUriQrCodeViewState extends State { ), ), ], - ) + ), ], ), ), diff --git a/lib/pages/receive_view/receive_view.dart b/lib/pages/receive_view/receive_view.dart index e9e85e8d1..844950949 100644 --- a/lib/pages/receive_view/receive_view.dart +++ b/lib/pages/receive_view/receive_view.dart @@ -17,10 +17,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:isar/isar.dart'; import 'package:qr_flutter/qr_flutter.dart'; + import '../../models/isar/models/isar_models.dart'; import '../../notifications/show_flush_bar.dart'; -import 'addresses/wallet_addresses_view.dart'; -import 'generate_receiving_uri_qr_code_view.dart'; import '../../providers/db/main_db_provider.dart'; import '../../providers/providers.dart'; import '../../route_generator.dart'; @@ -46,6 +45,8 @@ import '../../widgets/custom_loading_overlay.dart'; import '../../widgets/desktop/primary_button.dart'; import '../../widgets/desktop/secondary_button.dart'; import '../../widgets/rounded_white_container.dart'; +import 'addresses/wallet_addresses_view.dart'; +import 'generate_receiving_uri_qr_code_view.dart'; class ReceiveView extends ConsumerStatefulWidget { const ReceiveView({ @@ -193,9 +194,11 @@ class _ReceiveViewState extends ConsumerState { if (_supportsSpark) { _walletAddressTypes.insert(0, AddressType.spark); } else { - _walletAddressTypes.addAll((wallet as Bip39HDWallet) - .supportedAddressTypes - .where((e) => e != coin.primaryAddressType)); + _walletAddressTypes.addAll( + (wallet as Bip39HDWallet) + .supportedAddressTypes + .where((e) => e != coin.primaryAddressType), + ); } } @@ -544,16 +547,16 @@ class _ReceiveViewState extends ConsumerState { ); }, ), - if (ref.watch(pWallets - .select((value) => value.getWallet(walletId))) - is MultiAddressInterface || + if (ref.watch( + pWallets.select((value) => value.getWallet(walletId)), + ) is MultiAddressInterface || _supportsSpark) const SizedBox( height: 12, ), - if (ref.watch(pWallets - .select((value) => value.getWallet(walletId))) - is MultiAddressInterface || + if (ref.watch( + pWallets.select((value) => value.getWallet(walletId)), + ) is MultiAddressInterface || _supportsSpark) SecondaryButton( label: "Generate new address", @@ -573,34 +576,37 @@ class _ReceiveViewState extends ConsumerState { child: Column( children: [ QrImageView( - data: AddressUtils.buildUriString( - coin, - address, - {}, - ), - size: MediaQuery.of(context).size.width / 2, - foregroundColor: Theme.of(context) - .extension()! - .accentColorDark), + data: AddressUtils.buildUriString( + coin, + address, + {}, + ), + size: MediaQuery.of(context).size.width / 2, + foregroundColor: Theme.of(context) + .extension()! + .accentColorDark, + ), const SizedBox( height: 20, ), CustomTextButton( text: "Advanced options", onTap: () async { - unawaited(Navigator.of(context).push( - RouteGenerator.getRoute( - shouldUseMaterialRoute: - RouteGenerator.useMaterialPageRoute, - builder: (_) => GenerateUriQrCodeView( - coin: coin, - receivingAddress: address, - ), - settings: const RouteSettings( - name: GenerateUriQrCodeView.routeName, + unawaited( + Navigator.of(context).push( + RouteGenerator.getRoute( + shouldUseMaterialRoute: + RouteGenerator.useMaterialPageRoute, + builder: (_) => GenerateUriQrCodeView( + coin: coin, + receivingAddress: address, + ), + settings: const RouteSettings( + name: GenerateUriQrCodeView.routeName, + ), ), ), - )); + ); }, ), ], diff --git a/lib/pages/send_view/frost_ms/frost_send_view.dart b/lib/pages/send_view/frost_ms/frost_send_view.dart index 260cf866c..a5851977c 100644 --- a/lib/pages/send_view/frost_ms/frost_send_view.dart +++ b/lib/pages/send_view/frost_ms/frost_send_view.dart @@ -14,10 +14,10 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:tuple/tuple.dart'; + import '../../../frost_route_generator.dart'; import '../../../models/isar/models/isar_models.dart'; -import '../../coin_control/coin_control_view.dart'; -import 'recipient.dart'; import '../../../providers/frost_wallet/frost_wallet_providers.dart'; import '../../../providers/providers.dart'; import '../../../themes/coin_icon_provider.dart'; @@ -46,7 +46,8 @@ import '../../../widgets/rounded_white_container.dart'; import '../../../widgets/stack_dialog.dart'; import '../../../widgets/stack_text_field.dart'; import '../../../widgets/textfield_icon_button.dart'; -import 'package:tuple/tuple.dart'; +import '../../coin_control/coin_control_view.dart'; +import 'recipient.dart'; class FrostSendView extends ConsumerStatefulWidget { const FrostSendView({ @@ -164,9 +165,10 @@ class _FrostSendViewState extends ConsumerState { child: Text( "Ok", style: STextStyles.button(context).copyWith( - color: Theme.of(context) - .extension()! - .accentColorDark), + color: Theme.of(context) + .extension()! + .accentColorDark, + ), ), onPressed: () { Navigator.of(context).pop(); @@ -346,9 +348,11 @@ class _FrostSendViewState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( - ref.watch(pAmountFormatter(coin)).format(ref - .watch(pWalletBalance(walletId)) - .spendable), + ref.watch(pAmountFormatter(coin)).format( + ref + .watch(pWalletBalance(walletId)) + .spendable, + ), style: STextStyles.titleBold12(context).copyWith( fontSize: 10, @@ -358,7 +362,7 @@ class _FrostSendViewState extends ConsumerState { ], ), ), - ) + ), ], ), ), @@ -389,8 +393,10 @@ class _FrostSendViewState extends ConsumerState { ? null : () { ref - .read(pRecipient(recipientWidgetIndexes[i]) - .notifier) + .read( + pRecipient(recipientWidgetIndexes[i]) + .notifier, + ) .state = null; recipientWidgetIndexes.removeAt(i); setState(() {}); diff --git a/lib/pages/send_view/frost_ms/recipient.dart b/lib/pages/send_view/frost_ms/recipient.dart index 8bb6cbd12..6d9d9b7c4 100644 --- a/lib/pages/send_view/frost_ms/recipient.dart +++ b/lib/pages/send_view/frost_ms/recipient.dart @@ -2,6 +2,7 @@ import 'package:decimal/decimal.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../../providers/global/locale_provider.dart'; import '../../../themes/stack_colors.dart'; import '../../../utilities/address_utils.dart'; @@ -39,7 +40,8 @@ final pBarcodeScanner = final pRecipient = StateProvider.family<({String address, Amount? amount})?, int>( - (ref, index) => null); + (ref, index) => null, +); class Recipient extends ConsumerStatefulWidget { const Recipient({ @@ -233,7 +235,8 @@ class _RecipientState extends ConsumerState { semanticsLabel: "Clear Button. Clears The Address Field Input.", key: const Key( - "sendViewClearAddressFieldButtonKey"), + "sendViewClearAddressFieldButtonKey", + ), onTap: () { addressController.text = ""; @@ -249,7 +252,8 @@ class _RecipientState extends ConsumerState { semanticsLabel: "Paste Button. Pastes From Clipboard To Address Field Input.", key: const Key( - "sendViewPasteAddressFieldButtonKey"), + "sendViewPasteAddressFieldButtonKey", + ), onTap: () async { final ClipboardData? data = await ref .read(pClipboard) @@ -259,7 +263,9 @@ class _RecipientState extends ConsumerState { String content = data.text!.trim(); if (content.contains("\n")) { content = content.substring( - 0, content.indexOf("\n")); + 0, + content.indexOf("\n"), + ); } addressController.text = content.trim(); @@ -433,9 +439,10 @@ class _RecipientState extends ConsumerState { .watch(pAmountUnit(widget.coin)) .unitForCoin(widget.coin), style: STextStyles.smallMed14(context).copyWith( - color: Theme.of(context) - .extension()! - .accentColorDark), + color: Theme.of(context) + .extension()! + .accentColorDark, + ), ), ), ), diff --git a/lib/pages/send_view/frost_ms/send_steps/frost_send_step_1b.dart b/lib/pages/send_view/frost_ms/send_steps/frost_send_step_1b.dart index 9cac2cabc..058b980c5 100644 --- a/lib/pages/send_view/frost_ms/send_steps/frost_send_step_1b.dart +++ b/lib/pages/send_view/frost_ms/send_steps/frost_send_step_1b.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:isar/isar.dart'; + import '../../../../frost_route_generator.dart'; import '../../../../models/isar/models/isar_models.dart'; import '../../../../providers/db/main_db_provider.dart'; @@ -73,13 +74,14 @@ class _FrostSendStep1bState extends ConsumerState { .getUTXOs(wallet.walletId) .filter() .anyOf( - data.inputs, - (q, e) => q - .txidEqualTo(Format.uint8listToString(e.hash)) - .and() - .valueEqualTo(e.value) - .and() - .voutEqualTo(e.vout)) + data.inputs, + (q, e) => q + .txidEqualTo(Format.uint8listToString(e.hash)) + .and() + .valueEqualTo(e.value) + .and() + .voutEqualTo(e.vout), + ) .findAll(); // TODO add more data from 'data' and display to user ? diff --git a/lib/pages/send_view/frost_ms/send_steps/frost_send_step_2.dart b/lib/pages/send_view/frost_ms/send_steps/frost_send_step_2.dart index db198831f..724e62092 100644 --- a/lib/pages/send_view/frost_ms/send_steps/frost_send_step_2.dart +++ b/lib/pages/send_view/frost_ms/send_steps/frost_send_step_2.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../../../frost_route_generator.dart'; -import '../../../wallet_view/transaction_views/transaction_details_view.dart'; import '../../../../providers/frost_wallet/frost_wallet_providers.dart'; import '../../../../providers/global/wallets_provider.dart'; import '../../../../services/frost.dart'; @@ -17,6 +17,7 @@ import '../../../../widgets/detail_item.dart'; import '../../../../widgets/dialogs/frost/frost_error_dialog.dart'; import '../../../../widgets/rounded_white_container.dart'; import '../../../../widgets/textfields/frost_step_field.dart'; +import '../../../wallet_view/transaction_views/transaction_details_view.dart'; class FrostSendStep2 extends ConsumerStatefulWidget { const FrostSendStep2({super.key}); @@ -209,18 +210,20 @@ class _FrostSendStep2State extends ConsumerState { const SizedBox( height: 12, ), - Builder(builder: (context) { - final count = countPreprocesses(); - final colors = Theme.of(context).extension()!; - return DetailItem( - title: "Required preprocesses", - detail: "$count of $threshold", - horizontal: true, - overrideDetailTextColor: count >= threshold - ? colors.accentColorGreen - : colors.accentColorRed, - ); - }), + Builder( + builder: (context) { + final count = countPreprocesses(); + final colors = Theme.of(context).extension()!; + return DetailItem( + title: "Required preprocesses", + detail: "$count of $threshold", + horizontal: true, + overrideDetailTextColor: count >= threshold + ? colors.accentColorGreen + : colors.accentColorRed, + ); + }, + ), const SizedBox( height: 12, ), diff --git a/lib/pages/send_view/frost_ms/send_steps/frost_send_step_3.dart b/lib/pages/send_view/frost_ms/send_steps/frost_send_step_3.dart index 6b3c40362..de0ade7df 100644 --- a/lib/pages/send_view/frost_ms/send_steps/frost_send_step_3.dart +++ b/lib/pages/send_view/frost_ms/send_steps/frost_send_step_3.dart @@ -1,8 +1,8 @@ import 'package:coinlib_flutter/coinlib_flutter.dart' as cl; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../../../frost_route_generator.dart'; -import '../../../wallet_view/transaction_views/transaction_details_view.dart'; import '../../../../providers/frost_wallet/frost_wallet_providers.dart'; import '../../../../providers/global/wallets_provider.dart'; import '../../../../services/frost.dart'; @@ -18,6 +18,7 @@ import '../../../../widgets/detail_item.dart'; import '../../../../widgets/dialogs/frost/frost_error_dialog.dart'; import '../../../../widgets/frost_step_user_steps.dart'; import '../../../../widgets/textfields/frost_step_field.dart'; +import '../../../wallet_view/transaction_views/transaction_details_view.dart'; class FrostSendStep3 extends ConsumerStatefulWidget { const FrostSendStep3({super.key}); @@ -64,7 +65,8 @@ class _FrostSendStep3State extends ConsumerState { participantsWithoutMe = frostInfo.participants .toSet() .intersection( - ref.read(pFrostSelectParticipantsUnordered.state).state!.toSet()) + ref.read(pFrostSelectParticipantsUnordered.state).state!.toSet(), + ) .toList(); participantsWithoutMe.remove(myName); @@ -181,8 +183,9 @@ class _FrostSendStep3State extends ConsumerState { final List shares = []; for (final participant in participantsAll) { if (participantsWithoutMe.contains(participant)) { - shares.add(sharesCollected[ - participantsWithoutMe.indexOf(participant)]); + shares.add( + sharesCollected[participantsWithoutMe.indexOf(participant)], + ); } else { shares.add(""); } diff --git a/lib/pages/send_view/frost_ms/send_steps/frost_send_step_4.dart b/lib/pages/send_view/frost_ms/send_steps/frost_send_step_4.dart index 59596e846..a560d7151 100644 --- a/lib/pages/send_view/frost_ms/send_steps/frost_send_step_4.dart +++ b/lib/pages/send_view/frost_ms/send_steps/frost_send_step_4.dart @@ -2,9 +2,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; + import '../../../../frost_route_generator.dart'; -import '../../../wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart'; -import '../../../wallet_view/wallet_view.dart'; import '../../../../pages_desktop_specific/my_stack_view/my_stack_view.dart'; import '../../../../providers/frost_wallet/frost_wallet_providers.dart'; import '../../../../providers/global/wallets_provider.dart'; @@ -22,6 +21,8 @@ import '../../../../widgets/desktop/primary_button.dart'; import '../../../../widgets/detail_item.dart'; import '../../../../widgets/expandable.dart'; import '../../../../widgets/stack_dialog.dart'; +import '../../../wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart'; +import '../../../wallet_view/wallet_view.dart'; class FrostSendStep4 extends ConsumerStatefulWidget { const FrostSendStep4({super.key}); @@ -165,8 +166,9 @@ class _FrostSendStep4State extends ConsumerState { DetailItem( title: "Total", detail: ref.watch(pAmountFormatter(cryptoCurrency)).format( - ref.watch(pFrostTxData)!.fee! + - recipients.map((e) => e.amount).reduce((v, e) => v += e)), + ref.watch(pFrostTxData)!.fee! + + recipients.map((e) => e.amount).reduce((v, e) => v += e), + ), horizontal: true, ), const SizedBox( diff --git a/lib/pages/send_view/send_view.dart b/lib/pages/send_view/send_view.dart index da10718c9..623962845 100644 --- a/lib/pages/send_view/send_view.dart +++ b/lib/pages/send_view/send_view.dart @@ -18,15 +18,11 @@ import 'package:flutter/services.dart'; import 'package:flutter_native_splash/cli_commands.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:tuple/tuple.dart'; + import '../../models/isar/models/isar_models.dart'; import '../../models/paynym/paynym_account_lite.dart'; import '../../models/send_view_auto_fill_data.dart'; -import '../address_book_views/address_book_view.dart'; -import '../coin_control/coin_control_view.dart'; -import 'confirm_transaction_view.dart'; -import 'sub_widgets/building_transaction_dialog.dart'; -import 'sub_widgets/firo_balance_selection_sheet.dart'; -import 'sub_widgets/transaction_fee_selection_sheet.dart'; import '../../providers/providers.dart'; import '../../providers/ui/fee_rate_type_state_provider.dart'; import '../../providers/ui/preview_tx_button_state_provider.dart'; @@ -48,12 +44,6 @@ import '../../utilities/logger.dart'; import '../../utilities/prefs.dart'; import '../../utilities/text_styles.dart'; import '../../utilities/util.dart'; -import '../../wallets/crypto_currency/coins/epiccash.dart'; -import '../../wallets/crypto_currency/coins/ethereum.dart'; -import '../../wallets/crypto_currency/coins/firo.dart'; -import '../../wallets/crypto_currency/coins/monero.dart'; -import '../../wallets/crypto_currency/coins/stellar.dart'; -import '../../wallets/crypto_currency/coins/tezos.dart'; import '../../wallets/crypto_currency/crypto_currency.dart'; import '../../wallets/crypto_currency/intermediate/nano_currency.dart'; import '../../wallets/isar/providers/wallet_info_provider.dart'; @@ -75,7 +65,12 @@ import '../../widgets/rounded_white_container.dart'; import '../../widgets/stack_dialog.dart'; import '../../widgets/stack_text_field.dart'; import '../../widgets/textfield_icon_button.dart'; -import 'package:tuple/tuple.dart'; +import '../address_book_views/address_book_view.dart'; +import '../coin_control/coin_control_view.dart'; +import 'confirm_transaction_view.dart'; +import 'sub_widgets/building_transaction_dialog.dart'; +import 'sub_widgets/firo_balance_selection_sheet.dart'; +import 'sub_widgets/transaction_fee_selection_sheet.dart'; class SendView extends ConsumerStatefulWidget { const SendView({ @@ -1010,7 +1005,8 @@ class _SendViewState extends ConsumerState { if (ref.read(pSendAmount) == null) { setState(() { _calculateFeesFuture = calculateFees( - 0.toAmountAsRaw(fractionDigits: coin.fractionDigits)); + 0.toAmountAsRaw(fractionDigits: coin.fractionDigits), + ); }); } else { setState(() { diff --git a/lib/pages/send_view/sub_widgets/firo_balance_selection_sheet.dart b/lib/pages/send_view/sub_widgets/firo_balance_selection_sheet.dart index 3a890718b..912fad545 100644 --- a/lib/pages/send_view/sub_widgets/firo_balance_selection_sheet.dart +++ b/lib/pages/send_view/sub_widgets/firo_balance_selection_sheet.dart @@ -10,6 +10,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../../providers/providers.dart'; import '../../../providers/wallet/public_private_balance_state_provider.dart'; import '../../../themes/stack_colors.dart'; @@ -20,9 +21,9 @@ import '../../../wallets/wallet/impl/firo_wallet.dart'; class FiroBalanceSelectionSheet extends ConsumerStatefulWidget { const FiroBalanceSelectionSheet({ - Key? key, + super.key, required this.walletId, - }) : super(key: key); + }); final String walletId; @@ -125,12 +126,14 @@ class _FiroBalanceSelectionSheetState value: FiroType.spark, groupValue: ref .watch( - publicPrivateBalanceStateProvider.state) + publicPrivateBalanceStateProvider.state, + ) .state, onChanged: (x) { ref - .read(publicPrivateBalanceStateProvider - .state) + .read( + publicPrivateBalanceStateProvider.state, + ) .state = FiroType.spark; Navigator.of(context).pop(); @@ -168,7 +171,7 @@ class _FiroBalanceSelectionSheetState ), // ], // ), - ) + ), ], ), ), @@ -209,13 +212,16 @@ class _FiroBalanceSelectionSheetState .radioButtonIconEnabled, value: FiroType.lelantus, groupValue: ref - .watch(publicPrivateBalanceStateProvider - .state) + .watch( + publicPrivateBalanceStateProvider.state, + ) .state, onChanged: (x) { ref - .read(publicPrivateBalanceStateProvider - .state) + .read( + publicPrivateBalanceStateProvider + .state, + ) .state = FiroType.lelantus; Navigator.of(context).pop(); @@ -253,7 +259,7 @@ class _FiroBalanceSelectionSheetState ), // ], // ), - ) + ), ], ), ), @@ -288,12 +294,14 @@ class _FiroBalanceSelectionSheetState value: FiroType.public, groupValue: ref .watch( - publicPrivateBalanceStateProvider.state) + publicPrivateBalanceStateProvider.state, + ) .state, onChanged: (x) { ref - .read(publicPrivateBalanceStateProvider - .state) + .read( + publicPrivateBalanceStateProvider.state, + ) .state = FiroType.public; Navigator.of(context).pop(); }, diff --git a/lib/pages/send_view/sub_widgets/sending_transaction_dialog.dart b/lib/pages/send_view/sub_widgets/sending_transaction_dialog.dart index bd55b42b1..710cbfa1c 100644 --- a/lib/pages/send_view/sub_widgets/sending_transaction_dialog.dart +++ b/lib/pages/send_view/sub_widgets/sending_transaction_dialog.dart @@ -129,11 +129,11 @@ class ProgressAndSuccessController { class ProgressAndSuccess extends StatefulWidget { const ProgressAndSuccess({ - Key? key, + super.key, this.height = 24, this.width = 24, required this.controller, - }) : super(key: key); + }); final double height; final double width; diff --git a/lib/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart b/lib/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart index 1202138dd..a70098c03 100644 --- a/lib/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart +++ b/lib/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart @@ -22,10 +22,6 @@ import '../../../utilities/constants.dart'; import '../../../utilities/enums/fee_rate_type_enum.dart'; import '../../../utilities/logger.dart'; import '../../../utilities/text_styles.dart'; -import '../../../wallets/crypto_currency/coins/ethereum.dart'; -import '../../../wallets/crypto_currency/coins/firo.dart'; -import '../../../wallets/crypto_currency/coins/monero.dart'; -import '../../../wallets/crypto_currency/coins/wownero.dart'; import '../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../wallets/isar/providers/eth/current_token_wallet_provider.dart'; import '../../../wallets/isar/providers/wallet_info_provider.dart'; diff --git a/lib/pages/send_view/token_send_view.dart b/lib/pages/send_view/token_send_view.dart index 40d73c917..61ae1d1a9 100644 --- a/lib/pages/send_view/token_send_view.dart +++ b/lib/pages/send_view/token_send_view.dart @@ -39,7 +39,6 @@ import '../../utilities/logger.dart'; import '../../utilities/prefs.dart'; import '../../utilities/text_styles.dart'; import '../../utilities/util.dart'; -import '../../wallets/crypto_currency/coins/epiccash.dart'; import '../../wallets/crypto_currency/crypto_currency.dart'; import '../../wallets/isar/providers/eth/current_token_wallet_provider.dart'; import '../../wallets/isar/providers/eth/token_balance_provider.dart'; diff --git a/lib/pages/settings_views/global_settings_view/advanced_views/advanced_settings_view.dart b/lib/pages/settings_views/global_settings_view/advanced_views/advanced_settings_view.dart index 7de750ae3..3d5413798 100644 --- a/lib/pages/settings_views/global_settings_view/advanced_views/advanced_settings_view.dart +++ b/lib/pages/settings_views/global_settings_view/advanced_views/advanced_settings_view.dart @@ -10,10 +10,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'debug_view.dart'; -import 'manage_coin_units/manage_coin_units_view.dart'; -import 'manage_explorer_view.dart'; -import '../../../stack_privacy_calls.dart'; +import 'package:tuple/tuple.dart'; + import '../../../../providers/global/prefs_provider.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/constants.dart'; @@ -23,12 +21,15 @@ import '../../../../widgets/choose_coin_view.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../../widgets/custom_buttons/draggable_switch_button.dart'; import '../../../../widgets/rounded_white_container.dart'; -import 'package:tuple/tuple.dart'; +import '../../../stack_privacy_calls.dart'; +import 'debug_view.dart'; +import 'manage_coin_units/manage_coin_units_view.dart'; +import 'manage_explorer_view.dart'; class AdvancedSettingsView extends StatelessWidget { const AdvancedSettingsView({ - Key? key, - }) : super(key: key); + super.key, + }); static const String routeName = "/advancedSettings"; @@ -116,7 +117,8 @@ class AdvancedSettingsView extends StatelessWidget { child: DraggableSwitchButton( isOn: ref.watch( prefsChangeNotifierProvider.select( - (value) => value.showTestNetCoins), + (value) => value.showTestNetCoins, + ), ), onValueChanged: (newValue) { ref @@ -163,7 +165,8 @@ class AdvancedSettingsView extends StatelessWidget { child: DraggableSwitchButton( isOn: ref.watch( prefsChangeNotifierProvider.select( - (value) => value.enableCoinControl), + (value) => value.enableCoinControl, + ), ), onValueChanged: (newValue) { ref @@ -224,7 +227,7 @@ class AdvancedSettingsView extends StatelessWidget { : "\nIncognito", style: STextStyles.label(context) .copyWith(fontSize: 15.0), - ) + ), ], ), ), @@ -249,11 +252,14 @@ class AdvancedSettingsView extends StatelessWidget { ), ), onPressed: () { - Navigator.of(context).pushNamed(ChooseCoinView.routeName, - arguments: const Tuple3( - "Manage block explorers", - "block explorer", - ManageExplorerView.routeName)); + Navigator.of(context).pushNamed( + ChooseCoinView.routeName, + arguments: const Tuple3( + "Manage block explorers", + "block explorer", + ManageExplorerView.routeName, + ), + ); }, child: Padding( padding: const EdgeInsets.symmetric( diff --git a/lib/pages/settings_views/global_settings_view/advanced_views/debug_view.dart b/lib/pages/settings_views/global_settings_view/advanced_views/debug_view.dart index 8bbaf9afd..518cf32da 100644 --- a/lib/pages/settings_views/global_settings_view/advanced_views/debug_view.dart +++ b/lib/pages/settings_views/global_settings_view/advanced_views/debug_view.dart @@ -23,9 +23,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:lelantus/git_versions.dart' as FIRO_VERSIONS; import 'package:package_info_plus/package_info_plus.dart'; + import '../../../../models/isar/models/log.dart'; import '../../../../notifications/show_flush_bar.dart'; -import '../stack_backup_views/helpers/swb_file_system.dart'; import '../../../../providers/global/debug_service_provider.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/assets.dart'; @@ -44,9 +44,10 @@ import '../../../../widgets/rounded_container.dart'; import '../../../../widgets/stack_dialog.dart'; import '../../../../widgets/stack_text_field.dart'; import '../../../../widgets/textfield_icon_button.dart'; +import '../stack_backup_views/helpers/swb_file_system.dart'; class DebugView extends ConsumerStatefulWidget { - const DebugView({Key? key}) : super(key: key); + const DebugView({super.key}); static const String routeName = "/debug"; @@ -68,7 +69,8 @@ class _DebugViewState extends ConsumerState { } return unfiltered .where( - (e) => (e.toString().toLowerCase().contains(filter.toLowerCase()))) + (e) => (e.toString().toLowerCase().contains(filter.toLowerCase())), + ) .toList(); } @@ -174,19 +176,21 @@ class _DebugViewState extends ConsumerState { Navigator.of(context).pop(); bool shouldPop = false; - unawaited(showDialog( - barrierDismissible: false, - context: context, - builder: (_) => WillPopScope( - onWillPop: () async { - return shouldPop; - }, - child: const CustomLoadingOverlay( - message: "Deleting logs...", - eventBus: null, + unawaited( + showDialog( + barrierDismissible: false, + context: context, + builder: (_) => WillPopScope( + onWillPop: () async { + return shouldPop; + }, + child: const CustomLoadingOverlay( + message: "Deleting logs...", + eventBus: null, + ), ), ), - )); + ); await ref .read(debugServiceProvider) @@ -196,10 +200,13 @@ class _DebugViewState extends ConsumerState { if (mounted) { Navigator.pop(context); - unawaited(showFloatingFlushBar( + unawaited( + showFloatingFlushBar( type: FlushBarType.info, context: context, - message: 'Logs cleared!')); + message: 'Logs cleared!', + ), + ); setState(() {}); } @@ -301,32 +308,34 @@ class _DebugViewState extends ConsumerState { final signature = packageInfo.buildSignature; final appName = packageInfo.appName; - String firoCommit = + final String firoCommit = FIRO_VERSIONS.getPluginVersion(); - String epicCashCommit = + final String epicCashCommit = EPIC_VERSIONS.getPluginVersion(); - String moneroCommit = + final String moneroCommit = MONERO_VERSIONS.getPluginVersion(); - DeviceInfoPlugin deviceInfoPlugin = + final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin(); final deviceInfo = await deviceInfoPlugin.deviceInfo; - var deviceInfoMap = deviceInfo.toMap(); + final deviceInfoMap = deviceInfo.toMap(); deviceInfoMap.remove("systemFeatures"); final logs = filtered( - ref.watch(debugServiceProvider - .select((value) => - value.recentLogs)), - _searchTerm) - .reversed - .toList(growable: false); - List errorLogs = []; - for (var log in logs) { + ref.watch( + debugServiceProvider.select( + (value) => value.recentLogs, + ), + ), + _searchTerm, + ).reversed.toList(growable: false); + final List errorLogs = []; + for (final log in logs) { if (log.logLevel == LogLevel.Error || log.logLevel == LogLevel.Fatal) { errorLogs.add( - "${log.logLevel}: ${log.message}"); + "${log.logLevel}: ${log.message}", + ); } } @@ -342,14 +351,16 @@ class _DebugViewState extends ConsumerState { "errorLogs": errorLogs, }; Logging.instance.log( - json.encode(finalDebugMap), - level: LogLevel.Info, - printFullLength: true); + json.encode(finalDebugMap), + level: LogLevel.Info, + printFullLength: true, + ); const ClipboardInterface clipboard = ClipboardWrapper(); await clipboard.setData( ClipboardData( - text: json.encode(finalDebugMap)), + text: json.encode(finalDebugMap), + ), ); } catch (e, s) { Logging.instance @@ -400,19 +411,22 @@ class _DebugViewState extends ConsumerState { if (path != null) { final eventBus = EventBus(); bool shouldPop = false; - unawaited(showDialog( - barrierDismissible: false, - context: context, - builder: (_) => WillPopScope( - onWillPop: () async { - return shouldPop; - }, - child: CustomLoadingOverlay( - message: "Generating Stack logs file", - eventBus: eventBus, + unawaited( + showDialog( + barrierDismissible: false, + context: context, + builder: (_) => WillPopScope( + onWillPop: () async { + return shouldPop; + }, + child: CustomLoadingOverlay( + message: + "Generating Stack logs file", + eventBus: eventBus, + ), ), ), - )); + ); bool logsSaved = true; String? filename; @@ -459,7 +473,7 @@ class _DebugViewState extends ConsumerState { }, ), ], - ) + ), ], ), ), @@ -470,11 +484,11 @@ class _DebugViewState extends ConsumerState { body: Builder( builder: (context) { final logs = filtered( - ref.watch(debugServiceProvider - .select((value) => value.recentLogs)), - _searchTerm) - .reversed - .toList(growable: false); + ref.watch( + debugServiceProvider.select((value) => value.recentLogs), + ), + _searchTerm, + ).reversed.toList(growable: false); return CustomScrollView( reverse: true, @@ -493,7 +507,8 @@ class _DebugViewState extends ConsumerState { return Container( key: Key( - "log_${log.id}_${log.timestampInMillisUTC}"), + "log_${log.id}_${log.timestampInMillisUTC}", + ), decoration: BoxDecoration( color: Theme.of(context) .extension()! diff --git a/lib/pages/settings_views/global_settings_view/advanced_views/manage_coin_units/edit_coin_units_view.dart b/lib/pages/settings_views/global_settings_view/advanced_views/manage_coin_units/edit_coin_units_view.dart index 65cf6cbd6..08d801cc4 100644 --- a/lib/pages/settings_views/global_settings_view/advanced_views/manage_coin_units/edit_coin_units_view.dart +++ b/lib/pages/settings_views/global_settings_view/advanced_views/manage_coin_units/edit_coin_units_view.dart @@ -257,7 +257,7 @@ class _EditCoinUnitsViewState extends ConsumerState { ), ), ), - ) + ), ], ), SizedBox( diff --git a/lib/pages/settings_views/global_settings_view/advanced_views/manage_explorer_view.dart b/lib/pages/settings_views/global_settings_view/advanced_views/manage_explorer_view.dart index b688a1f6d..a50568e52 100644 --- a/lib/pages/settings_views/global_settings_view/advanced_views/manage_explorer_view.dart +++ b/lib/pages/settings_views/global_settings_view/advanced_views/manage_explorer_view.dart @@ -10,6 +10,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../../../themes/stack_colors.dart'; import '../../../../utilities/block_explorers.dart'; import '../../../../utilities/text_styles.dart'; @@ -39,10 +40,10 @@ class _ManageExplorerViewState extends ConsumerState { void initState() { super.initState(); textEditingController = TextEditingController( - text: - getBlockExplorerTransactionUrlFor(coin: widget.coin, txid: "[TXID]") - .toString() - .replaceAll("%5BTXID%5D", "[TXID]")); + text: getBlockExplorerTransactionUrlFor(coin: widget.coin, txid: "[TXID]") + .toString() + .replaceAll("%5BTXID%5D", "[TXID]"), + ); } @override @@ -72,32 +73,33 @@ class _ManageExplorerViewState extends ConsumerState { child: Column( children: [ Expanded( - child: Column( - children: [ - TextField( - controller: textEditingController, - decoration: const InputDecoration( - border: OutlineInputBorder(), - ), - ), - const SizedBox( - height: 8, - ), - RoundedWhiteContainer( - child: Center( - child: Text( - "Edit your block explorer above. Keep in mind that " - "every block explorer has a slightly different URL " - "scheme.\n\nPaste in your block explorer of choice," - " then edit in [TXID] where the transaction ID " - "should go, and Stack Wallet will auto fill the " - "transaction ID in that place of URL.", - style: STextStyles.itemSubtitle(context), + child: Column( + children: [ + TextField( + controller: textEditingController, + decoration: const InputDecoration( + border: OutlineInputBorder(), ), ), - ), - ], - )), + const SizedBox( + height: 8, + ), + RoundedWhiteContainer( + child: Center( + child: Text( + "Edit your block explorer above. Keep in mind that " + "every block explorer has a slightly different URL " + "scheme.\n\nPaste in your block explorer of choice," + " then edit in [TXID] where the transaction ID " + "should go, and Stack Wallet will auto fill the " + "transaction ID in that place of URL.", + style: STextStyles.itemSubtitle(context), + ), + ), + ), + ], + ), + ), Align( alignment: Alignment.bottomCenter, child: ConstrainedBox( @@ -129,7 +131,7 @@ class _ManageExplorerViewState extends ConsumerState { ), ), ), - ) + ), ], ), ), diff --git a/lib/pages/settings_views/global_settings_view/appearance_settings/appearance_settings_view.dart b/lib/pages/settings_views/global_settings_view/appearance_settings/appearance_settings_view.dart index 6981508e6..fe17f8ffc 100644 --- a/lib/pages/settings_views/global_settings_view/appearance_settings/appearance_settings_view.dart +++ b/lib/pages/settings_views/global_settings_view/appearance_settings/appearance_settings_view.dart @@ -10,8 +10,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'manage_themes.dart'; -import 'sub_widgets/theme_options_widget.dart'; + import '../../../../providers/providers.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/constants.dart'; @@ -21,9 +20,11 @@ import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../../widgets/custom_buttons/draggable_switch_button.dart'; import '../../../../widgets/desktop/secondary_button.dart'; import '../../../../widgets/rounded_white_container.dart'; +import 'manage_themes.dart'; +import 'sub_widgets/theme_options_widget.dart'; class AppearanceSettingsView extends ConsumerWidget { - const AppearanceSettingsView({Key? key}) : super(key: key); + const AppearanceSettingsView({super.key}); static const String routeName = "/appearanceSettings"; @@ -89,17 +90,19 @@ class AppearanceSettingsView extends ConsumerWidget { child: DraggableSwitchButton( isOn: ref.watch( prefsChangeNotifierProvider.select( - (value) => - value.showFavoriteWallets), + (value) => + value.showFavoriteWallets, + ), ), onValueChanged: (newValue) { ref .read( - prefsChangeNotifierProvider) + prefsChangeNotifierProvider, + ) .showFavoriteWallets = newValue; }, ), - ) + ), ], ), ), @@ -145,7 +148,7 @@ class AppearanceSettingsView extends ConsumerWidget { ManageThemesView.routeName, ); }, - ) + ), ], ), ), diff --git a/lib/pages/settings_views/global_settings_view/appearance_settings/manage_themes.dart b/lib/pages/settings_views/global_settings_view/appearance_settings/manage_themes.dart index bea81abba..4744d857f 100644 --- a/lib/pages/settings_views/global_settings_view/appearance_settings/manage_themes.dart +++ b/lib/pages/settings_views/global_settings_view/appearance_settings/manage_themes.dart @@ -33,7 +33,7 @@ import '../../../../widgets/rounded_white_container.dart'; import 'package:tuple/tuple.dart'; class ManageThemesView extends ConsumerStatefulWidget { - const ManageThemesView({Key? key}) : super(key: key); + const ManageThemesView({super.key}); static const String routeName = "/manageThemes"; @@ -220,9 +220,9 @@ class _ManageThemesViewState extends ConsumerState { class IncognitoInstalledThemes extends ConsumerStatefulWidget { const IncognitoInstalledThemes({ - Key? key, + super.key, required this.cardWidth, - }) : super(key: key); + }); final double cardWidth; diff --git a/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/install_theme_from_file_dialog.dart b/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/install_theme_from_file_dialog.dart index 1574c3cfe..b95384ba1 100644 --- a/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/install_theme_from_file_dialog.dart +++ b/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/install_theme_from_file_dialog.dart @@ -27,7 +27,7 @@ import '../../../../../widgets/desktop/secondary_button.dart'; import '../../../../../widgets/stack_dialog.dart'; class InstallThemeFromFileDialog extends ConsumerStatefulWidget { - const InstallThemeFromFileDialog({Key? key}) : super(key: key); + const InstallThemeFromFileDialog({super.key}); @override ConsumerState createState() => @@ -189,7 +189,7 @@ class _InstallThemeFromFileDialogState ), ), ], - ) + ), ], ), ); diff --git a/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/stack_theme_card.dart b/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/stack_theme_card.dart index 5b9c2fe90..3ab865653 100644 --- a/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/stack_theme_card.dart +++ b/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/stack_theme_card.dart @@ -15,6 +15,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:isar/isar.dart'; + import '../../../../../models/isar/stack_theme.dart'; import '../../../../../notifications/show_flush_bar.dart'; import '../../../../../providers/db/main_db_provider.dart'; @@ -34,9 +35,9 @@ import '../../../../../widgets/stack_dialog.dart'; class StackThemeCard extends ConsumerStatefulWidget { const StackThemeCard({ - Key? key, + super.key, required this.data, - }) : super(key: key); + }); final StackThemeMetaData data; @@ -130,7 +131,8 @@ class _StackThemeCardState extends ConsumerState { final themeDir = Directory("${themesDir.path}/${widget.data.id}"); int bytes = 0; if (await themeDir.exists()) { - await for (FileSystemEntity entity in themeDir.list(recursive: true)) { + await for (final FileSystemEntity entity + in themeDir.list(recursive: true)) { if (entity is File) { bytes += await entity.length(); } @@ -142,13 +144,13 @@ class _StackThemeCardState extends ConsumerState { if (bytes < 1024) { return '$bytes B'; } else if (bytes < 1048576) { - double kbSize = bytes / 1024; + final double kbSize = bytes / 1024; return '${kbSize.toStringAsFixed(2)} KB'; } else if (bytes < 1073741824) { - double mbSize = bytes / 1048576; + final double mbSize = bytes / 1048576; return '${mbSize.toStringAsFixed(2)} MB'; } else { - double gbSize = bytes / 1073741824; + final double gbSize = bytes / 1073741824; return '${gbSize.toStringAsFixed(2)} GB'; } } diff --git a/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/theme_option.dart b/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/theme_option.dart index 57cf428d5..185163996 100644 --- a/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/theme_option.dart +++ b/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/theme_option.dart @@ -15,13 +15,13 @@ import '../../../../../utilities/text_styles.dart'; class ThemeOption extends StatelessWidget { const ThemeOption({ - Key? key, + super.key, required this.onPressed, required this.onChanged, required this.label, required this.value, required this.groupValue, - }) : super(key: key); + }); final VoidCallback onPressed; final void Function(Object?) onChanged; diff --git a/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/theme_options_widget.dart b/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/theme_options_widget.dart index 8f3a78678..9435385fc 100644 --- a/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/theme_options_widget.dart +++ b/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/theme_options_widget.dart @@ -27,7 +27,7 @@ import '../../../../../widgets/custom_buttons/blue_text_button.dart'; import 'package:tuple/tuple.dart'; class ThemeOptionsWidget extends ConsumerStatefulWidget { - const ThemeOptionsWidget({Key? key}) : super(key: key); + const ThemeOptionsWidget({super.key}); @override ConsumerState createState() => _ThemeOptionsWidgetState(); diff --git a/lib/pages/settings_views/global_settings_view/appearance_settings/system_brightness_theme_selection_view.dart b/lib/pages/settings_views/global_settings_view/appearance_settings/system_brightness_theme_selection_view.dart index 85154430e..becef5fb6 100644 --- a/lib/pages/settings_views/global_settings_view/appearance_settings/system_brightness_theme_selection_view.dart +++ b/lib/pages/settings_views/global_settings_view/appearance_settings/system_brightness_theme_selection_view.dart @@ -10,7 +10,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'sub_widgets/theme_option.dart'; +import 'package:tuple/tuple.dart'; + import '../../../../providers/global/prefs_provider.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../themes/theme_providers.dart'; @@ -19,10 +20,10 @@ import '../../../../utilities/text_styles.dart'; import '../../../../widgets/background.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../../widgets/rounded_white_container.dart'; -import 'package:tuple/tuple.dart'; +import 'sub_widgets/theme_option.dart'; class SystemBrightnessThemeSelectionView extends ConsumerStatefulWidget { - const SystemBrightnessThemeSelectionView({Key? key}) : super(key: key); + const SystemBrightnessThemeSelectionView({super.key}); static const String routeName = "/chooseSystemTheme"; @@ -153,7 +154,8 @@ class _SystemBrightnessThemeSelectionViewState if (newValue == value && ref .read( - prefsChangeNotifierProvider) + prefsChangeNotifierProvider, + ) .systemBrightnessLightThemeId != value) { _setTheme( @@ -167,9 +169,11 @@ class _SystemBrightnessThemeSelectionViewState value: installedThemeIdNames[i ~/ 2].item1, groupValue: ref.watch( - prefsChangeNotifierProvider.select( - (value) => value - .systemBrightnessLightThemeId)), + prefsChangeNotifierProvider.select( + (value) => value + .systemBrightnessLightThemeId, + ), + ), ), ], ), @@ -215,7 +219,8 @@ class _SystemBrightnessThemeSelectionViewState if (newValue == value && ref .read( - prefsChangeNotifierProvider) + prefsChangeNotifierProvider, + ) .systemBrightnessDarkThemeId != value) { _setTheme( @@ -229,9 +234,11 @@ class _SystemBrightnessThemeSelectionViewState value: installedThemeIdNames[i ~/ 2].item1, groupValue: ref.watch( - prefsChangeNotifierProvider.select( - (value) => value - .systemBrightnessDarkThemeId)), + prefsChangeNotifierProvider.select( + (value) => value + .systemBrightnessDarkThemeId, + ), + ), ), ], ), diff --git a/lib/pages/settings_views/global_settings_view/currency_view.dart b/lib/pages/settings_views/global_settings_view/currency_view.dart index c9b618b1e..18c8a6a12 100644 --- a/lib/pages/settings_views/global_settings_view/currency_view.dart +++ b/lib/pages/settings_views/global_settings_view/currency_view.dart @@ -11,6 +11,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../../providers/global/base_currencies_provider.dart'; import '../../../providers/providers.dart'; import '../../../themes/stack_colors.dart'; @@ -30,7 +31,7 @@ import '../../../widgets/stack_text_field.dart'; import '../../../widgets/textfield_icon_button.dart'; class BaseCurrencySettingsView extends ConsumerStatefulWidget { - const BaseCurrencySettingsView({Key? key}) : super(key: key); + const BaseCurrencySettingsView({super.key}); static const String routeName = "/baseCurrencySettings"; @@ -155,7 +156,8 @@ class _CurrencyViewState extends ConsumerState { if (FocusScope.of(context).hasFocus) { FocusScope.of(context).unfocus(); await Future.delayed( - const Duration(milliseconds: 75)); + const Duration(milliseconds: 75), + ); } if (mounted) { Navigator.of(context).pop(); @@ -329,7 +331,8 @@ class _CurrencyViewState extends ConsumerState { child: Padding( padding: const EdgeInsets.all(4), key: Key( - "currencySelect_${currenciesWithoutSelected[index]}"), + "currencySelect_${currenciesWithoutSelected[index]}", + ), child: RoundedContainer( padding: const EdgeInsets.all(0), color: currenciesWithoutSelected[index] == current @@ -385,29 +388,34 @@ class _CurrencyViewState extends ConsumerState { index] == current) ? const Key( - "selectedCurrencySettingsCurrencyText") + "selectedCurrencySettingsCurrencyText", + ) : null, style: STextStyles.largeMedium14( - context), + context, + ), ), const SizedBox( height: 2, ), Text( - ref.watch(baseCurrenciesProvider - .select((value) => - value.map))[ - currenciesWithoutSelected[ - index]] ?? + ref.watch( + baseCurrenciesProvider.select( + (value) => value.map, + ), + )[currenciesWithoutSelected[ + index]] ?? "", key: (currenciesWithoutSelected[ index] == current) ? const Key( - "selectedCurrencySettingsCurrencyTextDescription") + "selectedCurrencySettingsCurrencyTextDescription", + ) : null, style: STextStyles.itemSubtitle( - context), + context, + ), ), ], ), diff --git a/lib/pages/settings_views/global_settings_view/delete_account_view.dart b/lib/pages/settings_views/global_settings_view/delete_account_view.dart index 4294592a3..743488d4c 100644 --- a/lib/pages/settings_views/global_settings_view/delete_account_view.dart +++ b/lib/pages/settings_views/global_settings_view/delete_account_view.dart @@ -9,8 +9,8 @@ */ import 'package:flutter/material.dart'; + import '../../../db/hive/db.dart'; -import '../../intro_view.dart'; import '../../../themes/stack_colors.dart'; import '../../../utilities/text_styles.dart'; import '../../../utilities/util.dart'; @@ -20,9 +20,10 @@ import '../../../widgets/desktop/desktop_scaffold.dart'; import '../../../widgets/desktop/primary_button.dart'; import '../../../widgets/rounded_white_container.dart'; import '../../../widgets/stack_dialog.dart'; +import '../../intro_view.dart'; class DeleteAccountView extends StatefulWidget { - const DeleteAccountView({Key? key}) : super(key: key); + const DeleteAccountView({super.key}); static const String routeName = "/deleteAccountView"; @@ -51,9 +52,9 @@ class _DeleteAccountViewState extends State { child: Text( "Cancel", style: STextStyles.button(context).copyWith( - color: Theme.of(context) - .extension()! - .accentColorDark), + color: + Theme.of(context).extension()!.accentColorDark, + ), ), ), rightButton: TextButton( @@ -91,7 +92,8 @@ class _DeleteAccountViewState extends State { if (FocusScope.of(context).hasFocus) { FocusScope.of(context).unfocus(); await Future.delayed( - const Duration(milliseconds: 75)); + const Duration(milliseconds: 75), + ); } if (mounted) { Navigator.of(context).pop(); @@ -117,7 +119,7 @@ class _DeleteAccountViewState extends State { PrimaryButton( label: "Confirm", onPressed: onConfirmDeleteAccount, - ) + ), ], ), ), diff --git a/lib/pages/settings_views/global_settings_view/global_settings_view.dart b/lib/pages/settings_views/global_settings_view/global_settings_view.dart index c15d0f8d9..400bff87d 100644 --- a/lib/pages/settings_views/global_settings_view/global_settings_view.dart +++ b/lib/pages/settings_views/global_settings_view/global_settings_view.dart @@ -11,8 +11,17 @@ import 'dart:io'; import 'package:flutter/material.dart'; + +import '../../../route_generator.dart'; +import '../../../themes/stack_colors.dart'; +import '../../../utilities/assets.dart'; +import '../../../utilities/text_styles.dart'; +import '../../../widgets/background.dart'; +import '../../../widgets/custom_buttons/app_bar_icon_button.dart'; +import '../../../widgets/rounded_white_container.dart'; import '../../address_book_views/address_book_view.dart'; import '../../pinpad_views/lock_screen_view.dart'; +import '../sub_widgets/settings_list_button.dart'; import 'about_view.dart'; import 'advanced_views/advanced_settings_view.dart'; import 'appearance_settings/appearance_settings_view.dart'; @@ -26,19 +35,11 @@ import 'startup_preferences/startup_preferences_view.dart'; import 'support_view.dart'; import 'syncing_preferences_views/syncing_preferences_view.dart'; import 'tor_settings/tor_settings_view.dart'; -import '../sub_widgets/settings_list_button.dart'; -import '../../../route_generator.dart'; -import '../../../themes/stack_colors.dart'; -import '../../../utilities/assets.dart'; -import '../../../utilities/text_styles.dart'; -import '../../../widgets/background.dart'; -import '../../../widgets/custom_buttons/app_bar_icon_button.dart'; -import '../../../widgets/rounded_white_container.dart'; class GlobalSettingsView extends StatelessWidget { const GlobalSettingsView({ - Key? key, - }) : super(key: key); + super.key, + }); static const String routeName = "/globalSettings"; @@ -116,7 +117,8 @@ class GlobalSettingsView extends StatelessWidget { "Stack backup", ), settings: const RouteSettings( - name: "/swblockscreen"), + name: "/swblockscreen", + ), ), ); }, @@ -142,7 +144,8 @@ class GlobalSettingsView extends StatelessWidget { title: "Currency", onPressed: () { Navigator.of(context).pushNamed( - BaseCurrencySettingsView.routeName); + BaseCurrencySettingsView.routeName, + ); }, ), const SizedBox( @@ -154,7 +157,8 @@ class GlobalSettingsView extends StatelessWidget { title: "Language", onPressed: () { Navigator.of(context).pushNamed( - LanguageSettingsView.routeName); + LanguageSettingsView.routeName, + ); }, ), const SizedBox( @@ -190,7 +194,8 @@ class GlobalSettingsView extends StatelessWidget { title: "Syncing preferences", onPressed: () { Navigator.of(context).pushNamed( - SyncingPreferencesView.routeName); + SyncingPreferencesView.routeName, + ); }, ), const SizedBox( @@ -202,7 +207,8 @@ class GlobalSettingsView extends StatelessWidget { title: "Startup", onPressed: () { Navigator.of(context).pushNamed( - StartupPreferencesView.routeName); + StartupPreferencesView.routeName, + ); }, ), const SizedBox( @@ -229,7 +235,8 @@ class GlobalSettingsView extends StatelessWidget { title: "Delete account", onPressed: () async { await Navigator.of(context).pushNamed( - DeleteAccountView.routeName); + DeleteAccountView.routeName, + ); }, ), const SizedBox( @@ -253,7 +260,8 @@ class GlobalSettingsView extends StatelessWidget { title: "Advanced", onPressed: () { Navigator.of(context).pushNamed( - AdvancedSettingsView.routeName); + AdvancedSettingsView.routeName, + ); }, ), const SizedBox( diff --git a/lib/pages/settings_views/global_settings_view/hidden_settings.dart b/lib/pages/settings_views/global_settings_view/hidden_settings.dart index c1370624e..973dab913 100644 --- a/lib/pages/settings_views/global_settings_view/hidden_settings.dart +++ b/lib/pages/settings_views/global_settings_view/hidden_settings.dart @@ -20,7 +20,6 @@ import '../../../themes/stack_colors.dart'; import '../../../utilities/assets.dart'; import '../../../utilities/constants.dart'; import '../../../utilities/text_styles.dart'; -import '../../../wallets/crypto_currency/coins/stellar.dart'; import '../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../widgets/background.dart'; import '../../../widgets/custom_buttons/app_bar_icon_button.dart'; diff --git a/lib/pages/settings_views/global_settings_view/language_view.dart b/lib/pages/settings_views/global_settings_view/language_view.dart index 685736409..8f1ca3012 100644 --- a/lib/pages/settings_views/global_settings_view/language_view.dart +++ b/lib/pages/settings_views/global_settings_view/language_view.dart @@ -11,6 +11,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../../providers/providers.dart'; import '../../../themes/stack_colors.dart'; import '../../../utilities/assets.dart'; @@ -26,7 +27,7 @@ import '../../../widgets/stack_text_field.dart'; import '../../../widgets/textfield_icon_button.dart'; class LanguageSettingsView extends ConsumerStatefulWidget { - const LanguageSettingsView({Key? key}) : super(key: key); + const LanguageSettingsView({super.key}); static const String routeName = "/languageSettings"; @@ -82,7 +83,8 @@ class _LanguageViewState extends ConsumerState { List _filtered() { return listWithoutSelected .where( - (element) => element.toLowerCase().contains(filter.toLowerCase())) + (element) => element.toLowerCase().contains(filter.toLowerCase()), + ) .toList(); } @@ -225,7 +227,8 @@ class _LanguageViewState extends ConsumerState { child: Padding( padding: const EdgeInsets.all(4), key: Key( - "languageSelect_${listWithoutSelected[index]}"), + "languageSelect_${listWithoutSelected[index]}", + ), child: RoundedContainer( padding: const EdgeInsets.all(0), color: index == 0 @@ -275,10 +278,12 @@ class _LanguageViewState extends ConsumerState { listWithoutSelected[index], key: (index == 0) ? const Key( - "selectedLanguageSettingsLanguageText") + "selectedLanguageSettingsLanguageText", + ) : null, style: STextStyles.largeMedium14( - context), + context, + ), ), const SizedBox( height: 2, @@ -287,10 +292,12 @@ class _LanguageViewState extends ConsumerState { listWithoutSelected[index], key: (index == 0) ? const Key( - "selectedLanguageSettingsLanguageTextDescription") + "selectedLanguageSettingsLanguageTextDescription", + ) : null, style: STextStyles.itemSubtitle( - context), + context, + ), ), ], ), diff --git a/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart b/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart index e5a1ad049..4ccaa47bf 100644 --- a/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart +++ b/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart @@ -14,6 +14,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:uuid/uuid.dart'; + import '../../../../models/node_model.dart'; import '../../../../notifications/show_flush_bar.dart'; import '../../../../providers/global/secure_store_provider.dart'; @@ -25,10 +27,6 @@ import '../../../../utilities/flutter_secure_storage_interface.dart'; import '../../../../utilities/test_node_connection.dart'; import '../../../../utilities/text_styles.dart'; import '../../../../utilities/util.dart'; -import '../../../../wallets/crypto_currency/coins/epiccash.dart'; -import '../../../../wallets/crypto_currency/coins/ethereum.dart'; -import '../../../../wallets/crypto_currency/coins/monero.dart'; -import '../../../../wallets/crypto_currency/coins/wownero.dart'; import '../../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../../wallets/crypto_currency/intermediate/cryptonote_currency.dart'; import '../../../../widgets/background.dart'; @@ -41,7 +39,6 @@ import '../../../../widgets/icon_widgets/x_icon.dart'; import '../../../../widgets/stack_dialog.dart'; import '../../../../widgets/stack_text_field.dart'; import '../../../../widgets/textfield_icon_button.dart'; -import 'package:uuid/uuid.dart'; // import 'package:web3dart/web3dart.dart'; enum AddEditNodeViewType { add, edit } @@ -187,9 +184,10 @@ class _AddEditNodeViewState extends ConsumerState { child: Text( "Cancel", style: STextStyles.button(context).copyWith( - color: Theme.of(context) - .extension()! - .accentColorDark), + color: Theme.of(context) + .extension()! + .accentColorDark, + ), ), ), rightButton: TextButton( @@ -232,7 +230,7 @@ class _AddEditNodeViewState extends ConsumerState { switch (viewType) { case AddEditNodeViewType.add: - NodeModel node = NodeModel( + final NodeModel node = NodeModel( host: address, port: formData.port!, name: formData.name!, @@ -257,7 +255,7 @@ class _AddEditNodeViewState extends ConsumerState { } break; case AddEditNodeViewType.edit: - NodeModel node = NodeModel( + final NodeModel node = NodeModel( host: address, port: formData.port!, name: formData.name!, @@ -315,8 +313,10 @@ class _AddEditNodeViewState extends ConsumerState { Widget build(BuildContext context) { final NodeModel? node = viewType == AddEditNodeViewType.edit && nodeId != null - ? ref.watch(nodeServiceChangeNotifierProvider - .select((value) => value.getNodeById(id: nodeId!))) + ? ref.watch( + nodeServiceChangeNotifierProvider + .select((value) => value.getNodeById(id: nodeId!)), + ) : null; return ConditionalParent( @@ -344,8 +344,10 @@ class _AddEditNodeViewState extends ConsumerState { actions: [ if (viewType == AddEditNodeViewType.edit && ref - .watch(nodeServiceChangeNotifierProvider - .select((value) => value.getNodesFor(coin))) + .watch( + nodeServiceChangeNotifierProvider + .select((value) => value.getNodesFor(coin)), + ) .length > 1) Padding( @@ -372,8 +374,10 @@ class _AddEditNodeViewState extends ConsumerState { height: 20, ), onPressed: () async { - Navigator.popUntil(context, - ModalRoute.withName(widget.routeOnSuccessOrDelete)); + Navigator.popUntil( + context, + ModalRoute.withName(widget.routeOnSuccessOrDelete), + ); await ref .read(nodeServiceChangeNotifierProvider) @@ -433,7 +437,7 @@ class _AddEditNodeViewState extends ConsumerState { Text( "Add new node", style: STextStyles.desktopH3(context), - ) + ), ], ), Padding( @@ -574,13 +578,13 @@ final nodeFormDataProvider = Provider((_) => NodeFormData()); class NodeForm extends ConsumerStatefulWidget { const NodeForm({ - Key? key, + super.key, this.node, required this.secureStore, required this.readOnly, required this.coin, this.onChanged, - }) : super(key: key); + }); final NodeModel? node; final SecureStorageInterface secureStore; @@ -1008,9 +1012,11 @@ class _NodeFormState extends ConsumerState { child: Checkbox( fillColor: !shouldBeReadOnly && enableSSLCheckbox ? null - : MaterialStateProperty.all(Theme.of(context) - .extension()! - .checkboxBGDisabled), + : MaterialStateProperty.all( + Theme.of(context) + .extension()! + .checkboxBGDisabled, + ), materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, value: _useSSL, @@ -1030,7 +1036,7 @@ class _NodeFormState extends ConsumerState { Text( "Use SSL", style: STextStyles.itemSubtitle12(context), - ) + ), ], ), ), @@ -1059,9 +1065,11 @@ class _NodeFormState extends ConsumerState { child: Checkbox( fillColor: !widget.readOnly ? null - : MaterialStateProperty.all(Theme.of(context) - .extension()! - .checkboxBGDisabled), + : MaterialStateProperty.all( + Theme.of(context) + .extension()! + .checkboxBGDisabled, + ), materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, value: _trusted, @@ -1081,7 +1089,7 @@ class _NodeFormState extends ConsumerState { Text( "Trusted", style: STextStyles.itemSubtitle12(context), - ) + ), ], ), ), @@ -1144,7 +1152,7 @@ class _NodeFormState extends ConsumerState { Text( "Use as failover", style: STextStyles.itemSubtitle12(context), - ) + ), ], ), ), diff --git a/lib/pages/settings_views/global_settings_view/manage_nodes_views/manage_nodes_view.dart b/lib/pages/settings_views/global_settings_view/manage_nodes_views/manage_nodes_view.dart index dd6eed5ee..34b37b621 100644 --- a/lib/pages/settings_views/global_settings_view/manage_nodes_views/manage_nodes_view.dart +++ b/lib/pages/settings_views/global_settings_view/manage_nodes_views/manage_nodes_view.dart @@ -13,9 +13,9 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; -import 'coin_nodes_view.dart'; -import '../../../../providers/providers.dart'; + import '../../../../app_config.dart'; +import '../../../../providers/providers.dart'; import '../../../../themes/coin_icon_provider.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/constants.dart'; @@ -24,6 +24,7 @@ import '../../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../../widgets/background.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../../widgets/rounded_white_container.dart'; +import 'coin_nodes_view.dart'; class ManageNodesView extends ConsumerStatefulWidget { const ManageNodesView({ @@ -90,8 +91,10 @@ class _ManageNodesViewState extends ConsumerState { ...coins.map( (coin) { final count = ref - .watch(nodeServiceChangeNotifierProvider - .select((value) => value.getNodesFor(coin))) + .watch( + nodeServiceChangeNotifierProvider + .select((value) => value.getNodesFor(coin)), + ) .length; return Padding( @@ -141,7 +144,7 @@ class _ManageNodesViewState extends ConsumerState { style: STextStyles.label(context), ), ], - ) + ), ], ), ), diff --git a/lib/pages/settings_views/global_settings_view/security_views/change_pin_view/change_pin_view.dart b/lib/pages/settings_views/global_settings_view/security_views/change_pin_view/change_pin_view.dart index 8d442f17f..57fa710ad 100644 --- a/lib/pages/settings_views/global_settings_view/security_views/change_pin_view/change_pin_view.dart +++ b/lib/pages/settings_views/global_settings_view/security_views/change_pin_view/change_pin_view.dart @@ -10,8 +10,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../../../../notifications/show_flush_bar.dart'; -import '../security_view.dart'; import '../../../../../providers/global/prefs_provider.dart'; import '../../../../../providers/global/secure_store_provider.dart'; import '../../../../../themes/stack_colors.dart'; @@ -21,11 +21,12 @@ import '../../../../../utilities/text_styles.dart'; import '../../../../../widgets/background.dart'; import '../../../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../../../widgets/custom_pin_put/custom_pin_put.dart'; +import '../security_view.dart'; class ChangePinView extends ConsumerStatefulWidget { const ChangePinView({ - Key? key, - }) : super(key: key); + super.key, + }); static const String routeName = "/changePin"; @@ -207,8 +208,9 @@ class _ChangePinViewState extends ConsumerState { onSubmit: (String pin) async { if (_pinPutController1.text == _pinPutController2.text) { // This should never fail as we are overwriting the existing pin - assert((await _secureStore.read(key: "stack_pin")) != - null); + assert( + (await _secureStore.read(key: "stack_pin")) != null, + ); await _secureStore.write(key: "stack_pin", value: pin); showFloatingFlushBar( @@ -219,7 +221,8 @@ class _ChangePinViewState extends ConsumerState { ); await Future.delayed( - const Duration(milliseconds: 1200)); + const Duration(milliseconds: 1200), + ); if (mounted) { Navigator.of(context).popUntil( diff --git a/lib/pages/settings_views/global_settings_view/security_views/security_view.dart b/lib/pages/settings_views/global_settings_view/security_views/security_view.dart index ef911d4a0..ff35130fe 100644 --- a/lib/pages/settings_views/global_settings_view/security_views/security_view.dart +++ b/lib/pages/settings_views/global_settings_view/security_views/security_view.dart @@ -24,8 +24,8 @@ import '../../../../widgets/rounded_white_container.dart'; class SecurityView extends StatelessWidget { const SecurityView({ - Key? key, - }) : super(key: key); + super.key, + }); static const String routeName = "/security"; diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/auto_backup_view.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/auto_backup_view.dart index 4a6218d2f..fdfda272c 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/auto_backup_view.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/auto_backup_view.dart @@ -12,8 +12,8 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:intl/intl.dart'; -import 'create_auto_backup_view.dart'; -import 'edit_auto_backup_view.dart'; +import 'package:url_launcher/url_launcher.dart'; + import '../../../../providers/global/auto_swb_service_provider.dart'; import '../../../../providers/providers.dart'; import '../../../../themes/stack_colors.dart'; @@ -29,10 +29,11 @@ import '../../../../widgets/custom_buttons/draggable_switch_button.dart'; import '../../../../widgets/rounded_white_container.dart'; import '../../../../widgets/stack_dialog.dart'; import '../../../../widgets/stack_text_field.dart'; -import 'package:url_launcher/url_launcher.dart'; +import 'create_auto_backup_view.dart'; +import 'edit_auto_backup_view.dart'; class AutoBackupView extends ConsumerStatefulWidget { - const AutoBackupView({Key? key}) : super(key: key); + const AutoBackupView({super.key}); static const String routeName = "/stackAutoBackup"; @@ -72,8 +73,8 @@ class _AutoBackupViewState extends ConsumerState { } else { // if greater than a week return the actual date return DateFormat.yMMMMd( - ref.read(localeServiceChangeNotifierProvider).locale) - .format(time); + ref.read(localeServiceChangeNotifierProvider).locale, + ).format(time); } if (value == 1) { @@ -200,7 +201,8 @@ class _AutoBackupViewState extends ConsumerState { fileLocationController.text = ref.read(prefsChangeNotifierProvider).autoBackupLocation ?? " "; frequencyController.text = Format.prettyFrequencyType( - ref.read(prefsChangeNotifierProvider).backupFrequencyType); + ref.read(prefsChangeNotifierProvider).backupFrequencyType, + ); fileLocationFocusNode = FocusNode(); passwordFocusNode = FocusNode(); @@ -225,8 +227,9 @@ class _AutoBackupViewState extends ConsumerState { Widget build(BuildContext context) { debugPrint("BUILD: $runtimeType"); - bool isEnabledAutoBackup = ref.watch(prefsChangeNotifierProvider - .select((value) => value.isAutoBackupEnabled)); + final bool isEnabledAutoBackup = ref.watch( + prefsChangeNotifierProvider.select((value) => value.isAutoBackupEnabled), + ); ref.listen( prefsChangeNotifierProvider @@ -312,8 +315,9 @@ class _AutoBackupViewState extends ConsumerState { style: STextStyles.label(context), children: [ const TextSpan( - text: - "Auto Backup is a custom Stack Wallet feature that offers a convenient backup of your data.\n\nTo ensure maximum security, we recommend using a unique password that you haven't used anywhere else on the internet before. Your password is not stored.\n\nFor more information, please see our website "), + text: + "Auto Backup is a custom Stack Wallet feature that offers a convenient backup of your data.\n\nTo ensure maximum security, we recommend using a unique password that you haven't used anywhere else on the internet before. Your password is not stored.\n\nFor more information, please see our website ", + ), TextSpan( text: "stackwallet.com.", style: STextStyles.richLink(context), @@ -346,7 +350,7 @@ class _AutoBackupViewState extends ConsumerState { Text( "Backed up ${prettySinceLastBackupString(ref.watch(prefsChangeNotifierProvider.select((value) => value.lastAutoBackup)))}", style: STextStyles.itemSubtitle(context), - ) + ), ], ), ), @@ -465,7 +469,7 @@ class _AutoBackupViewState extends ConsumerState { .pushNamed(EditAutoBackupView.routeName); }, ), - ) + ), ], ), ], diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/create_auto_backup_view.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/create_auto_backup_view.dart index f7a36bcba..1583950c5 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/create_auto_backup_view.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/create_auto_backup_view.dart @@ -16,11 +16,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:stack_wallet_backup/stack_wallet_backup.dart'; +import 'package:zxcvbn/zxcvbn.dart'; + import '../../../../notifications/show_flush_bar.dart'; -import 'auto_backup_view.dart'; -import 'helpers/restore_create_backup.dart'; -import 'helpers/swb_file_system.dart'; -import 'sub_views/backup_frequency_type_select_sheet.dart'; import '../../../../providers/global/prefs_provider.dart'; import '../../../../providers/global/secure_store_provider.dart'; import '../../../../themes/stack_colors.dart'; @@ -36,12 +34,15 @@ import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../../widgets/progress_bar.dart'; import '../../../../widgets/stack_dialog.dart'; import '../../../../widgets/stack_text_field.dart'; -import 'package:zxcvbn/zxcvbn.dart'; +import 'auto_backup_view.dart'; +import 'helpers/restore_create_backup.dart'; +import 'helpers/swb_file_system.dart'; +import 'sub_views/backup_frequency_type_select_sheet.dart'; class CreateAutoBackupView extends ConsumerStatefulWidget { const CreateAutoBackupView({ - Key? key, - }) : super(key: key); + super.key, + }); static const String routeName = "/createAutoBackup"; @@ -134,542 +135,563 @@ class _EnableAutoBackupViewState extends ConsumerState { ), body: Padding( padding: const EdgeInsets.all(16), - child: LayoutBuilder(builder: (context, constraints) { - return SingleChildScrollView( - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: constraints.maxHeight, - ), - child: IntrinsicHeight( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Text( - "Create your backup file", - style: STextStyles.smallMed12(context), - ), - const SizedBox( - height: 10, - ), - if (!Platform.isAndroid && !Platform.isIOS) - TextField( - autocorrect: Util.isDesktop ? false : true, - enableSuggestions: Util.isDesktop ? false : true, - onTap: Platform.isAndroid || Platform.isIOS - ? null - : () async { - try { - await stackFileSystem.prepareStorage(); - - if (mounted) { - await stackFileSystem.pickDir(context); - } - - if (mounted) { - setState(() { - fileLocationController.text = - stackFileSystem.dirPath ?? ""; - }); - } - } catch (e, s) { - Logging.instance - .log("$e\n$s", level: LogLevel.Error); - } - }, - controller: fileLocationController, - style: STextStyles.field(context), - decoration: InputDecoration( - hintText: "Save to...", - hintStyle: STextStyles.fieldLabel(context), - suffixIcon: UnconstrainedBox( - child: Row( - children: [ - const SizedBox( - width: 16, - ), - SvgPicture.asset( - Assets.svg.folder, - color: Theme.of(context) - .extension()! - .textDark3, - width: 16, - height: 16, - ), - const SizedBox( - width: 12, - ), - ], - ), - ), - ), - key: const Key( - "createBackupSaveToFileLocationTextFieldKey"), - readOnly: true, - toolbarOptions: const ToolbarOptions( - copy: true, - cut: false, - paste: false, - selectAll: false, - ), - onChanged: (newValue) {}, + child: LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: constraints.maxHeight, + ), + child: IntrinsicHeight( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + "Create your backup file", + style: STextStyles.smallMed12(context), ), - if (!Platform.isAndroid && !Platform.isIOS) const SizedBox( height: 10, ), - ClipRRect( - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, - ), - child: TextField( - key: const Key("createBackupPasswordFieldKey1"), - focusNode: passwordFocusNode, - controller: passwordController, - style: STextStyles.field(context), - obscureText: hidePassword, - enableSuggestions: false, - autocorrect: false, - decoration: standardInputDecoration( - "Create passphrase", - passwordFocusNode, - context, - ).copyWith( - suffixIcon: UnconstrainedBox( - child: Row( - children: [ - const SizedBox( - width: 16, - ), - GestureDetector( - key: const Key( - "createBackupPasswordFieldShowPasswordButtonKey"), - onTap: () async { - setState(() { - hidePassword = !hidePassword; - }); - }, - child: SvgPicture.asset( - hidePassword - ? Assets.svg.eye - : Assets.svg.eyeSlash, - color: Theme.of(context) - .extension()! - .textDark3, - width: 16, - height: 16, - ), - ), - const SizedBox( - width: 12, - ), - ], - ), - ), - ), - onChanged: (newValue) { - if (newValue.isEmpty) { - setState(() { - passwordFeedback = ""; - }); - return; - } - final result = zxcvbn.evaluate(newValue); - String suggestionsAndTips = ""; - for (var sug - in result.feedback.suggestions!.toSet()) { - suggestionsAndTips += "$sug\n"; - } - suggestionsAndTips += result.feedback.warning!; - String feedback = - // "Password Strength: ${((result.score! / 4.0) * 100).toInt()}%\n" - suggestionsAndTips; - - passwordStrength = result.score! / 4; - - // hack fix to format back string returned from zxcvbn - if (feedback.contains("phrasesNo need")) { - feedback = feedback.replaceFirst( - "phrasesNo need", "phrases\nNo need"); - } - - if (feedback.endsWith("\n")) { - feedback = - feedback.substring(0, feedback.length - 2); - } - - setState(() { - passwordFeedback = feedback; - }); - }, - ), - ), - if (passwordFocusNode.hasFocus || - passwordRepeatFocusNode.hasFocus || - passwordController.text.isNotEmpty) - Padding( - padding: EdgeInsets.only( - left: 12, - right: 12, - top: passwordFeedback.isNotEmpty ? 4 : 0, - ), - child: passwordFeedback.isNotEmpty - ? Text( - passwordFeedback, - style: STextStyles.infoSmall(context), - ) - : null, - ), - if (passwordFocusNode.hasFocus || - passwordRepeatFocusNode.hasFocus || - passwordController.text.isNotEmpty) - Padding( - padding: const EdgeInsets.only( - left: 12, - right: 12, - top: 10, - ), - child: ProgressBar( - key: const Key("createStackBackUpProgressBar"), - width: MediaQuery.of(context).size.width - 32 - 24, - height: 5, - fillColor: passwordStrength < 0.51 - ? Theme.of(context) - .extension()! - .accentColorRed - : passwordStrength < 1 - ? Theme.of(context) - .extension()! - .accentColorYellow - : Theme.of(context) - .extension()! - .accentColorGreen, - backgroundColor: Theme.of(context) - .extension()! - .buttonBackSecondary, - percent: passwordStrength < 0.25 - ? 0.03 - : passwordStrength, - ), - ), - const SizedBox( - height: 10, - ), - ClipRRect( - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, - ), - child: TextField( - key: const Key("createBackupPasswordFieldKey2"), - focusNode: passwordRepeatFocusNode, - controller: passwordRepeatController, - style: STextStyles.field(context), - obscureText: hidePassword, - enableSuggestions: false, - autocorrect: false, - decoration: standardInputDecoration( - "Confirm passphrase", - passwordRepeatFocusNode, - context, - ).copyWith( - suffixIcon: UnconstrainedBox( - child: Row( - children: [ - const SizedBox( - width: 16, - ), - GestureDetector( - key: const Key( - "createBackupPasswordFieldShowPasswordButtonKey"), - onTap: () async { - setState(() { - hidePassword = !hidePassword; - }); - }, - child: SvgPicture.asset( - hidePassword - ? Assets.svg.eye - : Assets.svg.eyeSlash, - color: Theme.of(context) - .extension()! - .textDark3, - width: 16, - height: 16, - ), - ), - const SizedBox( - width: 12, - ), - ], - ), - ), - ), - onChanged: (newValue) { - setState(() {}); - // TODO: ? check if passwords match? - }, - ), - ), - const SizedBox( - height: 32, - ), - Text( - "Auto Backup frequency", - style: STextStyles.smallMed12(context), - ), - const SizedBox( - height: 10, - ), - Stack( - children: [ + if (!Platform.isAndroid && !Platform.isIOS) TextField( autocorrect: Util.isDesktop ? false : true, enableSuggestions: Util.isDesktop ? false : true, - readOnly: true, - textInputAction: TextInputAction.none, - ), - Positioned.fill( - child: RawMaterialButton( - splashColor: Theme.of(context) - .extension()! - .highlight, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, - ), - ), - onPressed: () { - showModalBottomSheet( - backgroundColor: Colors.transparent, - context: context, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical( - top: Radius.circular(20), - ), - ), - builder: (_) => - const BackupFrequencyTypeSelectSheet(), - ); - }, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 12.0), + onTap: Platform.isAndroid || Platform.isIOS + ? null + : () async { + try { + await stackFileSystem.prepareStorage(); + + if (mounted) { + await stackFileSystem.pickDir(context); + } + + if (mounted) { + setState(() { + fileLocationController.text = + stackFileSystem.dirPath ?? ""; + }); + } + } catch (e, s) { + Logging.instance + .log("$e\n$s", level: LogLevel.Error); + } + }, + controller: fileLocationController, + style: STextStyles.field(context), + decoration: InputDecoration( + hintText: "Save to...", + hintStyle: STextStyles.fieldLabel(context), + suffixIcon: UnconstrainedBox( child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, children: [ - Text( - Format.prettyFrequencyType(ref.watch( - prefsChangeNotifierProvider.select( - (value) => - value.backupFrequencyType))), - style: - STextStyles.itemSubtitle12(context), + const SizedBox( + width: 16, ), - Padding( - padding: - const EdgeInsets.only(right: 4.0), - child: SvgPicture.asset( - Assets.svg.chevronDown, - color: Theme.of(context) - .extension()! - .textSubtitle2, - width: 12, - height: 6, - ), + SvgPicture.asset( + Assets.svg.folder, + color: Theme.of(context) + .extension()! + .textDark3, + width: 16, + height: 16, + ), + const SizedBox( + width: 12, ), ], ), ), ), - ) - ], - ), - const Spacer(), - const SizedBox( - height: 10, - ), - TextButton( - style: shouldEnableCreate - ? Theme.of(context) - .extension()! - .getPrimaryEnabledButtonStyle(context) - : Theme.of(context) - .extension()! - .getPrimaryDisabledButtonStyle(context), - onPressed: !shouldEnableCreate - ? null - : () async { - final String pathToSave = - fileLocationController.text; - final String passphrase = - passwordController.text; - final String repeatPassphrase = - passwordRepeatController.text; + key: const Key( + "createBackupSaveToFileLocationTextFieldKey", + ), + readOnly: true, + toolbarOptions: const ToolbarOptions( + copy: true, + cut: false, + paste: false, + selectAll: false, + ), + onChanged: (newValue) {}, + ), + if (!Platform.isAndroid && !Platform.isIOS) + const SizedBox( + height: 10, + ), + ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: TextField( + key: const Key("createBackupPasswordFieldKey1"), + focusNode: passwordFocusNode, + controller: passwordController, + style: STextStyles.field(context), + obscureText: hidePassword, + enableSuggestions: false, + autocorrect: false, + decoration: standardInputDecoration( + "Create passphrase", + passwordFocusNode, + context, + ).copyWith( + suffixIcon: UnconstrainedBox( + child: Row( + children: [ + const SizedBox( + width: 16, + ), + GestureDetector( + key: const Key( + "createBackupPasswordFieldShowPasswordButtonKey", + ), + onTap: () async { + setState(() { + hidePassword = !hidePassword; + }); + }, + child: SvgPicture.asset( + hidePassword + ? Assets.svg.eye + : Assets.svg.eyeSlash, + color: Theme.of(context) + .extension()! + .textDark3, + width: 16, + height: 16, + ), + ), + const SizedBox( + width: 12, + ), + ], + ), + ), + ), + onChanged: (newValue) { + if (newValue.isEmpty) { + setState(() { + passwordFeedback = ""; + }); + return; + } + final result = zxcvbn.evaluate(newValue); + String suggestionsAndTips = ""; + for (final sug + in result.feedback.suggestions!.toSet()) { + suggestionsAndTips += "$sug\n"; + } + suggestionsAndTips += result.feedback.warning!; + String feedback = + // "Password Strength: ${((result.score! / 4.0) * 100).toInt()}%\n" + suggestionsAndTips; - if (pathToSave.isEmpty) { - showFloatingFlushBar( - type: FlushBarType.warning, - message: "Directory not chosen", - context: context, - ); - return; - } - if (!(await Directory(pathToSave).exists())) { - showFloatingFlushBar( - type: FlushBarType.warning, - message: "Directory does not exist", - context: context, - ); - return; - } - if (passphrase.isEmpty) { - showFloatingFlushBar( - type: FlushBarType.warning, - message: "A passphrase is required", - context: context, - ); - return; - } - if (passphrase != repeatPassphrase) { - showFloatingFlushBar( - type: FlushBarType.warning, - message: "Passphrase does not match", - context: context, - ); - return; - } + passwordStrength = result.score! / 4; - showDialog( - context: context, - barrierDismissible: false, - builder: (_) => const StackDialog( - title: "Encrypting initial backup", - message: "This shouldn't take long", - ), + // hack fix to format back string returned from zxcvbn + if (feedback.contains("phrasesNo need")) { + feedback = feedback.replaceFirst( + "phrasesNo need", + "phrases\nNo need", ); + } - // make sure the dialog is able to be displayed for at least some time - final fut = Future.delayed( - const Duration(milliseconds: 300)); + if (feedback.endsWith("\n")) { + feedback = + feedback.substring(0, feedback.length - 2); + } - String adkString; - int adkVersion; - try { - final adk = - await compute(generateAdk, passphrase); - adkString = - Format.uint8listToString(adk.item2); - adkVersion = adk.item1; - } on Exception catch (e, s) { - String err = - getErrorMessageFromSWBException(e); - Logging.instance - .log("$err\n$s", level: LogLevel.Error); - // pop encryption progress dialog - Navigator.of(context).pop(); - showFloatingFlushBar( - type: FlushBarType.warning, - message: err, - context: context, - ); - return; - } catch (e, s) { - Logging.instance - .log("$e\n$s", level: LogLevel.Error); - // pop encryption progress dialog - Navigator.of(context).pop(); - showFloatingFlushBar( - type: FlushBarType.warning, - message: "$e", - context: context, - ); - return; - } - - await secureStore.write( - key: "auto_adk_string", value: adkString); - await secureStore.write( - key: "auto_adk_version_string", - value: adkVersion.toString()); - - final DateTime now = DateTime.now(); - final String fileToSave = - createAutoBackupFilename(pathToSave, now); - - final backup = await SWB.createStackWalletJSON( - secureStorage: secureStore, - ); - - bool result = - await SWB.encryptStackWalletWithADK( - fileToSave, - adkString, - jsonEncode(backup), - adkVersion, - ); - - // this future should already be complete unless there was an error encrypting - await Future.wait([fut]); - - if (mounted) { - // pop encryption progress dialog - Navigator.of(context).pop(); - - if (result) { - ref - .read(prefsChangeNotifierProvider) - .autoBackupLocation = pathToSave; - ref - .read(prefsChangeNotifierProvider) - .lastAutoBackup = now; - - ref - .read(prefsChangeNotifierProvider) - .isAutoBackupEnabled = true; - - await showDialog( - context: context, - barrierDismissible: false, - builder: (_) => Platform.isAndroid - ? StackOkDialog( - title: - "Stack Auto Backup enabled and saved to:", - message: fileToSave, - ) - : const StackOkDialog( - title: - "Stack Auto Backup enabled!"), - ); - if (mounted) { - passwordController.text = ""; - passwordRepeatController.text = ""; - - Navigator.of(context).popUntil( - ModalRoute.withName( - AutoBackupView.routeName)); - } - } else { - await showDialog( - context: context, - barrierDismissible: false, - builder: (_) => const StackOkDialog( - title: - "Failed to enable Auto Backup"), - ); - } - } - }, - child: Text( - "Enable Auto Backup", - style: STextStyles.button(context), + setState(() { + passwordFeedback = feedback; + }); + }, + ), ), - ), - ], + if (passwordFocusNode.hasFocus || + passwordRepeatFocusNode.hasFocus || + passwordController.text.isNotEmpty) + Padding( + padding: EdgeInsets.only( + left: 12, + right: 12, + top: passwordFeedback.isNotEmpty ? 4 : 0, + ), + child: passwordFeedback.isNotEmpty + ? Text( + passwordFeedback, + style: STextStyles.infoSmall(context), + ) + : null, + ), + if (passwordFocusNode.hasFocus || + passwordRepeatFocusNode.hasFocus || + passwordController.text.isNotEmpty) + Padding( + padding: const EdgeInsets.only( + left: 12, + right: 12, + top: 10, + ), + child: ProgressBar( + key: const Key("createStackBackUpProgressBar"), + width: + MediaQuery.of(context).size.width - 32 - 24, + height: 5, + fillColor: passwordStrength < 0.51 + ? Theme.of(context) + .extension()! + .accentColorRed + : passwordStrength < 1 + ? Theme.of(context) + .extension()! + .accentColorYellow + : Theme.of(context) + .extension()! + .accentColorGreen, + backgroundColor: Theme.of(context) + .extension()! + .buttonBackSecondary, + percent: passwordStrength < 0.25 + ? 0.03 + : passwordStrength, + ), + ), + const SizedBox( + height: 10, + ), + ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: TextField( + key: const Key("createBackupPasswordFieldKey2"), + focusNode: passwordRepeatFocusNode, + controller: passwordRepeatController, + style: STextStyles.field(context), + obscureText: hidePassword, + enableSuggestions: false, + autocorrect: false, + decoration: standardInputDecoration( + "Confirm passphrase", + passwordRepeatFocusNode, + context, + ).copyWith( + suffixIcon: UnconstrainedBox( + child: Row( + children: [ + const SizedBox( + width: 16, + ), + GestureDetector( + key: const Key( + "createBackupPasswordFieldShowPasswordButtonKey", + ), + onTap: () async { + setState(() { + hidePassword = !hidePassword; + }); + }, + child: SvgPicture.asset( + hidePassword + ? Assets.svg.eye + : Assets.svg.eyeSlash, + color: Theme.of(context) + .extension()! + .textDark3, + width: 16, + height: 16, + ), + ), + const SizedBox( + width: 12, + ), + ], + ), + ), + ), + onChanged: (newValue) { + setState(() {}); + // TODO: ? check if passwords match? + }, + ), + ), + const SizedBox( + height: 32, + ), + Text( + "Auto Backup frequency", + style: STextStyles.smallMed12(context), + ), + const SizedBox( + height: 10, + ), + Stack( + children: [ + TextField( + autocorrect: Util.isDesktop ? false : true, + enableSuggestions: Util.isDesktop ? false : true, + readOnly: true, + textInputAction: TextInputAction.none, + ), + Positioned.fill( + child: RawMaterialButton( + splashColor: Theme.of(context) + .extension()! + .highlight, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + onPressed: () { + showModalBottomSheet( + backgroundColor: Colors.transparent, + context: context, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.circular(20), + ), + ), + builder: (_) => + const BackupFrequencyTypeSelectSheet(), + ); + }, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 12.0, + ), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + Format.prettyFrequencyType( + ref.watch( + prefsChangeNotifierProvider.select( + (value) => + value.backupFrequencyType, + ), + ), + ), + style: + STextStyles.itemSubtitle12(context), + ), + Padding( + padding: + const EdgeInsets.only(right: 4.0), + child: SvgPicture.asset( + Assets.svg.chevronDown, + color: Theme.of(context) + .extension()! + .textSubtitle2, + width: 12, + height: 6, + ), + ), + ], + ), + ), + ), + ), + ], + ), + const Spacer(), + const SizedBox( + height: 10, + ), + TextButton( + style: shouldEnableCreate + ? Theme.of(context) + .extension()! + .getPrimaryEnabledButtonStyle(context) + : Theme.of(context) + .extension()! + .getPrimaryDisabledButtonStyle(context), + onPressed: !shouldEnableCreate + ? null + : () async { + final String pathToSave = + fileLocationController.text; + final String passphrase = + passwordController.text; + final String repeatPassphrase = + passwordRepeatController.text; + + if (pathToSave.isEmpty) { + showFloatingFlushBar( + type: FlushBarType.warning, + message: "Directory not chosen", + context: context, + ); + return; + } + if (!(await Directory(pathToSave).exists())) { + showFloatingFlushBar( + type: FlushBarType.warning, + message: "Directory does not exist", + context: context, + ); + return; + } + if (passphrase.isEmpty) { + showFloatingFlushBar( + type: FlushBarType.warning, + message: "A passphrase is required", + context: context, + ); + return; + } + if (passphrase != repeatPassphrase) { + showFloatingFlushBar( + type: FlushBarType.warning, + message: "Passphrase does not match", + context: context, + ); + return; + } + + showDialog( + context: context, + barrierDismissible: false, + builder: (_) => const StackDialog( + title: "Encrypting initial backup", + message: "This shouldn't take long", + ), + ); + + // make sure the dialog is able to be displayed for at least some time + final fut = Future.delayed( + const Duration(milliseconds: 300), + ); + + String adkString; + int adkVersion; + try { + final adk = + await compute(generateAdk, passphrase); + adkString = + Format.uint8listToString(adk.item2); + adkVersion = adk.item1; + } on Exception catch (e, s) { + final String err = + getErrorMessageFromSWBException(e); + Logging.instance + .log("$err\n$s", level: LogLevel.Error); + // pop encryption progress dialog + Navigator.of(context).pop(); + showFloatingFlushBar( + type: FlushBarType.warning, + message: err, + context: context, + ); + return; + } catch (e, s) { + Logging.instance + .log("$e\n$s", level: LogLevel.Error); + // pop encryption progress dialog + Navigator.of(context).pop(); + showFloatingFlushBar( + type: FlushBarType.warning, + message: "$e", + context: context, + ); + return; + } + + await secureStore.write( + key: "auto_adk_string", + value: adkString, + ); + await secureStore.write( + key: "auto_adk_version_string", + value: adkVersion.toString(), + ); + + final DateTime now = DateTime.now(); + final String fileToSave = + createAutoBackupFilename(pathToSave, now); + + final backup = + await SWB.createStackWalletJSON( + secureStorage: secureStore, + ); + + final bool result = + await SWB.encryptStackWalletWithADK( + fileToSave, + adkString, + jsonEncode(backup), + adkVersion, + ); + + // this future should already be complete unless there was an error encrypting + await Future.wait([fut]); + + if (mounted) { + // pop encryption progress dialog + Navigator.of(context).pop(); + + if (result) { + ref + .read(prefsChangeNotifierProvider) + .autoBackupLocation = pathToSave; + ref + .read(prefsChangeNotifierProvider) + .lastAutoBackup = now; + + ref + .read(prefsChangeNotifierProvider) + .isAutoBackupEnabled = true; + + await showDialog( + context: context, + barrierDismissible: false, + builder: (_) => Platform.isAndroid + ? StackOkDialog( + title: + "Stack Auto Backup enabled and saved to:", + message: fileToSave, + ) + : const StackOkDialog( + title: + "Stack Auto Backup enabled!", + ), + ); + if (mounted) { + passwordController.text = ""; + passwordRepeatController.text = ""; + + Navigator.of(context).popUntil( + ModalRoute.withName( + AutoBackupView.routeName, + ), + ); + } + } else { + await showDialog( + context: context, + barrierDismissible: false, + builder: (_) => const StackOkDialog( + title: "Failed to enable Auto Backup", + ), + ); + } + } + }, + child: Text( + "Enable Auto Backup", + style: STextStyles.button(context), + ), + ), + ], + ), ), ), - ), - ); - }), + ); + }, + ), ), ), ); diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/create_backup_information_view.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/create_backup_information_view.dart index ebdf2cac3..8b1c72ebd 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/create_backup_information_view.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/create_backup_information_view.dart @@ -17,7 +17,7 @@ import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../../widgets/rounded_white_container.dart'; class CreateBackupInfoView extends StatelessWidget { - const CreateBackupInfoView({Key? key}) : super(key: key); + const CreateBackupInfoView({super.key}); static const String routeName = "/createBackupInfo"; diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/create_backup_view.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/create_backup_view.dart index 118e128e8..e762d9f5b 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/create_backup_view.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/create_backup_view.dart @@ -15,9 +15,9 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:zxcvbn/zxcvbn.dart'; + import '../../../../notifications/show_flush_bar.dart'; -import 'helpers/restore_create_backup.dart'; -import 'helpers/swb_file_system.dart'; import '../../../../providers/global/secure_store_provider.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/assets.dart'; @@ -34,10 +34,11 @@ import '../../../../widgets/desktop/secondary_button.dart'; import '../../../../widgets/progress_bar.dart'; import '../../../../widgets/stack_dialog.dart'; import '../../../../widgets/stack_text_field.dart'; -import 'package:zxcvbn/zxcvbn.dart'; +import 'helpers/restore_create_backup.dart'; +import 'helpers/swb_file_system.dart'; class CreateBackupView extends StatefulWidget { - const CreateBackupView({Key? key}) : super(key: key); + const CreateBackupView({super.key}); static const String routeName = "/createBackup"; @@ -123,7 +124,8 @@ class _RestoreFromFileViewState extends State { if (FocusScope.of(context).hasFocus) { FocusScope.of(context).unfocus(); await Future.delayed( - const Duration(milliseconds: 75)); + const Duration(milliseconds: 75), + ); } if (mounted) { Navigator.of(context).pop(); @@ -165,11 +167,11 @@ class _RestoreFromFileViewState extends State { padding: const EdgeInsets.only(bottom: 10), child: Text( "Choose file location", - style: STextStyles.desktopTextExtraExtraSmall(context) - .copyWith( - color: Theme.of(context) - .extension()! - .textDark3), + style: + STextStyles.desktopTextExtraExtraSmall(context).copyWith( + color: + Theme.of(context).extension()!.textDark3, + ), ), ), child, @@ -180,74 +182,76 @@ class _RestoreFromFileViewState extends State { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ if (!Platform.isAndroid && !Platform.isIOS) - Consumer(builder: (context, ref, __) { - return Container( - color: Colors.transparent, - child: TextField( - autocorrect: Util.isDesktop ? false : true, - enableSuggestions: Util.isDesktop ? false : true, - onTap: Platform.isAndroid || Platform.isIOS - ? null - : () async { - try { - await stackFileSystem.prepareStorage(); + Consumer( + builder: (context, ref, __) { + return Container( + color: Colors.transparent, + child: TextField( + autocorrect: Util.isDesktop ? false : true, + enableSuggestions: Util.isDesktop ? false : true, + onTap: Platform.isAndroid || Platform.isIOS + ? null + : () async { + try { + await stackFileSystem.prepareStorage(); - if (mounted) { - await stackFileSystem.pickDir(context); - } + if (mounted) { + await stackFileSystem.pickDir(context); + } - if (mounted) { - setState(() { - fileLocationController.text = - stackFileSystem.dirPath ?? ""; - }); + if (mounted) { + setState(() { + fileLocationController.text = + stackFileSystem.dirPath ?? ""; + }); + } + } catch (e, s) { + Logging.instance + .log("$e\n$s", level: LogLevel.Error); } - } catch (e, s) { - Logging.instance - .log("$e\n$s", level: LogLevel.Error); - } - }, - controller: fileLocationController, - style: STextStyles.field(context), - decoration: InputDecoration( - hintText: "Save to...", - hintStyle: STextStyles.fieldLabel(context), - suffixIcon: UnconstrainedBox( - child: Row( - children: [ - const SizedBox( - width: 16, - ), - SvgPicture.asset( - Assets.svg.folder, - color: Theme.of(context) - .extension()! - .textDark3, - width: 16, - height: 16, - ), - const SizedBox( - width: 12, - ), - ], + }, + controller: fileLocationController, + style: STextStyles.field(context), + decoration: InputDecoration( + hintText: "Save to...", + hintStyle: STextStyles.fieldLabel(context), + suffixIcon: UnconstrainedBox( + child: Row( + children: [ + const SizedBox( + width: 16, + ), + SvgPicture.asset( + Assets.svg.folder, + color: Theme.of(context) + .extension()! + .textDark3, + width: 16, + height: 16, + ), + const SizedBox( + width: 12, + ), + ], + ), ), ), + key: const Key( + "createBackupSaveToFileLocationTextFieldKey"), + readOnly: true, + toolbarOptions: const ToolbarOptions( + copy: true, + cut: false, + paste: false, + selectAll: false, + ), + onChanged: (newValue) { + // ref.read(addressEntryDataProvider(widget.id)).address = newValue; + }, ), - key: - const Key("createBackupSaveToFileLocationTextFieldKey"), - readOnly: true, - toolbarOptions: const ToolbarOptions( - copy: true, - cut: false, - paste: false, - selectAll: false, - ), - onChanged: (newValue) { - // ref.read(addressEntryDataProvider(widget.id)).address = newValue; - }, - ), - ); - }), + ); + }, + ), if (!Platform.isAndroid && !Platform.isIOS) SizedBox( height: !isDesktop ? 8 : 24, @@ -257,11 +261,11 @@ class _RestoreFromFileViewState extends State { padding: const EdgeInsets.only(bottom: 10.0), child: Text( "Create a passphrase", - style: STextStyles.desktopTextExtraExtraSmall(context) - .copyWith( - color: Theme.of(context) - .extension()! - .textDark3), + style: + STextStyles.desktopTextExtraExtraSmall(context).copyWith( + color: + Theme.of(context).extension()!.textDark3, + ), textAlign: TextAlign.left, ), ), @@ -292,7 +296,8 @@ class _RestoreFromFileViewState extends State { ), GestureDetector( key: const Key( - "createBackupPasswordFieldShowPasswordButtonKey"), + "createBackupPasswordFieldShowPasswordButtonKey", + ), onTap: () async { setState(() { hidePassword = !hidePassword; @@ -323,7 +328,7 @@ class _RestoreFromFileViewState extends State { } final result = zxcvbn.evaluate(newValue); String suggestionsAndTips = ""; - for (var sug in result.feedback.suggestions!.toSet()) { + for (final sug in result.feedback.suggestions!.toSet()) { suggestionsAndTips += "$sug\n"; } suggestionsAndTips += result.feedback.warning!; @@ -336,7 +341,9 @@ class _RestoreFromFileViewState extends State { // hack fix to format back string returned from zxcvbn if (feedback.contains("phrasesNo need")) { feedback = feedback.replaceFirst( - "phrasesNo need", "phrases\nNo need"); + "phrasesNo need", + "phrases\nNo need", + ); } if (feedback.endsWith("\n")) { @@ -425,7 +432,8 @@ class _RestoreFromFileViewState extends State { ), GestureDetector( key: const Key( - "createBackupPasswordFieldShowPasswordButtonKey"), + "createBackupPasswordFieldShowPasswordButtonKey", + ), onTap: () async { setState(() { hidePassword = !hidePassword; @@ -458,247 +466,284 @@ class _RestoreFromFileViewState extends State { ), if (!isDesktop) const Spacer(), !isDesktop - ? Consumer(builder: (context, ref, __) { - return TextButton( - style: shouldEnableCreate - ? Theme.of(context) - .extension()! - .getPrimaryEnabledButtonStyle(context) - : Theme.of(context) - .extension()! - .getPrimaryDisabledButtonStyle(context), - onPressed: !shouldEnableCreate - ? null - : () async { - final String pathToSave = - fileLocationController.text; - final String passphrase = passwordController.text; - final String repeatPassphrase = - passwordRepeatController.text; + ? Consumer( + builder: (context, ref, __) { + return TextButton( + style: shouldEnableCreate + ? Theme.of(context) + .extension()! + .getPrimaryEnabledButtonStyle(context) + : Theme.of(context) + .extension()! + .getPrimaryDisabledButtonStyle(context), + onPressed: !shouldEnableCreate + ? null + : () async { + final String pathToSave = + fileLocationController.text; + final String passphrase = + passwordController.text; + final String repeatPassphrase = + passwordRepeatController.text; - if (pathToSave.isEmpty) { - unawaited(showFloatingFlushBar( - type: FlushBarType.warning, - message: "Directory not chosen", - context: context, - )); - return; - } - if (!(await Directory(pathToSave).exists())) { - unawaited(showFloatingFlushBar( - type: FlushBarType.warning, - message: "Directory does not exist", - context: context, - )); - return; - } - if (passphrase.isEmpty) { - unawaited(showFloatingFlushBar( - type: FlushBarType.warning, - message: "A passphrase is required", - context: context, - )); - return; - } - if (passphrase != repeatPassphrase) { - unawaited(showFloatingFlushBar( - type: FlushBarType.warning, - message: "Passphrase does not match", - context: context, - )); - return; - } - - unawaited(showDialog( - context: context, - barrierDismissible: false, - builder: (_) => const StackDialog( - title: "Encrypting backup", - message: "This shouldn't take long", - ), - )); - // make sure the dialog is able to be displayed for at least 1 second - await Future.delayed( - const Duration(seconds: 1)); - - final DateTime now = DateTime.now(); - final String fileToSave = - "$pathToSave/stackbackup_${now.year}_${now.month}_${now.day}_${now.hour}_${now.minute}_${now.second}.swb"; - - final backup = await SWB.createStackWalletJSON( - secureStorage: ref.read(secureStoreProvider)); - - bool result = - await SWB.encryptStackWalletWithPassphrase( - fileToSave, - passphrase, - jsonEncode(backup), - ); - - if (mounted) { - // pop encryption progress dialog - if (!isDesktop) Navigator.of(context).pop(); - - if (result) { - await showDialog( - context: context, - barrierDismissible: false, - builder: (_) => Platform.isAndroid - ? StackOkDialog( - title: "Backup saved to:", - message: fileToSave, - ) - : const StackOkDialog( - title: "Backup creation succeeded"), - ); - passwordController.text = ""; - passwordRepeatController.text = ""; - setState(() {}); - } else { - await showDialog( - context: context, - barrierDismissible: false, - builder: (_) => const StackOkDialog( - title: "Backup creation failed"), - ); - } - } - }, - child: Text( - "Create backup", - style: STextStyles.button(context), - ), - ); - }) - : Row( - children: [ - Consumer(builder: (context, ref, __) { - return PrimaryButton( - width: 183, - buttonHeight: ButtonHeight.m, - label: "Create backup", - enabled: shouldEnableCreate, - onPressed: !shouldEnableCreate - ? null - : () async { - final String pathToSave = - fileLocationController.text; - final String passphrase = - passwordController.text; - final String repeatPassphrase = - passwordRepeatController.text; - - if (pathToSave.isEmpty) { - unawaited(showFloatingFlushBar( + if (pathToSave.isEmpty) { + unawaited( + showFloatingFlushBar( type: FlushBarType.warning, message: "Directory not chosen", context: context, - )); - return; - } - if (!(await Directory(pathToSave).exists())) { - unawaited(showFloatingFlushBar( + ), + ); + return; + } + if (!(await Directory(pathToSave).exists())) { + unawaited( + showFloatingFlushBar( type: FlushBarType.warning, message: "Directory does not exist", context: context, - )); - return; - } - if (passphrase.isEmpty) { - unawaited(showFloatingFlushBar( + ), + ); + return; + } + if (passphrase.isEmpty) { + unawaited( + showFloatingFlushBar( type: FlushBarType.warning, message: "A passphrase is required", context: context, - )); - return; - } - if (passphrase != repeatPassphrase) { - unawaited(showFloatingFlushBar( + ), + ); + return; + } + if (passphrase != repeatPassphrase) { + unawaited( + showFloatingFlushBar( type: FlushBarType.warning, message: "Passphrase does not match", context: context, - )); - return; - } - - unawaited( - showDialog( - context: context, - barrierDismissible: false, - builder: (_) { - if (Util.isDesktop) { - return DesktopDialog( - maxHeight: double.infinity, - maxWidth: 450, - child: Padding( - padding: const EdgeInsets.all( - 32, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - "Encrypting initial backup", - style: - STextStyles.desktopH3( - context), - ), - const SizedBox( - height: 40, - ), - Text( - "This shouldn't take long", - style: STextStyles - .desktopTextExtraExtraSmall( - context), - ), - ], - ), - ), - ); - } else { - return const StackDialog( - title: "Encrypting initial backup", - message: "This shouldn't take long", - ); - } - }, ), ); + return; + } - await Future.delayed( - const Duration(seconds: 1)); + unawaited( + showDialog( + context: context, + barrierDismissible: false, + builder: (_) => const StackDialog( + title: "Encrypting backup", + message: "This shouldn't take long", + ), + ), + ); + // make sure the dialog is able to be displayed for at least 1 second + await Future.delayed( + const Duration(seconds: 1), + ); - // make sure the dialog is able to be displayed for at least 1 second - final fut = Future.delayed( - const Duration(seconds: 1)); + final DateTime now = DateTime.now(); + final String fileToSave = + "$pathToSave/stackbackup_${now.year}_${now.month}_${now.day}_${now.hour}_${now.minute}_${now.second}.swb"; - final DateTime now = DateTime.now(); - final String fileToSave = - "$pathToSave/stackbackup_${now.year}_${now.month}_${now.day}_${now.hour}_${now.minute}_${now.second}.swb"; + final backup = await SWB.createStackWalletJSON( + secureStorage: ref.read(secureStoreProvider), + ); - final backup = - await SWB.createStackWalletJSON( - secureStorage: - ref.read(secureStoreProvider)); + final bool result = + await SWB.encryptStackWalletWithPassphrase( + fileToSave, + passphrase, + jsonEncode(backup), + ); - bool result = await SWB - .encryptStackWalletWithPassphrase( - fileToSave, - passphrase, - jsonEncode(backup), - ); + if (mounted) { + // pop encryption progress dialog + if (!isDesktop) Navigator.of(context).pop(); - await Future.wait([fut]); + if (result) { + await showDialog( + context: context, + barrierDismissible: false, + builder: (_) => Platform.isAndroid + ? StackOkDialog( + title: "Backup saved to:", + message: fileToSave, + ) + : const StackOkDialog( + title: + "Backup creation succeeded", + ), + ); + passwordController.text = ""; + passwordRepeatController.text = ""; + setState(() {}); + } else { + await showDialog( + context: context, + barrierDismissible: false, + builder: (_) => const StackOkDialog( + title: "Backup creation failed", + ), + ); + } + } + }, + child: Text( + "Create backup", + style: STextStyles.button(context), + ), + ); + }, + ) + : Row( + children: [ + Consumer( + builder: (context, ref, __) { + return PrimaryButton( + width: 183, + buttonHeight: ButtonHeight.m, + label: "Create backup", + enabled: shouldEnableCreate, + onPressed: !shouldEnableCreate + ? null + : () async { + final String pathToSave = + fileLocationController.text; + final String passphrase = + passwordController.text; + final String repeatPassphrase = + passwordRepeatController.text; - if (mounted) { - // pop encryption progress dialog - if (!isDesktop) Navigator.of(context).pop(); + if (pathToSave.isEmpty) { + unawaited( + showFloatingFlushBar( + type: FlushBarType.warning, + message: "Directory not chosen", + context: context, + ), + ); + return; + } + if (!(await Directory(pathToSave) + .exists())) { + unawaited( + showFloatingFlushBar( + type: FlushBarType.warning, + message: "Directory does not exist", + context: context, + ), + ); + return; + } + if (passphrase.isEmpty) { + unawaited( + showFloatingFlushBar( + type: FlushBarType.warning, + message: "A passphrase is required", + context: context, + ), + ); + return; + } + if (passphrase != repeatPassphrase) { + unawaited( + showFloatingFlushBar( + type: FlushBarType.warning, + message: "Passphrase does not match", + context: context, + ), + ); + return; + } - if (result) { - await showDialog( + unawaited( + showDialog( + context: context, + barrierDismissible: false, + builder: (_) { + if (Util.isDesktop) { + return DesktopDialog( + maxHeight: double.infinity, + maxWidth: 450, + child: Padding( + padding: const EdgeInsets.all( + 32, + ), + child: Column( + mainAxisSize: + MainAxisSize.min, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + "Encrypting initial backup", + style: + STextStyles.desktopH3( + context, + ), + ), + const SizedBox( + height: 40, + ), + Text( + "This shouldn't take long", + style: STextStyles + .desktopTextExtraExtraSmall( + context, + ), + ), + ], + ), + ), + ); + } else { + return const StackDialog( + title: + "Encrypting initial backup", + message: + "This shouldn't take long", + ); + } + }, + ), + ); + + await Future.delayed( + const Duration(seconds: 1), + ); + + // make sure the dialog is able to be displayed for at least 1 second + final fut = Future.delayed( + const Duration(seconds: 1), + ); + + final DateTime now = DateTime.now(); + final String fileToSave = + "$pathToSave/stackbackup_${now.year}_${now.month}_${now.day}_${now.hour}_${now.minute}_${now.second}.swb"; + + final backup = + await SWB.createStackWalletJSON( + secureStorage: + ref.read(secureStoreProvider), + ); + + final bool result = await SWB + .encryptStackWalletWithPassphrase( + fileToSave, + passphrase, + jsonEncode(backup), + ); + + await Future.wait([fut]); + + if (mounted) { + // pop encryption progress dialog + if (!isDesktop) + Navigator.of(context).pop(); + + if (result) { + await showDialog( context: context, barrierDismissible: false, builder: (context) { @@ -726,7 +771,8 @@ class _RestoreFromFileViewState extends State { .start, children: [ const SizedBox( - height: 26), + height: 26, + ), Text( "Stack backup saved to: \n", style: STextStyles @@ -736,7 +782,8 @@ class _RestoreFromFileViewState extends State { fileToSave, style: STextStyles .desktopTextExtraExtraSmall( - context), + context, + ), ), const SizedBox( height: 40, @@ -754,40 +801,46 @@ class _RestoreFromFileViewState extends State { onPressed: () { int count = 0; Navigator.of( - context) - .popUntil((_) => - count++ >= - 2); + context, + ).popUntil( + (_) => + count++ >= + 2, + ); }, ), ), ], - ) + ), ], ), ), ); } else { return const StackOkDialog( - title: - "Backup creation succeeded"); + title: + "Backup creation succeeded", + ); } - }); - passwordController.text = ""; - passwordRepeatController.text = ""; - setState(() {}); - } else { - await showDialog( - context: context, - barrierDismissible: false, - builder: (_) => const StackOkDialog( - title: "Backup creation failed"), - ); + }, + ); + passwordController.text = ""; + passwordRepeatController.text = ""; + setState(() {}); + } else { + await showDialog( + context: context, + barrierDismissible: false, + builder: (_) => const StackOkDialog( + title: "Backup creation failed", + ), + ); + } } - } - }, - ); - }), + }, + ); + }, + ), const SizedBox( width: 16, ), diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/dialogs/cancel_stack_restore_dialog.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/dialogs/cancel_stack_restore_dialog.dart index 17c24aed0..f48c0017c 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/dialogs/cancel_stack_restore_dialog.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/dialogs/cancel_stack_restore_dialog.dart @@ -9,6 +9,7 @@ */ import 'package:flutter/material.dart'; + import '../../../../../themes/stack_colors.dart'; import '../../../../../utilities/text_styles.dart'; import '../../../../../utilities/util.dart'; @@ -20,8 +21,8 @@ import '../../../../../widgets/stack_dialog.dart'; class CancelStackRestoreDialog extends StatelessWidget { const CancelStackRestoreDialog({ - Key? key, - }) : super(key: key); + super.key, + }); @override Widget build(BuildContext context) { @@ -68,7 +69,11 @@ class CancelStackRestoreDialog extends StatelessWidget { maxWidth: 600, child: Padding( padding: const EdgeInsets.only( - top: 20, left: 32, right: 32, bottom: 20), + top: 20, + left: 32, + right: 32, + bottom: 20, + ), child: Column( children: [ Text( @@ -111,7 +116,7 @@ class CancelStackRestoreDialog extends StatelessWidget { onPressed: () { Navigator.of(context).pop(true); }, - ) + ), ], ), ], diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/edit_auto_backup_view.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/edit_auto_backup_view.dart index 107cefb70..5d495b3d9 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/edit_auto_backup_view.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/edit_auto_backup_view.dart @@ -18,11 +18,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:stack_wallet_backup/stack_wallet_backup.dart'; +import 'package:zxcvbn/zxcvbn.dart'; + import '../../../../notifications/show_flush_bar.dart'; -import 'auto_backup_view.dart'; -import 'helpers/restore_create_backup.dart'; -import 'helpers/swb_file_system.dart'; -import 'sub_views/backup_frequency_type_select_sheet.dart'; import '../../../../providers/global/prefs_provider.dart'; import '../../../../providers/global/secure_store_provider.dart'; import '../../../../themes/stack_colors.dart'; @@ -42,12 +40,15 @@ import '../../../../widgets/desktop/secondary_button.dart'; import '../../../../widgets/progress_bar.dart'; import '../../../../widgets/stack_dialog.dart'; import '../../../../widgets/stack_text_field.dart'; -import 'package:zxcvbn/zxcvbn.dart'; +import 'auto_backup_view.dart'; +import 'helpers/restore_create_backup.dart'; +import 'helpers/swb_file_system.dart'; +import 'sub_views/backup_frequency_type_select_sheet.dart'; class EditAutoBackupView extends ConsumerStatefulWidget { const EditAutoBackupView({ - Key? key, - }) : super(key: key); + super.key, + }); static const String routeName = "/editAutoBackup"; @@ -156,7 +157,7 @@ class _EditAutoBackupViewState extends ConsumerState { adkString = Format.uint8listToString(adk.item2); adkVersion = adk.item1; } on Exception catch (e, s) { - String err = getErrorMessageFromSWBException(e); + final String err = getErrorMessageFromSWBException(e); Logging.instance.log("$err\n$s", level: LogLevel.Error); // pop encryption progress dialog Navigator.of(context).pop(); @@ -184,7 +185,9 @@ class _EditAutoBackupViewState extends ConsumerState { await secureStore.write(key: "auto_adk_string", value: adkString); await secureStore.write( - key: "auto_adk_version_string", value: adkVersion.toString()); + key: "auto_adk_version_string", + value: adkVersion.toString(), + ); final DateTime now = DateTime.now(); final String fileToSave = createAutoBackupFilename(pathToSave, now); @@ -193,7 +196,7 @@ class _EditAutoBackupViewState extends ConsumerState { secureStorage: ref.read(secureStoreProvider), ); - bool result = await SWB.encryptStackWalletWithADK( + final bool result = await SWB.encryptStackWalletWithADK( fileToSave, adkString, jsonEncode(backup), @@ -311,18 +314,20 @@ class _EditAutoBackupViewState extends ConsumerState { ), body: Padding( padding: const EdgeInsets.all(16), - child: LayoutBuilder(builder: (context, constraints) { - return SingleChildScrollView( - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: constraints.maxHeight, + child: LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: constraints.maxHeight, + ), + child: IntrinsicHeight( + child: child, + ), ), - child: IntrinsicHeight( - child: child, - ), - ), - ); - }), + ); + }, + ), ), ), ), @@ -448,7 +453,8 @@ class _EditAutoBackupViewState extends ConsumerState { ), GestureDetector( key: const Key( - "createBackupPasswordFieldShowPasswordButtonKey"), + "createBackupPasswordFieldShowPasswordButtonKey", + ), onTap: () async { setState(() { hidePassword = !hidePassword; @@ -479,7 +485,7 @@ class _EditAutoBackupViewState extends ConsumerState { } final result = zxcvbn.evaluate(newValue); String suggestionsAndTips = ""; - for (var sug in result.feedback.suggestions!.toSet()) { + for (final sug in result.feedback.suggestions!.toSet()) { suggestionsAndTips += "$sug\n"; } suggestionsAndTips += result.feedback.warning!; @@ -492,7 +498,9 @@ class _EditAutoBackupViewState extends ConsumerState { // hack fix to format back string returned from zxcvbn if (feedback.contains("phrasesNo need")) { feedback = feedback.replaceFirst( - "phrasesNo need", "phrases\nNo need"); + "phrasesNo need", + "phrases\nNo need", + ); } if (feedback.endsWith("\n")) { @@ -580,7 +588,8 @@ class _EditAutoBackupViewState extends ConsumerState { ), GestureDetector( key: const Key( - "createBackupPasswordFieldShowPasswordButtonKey"), + "createBackupPasswordFieldShowPasswordButtonKey", + ), onTap: () async { setState(() { hidePassword = !hidePassword; @@ -735,9 +744,13 @@ class _EditAutoBackupViewState extends ConsumerState { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - Format.prettyFrequencyType(ref.watch( + Format.prettyFrequencyType( + ref.watch( prefsChangeNotifierProvider.select( - (value) => value.backupFrequencyType))), + (value) => value.backupFrequencyType, + ), + ), + ), style: STextStyles.itemSubtitle12(context), ), Padding( @@ -755,7 +768,7 @@ class _EditAutoBackupViewState extends ConsumerState { ), ), ), - ) + ), ], ), if (!isDesktop) const Spacer(), @@ -799,7 +812,7 @@ class _EditAutoBackupViewState extends ConsumerState { "Save", style: STextStyles.button(context), ), - ) + ), ], ), ); diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/restore_from_encrypted_string_view.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/restore_from_encrypted_string_view.dart index 3bbb3cd5e..881319202 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/restore_from_encrypted_string_view.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/restore_from_encrypted_string_view.dart @@ -12,10 +12,9 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:tuple/tuple.dart'; + import '../../../../notifications/show_flush_bar.dart'; -import '../../../home_view/home_view.dart'; -import 'helpers/restore_create_backup.dart'; -import 'sub_views/stack_restore_progress_view.dart'; import '../../../../route_generator.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/assets.dart'; @@ -25,13 +24,15 @@ import '../../../../widgets/background.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../../widgets/loading_indicator.dart'; import '../../../../widgets/stack_text_field.dart'; -import 'package:tuple/tuple.dart'; +import '../../../home_view/home_view.dart'; +import 'helpers/restore_create_backup.dart'; +import 'sub_views/stack_restore_progress_view.dart'; class RestoreFromEncryptedStringView extends ConsumerStatefulWidget { const RestoreFromEncryptedStringView({ - Key? key, + super.key, required this.encrypted, - }) : super(key: key); + }); static const String routeName = "/restoreFromEncryptedString"; @@ -131,7 +132,8 @@ class _RestoreFromEncryptedStringViewState ), GestureDetector( key: const Key( - "restoreFromFilePasswordFieldShowPasswordButtonKey"), + "restoreFromFilePasswordFieldShowPasswordButtonKey", + ), onTap: () async { setState(() { hidePassword = !hidePassword; @@ -181,7 +183,8 @@ class _RestoreFromEncryptedStringViewState if (FocusScope.of(context).hasFocus) { FocusScope.of(context).unfocus(); await Future.delayed( - const Duration(milliseconds: 75)); + const Duration(milliseconds: 75), + ); } bool shouldPop = false; @@ -205,8 +208,8 @@ class _RestoreFromEncryptedStringViewState "Decrypting Stack backup file", style: STextStyles.pageTitleH2( - context) - .copyWith( + context, + ).copyWith( color: Theme.of(context) .extension< StackColors>()! diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/restore_from_file_view.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/restore_from_file_view.dart index d7a659ac2..0f9cc5b39 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/restore_from_file_view.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/restore_from_file_view.dart @@ -40,7 +40,7 @@ import 'helpers/swb_file_system.dart'; import 'sub_views/stack_restore_progress_view.dart'; class RestoreFromFileView extends ConsumerStatefulWidget { - const RestoreFromFileView({Key? key}) : super(key: key); + const RestoreFromFileView({super.key}); static const String routeName = "/restoreFromFile"; @@ -85,113 +85,190 @@ class _RestoreFromFileViewState extends ConsumerState { final isDesktop = Util.isDesktop; return ConditionalParent( - condition: !isDesktop, - builder: (child) { - return Background( - child: Scaffold( - backgroundColor: - Theme.of(context).extension()!.background, - appBar: AppBar( - leading: AppBarBackButton( - onPressed: () async { - if (FocusScope.of(context).hasFocus) { - FocusScope.of(context).unfocus(); - await Future.delayed( - const Duration(milliseconds: 75)); - } - if (mounted) { - Navigator.of(context).pop(); - } - }, - ), - title: Text( - "Restore from file", - style: STextStyles.navBarTitle(context), - ), - ), - body: Padding( - padding: const EdgeInsets.all(16), - child: LayoutBuilder( - builder: (context, constraints) { - return SingleChildScrollView( - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: constraints.maxHeight, - ), - child: IntrinsicHeight( - child: child, - ), - ), + condition: !isDesktop, + builder: (child) { + return Background( + child: Scaffold( + backgroundColor: + Theme.of(context).extension()!.background, + appBar: AppBar( + leading: AppBarBackButton( + onPressed: () async { + if (FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + await Future.delayed( + const Duration(milliseconds: 75), ); - }, - ), - ), - ), - ); - }, - child: ConditionalParent( - condition: isDesktop, - builder: (child) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.only(bottom: 10.0), - child: Text( - "Choose file location", - style: STextStyles.desktopTextExtraExtraSmall(context) - .copyWith( - color: Theme.of(context) - .extension()! - .textDark3), - textAlign: TextAlign.left, - ), - ), - child, - ], - ); - }, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - TextField( - autocorrect: Util.isDesktop ? false : true, - enableSuggestions: Util.isDesktop ? false : true, - onTap: () async { - try { - await stackFileSystem.prepareStorage(); - if (mounted) { - await stackFileSystem.openFile(context); - } - - if (mounted) { - setState(() { - fileLocationController.text = - stackFileSystem.filePath ?? ""; - }); - } - } catch (e, s) { - Logging.instance.log("$e\n$s", level: LogLevel.Error); + } + if (mounted) { + Navigator.of(context).pop(); } }, - controller: fileLocationController, + ), + title: Text( + "Restore from file", + style: STextStyles.navBarTitle(context), + ), + ), + body: Padding( + padding: const EdgeInsets.all(16), + child: LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: constraints.maxHeight, + ), + child: IntrinsicHeight( + child: child, + ), + ), + ); + }, + ), + ), + ), + ); + }, + child: ConditionalParent( + condition: isDesktop, + builder: (child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 10.0), + child: Text( + "Choose file location", + style: + STextStyles.desktopTextExtraExtraSmall(context).copyWith( + color: + Theme.of(context).extension()!.textDark3, + ), + textAlign: TextAlign.left, + ), + ), + child, + ], + ); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + TextField( + autocorrect: Util.isDesktop ? false : true, + enableSuggestions: Util.isDesktop ? false : true, + onTap: () async { + try { + await stackFileSystem.prepareStorage(); + if (mounted) { + await stackFileSystem.openFile(context); + } + + if (mounted) { + setState(() { + fileLocationController.text = + stackFileSystem.filePath ?? ""; + }); + } + } catch (e, s) { + Logging.instance.log("$e\n$s", level: LogLevel.Error); + } + }, + controller: fileLocationController, + style: STextStyles.field(context), + decoration: InputDecoration( + hintText: "Choose file...", + hintStyle: STextStyles.fieldLabel(context), + suffixIcon: UnconstrainedBox( + child: Row( + children: [ + const SizedBox( + width: 16, + ), + SvgPicture.asset( + Assets.svg.folder, + color: Theme.of(context) + .extension()! + .textDark3, + width: 16, + height: 16, + ), + const SizedBox( + width: 12, + ), + ], + ), + ), + ), + key: const Key("restoreFromFileLocationTextFieldKey"), + readOnly: true, + toolbarOptions: const ToolbarOptions( + copy: true, + cut: false, + paste: false, + selectAll: false, + ), + onChanged: (newValue) {}, + ), + SizedBox( + height: !isDesktop ? 8 : 24, + ), + if (isDesktop) + Padding( + padding: const EdgeInsets.only(bottom: 10.0), + child: Text( + "Enter passphrase", + style: + STextStyles.desktopTextExtraExtraSmall(context).copyWith( + color: + Theme.of(context).extension()!.textDark3, + ), + textAlign: TextAlign.left, + ), + ), + ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: TextField( + key: const Key("restoreFromFilePasswordFieldKey"), + focusNode: passwordFocusNode, + controller: passwordController, style: STextStyles.field(context), - decoration: InputDecoration( - hintText: "Choose file...", - hintStyle: STextStyles.fieldLabel(context), + obscureText: hidePassword, + enableSuggestions: false, + autocorrect: false, + decoration: standardInputDecoration( + "Enter passphrase", + passwordFocusNode, + context, + ).copyWith( + labelStyle: + isDesktop ? STextStyles.fieldLabel(context) : null, suffixIcon: UnconstrainedBox( child: Row( children: [ const SizedBox( width: 16, ), - SvgPicture.asset( - Assets.svg.folder, - color: Theme.of(context) - .extension()! - .textDark3, - width: 16, - height: 16, + GestureDetector( + key: const Key( + "restoreFromFilePasswordFieldShowPasswordButtonKey", + ), + onTap: () async { + setState(() { + hidePassword = !hidePassword; + }); + }, + child: SvgPicture.asset( + hidePassword ? Assets.svg.eye : Assets.svg.eyeSlash, + color: Theme.of(context) + .extension()! + .textDark3, + width: 16, + height: 16, + ), ), const SizedBox( width: 12, @@ -200,396 +277,319 @@ class _RestoreFromFileViewState extends ConsumerState { ), ), ), - key: const Key("restoreFromFileLocationTextFieldKey"), - readOnly: true, - toolbarOptions: const ToolbarOptions( - copy: true, - cut: false, - paste: false, - selectAll: false, - ), - onChanged: (newValue) {}, + onChanged: (newValue) { + setState(() {}); + }, ), - SizedBox( - height: !isDesktop ? 8 : 24, - ), - if (isDesktop) - Padding( - padding: const EdgeInsets.only(bottom: 10.0), - child: Text( - "Enter passphrase", - style: STextStyles.desktopTextExtraExtraSmall(context) - .copyWith( - color: Theme.of(context) - .extension()! - .textDark3), - textAlign: TextAlign.left, - ), - ), - ClipRRect( - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, - ), - child: TextField( - key: const Key("restoreFromFilePasswordFieldKey"), - focusNode: passwordFocusNode, - controller: passwordController, - style: STextStyles.field(context), - obscureText: hidePassword, - enableSuggestions: false, - autocorrect: false, - decoration: standardInputDecoration( - "Enter passphrase", - passwordFocusNode, - context, - ).copyWith( - labelStyle: - isDesktop ? STextStyles.fieldLabel(context) : null, - suffixIcon: UnconstrainedBox( - child: Row( - children: [ - const SizedBox( - width: 16, - ), - GestureDetector( - key: const Key( - "restoreFromFilePasswordFieldShowPasswordButtonKey"), - onTap: () async { - setState(() { - hidePassword = !hidePassword; - }); - }, - child: SvgPicture.asset( - hidePassword - ? Assets.svg.eye - : Assets.svg.eyeSlash, - color: Theme.of(context) - .extension()! - .textDark3, - width: 16, - height: 16, - ), - ), - const SizedBox( - width: 12, - ), - ], - ), - ), - ), - onChanged: (newValue) { - setState(() {}); - }, - ), - ), - const SizedBox( - height: 16, - ), - if (!isDesktop) const Spacer(), - !isDesktop - ? TextButton( - style: passwordController.text.isEmpty || - fileLocationController.text.isEmpty - ? Theme.of(context) - .extension()! - .getPrimaryDisabledButtonStyle(context) - : Theme.of(context) - .extension()! - .getPrimaryEnabledButtonStyle(context), - onPressed: passwordController.text.isEmpty || - fileLocationController.text.isEmpty - ? null - : () async { - final String fileToRestore = - fileLocationController.text; - final String passphrase = passwordController.text; + ), + const SizedBox( + height: 16, + ), + if (!isDesktop) const Spacer(), + !isDesktop + ? TextButton( + style: passwordController.text.isEmpty || + fileLocationController.text.isEmpty + ? Theme.of(context) + .extension()! + .getPrimaryDisabledButtonStyle(context) + : Theme.of(context) + .extension()! + .getPrimaryEnabledButtonStyle(context), + onPressed: passwordController.text.isEmpty || + fileLocationController.text.isEmpty + ? null + : () async { + final String fileToRestore = + fileLocationController.text; + final String passphrase = passwordController.text; - if (FocusScope.of(context).hasFocus) { - FocusScope.of(context).unfocus(); - await Future.delayed( - const Duration(milliseconds: 75)); - } + if (FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + await Future.delayed( + const Duration(milliseconds: 75), + ); + } - if (!(await File(fileToRestore).exists())) { + if (!(await File(fileToRestore).exists())) { + await showFloatingFlushBar( + type: FlushBarType.warning, + message: "Backup file does not exist", + context: context, + ); + return; + } + + bool shouldPop = false; + unawaited( + showDialog( + barrierDismissible: false, + context: context, + builder: (_) => WillPopScope( + onWillPop: () async { + return shouldPop; + }, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.stretch, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Material( + color: Colors.transparent, + child: Center( + child: Text( + "Decrypting Stack backup file", + style: STextStyles.pageTitleH2( + context, + ).copyWith( + color: Theme.of(context) + .extension()! + .textWhite, + ), + ), + ), + ), + const SizedBox( + height: 64, + ), + const Center( + child: LoadingIndicator( + width: 100, + ), + ), + ], + ), + ), + ), + ); + + final String? jsonString = await compute( + SWB.decryptStackWalletWithPassphrase, + Tuple2(fileToRestore, passphrase), + debugLabel: "stack wallet decryption compute", + ); + + if (mounted) { + // pop LoadingIndicator + shouldPop = true; + Navigator.of(context).pop(); + + passwordController.text = ""; + + if (jsonString == null) { await showFloatingFlushBar( type: FlushBarType.warning, - message: "Backup file does not exist", + message: "Failed to decrypt backup file", context: context, ); return; } - bool shouldPop = false; - unawaited( - showDialog( - barrierDismissible: false, - context: context, - builder: (_) => WillPopScope( - onWillPop: () async { - return shouldPop; - }, - child: Column( - crossAxisAlignment: - CrossAxisAlignment.stretch, - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - Material( - color: Colors.transparent, - child: Center( - child: Text( - "Decrypting Stack backup file", - style: STextStyles.pageTitleH2( - context) - .copyWith( - color: Theme.of(context) - .extension()! - .textWhite, - ), - ), - ), - ), - const SizedBox( - height: 64, - ), - const Center( - child: LoadingIndicator( - width: 100, - ), - ), - ], - ), + await Navigator.of(context).push( + RouteGenerator.getRoute( + builder: (_) => StackRestoreProgressView( + jsonString: jsonString, + shouldPushToHome: true, ), ), ); + } + }, + child: Text( + "Restore", + style: STextStyles.button(context), + ), + ) + : Row( + children: [ + PrimaryButton( + width: 183, + buttonHeight: ButtonHeight.m, + label: "Restore", + enabled: !(passwordController.text.isEmpty || + fileLocationController.text.isEmpty), + onPressed: passwordController.text.isEmpty || + fileLocationController.text.isEmpty + ? null + : () async { + final String fileToRestore = + fileLocationController.text; + final String passphrase = + passwordController.text; - final String? jsonString = await compute( - SWB.decryptStackWalletWithPassphrase, - Tuple2(fileToRestore, passphrase), - debugLabel: "stack wallet decryption compute", - ); + if (FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + await Future.delayed( + const Duration(milliseconds: 75), + ); + } - if (mounted) { - // pop LoadingIndicator - shouldPop = true; - Navigator.of(context).pop(); - - passwordController.text = ""; - - if (jsonString == null) { + if (!(await File(fileToRestore).exists())) { await showFloatingFlushBar( type: FlushBarType.warning, - message: "Failed to decrypt backup file", + message: "Backup file does not exist", context: context, ); return; } - await Navigator.of(context).push( - RouteGenerator.getRoute( - builder: (_) => StackRestoreProgressView( - jsonString: jsonString, - shouldPushToHome: true, + bool shouldPop = false; + unawaited( + showDialog( + barrierDismissible: false, + context: context, + builder: (_) => WillPopScope( + onWillPop: () async { + return shouldPop; + }, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.stretch, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Material( + color: Colors.transparent, + child: Center( + child: Text( + "Decrypting Stack backup file", + style: STextStyles.pageTitleH2( + context, + ).copyWith( + color: Theme.of(context) + .extension()! + .textWhite, + ), + ), + ), + ), + const SizedBox( + height: 64, + ), + const Center( + child: LoadingIndicator( + width: 100, + ), + ), + ], + ), ), ), ); - } - }, - child: Text( - "Restore", - style: STextStyles.button(context), - ), - ) - : Row( - children: [ - PrimaryButton( - width: 183, - buttonHeight: ButtonHeight.m, - label: "Restore", - enabled: !(passwordController.text.isEmpty || - fileLocationController.text.isEmpty), - onPressed: passwordController.text.isEmpty || - fileLocationController.text.isEmpty - ? null - : () async { - final String fileToRestore = - fileLocationController.text; - final String passphrase = - passwordController.text; - if (FocusScope.of(context).hasFocus) { - FocusScope.of(context).unfocus(); - await Future.delayed( - const Duration(milliseconds: 75)); - } + final String? jsonString = await compute( + SWB.decryptStackWalletWithPassphrase, + Tuple2(fileToRestore, passphrase), + debugLabel: "stack wallet decryption compute", + ); - if (!(await File(fileToRestore).exists())) { + if (mounted) { + // pop LoadingIndicator + shouldPop = true; + Navigator.of( + context, + rootNavigator: true, + ).pop(); + + passwordController.text = ""; + + if (jsonString == null) { await showFloatingFlushBar( type: FlushBarType.warning, - message: "Backup file does not exist", + message: "Failed to decrypt backup file", context: context, ); return; } - bool shouldPop = false; - unawaited( - showDialog( - barrierDismissible: false, - context: context, - builder: (_) => WillPopScope( - onWillPop: () async { - return shouldPop; - }, - child: Column( - crossAxisAlignment: - CrossAxisAlignment.stretch, - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - Material( - color: Colors.transparent, - child: Center( - child: Text( - "Decrypting Stack backup file", - style: - STextStyles.pageTitleH2( - context) - .copyWith( - color: Theme.of(context) - .extension< - StackColors>()! - .textWhite, - ), + await showDialog( + context: context, + useSafeArea: false, + barrierDismissible: false, + builder: (context) { + return DesktopDialog( + maxHeight: 750, + maxWidth: 600, + child: LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: + constraints.maxHeight, ), - ), - ), - const SizedBox( - height: 64, - ), - const Center( - child: LoadingIndicator( - width: 100, - ), - ), - ], - ), - ), - ), - ); - - final String? jsonString = await compute( - SWB.decryptStackWalletWithPassphrase, - Tuple2(fileToRestore, passphrase), - debugLabel: - "stack wallet decryption compute", - ); - - if (mounted) { - // pop LoadingIndicator - shouldPop = true; - Navigator.of( - context, - rootNavigator: true, - ).pop(); - - passwordController.text = ""; - - if (jsonString == null) { - await showFloatingFlushBar( - type: FlushBarType.warning, - message: - "Failed to decrypt backup file", - context: context, - ); - return; - } - - await showDialog( - context: context, - useSafeArea: false, - barrierDismissible: false, - builder: (context) { - return DesktopDialog( - maxHeight: 750, - maxWidth: 600, - child: LayoutBuilder( - builder: (context, constraints) { - return SingleChildScrollView( - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: - constraints.maxHeight, - ), - child: IntrinsicHeight( - child: Column( + child: IntrinsicHeight( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + children: [ + Row( mainAxisAlignment: MainAxisAlignment - .start, + .spaceBetween, children: [ - Row( - mainAxisAlignment: - MainAxisAlignment - .spaceBetween, - children: [ - Padding( - padding: - const EdgeInsets - .all( - 32), - child: Text( - "Restore ${AppConfig.appName}", - style: STextStyles - .desktopH3( - context), - textAlign: - TextAlign - .center, - ), - ), - const DesktopDialogCloseButton(), - ], - ), Padding( padding: const EdgeInsets - .symmetric( - horizontal: - 32), - child: - StackRestoreProgressView( - jsonString: - jsonString, + .all( + 32, + ), + child: Text( + "Restore ${AppConfig.appName}", + style: STextStyles + .desktopH3( + context, + ), + textAlign: + TextAlign + .center, ), ), - const SizedBox( - height: 32, - ), + const DesktopDialogCloseButton(), ], ), - ), + Padding( + padding: + const EdgeInsets + .symmetric( + horizontal: 32, + ), + child: + StackRestoreProgressView( + jsonString: + jsonString, + ), + ), + const SizedBox( + height: 32, + ), + ], ), - ); - }, - ), - ); - }); - } - }, - ), - const SizedBox( - width: 16, - ), - SecondaryButton( - width: 183, - buttonHeight: ButtonHeight.m, - label: "Cancel", - onPressed: () {}, - ), - ], - ), - ], - ), - )); + ), + ), + ); + }, + ), + ); + }, + ); + } + }, + ), + const SizedBox( + width: 16, + ), + SecondaryButton( + width: 183, + buttonHeight: ButtonHeight.m, + label: "Cancel", + onPressed: () {}, + ), + ], + ), + ], + ), + ), + ); } } diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/stack_backup_view.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/stack_backup_view.dart index 2db8cbc9d..ac90e486a 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/stack_backup_view.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/stack_backup_view.dart @@ -23,8 +23,8 @@ import '../../../../widgets/rounded_white_container.dart'; class StackBackupView extends StatelessWidget { const StackBackupView({ - Key? key, - }) : super(key: key); + super.key, + }); static const String routeName = "/stackBackup"; diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/backup_frequency_type_select_sheet.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/backup_frequency_type_select_sheet.dart index 01203c0ae..d61c93c62 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/backup_frequency_type_select_sheet.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/backup_frequency_type_select_sheet.dart @@ -10,6 +10,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../../../../providers/global/prefs_provider.dart'; import '../../../../../themes/stack_colors.dart'; import '../../../../../utilities/constants.dart'; @@ -18,8 +19,8 @@ import '../../../../../utilities/text_styles.dart'; class BackupFrequencyTypeSelectSheet extends ConsumerWidget { const BackupFrequencyTypeSelectSheet({ - Key? key, - }) : super(key: key); + super.key, + }); String prettyFrequencyType(BackupFrequencyType type) { switch (type) { @@ -117,9 +118,10 @@ class BackupFrequencyTypeSelectSheet extends ConsumerWidget { .radioButtonIconEnabled, value: BackupFrequencyType.values[i], groupValue: ref.watch( - prefsChangeNotifierProvider.select( - (value) => - value.backupFrequencyType)), + prefsChangeNotifierProvider.select( + (value) => value.backupFrequencyType, + ), + ), onChanged: (x) { ref .read(prefsChangeNotifierProvider) @@ -137,7 +139,8 @@ class BackupFrequencyTypeSelectSheet extends ConsumerWidget { children: [ Text( prettyFrequencyType( - BackupFrequencyType.values[i]), + BackupFrequencyType.values[i], + ), style: STextStyles.titleBold12(context), textAlign: TextAlign.left, ), diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/recovery_phrase_view.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/recovery_phrase_view.dart index af533c9c4..dd1478b4a 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/recovery_phrase_view.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/recovery_phrase_view.dart @@ -21,11 +21,11 @@ import '../../../../../widgets/custom_buttons/app_bar_icon_button.dart'; class RecoverPhraseView extends StatelessWidget { const RecoverPhraseView({ - Key? key, + super.key, required this.walletName, required this.mnemonic, this.clipboardInterface = const ClipboardWrapper(), - }) : super(key: key); + }); static const String routeName = "/recoverPhrase"; diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/stack_restore_progress_view.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/stack_restore_progress_view.dart index bc8d9a329..b160eb2e1 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/stack_restore_progress_view.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/stack_restore_progress_view.dart @@ -44,11 +44,11 @@ import '../sub_widgets/restoring_wallet_card.dart'; class StackRestoreProgressView extends ConsumerStatefulWidget { const StackRestoreProgressView({ - Key? key, + super.key, required this.jsonString, this.fromFile = false, this.shouldPushToHome = false, - }) : super(key: key); + }); final String jsonString; final bool fromFile; @@ -65,41 +65,43 @@ class _StackRestoreProgressViewState Future _cancel() async { bool shouldPop = false; - unawaited(showDialog( - barrierDismissible: false, - context: context, - builder: (_) => WillPopScope( - onWillPop: () async { - return shouldPop; - }, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Material( - color: Colors.transparent, - child: Center( - child: Text( - "Cancelling restore. Please wait.", - style: STextStyles.pageTitleH2(context).copyWith( - color: - Theme.of(context).extension()!.textWhite, + unawaited( + showDialog( + barrierDismissible: false, + context: context, + builder: (_) => WillPopScope( + onWillPop: () async { + return shouldPop; + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Material( + color: Colors.transparent, + child: Center( + child: Text( + "Cancelling restore. Please wait.", + style: STextStyles.pageTitleH2(context).copyWith( + color: + Theme.of(context).extension()!.textWhite, + ), ), ), ), - ), - const SizedBox( - height: 64, - ), - const Center( - child: LoadingIndicator( - width: 100, + const SizedBox( + height: 64, ), - ), - ], + const Center( + child: LoadingIndicator( + width: 100, + ), + ), + ], + ), ), ), - )); + ); await SWB.cancelRestore(); shouldPop = true; @@ -108,9 +110,13 @@ class _StackRestoreProgressViewState if (mounted) { !isDesktop - ? Navigator.of(context).popUntil(ModalRoute.withName(widget.fromFile - ? RestoreFromEncryptedStringView.routeName - : StackBackupView.routeName)) + ? Navigator.of(context).popUntil( + ModalRoute.withName( + widget.fromFile + ? RestoreFromEncryptedStringView.routeName + : StackBackupView.routeName, + ), + ) : Navigator.of(context).popUntil((_) => count++ >= 2); } } @@ -219,9 +225,10 @@ class _StackRestoreProgressViewState void _addWalletsToHomeView() { ref.read(pWallets).loadAfterStackRestore( - ref.read(prefsChangeNotifierProvider), - ref.read(stackRestoringUIStateProvider).wallets, - Util.isDesktop); + ref.read(prefsChangeNotifierProvider), + ref.read(stackRestoringUIStateProvider).wallets, + Util.isDesktop, + ); } @override @@ -234,7 +241,7 @@ class _StackRestoreProgressViewState @override Widget build(BuildContext context) { - bool isDesktop = Util.isDesktop; + final bool isDesktop = Util.isDesktop; return ConditionalParent( condition: !isDesktop, @@ -250,7 +257,8 @@ class _StackRestoreProgressViewState if (FocusScope.of(context).hasFocus) { FocusScope.of(context).unfocus(); await Future.delayed( - const Duration(milliseconds: 75)); + const Duration(milliseconds: 75), + ); } if (_success) { _addWalletsToHomeView(); @@ -300,8 +308,10 @@ class _StackRestoreProgressViewState ), Consumer( builder: (_, ref, __) { - final state = ref.watch(stackRestoringUIStateProvider - .select((value) => value.preferences)); + final state = ref.watch( + stackRestoringUIStateProvider + .select((value) => value.preferences), + ); return !isDesktop ? RestoringItemCard( left: SizedBox( @@ -387,8 +397,10 @@ class _StackRestoreProgressViewState ), Consumer( builder: (_, ref, __) { - final state = ref.watch(stackRestoringUIStateProvider - .select((value) => value.addressBook)); + final state = ref.watch( + stackRestoringUIStateProvider + .select((value) => value.addressBook), + ); return !isDesktop ? RestoringItemCard( left: SizedBox( @@ -472,8 +484,10 @@ class _StackRestoreProgressViewState ), Consumer( builder: (_, ref, __) { - final state = ref.watch(stackRestoringUIStateProvider - .select((value) => value.nodes)); + final state = ref.watch( + stackRestoringUIStateProvider + .select((value) => value.nodes), + ); return !isDesktop ? RestoringItemCard( left: SizedBox( @@ -550,7 +564,8 @@ class _StackRestoreProgressViewState style: STextStyles.errorSmall(context), ) : null, - )); + ), + ); }, ), const SizedBox( @@ -558,8 +573,10 @@ class _StackRestoreProgressViewState ), Consumer( builder: (_, ref, __) { - final state = ref.watch(stackRestoringUIStateProvider - .select((value) => value.trades)); + final state = ref.watch( + stackRestoringUIStateProvider + .select((value) => value.trades), + ); return !isDesktop ? RestoringItemCard( left: SizedBox( @@ -651,8 +668,10 @@ class _StackRestoreProgressViewState height: 8, ), ...ref - .watch(stackRestoringUIStateProvider - .select((value) => value.walletStateProviders)) + .watch( + stackRestoringUIStateProvider + .select((value) => value.walletStateProviders), + ) .values .map( (provider) => Padding( @@ -708,12 +727,13 @@ class _StackRestoreProgressViewState enabled: true, label: "Done", onPressed: () async { - DesktopMenuItemId keyID = + final DesktopMenuItemId keyID = DesktopMenuItemId.myStack; ref - .read(currentDesktopMenuItemProvider - .state) + .read( + currentDesktopMenuItemProvider.state, + ) .state = keyID; if (widget.shouldPushToHome) { @@ -728,7 +748,8 @@ class _StackRestoreProgressViewState Navigator.of(context, rootNavigator: true) .popUntil( ModalRoute.withName( - DesktopHomeView.routeName), + DesktopHomeView.routeName, + ), ); } }, diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_widgets/restoring_item_card.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_widgets/restoring_item_card.dart index 6e00445ff..0d4f66c70 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_widgets/restoring_item_card.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_widgets/restoring_item_card.dart @@ -15,7 +15,7 @@ import '../../../../../widgets/rounded_white_container.dart'; class RestoringItemCard extends StatelessWidget { const RestoringItemCard({ - Key? key, + super.key, required this.left, required this.right, required this.title, @@ -23,7 +23,7 @@ class RestoringItemCard extends StatelessWidget { this.leftSize = 32.0, this.button, this.onRightTapped, - }) : super(key: key); + }); final Widget left; final Widget right; diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_widgets/restoring_wallet_card.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_widgets/restoring_wallet_card.dart index 29bebd5f1..f38104ccd 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_widgets/restoring_wallet_card.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_widgets/restoring_wallet_card.dart @@ -13,9 +13,8 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../../../../models/wallet_restore_state.dart'; -import '../sub_views/recovery_phrase_view.dart'; -import 'restoring_item_card.dart'; import '../../../../../providers/stack_restore/stack_restoring_ui_state_provider.dart'; import '../../../../../route_generator.dart'; import '../../../../../themes/coin_icon_provider.dart'; @@ -27,12 +26,14 @@ import '../../../../../utilities/text_styles.dart'; import '../../../../../utilities/util.dart'; import '../../../../../widgets/loading_indicator.dart'; import '../../../../../widgets/rounded_container.dart'; +import '../sub_views/recovery_phrase_view.dart'; +import 'restoring_item_card.dart'; class RestoringWalletCard extends ConsumerStatefulWidget { const RestoringWalletCard({ - Key? key, + super.key, required this.provider, - }) : super(key: key); + }); final ChangeNotifierProvider provider; @@ -106,8 +107,9 @@ class _RestoringWalletCardState extends ConsumerState { final wallet = ref.read(provider).wallet!; ref.read(stackRestoringUIStateProvider).update( - walletId: wallet.walletId, - restoringStatus: StackRestoringStatus.restoring); + walletId: wallet.walletId, + restoringStatus: StackRestoringStatus.restoring, + ); try { await wallet.recover(isRescan: true); @@ -191,9 +193,10 @@ class _RestoringWalletCardState extends ConsumerState { child: Text( "Show recovery phrase", style: STextStyles.infoSmall(context).copyWith( - color: Theme.of(context) - .extension()! - .accentColorDark), + color: Theme.of(context) + .extension()! + .accentColorDark, + ), ), ), ), @@ -229,8 +232,9 @@ class _RestoringWalletCardState extends ConsumerState { final wallet = ref.read(provider).wallet!; ref.read(stackRestoringUIStateProvider).update( - walletId: wallet.walletId, - restoringStatus: StackRestoringStatus.restoring); + walletId: wallet.walletId, + restoringStatus: StackRestoringStatus.restoring, + ); try { // final mnemonicList = await manager.mnemonic; @@ -339,9 +343,10 @@ class _RestoringWalletCardState extends ConsumerState { child: Text( "Show recovery phrase", style: STextStyles.infoSmall(context).copyWith( - color: Theme.of(context) - .extension()! - .accentColorDark), + color: Theme.of(context) + .extension()! + .accentColorDark, + ), ), ), ), diff --git a/lib/pages/settings_views/global_settings_view/startup_preferences/startup_preferences_view.dart b/lib/pages/settings_views/global_settings_view/startup_preferences/startup_preferences_view.dart index dad06ecb1..3970ff9c8 100644 --- a/lib/pages/settings_views/global_settings_view/startup_preferences/startup_preferences_view.dart +++ b/lib/pages/settings_views/global_settings_view/startup_preferences/startup_preferences_view.dart @@ -13,8 +13,8 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; + import '../../../../app_config.dart'; -import 'startup_wallet_selection_view.dart'; import '../../../../providers/providers.dart'; import '../../../../themes/coin_icon_provider.dart'; import '../../../../themes/stack_colors.dart'; @@ -24,9 +24,10 @@ import '../../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../../widgets/background.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../../widgets/rounded_white_container.dart'; +import 'startup_wallet_selection_view.dart'; class StartupPreferencesView extends ConsumerStatefulWidget { - const StartupPreferencesView({Key? key}) : super(key: key); + const StartupPreferencesView({super.key}); static const String routeName = "/startupPreferences"; @@ -130,15 +131,19 @@ class _StartupPreferencesViewState value: false, groupValue: ref.watch( prefsChangeNotifierProvider - .select((value) => value - .gotoWalletOnStartup), + .select( + (value) => + value.gotoWalletOnStartup, + ), ), onChanged: (value) { if (value is bool) { ref - .read( - prefsChangeNotifierProvider) - .gotoWalletOnStartup = value; + .read( + prefsChangeNotifierProvider, + ) + .gotoWalletOnStartup = + value; } }, ), @@ -155,14 +160,16 @@ class _StartupPreferencesViewState "Home screen", style: STextStyles.titleBold12( - context), + context, + ), textAlign: TextAlign.left, ), Text( "${AppConfig.appName} home screen", style: STextStyles.itemSubtitle( - context), + context, + ), textAlign: TextAlign.left, ), ], @@ -208,15 +215,19 @@ class _StartupPreferencesViewState value: true, groupValue: ref.watch( prefsChangeNotifierProvider - .select((value) => value - .gotoWalletOnStartup), + .select( + (value) => + value.gotoWalletOnStartup, + ), ), onChanged: (value) { if (value is bool) { ref - .read( - prefsChangeNotifierProvider) - .gotoWalletOnStartup = value; + .read( + prefsChangeNotifierProvider, + ) + .gotoWalletOnStartup = + value; } }, ), @@ -233,15 +244,17 @@ class _StartupPreferencesViewState "Specific wallet", style: STextStyles.titleBold12( - context), + context, + ), textAlign: TextAlign.left, ), (safe && ref.watch( prefsChangeNotifierProvider - .select((value) => - value - .startupWalletId), + .select( + (value) => value + .startupWalletId, + ), ) != null) ? Padding( @@ -257,8 +270,11 @@ class _StartupPreferencesViewState ref.watch( pWalletCoin( ref.watch( - prefsChangeNotifierProvider.select((value) => - value.startupWalletId!), + prefsChangeNotifierProvider + .select( + (value) => + value.startupWalletId!, + ), ), ), ), @@ -273,15 +289,19 @@ class _StartupPreferencesViewState ref.watch( pWalletName( ref.watch( - prefsChangeNotifierProvider.select( - (value) => - value.startupWalletId!), + prefsChangeNotifierProvider + .select( + (value) => + value + .startupWalletId!, + ), ), ), ), style: STextStyles .itemSubtitle( - context), + context, + ), ), ], ), @@ -290,7 +310,8 @@ class _StartupPreferencesViewState "Select a specific wallet to load into on startup", style: STextStyles .itemSubtitle( - context), + context, + ), textAlign: TextAlign.left, ), @@ -303,13 +324,19 @@ class _StartupPreferencesViewState ), ), ), - if (!ref.watch(prefsChangeNotifierProvider.select( - (value) => value.gotoWalletOnStartup))) + if (!ref.watch( + prefsChangeNotifierProvider.select( + (value) => value.gotoWalletOnStartup, + ), + )) const SizedBox( height: 12, ), - if (ref.watch(prefsChangeNotifierProvider.select( - (value) => value.gotoWalletOnStartup))) + if (ref.watch( + prefsChangeNotifierProvider.select( + (value) => value.gotoWalletOnStartup, + ), + )) Container( color: Colors.transparent, child: Padding( @@ -341,8 +368,9 @@ class _StartupPreferencesViewState ), onPressed: () { Navigator.of(context).pushNamed( - StartupWalletSelectionView - .routeName); + StartupWalletSelectionView + .routeName, + ); }, child: Column( crossAxisAlignment: @@ -351,7 +379,8 @@ class _StartupPreferencesViewState Text( "Select wallet...", style: STextStyles.link2( - context), + context, + ), textAlign: TextAlign.left, ), ], diff --git a/lib/pages/settings_views/global_settings_view/startup_preferences/startup_wallet_selection_view.dart b/lib/pages/settings_views/global_settings_view/startup_preferences/startup_wallet_selection_view.dart index 0be056535..7c073c5ce 100644 --- a/lib/pages/settings_views/global_settings_view/startup_preferences/startup_wallet_selection_view.dart +++ b/lib/pages/settings_views/global_settings_view/startup_preferences/startup_wallet_selection_view.dart @@ -26,7 +26,7 @@ import '../../../../widgets/custom_buttons/draggable_switch_button.dart'; import '../../../../widgets/rounded_white_container.dart'; class StartupWalletSelectionView extends ConsumerStatefulWidget { - const StartupWalletSelectionView({Key? key}) : super(key: key); + const StartupWalletSelectionView({super.key}); static const String routeName = "/startupWalletSelection"; @override diff --git a/lib/pages/settings_views/global_settings_view/support_view.dart b/lib/pages/settings_views/global_settings_view/support_view.dart index 8c3d14f32..8c348d99a 100644 --- a/lib/pages/settings_views/global_settings_view/support_view.dart +++ b/lib/pages/settings_views/global_settings_view/support_view.dart @@ -24,8 +24,8 @@ import 'package:url_launcher/url_launcher.dart'; class SupportView extends StatelessWidget { const SupportView({ - Key? key, - }) : super(key: key); + super.key, + }); static const String routeName = "/support"; @@ -131,13 +131,13 @@ class SupportView extends StatelessWidget { class AboutItem extends StatelessWidget { const AboutItem({ - Key? key, + super.key, required this.linkUrl, required this.label, required this.buttonText, required this.iconAsset, required this.isDesktop, - }) : super(key: key); + }); final String linkUrl; final String label; @@ -218,7 +218,7 @@ class AboutItem extends StatelessWidget { Text( buttonText, style: STextStyles.desktopTextExtraExtraSmall(context), - ) + ), // BlueTextButton( // text: buttonText, // onTap: () { diff --git a/lib/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_options_view.dart b/lib/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_options_view.dart index d20e9dcbc..b036f89f8 100644 --- a/lib/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_options_view.dart +++ b/lib/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_options_view.dart @@ -10,7 +10,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'wallet_syncing_options_view.dart'; + import '../../../../providers/providers.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/constants.dart'; @@ -23,9 +23,10 @@ import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../../widgets/desktop/desktop_dialog.dart'; import '../../../../widgets/desktop/desktop_dialog_close_button.dart'; import '../../../../widgets/rounded_white_container.dart'; +import 'wallet_syncing_options_view.dart'; class SyncingOptionsView extends ConsumerWidget { - const SyncingOptionsView({Key? key}) : super(key: key); + const SyncingOptionsView({super.key}); static const String routeName = "/syncingOptions"; @@ -314,14 +315,18 @@ class SyncingOptionsView extends ConsumerWidget { ), ), ), - if (ref.watch(prefsChangeNotifierProvider - .select((value) => value.syncType)) != + if (ref.watch( + prefsChangeNotifierProvider + .select((value) => value.syncType), + ) != SyncingType.selectedWalletsAtStartup) const SizedBox( height: 12, ), - if (ref.watch(prefsChangeNotifierProvider - .select((value) => value.syncType)) == + if (ref.watch( + prefsChangeNotifierProvider + .select((value) => value.syncType), + ) == SyncingType.selectedWalletsAtStartup) Container( color: Colors.transparent, @@ -351,7 +356,8 @@ class SyncingOptionsView extends ConsumerWidget { onPressed: () { !isDesktop ? Navigator.of(context).pushNamed( - WalletSyncingOptionsView.routeName) + WalletSyncingOptionsView.routeName, + ) : showDialog( context: context, useSafeArea: false, @@ -370,7 +376,8 @@ class SyncingOptionsView extends ConsumerWidget { Padding( padding: const EdgeInsets.all( - 32), + 32, + ), child: Text( "Select wallets to sync", style: STextStyles @@ -389,7 +396,8 @@ class SyncingOptionsView extends ConsumerWidget { ], ), ); - }); + }, + ); }, child: Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_preferences_view.dart b/lib/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_preferences_view.dart index 58a768a70..988d951f1 100644 --- a/lib/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_preferences_view.dart +++ b/lib/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_preferences_view.dart @@ -10,7 +10,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'syncing_options_view.dart'; + import '../../../../providers/providers.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/constants.dart'; @@ -20,9 +20,10 @@ import '../../../../widgets/background.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../../widgets/custom_buttons/draggable_switch_button.dart'; import '../../../../widgets/rounded_white_container.dart'; +import 'syncing_options_view.dart'; class SyncingPreferencesView extends ConsumerWidget { - const SyncingPreferencesView({Key? key}) : super(key: key); + const SyncingPreferencesView({super.key}); static const String routeName = "/syncingPreferences"; @@ -96,13 +97,17 @@ class SyncingPreferencesView extends ConsumerWidget { textAlign: TextAlign.left, ), Text( - _currentTypeDescription(ref.watch( + _currentTypeDescription( + ref.watch( prefsChangeNotifierProvider.select( - (value) => value.syncType))), + (value) => value.syncType, + ), + ), + ), style: STextStyles.itemSubtitle(context), textAlign: TextAlign.left, - ) + ), ], ), const Spacer(), @@ -145,12 +150,14 @@ class SyncingPreferencesView extends ConsumerWidget { child: DraggableSwitchButton( isOn: ref.watch( prefsChangeNotifierProvider.select( - (value) => value.wifiOnly), + (value) => value.wifiOnly, + ), ), onValueChanged: (newValue) { ref .read( - prefsChangeNotifierProvider) + prefsChangeNotifierProvider, + ) .wifiOnly = newValue; }, ), diff --git a/lib/pages/settings_views/global_settings_view/syncing_preferences_views/wallet_syncing_options_view.dart b/lib/pages/settings_views/global_settings_view/syncing_preferences_views/wallet_syncing_options_view.dart index 9b22c13ae..f974226b6 100644 --- a/lib/pages/settings_views/global_settings_view/syncing_preferences_views/wallet_syncing_options_view.dart +++ b/lib/pages/settings_views/global_settings_view/syncing_preferences_views/wallet_syncing_options_view.dart @@ -13,6 +13,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../../../providers/providers.dart'; import '../../../../themes/coin_icon_provider.dart'; import '../../../../themes/stack_colors.dart'; @@ -29,7 +30,7 @@ import '../../../../widgets/custom_buttons/draggable_switch_button.dart'; import '../../../../widgets/rounded_white_container.dart'; class WalletSyncingOptionsView extends ConsumerWidget { - const WalletSyncingOptionsView({Key? key}) : super(key: key); + const WalletSyncingOptionsView({super.key}); static const String routeName = "/walletSyncingOptions"; @@ -78,160 +79,175 @@ class WalletSyncingOptionsView extends ConsumerWidget { child: child, ); }, - child: LayoutBuilder(builder: (context, constraints) { - return SingleChildScrollView( - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: constraints.maxHeight - 24, - ), - child: IntrinsicHeight( - child: Padding( - padding: const EdgeInsets.all(4), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox( - height: 4, - ), - Text( - "Choose the wallets to sync automatically at startup", - style: STextStyles.smallMed12(context), - ), - const SizedBox( - height: 12, - ), - RoundedWhiteContainer( - padding: const EdgeInsets.all(0), - borderColor: !isDesktop - ? Colors.transparent - : Theme.of(context) - .extension()! - .background, - child: Column( - children: [ - ...walletInfos.map( - (info) => Padding( - padding: const EdgeInsets.all(12), - child: Row( - key: Key( - "syncingPrefsSelectedWalletIdGroupKey_${info.walletId}"), - children: [ - Container( - decoration: BoxDecoration( - color: ref - .watch(pCoinColor(info.coin)) - .withOpacity(0.5), - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, - ), - ), - child: Padding( - padding: const EdgeInsets.all(4), - child: SvgPicture.file( - File( - ref.watch( - coinIconProvider(info.coin), - ), + child: LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: constraints.maxHeight - 24, + ), + child: IntrinsicHeight( + child: Padding( + padding: const EdgeInsets.all(4), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox( + height: 4, + ), + Text( + "Choose the wallets to sync automatically at startup", + style: STextStyles.smallMed12(context), + ), + const SizedBox( + height: 12, + ), + RoundedWhiteContainer( + padding: const EdgeInsets.all(0), + borderColor: !isDesktop + ? Colors.transparent + : Theme.of(context) + .extension()! + .background, + child: Column( + children: [ + ...walletInfos.map( + (info) => Padding( + padding: const EdgeInsets.all(12), + child: Row( + key: Key( + "syncingPrefsSelectedWalletIdGroupKey_${info.walletId}", + ), + children: [ + Container( + decoration: BoxDecoration( + color: ref + .watch(pCoinColor(info.coin)) + .withOpacity(0.5), + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + child: Padding( + padding: const EdgeInsets.all(4), + child: SvgPicture.file( + File( + ref.watch( + coinIconProvider(info.coin), + ), + ), + width: 20, + height: 20, ), - width: 20, - height: 20, ), ), - ), - const SizedBox( - width: 12, - ), - Column( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - info.name, - style: - STextStyles.titleBold12(context), - ), - const SizedBox( - height: 2, - ), - Text( - ref + const SizedBox( + width: 12, + ), + Column( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + info.name, + style: STextStyles.titleBold12( + context), + ), + const SizedBox( + height: 2, + ), + Text( + ref + .watch( + pAmountFormatter(info.coin), + ) + .format( + ref + .watch( + pWalletBalance( + info.walletId, + ), + ) + .total, + ), + style: STextStyles.itemSubtitle( + context), + ), + ], + ), + const Spacer(), + SizedBox( + height: 20, + width: 40, + child: DraggableSwitchButton( + isOn: ref .watch( - pAmountFormatter(info.coin)) - .format(ref - .watch(pWalletBalance( - info.walletId)) - .total), - style: - STextStyles.itemSubtitle(context), - ) - ], - ), - const Spacer(), - SizedBox( - height: 20, - width: 40, - child: DraggableSwitchButton( - isOn: ref - .watch(prefsChangeNotifierProvider - .select((value) => value - .walletIdsSyncOnStartup)) - .contains(info.walletId), - onValueChanged: (value) { - // final syncType = ref - // .read(prefsChangeNotifierProvider) - // .syncType; - final ids = ref - .read(prefsChangeNotifierProvider) - .walletIdsSyncOnStartup - .toList(); - if (value) { - ids.add(info.walletId); - } else { - ids.remove(info.walletId); - } + prefsChangeNotifierProvider + .select( + (value) => value + .walletIdsSyncOnStartup, + ), + ) + .contains(info.walletId), + onValueChanged: (value) { + // final syncType = ref + // .read(prefsChangeNotifierProvider) + // .syncType; + final ids = ref + .read( + prefsChangeNotifierProvider) + .walletIdsSyncOnStartup + .toList(); + if (value) { + ids.add(info.walletId); + } else { + ids.remove(info.walletId); + } - // final wallet = ref - // .read(pWallets) - // .getWallet(info.walletId); - // - // switch (syncType) { - // case SyncingType.currentWalletOnly: - // if (info.walletId == - // ref.read( - // currentWalletIdProvider)) { - // wallet.shouldAutoSync = value; - // } - // break; - // case SyncingType - // .selectedWalletsAtStartup: - // case SyncingType - // .allWalletsOnStartup: - // wallet.shouldAutoSync = value; - // break; - // } + // final wallet = ref + // .read(pWallets) + // .getWallet(info.walletId); + // + // switch (syncType) { + // case SyncingType.currentWalletOnly: + // if (info.walletId == + // ref.read( + // currentWalletIdProvider)) { + // wallet.shouldAutoSync = value; + // } + // break; + // case SyncingType + // .selectedWalletsAtStartup: + // case SyncingType + // .allWalletsOnStartup: + // wallet.shouldAutoSync = value; + // break; + // } - ref - .read(prefsChangeNotifierProvider) - .walletIdsSyncOnStartup = ids; - }, + ref + .read( + prefsChangeNotifierProvider) + .walletIdsSyncOnStartup = ids; + }, + ), ), - ), - ], + ], + ), ), ), - ), - ], + ], + ), ), - ), - ], + ], + ), ), ), ), - ), - ); - }), + ); + }, + ), ), ); } diff --git a/lib/pages/settings_views/global_settings_view/tor_settings/tor_settings_view.dart b/lib/pages/settings_views/global_settings_view/tor_settings/tor_settings_view.dart index 0d6acf41d..425a7accc 100644 --- a/lib/pages/settings_views/global_settings_view/tor_settings/tor_settings_view.dart +++ b/lib/pages/settings_views/global_settings_view/tor_settings/tor_settings_view.dart @@ -14,6 +14,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:lottie/lottie.dart'; + import '../../../../providers/global/prefs_provider.dart'; import '../../../../services/event_bus/events/global/tor_connection_status_changed_event.dart'; import '../../../../services/tor_service.dart'; @@ -34,8 +35,8 @@ import '../../../../widgets/tor_subscription.dart'; class TorSettingsView extends ConsumerStatefulWidget { const TorSettingsView({ - Key? key, - }) : super(key: key); + super.key, + }); static const String routeName = "/torSettings"; @@ -563,24 +564,25 @@ class _UpperCaseTorTextState extends ConsumerState { @override Widget build(BuildContext context) { return TorSubscription( - onTorStatusChanged: (status) { - setState(() { - _status = status; - }); - }, - child: Text( - _label( + onTorStatusChanged: (status) { + setState(() { + _status = status; + }); + }, + child: Text( + _label( + _status, + ), + style: STextStyles.pageTitleH2( + context, + ).copyWith( + color: _color( _status, + Theme.of(context).extension()!, ), - style: STextStyles.pageTitleH2( - context, - ).copyWith( - color: _color( - _status, - Theme.of(context).extension()!, - ), - ), - )); + ), + ), + ); } } diff --git a/lib/pages/settings_views/wallet_settings_view/frost_ms/frost_ms_options_view.dart b/lib/pages/settings_views/wallet_settings_view/frost_ms/frost_ms_options_view.dart index a9bea43be..3404b395e 100644 --- a/lib/pages/settings_views/wallet_settings_view/frost_ms/frost_ms_options_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/frost_ms/frost_ms_options_view.dart @@ -10,10 +10,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../../../frost_route_generator.dart'; -import '../../sub_widgets/settings_list_button.dart'; -import 'frost_participants_view.dart'; -import 'initiate_resharing/initiate_resharing_view.dart'; import '../../../../pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart'; import '../../../../providers/db/main_db_provider.dart'; import '../../../../providers/frost_wallet/frost_wallet_providers.dart'; @@ -31,6 +29,9 @@ import '../../../../widgets/desktop/desktop_app_bar.dart'; import '../../../../widgets/desktop/desktop_scaffold.dart'; import '../../../../widgets/frost_scaffold.dart'; import '../../../../widgets/rounded_white_container.dart'; +import '../../sub_widgets/settings_list_button.dart'; +import 'frost_participants_view.dart'; +import 'initiate_resharing/initiate_resharing_view.dart'; class FrostMSWalletOptionsView extends ConsumerWidget { const FrostMSWalletOptionsView({ @@ -64,20 +65,21 @@ class FrostMSWalletOptionsView extends ConsumerWidget { condition: !Util.isDesktop, builder: (child) => Background( child: Scaffold( - backgroundColor: - Theme.of(context).extension()!.background, - appBar: AppBar( - leading: AppBarBackButton( - onPressed: () { - Navigator.of(context).pop(); - }, - ), - title: Text( - "FROST Multisig options", - style: STextStyles.navBarTitle(context), - ), + backgroundColor: + Theme.of(context).extension()!.background, + appBar: AppBar( + leading: AppBarBackButton( + onPressed: () { + Navigator.of(context).pop(); + }, ), - body: child), + title: Text( + "FROST Multisig options", + style: STextStyles.navBarTitle(context), + ), + ), + body: child, + ), ), child: Padding( padding: const EdgeInsets.only( diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_backup_views/wallet_backup_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_backup_views/wallet_backup_view.dart index 102de03f1..e2b861db1 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_backup_views/wallet_backup_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_backup_views/wallet_backup_view.dart @@ -15,10 +15,9 @@ import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:qr_flutter/qr_flutter.dart'; + import '../../../../app_config.dart'; import '../../../../notifications/show_flush_bar.dart'; -import '../../../add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart'; -import '../../../wallet_view/transaction_views/transaction_details_view.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/address_utils.dart'; import '../../../../utilities/assets.dart'; @@ -33,6 +32,8 @@ import '../../../../widgets/custom_buttons/simple_copy_button.dart'; import '../../../../widgets/detail_item.dart'; import '../../../../widgets/rounded_white_container.dart'; import '../../../../widgets/stack_dialog.dart'; +import '../../../add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart'; +import '../../../wallet_view/transaction_views/transaction_details_view.dart'; class WalletBackupView extends ConsumerWidget { const WalletBackupView({ @@ -94,12 +95,14 @@ class WalletBackupView extends ConsumerWidget { onPressed: () async { await clipboardInterface .setData(ClipboardData(text: mnemonic.join(" "))); - unawaited(showFloatingFlushBar( - type: FlushBarType.info, - message: "Copied to clipboard", - iconAsset: Assets.svg.copy, - context: context, - )); + unawaited( + showFloatingFlushBar( + type: FlushBarType.info, + message: "Copied to clipboard", + iconAsset: Assets.svg.copy, + context: context, + ), + ); }, ), ), @@ -256,7 +259,8 @@ class WalletBackupView extends ConsumerWidget { color: Theme.of(context).extension()!.popupBG, borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius), + Constants.size.circularBorderRadius, + ), ), child: Padding( padding: const EdgeInsets.all(12), @@ -285,7 +289,8 @@ class WalletBackupView extends ConsumerWidget { .extension()! .getPrimaryEnabledButtonStyle(context), onPressed: () { - String data = AddressUtils.encodeQRSeedData(mnemonic); + final String data = + AddressUtils.encodeQRSeedData(mnemonic); showDialog( context: context, @@ -313,14 +318,15 @@ class WalletBackupView extends ConsumerWidget { width: width + 20, height: width + 20, child: QrImageView( - data: data, - size: width, - backgroundColor: Theme.of(context) - .extension()! - .popupBG, - foregroundColor: Theme.of(context) - .extension()! - .accentColorDark), + data: data, + size: width, + backgroundColor: Theme.of(context) + .extension()! + .popupBG, + foregroundColor: Theme.of(context) + .extension()! + .accentColorDark, + ), ), ), ), @@ -338,14 +344,16 @@ class WalletBackupView extends ConsumerWidget { style: Theme.of(context) .extension()! .getSecondaryEnabledButtonStyle( - context), + context, + ), child: Text( "Cancel", style: STextStyles.button(context) .copyWith( - color: Theme.of(context) - .extension()! - .accentColorDark), + color: Theme.of(context) + .extension()! + .accentColorDark, + ), ), ), ), diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/sub_widgets/confirm_full_rescan.dart b/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/sub_widgets/confirm_full_rescan.dart index f5281435b..530233ec4 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/sub_widgets/confirm_full_rescan.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/sub_widgets/confirm_full_rescan.dart @@ -19,8 +19,7 @@ import '../../../../../widgets/desktop/secondary_button.dart'; import '../../../../../widgets/stack_dialog.dart'; class ConfirmFullRescanDialog extends StatelessWidget { - const ConfirmFullRescanDialog({Key? key, required this.onConfirm}) - : super(key: key); + const ConfirmFullRescanDialog({super.key, required this.onConfirm}); final VoidCallback onConfirm; @@ -87,10 +86,10 @@ class ConfirmFullRescanDialog extends StatelessWidget { ), ), ], - ) + ), ], ), - ) + ), ], ), ); diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/sub_widgets/rescanning_dialog.dart b/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/sub_widgets/rescanning_dialog.dart index f243b977f..647eb2e33 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/sub_widgets/rescanning_dialog.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/sub_widgets/rescanning_dialog.dart @@ -17,9 +17,9 @@ import '../../../../../widgets/stack_dialog.dart'; class RescanningDialog extends StatefulWidget { const RescanningDialog({ - Key? key, + super.key, // required this.onCancel, - }) : super(key: key); + }); // final VoidCallback onCancel; diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart index ff80ff3c4..da50b6208 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart @@ -216,7 +216,7 @@ class _WalletNetworkSettingsViewState } String _percentString(double value) { - double realPercent = (value * 10000).ceil().clamp(0, 10000) / 100.0; + final double realPercent = (value * 10000).ceil().clamp(0, 10000) / 100.0; if (realPercent > 99.99 && _currentSyncStatus == WalletSyncStatus.syncing) { return "99.99%"; } diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart index a0cb79f9d..1af5c6e6b 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart @@ -13,9 +13,33 @@ import 'dart:async'; import 'package:event_bus/event_bus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:tuple/tuple.dart'; + import '../../../db/hive/db.dart'; import '../../../models/epicbox_config_model.dart'; import '../../../notifications/show_flush_bar.dart'; +import '../../../providers/global/wallets_provider.dart'; +import '../../../providers/ui/transaction_filter_provider.dart'; +import '../../../route_generator.dart'; +import '../../../services/event_bus/events/global/node_connection_status_changed_event.dart'; +import '../../../services/event_bus/events/global/wallet_sync_status_changed_event.dart'; +import '../../../services/event_bus/global_event_bus.dart'; +import '../../../themes/stack_colors.dart'; +import '../../../utilities/assets.dart'; +import '../../../utilities/show_loading.dart'; +import '../../../utilities/text_styles.dart'; +import '../../../utilities/util.dart'; +import '../../../wallets/crypto_currency/crypto_currency.dart'; +import '../../../wallets/crypto_currency/intermediate/frost_currency.dart'; +import '../../../wallets/crypto_currency/intermediate/nano_currency.dart'; +import '../../../wallets/wallet/impl/bitcoin_frost_wallet.dart'; +import '../../../wallets/wallet/impl/epiccash_wallet.dart'; +import '../../../wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart'; +import '../../../widgets/background.dart'; +import '../../../widgets/custom_buttons/app_bar_icon_button.dart'; +import '../../../widgets/desktop/secondary_button.dart'; +import '../../../widgets/rounded_white_container.dart'; +import '../../../widgets/stack_dialog.dart'; import '../../address_book_views/address_book_view.dart'; import '../../home_view/home_view.dart'; import '../../pinpad_views/lock_screen_view.dart'; @@ -28,30 +52,6 @@ import 'wallet_network_settings_view/wallet_network_settings_view.dart'; import 'wallet_settings_wallet_settings/change_representative_view.dart'; import 'wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart'; import 'wallet_settings_wallet_settings/xpub_view.dart'; -import '../../../providers/global/wallets_provider.dart'; -import '../../../providers/ui/transaction_filter_provider.dart'; -import '../../../route_generator.dart'; -import '../../../services/event_bus/events/global/node_connection_status_changed_event.dart'; -import '../../../services/event_bus/events/global/wallet_sync_status_changed_event.dart'; -import '../../../services/event_bus/global_event_bus.dart'; -import '../../../themes/stack_colors.dart'; -import '../../../utilities/assets.dart'; -import '../../../utilities/show_loading.dart'; -import '../../../utilities/text_styles.dart'; -import '../../../utilities/util.dart'; -import '../../../wallets/crypto_currency/coins/firo.dart'; -import '../../../wallets/crypto_currency/crypto_currency.dart'; -import '../../../wallets/crypto_currency/intermediate/frost_currency.dart'; -import '../../../wallets/crypto_currency/intermediate/nano_currency.dart'; -import '../../../wallets/wallet/impl/bitcoin_frost_wallet.dart'; -import '../../../wallets/wallet/impl/epiccash_wallet.dart'; -import '../../../wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart'; -import '../../../widgets/background.dart'; -import '../../../widgets/custom_buttons/app_bar_icon_button.dart'; -import '../../../widgets/desktop/secondary_button.dart'; -import '../../../widgets/rounded_white_container.dart'; -import '../../../widgets/stack_dialog.dart'; -import 'package:tuple/tuple.dart'; /// [eventBus] should only be set during testing class WalletSettingsView extends ConsumerStatefulWidget { @@ -325,8 +325,9 @@ class _WalletSettingsViewState extends ConsumerState { "View recovery phrase", ), settings: const RouteSettings( - name: - "/viewRecoverPhraseLockscreen"), + name: + "/viewRecoverPhraseLockscreen", + ), ), ); } @@ -357,7 +358,8 @@ class _WalletSettingsViewState extends ConsumerState { title: "Syncing preferences", onPressed: () { Navigator.of(context).pushNamed( - SyncingPreferencesView.routeName); + SyncingPreferencesView.routeName, + ); }, ), if (xPubEnabled) @@ -494,9 +496,10 @@ class _WalletSettingsViewState extends ConsumerState { child: Text( "Log out", style: STextStyles.button(context).copyWith( - color: Theme.of(context) - .extension()! - .accentColorDark), + color: Theme.of(context) + .extension()! + .accentColorDark, + ), ), ); }, @@ -517,9 +520,9 @@ class _WalletSettingsViewState extends ConsumerState { class EpicBoxInfoForm extends ConsumerStatefulWidget { const EpicBoxInfoForm({ - Key? key, + super.key, required this.walletId, - }) : super(key: key); + }); final String walletId; @@ -603,9 +606,9 @@ class _EpiBoxInfoFormState extends ConsumerState { child: Text( "Save", style: STextStyles.button(context).copyWith( - color: Theme.of(context) - .extension()! - .accentColorDark), + color: + Theme.of(context).extension()!.accentColorDark, + ), ), ), ], diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/change_representative_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/change_representative_view.dart index 23a8c6cd8..cfca56bf2 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/change_representative_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/change_representative_view.dart @@ -14,6 +14,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../../../notifications/show_flush_bar.dart'; import '../../../../providers/global/wallets_provider.dart'; import '../../../../themes/stack_colors.dart'; @@ -38,10 +39,10 @@ import '../../../../widgets/textfield_icon_button.dart'; class ChangeRepresentativeView extends ConsumerStatefulWidget { const ChangeRepresentativeView({ - Key? key, + super.key, required this.walletId, this.clipboardInterface = const ClipboardWrapper(), - }) : super(key: key); + }); final String walletId; final ClipboardInterface clipboardInterface; @@ -80,21 +81,22 @@ class _ChangeRepresentativeViewState final changeFuture = wallet.changeRepresentative; final result = await showLoading( - whileFuture: changeFuture(_textController.text), - context: context, - message: "Updating representative...", - rootNavigator: Util.isDesktop, - onException: (ex) { - String msg = ex.toString(); - while (msg.isNotEmpty && msg.startsWith("Exception:")) { - msg = msg.substring(10).trim(); - } - showFloatingFlushBar( - type: FlushBarType.warning, - message: msg, - context: context, - ); - }); + whileFuture: changeFuture(_textController.text), + context: context, + message: "Updating representative...", + rootNavigator: Util.isDesktop, + onException: (ex) { + String msg = ex.toString(); + while (msg.isNotEmpty && msg.startsWith("Exception:")) { + msg = msg.substring(10).trim(); + } + showFloatingFlushBar( + type: FlushBarType.warning, + message: msg, + context: context, + ); + }, + ); if (mounted) { if (result != null && result) { @@ -129,12 +131,14 @@ class _ChangeRepresentativeViewState await _clipboardInterface .setData(ClipboardData(text: representative ?? "")); if (mounted) { - unawaited(showFloatingFlushBar( - type: FlushBarType.info, - message: "Copied to clipboard", - iconAsset: Assets.svg.copy, - context: context, - )); + unawaited( + showFloatingFlushBar( + type: FlushBarType.info, + message: "Copied to clipboard", + iconAsset: Assets.svg.copy, + context: context, + ), + ); } } @@ -284,7 +288,8 @@ class _ChangeRepresentativeViewState Text( "Current representative", style: STextStyles.desktopTextExtraExtraSmall( - context), + context, + ), ), const SizedBox( height: 4, @@ -300,8 +305,8 @@ class _ChangeRepresentativeViewState representative!, style: isDesktop ? STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( + context, + ).copyWith( color: Theme.of(context) .extension()! .textDark, diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_warning_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_warning_view.dart index d03cfe534..b33c44c9c 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_warning_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_warning_view.dart @@ -10,7 +10,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'delete_wallet_recovery_phrase_view.dart'; + import '../../../../providers/providers.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/text_styles.dart'; @@ -19,6 +19,7 @@ import '../../../../wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.da import '../../../../widgets/background.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../../widgets/rounded_container.dart'; +import 'delete_wallet_recovery_phrase_view.dart'; class DeleteWalletWarningView extends ConsumerWidget { const DeleteWalletWarningView({ @@ -87,9 +88,10 @@ class DeleteWalletWarningView extends ConsumerWidget { child: Text( "Cancel", style: STextStyles.button(context).copyWith( - color: Theme.of(context) - .extension()! - .accentColorDark), + color: Theme.of(context) + .extension()! + .accentColorDark, + ), ), ), const SizedBox( diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/rename_wallet_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/rename_wallet_view.dart index a0fb54eed..319cc067f 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/rename_wallet_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/rename_wallet_view.dart @@ -27,9 +27,9 @@ import '../../../../widgets/textfield_icon_button.dart'; class RenameWalletView extends ConsumerStatefulWidget { const RenameWalletView({ - Key? key, + super.key, required this.walletId, - }) : super(key: key); + }); static const String routeName = "/renameWallet"; diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart index 90e79b216..a3c3a2969 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart @@ -25,9 +25,9 @@ import '../../../../widgets/stack_dialog.dart'; class WalletSettingsWalletSettingsView extends ConsumerWidget { const WalletSettingsWalletSettingsView({ - Key? key, + super.key, required this.walletId, - }) : super(key: key); + }); static const String routeName = "/walletSettingsWalletSettings"; diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart index c07e6b162..ad245d321 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart @@ -15,6 +15,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:qr_flutter/qr_flutter.dart'; + import '../../../../notifications/show_flush_bar.dart'; import '../../../../providers/global/wallets_provider.dart'; import '../../../../themes/stack_colors.dart'; @@ -35,10 +36,10 @@ import '../../../../widgets/rounded_white_container.dart'; class XPubView extends ConsumerStatefulWidget { const XPubView({ - Key? key, + super.key, required this.walletId, this.clipboardInterface = const ClipboardWrapper(), - }) : super(key: key); + }); final String walletId; final ClipboardInterface clipboardInterface; @@ -73,12 +74,14 @@ class _XPubViewState extends ConsumerState { Future _copy() async { await _clipboardInterface.setData(ClipboardData(text: xpub!)); if (mounted) { - unawaited(showFloatingFlushBar( - type: FlushBarType.info, - message: "Copied to clipboard", - iconAsset: Assets.svg.copy, - context: context, - )); + unawaited( + showFloatingFlushBar( + type: FlushBarType.info, + message: "Copied to clipboard", + iconAsset: Assets.svg.copy, + context: context, + ), + ); } } @@ -230,11 +233,11 @@ class _XPubViewState extends ConsumerState { class _XPub extends StatelessWidget { const _XPub({ - Key? key, + super.key, required this.xpub, required this.height, this.clipboardInterface = const ClipboardWrapper(), - }) : super(key: key); + }); final String xpub; final double height; diff --git a/lib/pages/special/firo_rescan_recovery_error_dialog.dart b/lib/pages/special/firo_rescan_recovery_error_dialog.dart index fa6b3ff1d..3a80b0b85 100644 --- a/lib/pages/special/firo_rescan_recovery_error_dialog.dart +++ b/lib/pages/special/firo_rescan_recovery_error_dialog.dart @@ -1,9 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import '../pinpad_views/lock_screen_view.dart'; -import '../settings_views/wallet_settings_view/wallet_backup_views/wallet_backup_view.dart'; -import '../settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_warning_view.dart'; + import '../../pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_delete_wallet_dialog.dart'; import '../../pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart'; import '../../providers/global/wallets_provider.dart'; @@ -23,6 +21,9 @@ import '../../widgets/desktop/desktop_scaffold.dart'; import '../../widgets/desktop/primary_button.dart'; import '../../widgets/desktop/secondary_button.dart'; import '../../widgets/stack_dialog.dart'; +import '../pinpad_views/lock_screen_view.dart'; +import '../settings_views/wallet_settings_view/wallet_backup_views/wallet_backup_view.dart'; +import '../settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_warning_view.dart'; enum FiroRescanRecoveryErrorViewOption { retry, @@ -149,9 +150,10 @@ class _FiroRescanRecoveryErrorViewState child: Text( "Cancel", style: STextStyles.button(context).copyWith( - color: Theme.of(context) - .extension()! - .accentColorDark), + color: Theme.of(context) + .extension()! + .accentColorDark, + ), ), ), rightButton: TextButton( @@ -179,7 +181,8 @@ class _FiroRescanRecoveryErrorViewState "Delete wallet", ), settings: const RouteSettings( - name: "/deleteWalletLockscreen"), + name: "/deleteWalletLockscreen", + ), ), ); }, @@ -249,7 +252,7 @@ class _FiroRescanRecoveryErrorViewState name: UnlockWalletKeysDesktop.routeName, arguments: widget.walletId, ), - ) + ), ]; }, ), @@ -281,7 +284,8 @@ class _FiroRescanRecoveryErrorViewState "View recovery phrase", ), settings: const RouteSettings( - name: "/viewRecoverPhraseLockscreen"), + name: "/viewRecoverPhraseLockscreen", + ), ), ); } diff --git a/lib/pages/stack_privacy_calls.dart b/lib/pages/stack_privacy_calls.dart index ad95b3903..52b5323c6 100644 --- a/lib/pages/stack_privacy_calls.dart +++ b/lib/pages/stack_privacy_calls.dart @@ -14,8 +14,8 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../db/hive/db.dart'; -import 'pinpad_views/create_pin_view.dart'; import '../pages_desktop_specific/password/create_password_view.dart'; import '../providers/global/prefs_provider.dart'; import '../providers/global/price_provider.dart'; @@ -32,12 +32,13 @@ import '../widgets/desktop/desktop_app_bar.dart'; import '../widgets/desktop/desktop_scaffold.dart'; import '../widgets/desktop/primary_button.dart'; import '../widgets/rounded_white_container.dart'; +import 'pinpad_views/create_pin_view.dart'; class StackPrivacyCalls extends ConsumerStatefulWidget { const StackPrivacyCalls({ - Key? key, + super.key, required this.isSettings, - }) : super(key: key); + }); final bool isSettings; @@ -154,7 +155,8 @@ class _StackPrivacyCalls extends ConsumerState { text: TextSpan( style: isDesktop ? STextStyles.desktopTextExtraExtraSmall( - context) + context, + ) : STextStyles.label(context).copyWith( fontSize: 12.0, ), @@ -162,18 +164,21 @@ class _StackPrivacyCalls extends ConsumerState { ? [ if (Constants.enableExchange) const TextSpan( - text: - "Exchange data preloaded for a seamless experience.\n\n"), - const TextSpan( text: - "CoinGecko enabled: (24 hour price change shown in-app, total wallet value shown in USD or other currency).\n\n"), + "Exchange data preloaded for a seamless experience.\n\n", + ), + const TextSpan( + text: + "CoinGecko enabled: (24 hour price change shown in-app, total wallet value shown in USD or other currency).\n\n", + ), TextSpan( text: "Recommended for most crypto users.", style: isDesktop ? STextStyles .desktopTextExtraExtraSmall600( - context) + context, + ) : TextStyle( color: Theme.of(context) .extension()! @@ -185,18 +190,21 @@ class _StackPrivacyCalls extends ConsumerState { : [ if (Constants.enableExchange) const TextSpan( - text: - "Exchange data not preloaded (slower experience).\n\n"), - const TextSpan( text: - "CoinGecko disabled (price changes not shown, no wallet value shown in other currencies).\n\n"), + "Exchange data not preloaded (slower experience).\n\n", + ), + const TextSpan( + text: + "CoinGecko disabled (price changes not shown, no wallet value shown in other currencies).\n\n", + ), TextSpan( text: "Recommended for the privacy conscious.", style: isDesktop ? STextStyles .desktopTextExtraExtraSmall600( - context) + context, + ) : TextStyle( color: Theme.of(context) .extension()! @@ -239,9 +247,10 @@ class _StackPrivacyCalls extends ConsumerState { DB.instance .put( - boxName: DB.boxNamePrefs, - key: "externalCalls", - value: isEasy) + boxName: DB.boxNamePrefs, + key: "externalCalls", + value: isEasy, + ) .then((_) { if (isEasy) { unawaited( @@ -290,10 +299,10 @@ class _StackPrivacyCalls extends ConsumerState { class PrivacyToggle extends ConsumerStatefulWidget { const PrivacyToggle({ - Key? key, + super.key, required this.externalCallsEnabled, this.onChanged, - }) : super(key: key); + }); final bool externalCallsEnabled; final void Function(bool)? onChanged; diff --git a/lib/pages/token_view/my_tokens_view.dart b/lib/pages/token_view/my_tokens_view.dart index a8b39c9d2..eef37ced9 100644 --- a/lib/pages/token_view/my_tokens_view.dart +++ b/lib/pages/token_view/my_tokens_view.dart @@ -30,9 +30,9 @@ import '../../widgets/textfield_icon_button.dart'; class MyTokensView extends ConsumerStatefulWidget { const MyTokensView({ - Key? key, + super.key, required this.walletId, - }) : super(key: key); + }); static const String routeName = "/myTokens"; final String walletId; diff --git a/lib/pages/token_view/sub_widgets/my_token_select_item.dart b/lib/pages/token_view/sub_widgets/my_token_select_item.dart index 8d8ef8711..4f1eec6d1 100644 --- a/lib/pages/token_view/sub_widgets/my_token_select_item.dart +++ b/lib/pages/token_view/sub_widgets/my_token_select_item.dart @@ -12,8 +12,8 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../../models/isar/models/ethereum/eth_contract.dart'; -import '../token_view.dart'; import '../../../pages_desktop_specific/my_stack_view/wallet_view/desktop_token_view.dart'; import '../../../providers/db/main_db_provider.dart'; import '../../../providers/providers.dart'; @@ -24,7 +24,6 @@ import '../../../utilities/constants.dart'; import '../../../utilities/show_loading.dart'; import '../../../utilities/text_styles.dart'; import '../../../utilities/util.dart'; -import '../../../wallets/crypto_currency/coins/ethereum.dart'; import '../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../wallets/isar/providers/eth/current_token_wallet_provider.dart'; import '../../../wallets/isar/providers/eth/token_balance_provider.dart'; @@ -36,13 +35,14 @@ import '../../../widgets/desktop/primary_button.dart'; import '../../../widgets/dialogs/basic_dialog.dart'; import '../../../widgets/icon_widgets/eth_token_icon.dart'; import '../../../widgets/rounded_white_container.dart'; +import '../token_view.dart'; class MyTokenSelectItem extends ConsumerStatefulWidget { const MyTokenSelectItem({ - Key? key, + super.key, required this.walletId, required this.token, - }) : super(key: key); + }); final String walletId; final EthContract token; @@ -124,7 +124,9 @@ class _MyTokenSelectItemState extends ConsumerState { if (mounted) { final address = ref.read(pWalletReceivingAddress(widget.walletId)); await cachedBalance.fetchAndUpdateCachedBalance( - address, ref.read(mainDBProvider)); + address, + ref.read(mainDBProvider), + ); if (mounted) { setState(() {}); } @@ -180,16 +182,22 @@ class _MyTokenSelectItemState extends ConsumerState { const Spacer(), Text( ref - .watch(pAmountFormatter( - Ethereum(CryptoCurrencyNetwork.main))) + .watch( + pAmountFormatter( + Ethereum(CryptoCurrencyNetwork.main), + ), + ) .format( ref - .watch(pTokenBalance( - ( - walletId: widget.walletId, - contractAddress: widget.token.address + .watch( + pTokenBalance( + ( + walletId: widget.walletId, + contractAddress: + widget.token.address + ), ), - )) + ) .total, ethContract: widget.token, ), @@ -213,7 +221,8 @@ class _MyTokenSelectItemState extends ConsumerState { widget.token.symbol, style: isDesktop ? STextStyles.desktopTextExtraExtraSmall( - context) + context, + ) : STextStyles.itemSubtitle(context), ), const Spacer(), @@ -233,7 +242,8 @@ class _MyTokenSelectItemState extends ConsumerState { )}", style: isDesktop ? STextStyles.desktopTextExtraExtraSmall( - context) + context, + ) : STextStyles.itemSubtitle(context), ), ], diff --git a/lib/pages/token_view/sub_widgets/my_tokens_list.dart b/lib/pages/token_view/sub_widgets/my_tokens_list.dart index d6c26529d..f6ef70c38 100644 --- a/lib/pages/token_view/sub_widgets/my_tokens_list.dart +++ b/lib/pages/token_view/sub_widgets/my_tokens_list.dart @@ -11,18 +11,19 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:isar/isar.dart'; + import '../../../db/isar/main_db.dart'; import '../../../models/isar/models/ethereum/eth_contract.dart'; -import 'my_token_select_item.dart'; import '../../../utilities/util.dart'; +import 'my_token_select_item.dart'; class MyTokensList extends StatelessWidget { const MyTokensList({ - Key? key, + super.key, required this.walletId, required this.searchTerm, required this.tokenContracts, - }) : super(key: key); + }); final String walletId; final String searchTerm; @@ -39,7 +40,9 @@ class MyTokensList extends StatelessWidget { .getEthContracts() .filter() .anyOf( - tokenContracts, (q, e) => q.addressEqualTo(e)) + tokenContracts, + (q, e) => q.addressEqualTo(e), + ) .and() .group( (q) => q @@ -57,7 +60,9 @@ class MyTokensList extends StatelessWidget { .getEthContracts() .filter() .anyOf( - tokenContracts, (q, e) => q.addressEqualTo(e)) + tokenContracts, + (q, e) => q.addressEqualTo(e), + ) .findAllSync(); } diff --git a/lib/pages/token_view/sub_widgets/no_tokens_found.dart b/lib/pages/token_view/sub_widgets/no_tokens_found.dart index 5b7853c39..30e7b6bfb 100644 --- a/lib/pages/token_view/sub_widgets/no_tokens_found.dart +++ b/lib/pages/token_view/sub_widgets/no_tokens_found.dart @@ -13,7 +13,7 @@ import '../../../utilities/text_styles.dart'; import '../../../widgets/rounded_white_container.dart'; class NoTokensFound extends StatelessWidget { - const NoTokensFound({Key? key}) : super(key: key); + const NoTokensFound({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/token_view/sub_widgets/token_summary.dart b/lib/pages/token_view/sub_widgets/token_summary.dart index 30f72920d..d20662eb4 100644 --- a/lib/pages/token_view/sub_widgets/token_summary.dart +++ b/lib/pages/token_view/sub_widgets/token_summary.dart @@ -31,7 +31,6 @@ import '../../../utilities/amount/amount_formatter.dart'; import '../../../utilities/assets.dart'; import '../../../utilities/constants.dart'; import '../../../utilities/text_styles.dart'; -import '../../../wallets/crypto_currency/coins/ethereum.dart'; import '../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../wallets/isar/providers/eth/current_token_wallet_provider.dart'; import '../../../wallets/isar/providers/eth/token_balance_provider.dart'; @@ -361,9 +360,9 @@ class TokenOptionsButton extends StatelessWidget { class CoinTickerTag extends ConsumerWidget { const CoinTickerTag({ - Key? key, + super.key, required this.walletId, - }) : super(key: key); + }); final String walletId; diff --git a/lib/pages/token_view/sub_widgets/token_transaction_list_widget.dart b/lib/pages/token_view/sub_widgets/token_transaction_list_widget.dart index 45f0921f8..657a5283a 100644 --- a/lib/pages/token_view/sub_widgets/token_transaction_list_widget.dart +++ b/lib/pages/token_view/sub_widgets/token_transaction_list_widget.dart @@ -26,9 +26,9 @@ import '../../../widgets/loading_indicator.dart'; class TokenTransactionsList extends ConsumerStatefulWidget { const TokenTransactionsList({ - Key? key, + super.key, required this.walletId, - }) : super(key: key); + }); final String walletId; @@ -81,7 +81,7 @@ class _TransactionsListState extends ConsumerState { IndexWhereClause.equalTo( indexName: 'walletId', value: [widget.walletId], - ) + ), ], filter: ref.read(pCurrentTokenWallet)!.transactionFilterOperation, sortBy: [ diff --git a/lib/pages/token_view/token_contract_details_view.dart b/lib/pages/token_view/token_contract_details_view.dart index 4553ca537..08e82e0ae 100644 --- a/lib/pages/token_view/token_contract_details_view.dart +++ b/lib/pages/token_view/token_contract_details_view.dart @@ -24,10 +24,10 @@ import '../../widgets/rounded_white_container.dart'; class TokenContractDetailsView extends ConsumerStatefulWidget { const TokenContractDetailsView({ - Key? key, + super.key, required this.contractAddress, required this.walletId, - }) : super(key: key); + }); static const String routeName = "/tokenContractDetailsView"; @@ -154,11 +154,11 @@ class _TokenContractDetailsViewState class _Item extends StatelessWidget { const _Item({ - Key? key, + super.key, required this.title, required this.data, required this.button, - }) : super(key: key); + }); final String title; final String data; diff --git a/lib/pages/token_view/token_view.dart b/lib/pages/token_view/token_view.dart index c8cbd0e87..b6acc49c5 100644 --- a/lib/pages/token_view/token_view.dart +++ b/lib/pages/token_view/token_view.dart @@ -12,10 +12,8 @@ import 'package:event_bus/event_bus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; -import 'sub_widgets/token_summary.dart'; -import 'sub_widgets/token_transaction_list_widget.dart'; -import 'token_contract_details_view.dart'; -import '../wallet_view/transaction_views/tx_v2/all_transactions_v2_view.dart'; +import 'package:tuple/tuple.dart'; + import '../../services/event_bus/events/global/wallet_sync_status_changed_event.dart'; import '../../themes/stack_colors.dart'; import '../../utilities/assets.dart'; @@ -26,16 +24,19 @@ import '../../widgets/background.dart'; import '../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../widgets/custom_buttons/blue_text_button.dart'; import '../../widgets/icon_widgets/eth_token_icon.dart'; -import 'package:tuple/tuple.dart'; +import '../wallet_view/transaction_views/tx_v2/all_transactions_v2_view.dart'; +import 'sub_widgets/token_summary.dart'; +import 'sub_widgets/token_transaction_list_widget.dart'; +import 'token_contract_details_view.dart'; /// [eventBus] should only be set during testing class TokenView extends ConsumerStatefulWidget { const TokenView({ - Key? key, + super.key, required this.walletId, this.popPrevious = false, this.eventBus, - }) : super(key: key); + }); static const String routeName = "/token"; @@ -112,8 +113,10 @@ class _TokenViewState extends ConsumerState { ), Flexible( child: Text( - ref.watch(pCurrentTokenWallet - .select((value) => value!.tokenContract.name)), + ref.watch( + pCurrentTokenWallet + .select((value) => value!.tokenContract.name), + ), style: STextStyles.navBarTitle(context), overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, @@ -141,8 +144,10 @@ class _TokenViewState extends ConsumerState { Navigator.of(context).pushNamed( TokenContractDetailsView.routeName, arguments: Tuple2( - ref.watch(pCurrentTokenWallet - .select((value) => value!.tokenContract.address)), + ref.watch( + pCurrentTokenWallet.select( + (value) => value!.tokenContract.address), + ), widget.walletId, ), ); diff --git a/lib/pages/wallet_view/sub_widgets/no_transactions_found.dart b/lib/pages/wallet_view/sub_widgets/no_transactions_found.dart index fff5ebf16..8be6fc5ad 100644 --- a/lib/pages/wallet_view/sub_widgets/no_transactions_found.dart +++ b/lib/pages/wallet_view/sub_widgets/no_transactions_found.dart @@ -13,7 +13,7 @@ import '../../../utilities/text_styles.dart'; import '../../../widgets/rounded_white_container.dart'; class NoTransActionsFound extends StatelessWidget { - const NoTransActionsFound({Key? key}) : super(key: key); + const NoTransActionsFound({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/wallet_view/sub_widgets/transactions_list.dart b/lib/pages/wallet_view/sub_widgets/transactions_list.dart index db97ebcea..8e2fb5cb6 100644 --- a/lib/pages/wallet_view/sub_widgets/transactions_list.dart +++ b/lib/pages/wallet_view/sub_widgets/transactions_list.dart @@ -13,10 +13,9 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:isar/isar.dart'; +import 'package:tuple/tuple.dart'; + import '../../../models/isar/models/isar_models.dart'; -import '../../exchange_view/trade_details_view.dart'; -import 'no_transactions_found.dart'; -import '../wallet_view.dart'; import '../../../providers/db/main_db_provider.dart'; import '../../../providers/global/trades_service_provider.dart'; import '../../../providers/global/wallets_provider.dart'; @@ -32,7 +31,9 @@ import '../../../widgets/desktop/desktop_dialog_close_button.dart'; import '../../../widgets/loading_indicator.dart'; import '../../../widgets/trade_card.dart'; import '../../../widgets/transaction_card.dart'; -import 'package:tuple/tuple.dart'; +import '../../exchange_view/trade_details_view.dart'; +import '../wallet_view.dart'; +import 'no_transactions_found.dart'; class TransactionsList extends ConsumerStatefulWidget { const TransactionsList({ @@ -106,10 +107,12 @@ class _TransactionsListState extends ConsumerState { ), TradeCard( // this may mess with combined firo transactions - key: Key(tx.txid + - tx.type.name + - tx.address.value.toString() + - trade.uuid), // + key: Key( + tx.txid + + tx.type.name + + tx.address.value.toString() + + trade.uuid, + ), // trade: trade, onTap: () async { final walletName = ref.read(pWalletName(widget.walletId)); @@ -184,7 +187,7 @@ class _TransactionsListState extends ConsumerState { ); } }, - ) + ), ], ), ); diff --git a/lib/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart b/lib/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart index d94e183ec..c2fc747ad 100644 --- a/lib/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart +++ b/lib/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart @@ -19,7 +19,6 @@ import '../../../utilities/amount/amount_formatter.dart'; import '../../../utilities/constants.dart'; import '../../../utilities/enums/wallet_balance_toggle_state.dart'; import '../../../utilities/text_styles.dart'; -import '../../../wallets/crypto_currency/coins/firo.dart'; import '../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../wallets/isar/providers/wallet_info_provider.dart'; @@ -360,7 +359,7 @@ class BalanceSelector extends ConsumerWidget { .extension()! .textSubtitle1, ), - ) + ), ], ), ], diff --git a/lib/pages/wallet_view/sub_widgets/wallet_navigation_bar.dart b/lib/pages/wallet_view/sub_widgets/wallet_navigation_bar.dart index 05e39c079..a022be588 100644 --- a/lib/pages/wallet_view/sub_widgets/wallet_navigation_bar.dart +++ b/lib/pages/wallet_view/sub_widgets/wallet_navigation_bar.dart @@ -7,5 +7,3 @@ * Generated by Cypher Stack on 2023-05-26 * */ - - diff --git a/lib/pages/wallet_view/sub_widgets/wallet_refresh_button.dart b/lib/pages/wallet_view/sub_widgets/wallet_refresh_button.dart index b9f8c4ca1..6e98f4a3f 100644 --- a/lib/pages/wallet_view/sub_widgets/wallet_refresh_button.dart +++ b/lib/pages/wallet_view/sub_widgets/wallet_refresh_button.dart @@ -13,6 +13,7 @@ import 'dart:async'; import 'package:event_bus/event_bus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../../providers/global/wallets_provider.dart'; import '../../../services/event_bus/events/global/wallet_sync_status_changed_event.dart'; import '../../../services/event_bus/global_event_bus.dart'; @@ -25,14 +26,14 @@ import '../../../widgets/animated_widgets/rotating_arrows.dart'; /// [eventBus] should only be set during testing class WalletRefreshButton extends ConsumerStatefulWidget { const WalletRefreshButton({ - Key? key, + super.key, required this.walletId, required this.initialSyncStatus, this.tokenContractAddress, this.onPressed, this.eventBus, this.overrideIconColor, - }) : super(key: key); + }); final String walletId; final WalletSyncStatus initialSyncStatus; @@ -99,59 +100,57 @@ class _RefreshButtonState extends ConsumerState { final isDesktop = Util.isDesktop; return SizedBox( - height: isDesktop ? 22 : 36, - width: isDesktop ? 22 : 36, - child: Semantics( - label: "Refresh Button. Refreshes The Values In Summary.", - excludeSemantics: true, - child: MaterialButton( - color: isDesktop - ? Theme.of(context) - .extension()! - .buttonBackSecondary - : null, - splashColor: Theme.of(context).extension()!.highlight, - onPressed: () { - if (widget.tokenContractAddress == null) { - final wallet = ref.read(pWallets).getWallet(widget.walletId); - final isRefreshing = wallet.refreshMutex.isLocked; - if (!isRefreshing) { - _spinController.repeat?.call(); - wallet.refresh().then((_) => _spinController.stop?.call()); - } - } else { - if (!ref.read(pCurrentTokenWallet)!.refreshMutex.isLocked) { - ref.read(pCurrentTokenWallet)!.refresh(); - } + height: isDesktop ? 22 : 36, + width: isDesktop ? 22 : 36, + child: Semantics( + label: "Refresh Button. Refreshes The Values In Summary.", + excludeSemantics: true, + child: MaterialButton( + color: isDesktop + ? Theme.of(context).extension()!.buttonBackSecondary + : null, + splashColor: Theme.of(context).extension()!.highlight, + onPressed: () { + if (widget.tokenContractAddress == null) { + final wallet = ref.read(pWallets).getWallet(widget.walletId); + final isRefreshing = wallet.refreshMutex.isLocked; + if (!isRefreshing) { + _spinController.repeat?.call(); + wallet.refresh().then((_) => _spinController.stop?.call()); } - }, - elevation: 0, - highlightElevation: 0, - hoverElevation: 0, - padding: EdgeInsets.zero, - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, - ), - ), - child: RotatingArrows( - spinByDefault: - widget.initialSyncStatus == WalletSyncStatus.syncing, - width: isDesktop ? 12 : 24, - height: isDesktop ? 12 : 24, - controller: _spinController, - color: widget.overrideIconColor != null - ? widget.overrideIconColor! - : isDesktop - ? Theme.of(context) - .extension()! - .textFieldDefaultSearchIconRight - : Theme.of(context) - .extension()! - .textFavoriteCard, + } else { + if (!ref.read(pCurrentTokenWallet)!.refreshMutex.isLocked) { + ref.read(pCurrentTokenWallet)!.refresh(); + } + } + }, + elevation: 0, + highlightElevation: 0, + hoverElevation: 0, + padding: EdgeInsets.zero, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, ), ), - )); + child: RotatingArrows( + spinByDefault: widget.initialSyncStatus == WalletSyncStatus.syncing, + width: isDesktop ? 12 : 24, + height: isDesktop ? 12 : 24, + controller: _spinController, + color: widget.overrideIconColor != null + ? widget.overrideIconColor! + : isDesktop + ? Theme.of(context) + .extension()! + .textFieldDefaultSearchIconRight + : Theme.of(context) + .extension()! + .textFavoriteCard, + ), + ), + ), + ); } } diff --git a/lib/pages/wallet_view/transaction_views/all_transactions_view.dart b/lib/pages/wallet_view/transaction_views/all_transactions_view.dart index e946cf6d5..232e3ee2c 100644 --- a/lib/pages/wallet_view/transaction_views/all_transactions_view.dart +++ b/lib/pages/wallet_view/transaction_views/all_transactions_view.dart @@ -14,14 +14,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:isar/isar.dart'; +import 'package:tuple/tuple.dart'; + import '../../../models/isar/models/blockchain_data/transaction.dart'; import '../../../models/isar/models/contact_entry.dart'; import '../../../models/isar/models/transaction_note.dart'; import '../../../models/transaction_filter.dart'; import '../../../notifications/show_flush_bar.dart'; -import '../sub_widgets/tx_icon.dart'; -import 'transaction_details_view.dart'; -import 'transaction_search_filter_view.dart'; import '../../../providers/db/main_db_provider.dart'; import '../../../providers/global/address_book_service_provider.dart'; import '../../../providers/providers.dart'; @@ -34,7 +33,6 @@ import '../../../utilities/constants.dart'; import '../../../utilities/format.dart'; import '../../../utilities/text_styles.dart'; import '../../../utilities/util.dart'; -import '../../../wallets/crypto_currency/coins/epiccash.dart'; import '../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../widgets/conditional_parent.dart'; @@ -49,7 +47,9 @@ import '../../../widgets/rounded_white_container.dart'; import '../../../widgets/stack_text_field.dart'; import '../../../widgets/textfield_icon_button.dart'; import '../../../widgets/transaction_card.dart'; -import 'package:tuple/tuple.dart'; +import '../sub_widgets/tx_icon.dart'; +import 'transaction_details_view.dart'; +import 'transaction_search_filter_view.dart'; typedef _GroupedTransactions = ({ String label, @@ -94,8 +94,10 @@ class _TransactionDetailsViewState extends ConsumerState { } // TODO: optimise search+filter - List filter( - {required List transactions, TransactionFilter? filter}) { + List filter({ + required List transactions, + TransactionFilter? filter, + }) { if (filter == null) { return transactions; } @@ -147,8 +149,12 @@ class _TransactionDetailsViewState extends ConsumerState { }).toList(); } - bool _isKeywordMatch(Transaction tx, String keyword, - List contacts, List notes) { + bool _isKeywordMatch( + Transaction tx, + String keyword, + List contacts, + List notes, + ) { if (keyword.isEmpty) { return true; } @@ -157,11 +163,13 @@ class _TransactionDetailsViewState extends ConsumerState { // check if address book name contains contains |= contacts - .where((e) => - e.addresses - .where((a) => a.address == tx.address.value?.value) - .isNotEmpty && - e.name.toLowerCase().contains(keyword)) + .where( + (e) => + e.addresses + .where((a) => a.address == tx.address.value?.value) + .isNotEmpty && + e.name.toLowerCase().contains(keyword), + ) .isNotEmpty; // check if address contains @@ -218,9 +226,9 @@ class _TransactionDetailsViewState extends ConsumerState { List<_GroupedTransactions> groupTransactionsByMonth( List transactions, ) { - Map map = {}; + final Map map = {}; - for (var tx in transactions) { + for (final tx in transactions) { final date = DateTime.fromMillisecondsSinceEpoch(tx.timestamp * 1000); final monthYear = "${Constants.monthMap[date.month]} ${date.year}"; if (map[monthYear] == null) { @@ -285,7 +293,8 @@ class _TransactionDetailsViewState extends ConsumerState { if (FocusScope.of(context).hasFocus) { FocusScope.of(context).unfocus(); await Future.delayed( - const Duration(milliseconds: 75)); + const Duration(milliseconds: 75), + ); } if (mounted) { Navigator.of(context).pop(); @@ -486,39 +495,45 @@ class _TransactionDetailsViewState extends ConsumerState { ref.watch(transactionFilterProvider.state).state; return FutureBuilder( - future: ref.watch(mainDBProvider).isar.transactions.buildQuery< - Transaction>( - whereClauses: [ - IndexWhereClause.equalTo( - indexName: 'walletId', - value: [widget.walletId], - ) - ], - // TODO: [prio=med] add filters to wallet or cryptocurrency class - // eth tokens should all be on v2 txn now so this should not be needed here - // filter: widget.contractAddress != null - // ? FilterGroup.and([ - // FilterCondition.equalTo( - // property: r"contractAddress", - // value: widget.contractAddress!, - // ), - // const FilterCondition.equalTo( - // property: r"subType", - // value: TransactionSubType.ethToken, - // ), - // ]) - // : null, - sortBy: [ - const SortProperty( - property: "timestamp", - sort: Sort.desc, - ), - ]).findAll(), + future: ref + .watch(mainDBProvider) + .isar + .transactions + .buildQuery( + whereClauses: [ + IndexWhereClause.equalTo( + indexName: 'walletId', + value: [widget.walletId], + ), + ], + // TODO: [prio=med] add filters to wallet or cryptocurrency class + // eth tokens should all be on v2 txn now so this should not be needed here + // filter: widget.contractAddress != null + // ? FilterGroup.and([ + // FilterCondition.equalTo( + // property: r"contractAddress", + // value: widget.contractAddress!, + // ), + // const FilterCondition.equalTo( + // property: r"subType", + // value: TransactionSubType.ethToken, + // ), + // ]) + // : null, + sortBy: [ + const SortProperty( + property: "timestamp", + sort: Sort.desc, + ), + ], + ).findAll(), builder: (_, AsyncSnapshot> snapshot) { if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) { final filtered = filter( - transactions: snapshot.data!, filter: criteria); + transactions: snapshot.data!, + filter: criteria, + ); final searched = search(_searchString, filtered); searched.sort((a, b) { @@ -570,7 +585,8 @@ class _TransactionDetailsViewState extends ConsumerState { padding: const EdgeInsets.all(4), child: DesktopTransactionCardRow( key: Key( - "transactionCard_key_${month.transactions[index].txid}"), + "transactionCard_key_${month.transactions[index].txid}", + ), transaction: month.transactions[index], walletId: walletId, @@ -586,7 +602,8 @@ class _TransactionDetailsViewState extends ConsumerState { ...month.transactions.map( (tx) => TransactionCard( key: Key( - "transactionCard_key_${tx.txid}"), + "transactionCard_key_${tx.txid}", + ), transaction: tx, walletId: walletId, ), @@ -616,7 +633,7 @@ class _TransactionDetailsViewState extends ConsumerState { } class TransactionFilterOptionBar extends ConsumerStatefulWidget { - const TransactionFilterOptionBar({Key? key}) : super(key: key); + const TransactionFilterOptionBar({super.key}); @override ConsumerState createState() => @@ -774,10 +791,10 @@ class _TransactionFilterOptionBarState class TransactionFilterOptionBarItem extends StatelessWidget { const TransactionFilterOptionBarItem({ - Key? key, + super.key, required this.label, this.onPressed, - }) : super(key: key); + }); final String label; final void Function(String)? onPressed; @@ -789,9 +806,10 @@ class TransactionFilterOptionBarItem extends StatelessWidget { child: Container( height: 32, decoration: BoxDecoration( - color: - Theme.of(context).extension()!.buttonBackSecondary, - borderRadius: BorderRadius.circular(1000)), + color: + Theme.of(context).extension()!.buttonBackSecondary, + borderRadius: BorderRadius.circular(1000), + ), child: Padding( padding: const EdgeInsets.symmetric( horizontal: 14, @@ -894,7 +912,8 @@ class _DesktopTransactionCardRowState @override Widget build(BuildContext context) { final locale = ref.watch( - localeServiceChangeNotifierProvider.select((value) => value.locale)); + localeServiceChangeNotifierProvider.select((value) => value.locale), + ); final baseCurrency = ref .watch(prefsChangeNotifierProvider.select((value) => value.currency)); @@ -902,8 +921,10 @@ class _DesktopTransactionCardRowState final coin = ref.watch(pWalletCoin(walletId)); final price = ref - .watch(priceAnd24hChangeNotifierProvider - .select((value) => value.getPrice(coin))) + .watch( + priceAnd24hChangeNotifierProvider + .select((value) => value.getPrice(coin)), + ) .item1; late final String prefix; @@ -1028,8 +1049,10 @@ class _DesktopTransactionCardRowState }, ), ), - if (ref.watch(prefsChangeNotifierProvider - .select((value) => value.externalCalls))) + if (ref.watch( + prefsChangeNotifierProvider + .select((value) => value.externalCalls), + )) Expanded( flex: 4, child: Builder( diff --git a/lib/pages/wallet_view/transaction_views/dialogs/cancelling_transaction_progress_dialog.dart b/lib/pages/wallet_view/transaction_views/dialogs/cancelling_transaction_progress_dialog.dart index 0039dc5bb..f1824d17e 100644 --- a/lib/pages/wallet_view/transaction_views/dialogs/cancelling_transaction_progress_dialog.dart +++ b/lib/pages/wallet_view/transaction_views/dialogs/cancelling_transaction_progress_dialog.dart @@ -13,7 +13,7 @@ import '../../../../widgets/animated_widgets/rotating_arrows.dart'; import '../../../../widgets/stack_dialog.dart'; class CancellingTransactionProgressDialog extends StatefulWidget { - const CancellingTransactionProgressDialog({Key? key}) : super(key: key); + const CancellingTransactionProgressDialog({super.key}); @override State createState() => diff --git a/lib/pages/wallet_view/transaction_views/edit_note_view.dart b/lib/pages/wallet_view/transaction_views/edit_note_view.dart index ccae6c3fc..4d0c584eb 100644 --- a/lib/pages/wallet_view/transaction_views/edit_note_view.dart +++ b/lib/pages/wallet_view/transaction_views/edit_note_view.dart @@ -10,6 +10,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../../models/isar/models/transaction_note.dart'; import '../../../providers/db/main_db_provider.dart'; import '../../../providers/providers.dart'; @@ -28,10 +29,10 @@ import '../../../widgets/textfield_icon_button.dart'; class EditNoteView extends ConsumerStatefulWidget { const EditNoteView({ - Key? key, + super.key, required this.txid, required this.walletId, - }) : super(key: key); + }); static const String routeName = "/editNote"; @@ -92,7 +93,8 @@ class _EditNoteViewState extends ConsumerState { if (FocusScope.of(context).hasFocus) { FocusScope.of(context).unfocus(); await Future.delayed( - const Duration(milliseconds: 75)); + const Duration(milliseconds: 75), + ); } if (mounted) { Navigator.of(context).pop(); @@ -230,7 +232,7 @@ class _EditNoteViewState extends ConsumerState { "Save", style: STextStyles.button(context), ), - ) + ), ], ), ), @@ -241,9 +243,9 @@ class _EditNoteViewState extends ConsumerState { class MobileEditNoteScaffold extends StatelessWidget { const MobileEditNoteScaffold({ - Key? key, + super.key, required this.child, - }) : super(key: key); + }); final Widget child; diff --git a/lib/pages/wallet_view/transaction_views/transaction_details_view.dart b/lib/pages/wallet_view/transaction_views/transaction_details_view.dart index 38fffe56e..dad531969 100644 --- a/lib/pages/wallet_view/transaction_views/transaction_details_view.dart +++ b/lib/pages/wallet_view/transaction_views/transaction_details_view.dart @@ -36,13 +36,6 @@ import '../../../utilities/format.dart'; import '../../../utilities/logger.dart'; import '../../../utilities/text_styles.dart'; import '../../../utilities/util.dart'; -import '../../../wallets/crypto_currency/coins/bitcoincash.dart'; -import '../../../wallets/crypto_currency/coins/ecash.dart'; -import '../../../wallets/crypto_currency/coins/epiccash.dart'; -import '../../../wallets/crypto_currency/coins/ethereum.dart'; -import '../../../wallets/crypto_currency/coins/firo.dart'; -import '../../../wallets/crypto_currency/coins/monero.dart'; -import '../../../wallets/crypto_currency/coins/wownero.dart'; import '../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../wallets/crypto_currency/intermediate/nano_currency.dart'; import '../../../wallets/isar/providers/wallet_info_provider.dart'; @@ -1071,7 +1064,7 @@ class _TransactionDetailsViewState : const EdgeInsets.all(12), child: Builder( builder: (context) { - String feeString = showFeePending + final String feeString = showFeePending ? _transaction.isConfirmed( currentHeight, minConfirms, @@ -1734,7 +1727,7 @@ class _TransactionDetailsViewState } class _Divider extends StatelessWidget { - const _Divider({Key? key}) : super(key: key); + const _Divider({super.key}); @override Widget build(BuildContext context) { @@ -1747,9 +1740,9 @@ class _Divider extends StatelessWidget { class IconCopyButton extends StatelessWidget { const IconCopyButton({ - Key? key, + super.key, required this.data, - }) : super(key: key); + }); final String data; @@ -1793,9 +1786,9 @@ class IconCopyButton extends StatelessWidget { class IconPencilButton extends StatelessWidget { const IconPencilButton({ - Key? key, + super.key, this.onPressed, - }) : super(key: key); + }); final VoidCallback? onPressed; diff --git a/lib/pages/wallet_view/transaction_views/transaction_search_filter_view.dart b/lib/pages/wallet_view/transaction_views/transaction_search_filter_view.dart index 5e4244177..2eebe5dbe 100644 --- a/lib/pages/wallet_view/transaction_views/transaction_search_filter_view.dart +++ b/lib/pages/wallet_view/transaction_views/transaction_search_filter_view.dart @@ -12,6 +12,7 @@ import 'package:decimal/decimal.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../../models/transaction_filter.dart'; import '../../../providers/global/locale_provider.dart'; import '../../../providers/ui/transaction_filter_provider.dart'; @@ -117,9 +118,10 @@ class _TransactionSearchViewState return Text( isDateSelected ? "From..." : _fromDateString, style: STextStyles.fieldLabel(context).copyWith( - color: isDateSelected - ? Theme.of(context).extension()!.textSubtitle2 - : Theme.of(context).extension()!.accentColorDark), + color: isDateSelected + ? Theme.of(context).extension()!.textSubtitle2 + : Theme.of(context).extension()!.accentColorDark, + ), ); } @@ -128,9 +130,10 @@ class _TransactionSearchViewState return Text( isDateSelected ? "To..." : _toDateString, style: STextStyles.fieldLabel(context).copyWith( - color: isDateSelected - ? Theme.of(context).extension()!.textSubtitle2 - : Theme.of(context).extension()!.accentColorDark), + color: isDateSelected + ? Theme.of(context).extension()!.textSubtitle2 + : Theme.of(context).extension()!.accentColorDark, + ), ); } @@ -173,7 +176,8 @@ class _TransactionSearchViewState !_selectedFromDate!.isBefore(_selectedToDate!); if (flag) { _selectedToDate = DateTime.fromMillisecondsSinceEpoch( - _selectedFromDate!.millisecondsSinceEpoch); + _selectedFromDate!.millisecondsSinceEpoch, + ); } setState(() { @@ -227,7 +231,7 @@ class _TransactionSearchViewState child: FittedBox( child: _dateFromText, ), - ) + ), ], ), ), @@ -263,7 +267,8 @@ class _TransactionSearchViewState !_selectedToDate!.isAfter(_selectedFromDate!); if (flag) { _selectedFromDate = DateTime.fromMillisecondsSinceEpoch( - _selectedToDate!.millisecondsSinceEpoch); + _selectedToDate!.millisecondsSinceEpoch, + ); } setState(() { @@ -317,7 +322,7 @@ class _TransactionSearchViewState child: FittedBox( child: _dateToText, ), - ) + ), ], ), ), @@ -449,7 +454,8 @@ class _TransactionSearchViewState width: 20, child: Checkbox( key: const Key( - "transactionSearchViewSentCheckboxKey"), + "transactionSearchViewSentCheckboxKey", + ), materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, value: _isActiveSentCheckbox, @@ -481,7 +487,7 @@ class _TransactionSearchViewState ], ), ), - ) + ), ], ), ), @@ -508,7 +514,8 @@ class _TransactionSearchViewState width: 20, child: Checkbox( key: const Key( - "transactionSearchViewReceivedCheckboxKey"), + "transactionSearchViewReceivedCheckboxKey", + ), materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, value: _isActiveReceivedCheckbox, @@ -540,7 +547,7 @@ class _TransactionSearchViewState ], ), ), - ) + ), ], ), ), @@ -567,7 +574,8 @@ class _TransactionSearchViewState width: 20, child: Checkbox( key: const Key( - "transactionSearchViewSentCheckboxKey"), + "transactionSearchViewSentCheckboxKey", + ), materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, value: _isActiveTradeCheckbox, @@ -599,7 +607,7 @@ class _TransactionSearchViewState ], ), ), - ) + ), ], ), ), diff --git a/lib/pages/wallet_view/transaction_views/tx_v2/all_transactions_v2_view.dart b/lib/pages/wallet_view/transaction_views/tx_v2/all_transactions_v2_view.dart index e8dfcbc42..24dd77005 100644 --- a/lib/pages/wallet_view/transaction_views/tx_v2/all_transactions_v2_view.dart +++ b/lib/pages/wallet_view/transaction_views/tx_v2/all_transactions_v2_view.dart @@ -625,7 +625,7 @@ class _AllTransactionsV2ViewState extends ConsumerState { } class TransactionFilterOptionBar extends ConsumerStatefulWidget { - const TransactionFilterOptionBar({Key? key}) : super(key: key); + const TransactionFilterOptionBar({super.key}); @override ConsumerState createState() => @@ -783,10 +783,10 @@ class _TransactionFilterOptionBarState class TransactionFilterOptionBarItem extends StatelessWidget { const TransactionFilterOptionBarItem({ - Key? key, + super.key, required this.label, this.onPressed, - }) : super(key: key); + }); final String label; final void Function(String)? onPressed; @@ -841,10 +841,10 @@ class TransactionFilterOptionBarItem extends StatelessWidget { class DesktopTransactionCardRow extends ConsumerStatefulWidget { const DesktopTransactionCardRow({ - Key? key, + super.key, required this.transaction, required this.walletId, - }) : super(key: key); + }); final TransactionV2 transaction; final String walletId; diff --git a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_card.dart b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_card.dart index db18d772c..0cce71e0f 100644 --- a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_card.dart +++ b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_card.dart @@ -2,10 +2,9 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; import '../../../../models/isar/models/isar_models.dart'; -import '../../sub_widgets/tx_icon.dart'; -import 'transaction_v2_details_view.dart'; import '../../../../providers/db/main_db_provider.dart'; import '../../../../providers/global/locale_provider.dart'; import '../../../../providers/global/prefs_provider.dart'; @@ -18,11 +17,12 @@ import '../../../../utilities/constants.dart'; import '../../../../utilities/format.dart'; import '../../../../utilities/text_styles.dart'; import '../../../../utilities/util.dart'; -import '../../../../wallets/crypto_currency/coins/ethereum.dart'; import '../../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/spark_interface.dart'; import '../../../../widgets/desktop/desktop_dialog.dart'; +import '../../sub_widgets/tx_icon.dart'; +import 'transaction_v2_details_view.dart'; class TransactionCardV2 extends ConsumerStatefulWidget { const TransactionCardV2({ @@ -98,15 +98,20 @@ class _TransactionCardStateV2 extends ConsumerState { @override Widget build(BuildContext context) { final locale = ref.watch( - localeServiceChangeNotifierProvider.select((value) => value.locale)); + localeServiceChangeNotifierProvider.select((value) => value.locale), + ); final baseCurrency = ref .watch(prefsChangeNotifierProvider.select((value) => value.currency)); final price = ref - .watch(priceAnd24hChangeNotifierProvider.select((value) => isTokenTx - ? value.getTokenPrice(tokenContract!.address) - : value.getPrice(coin))) + .watch( + priceAnd24hChangeNotifierProvider.select( + (value) => isTokenTx + ? value.getTokenPrice(tokenContract!.address) + : value.getPrice(coin), + ), + ) .item1; final currentHeight = ref.watch(pWalletChainHeight(walletId)); @@ -117,39 +122,45 @@ class _TransactionCardStateV2 extends ConsumerState { if (_transaction.subType == TransactionSubType.cashFusion) { amount = _transaction.getAmountReceivedInThisWallet( - fractionDigits: fractionDigits); + fractionDigits: fractionDigits, + ); } else { switch (_transaction.type) { case TransactionType.outgoing: amount = _transaction.getAmountSentFromThisWallet( - fractionDigits: fractionDigits); + fractionDigits: fractionDigits, + ); break; case TransactionType.incoming: case TransactionType.sentToSelf: if (_transaction.subType == TransactionSubType.sparkMint) { amount = _transaction.getAmountSparkSelfMinted( - fractionDigits: fractionDigits); + fractionDigits: fractionDigits, + ); } else if (_transaction.subType == TransactionSubType.sparkSpend) { final changeAddress = (ref.watch(pWallets).getWallet(walletId) as SparkInterface) .sparkChangeAddress; amount = Amount( rawValue: _transaction.outputs - .where((e) => - e.walletOwns && !e.addresses.contains(changeAddress)) + .where( + (e) => e.walletOwns && !e.addresses.contains(changeAddress), + ) .fold(BigInt.zero, (p, e) => p + e.value), fractionDigits: coin.fractionDigits, ); } else { amount = _transaction.getAmountReceivedInThisWallet( - fractionDigits: fractionDigits); + fractionDigits: fractionDigits, + ); } break; case TransactionType.unknown: amount = _transaction.getAmountSentFromThisWallet( - fractionDigits: fractionDigits); + fractionDigits: fractionDigits, + ); break; } } @@ -262,13 +273,17 @@ class _TransactionCardStateV2 extends ConsumerState { ), ), ), - if (ref.watch(prefsChangeNotifierProvider - .select((value) => value.externalCalls))) + if (ref.watch( + prefsChangeNotifierProvider + .select((value) => value.externalCalls), + )) const SizedBox( width: 10, ), - if (ref.watch(prefsChangeNotifierProvider - .select((value) => value.externalCalls))) + if (ref.watch( + prefsChangeNotifierProvider + .select((value) => value.externalCalls), + )) Flexible( child: FittedBox( fit: BoxFit.scaleDown, diff --git a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart index 3ed6397d6..2780b4033 100644 --- a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart +++ b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart @@ -37,12 +37,6 @@ import '../../../../utilities/format.dart'; import '../../../../utilities/logger.dart'; import '../../../../utilities/text_styles.dart'; import '../../../../utilities/util.dart'; -import '../../../../wallets/crypto_currency/coins/bitcoincash.dart'; -import '../../../../wallets/crypto_currency/coins/ecash.dart'; -import '../../../../wallets/crypto_currency/coins/epiccash.dart'; -import '../../../../wallets/crypto_currency/coins/firo.dart'; -import '../../../../wallets/crypto_currency/coins/monero.dart'; -import '../../../../wallets/crypto_currency/coins/wownero.dart'; import '../../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../../wallets/crypto_currency/intermediate/nano_currency.dart'; import '../../../../wallets/isar/models/spark_coin.dart'; @@ -1276,7 +1270,7 @@ class _TransactionV2DetailsViewState : const EdgeInsets.all(12), child: Builder( builder: (context) { - String feeString = showFeePending + final String feeString = showFeePending ? _transaction.isConfirmed( currentHeight, minConfirms, diff --git a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_list.dart b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_list.dart index 859285eac..b805cd819 100644 --- a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_list.dart +++ b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_list.dart @@ -13,12 +13,9 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:isar/isar.dart'; + import '../../../../models/isar/models/blockchain_data/transaction.dart'; import '../../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; -import '../../sub_widgets/no_transactions_found.dart'; -import 'fusion_tx_group_card.dart'; -import 'transaction_v2_list_item.dart'; -import '../../wallet_view.dart'; import '../../../../providers/db/main_db_provider.dart'; import '../../../../providers/global/wallets_provider.dart'; import '../../../../themes/stack_colors.dart'; @@ -26,12 +23,16 @@ import '../../../../utilities/constants.dart'; import '../../../../utilities/util.dart'; import '../../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../../widgets/loading_indicator.dart'; +import '../../sub_widgets/no_transactions_found.dart'; +import '../../wallet_view.dart'; +import 'fusion_tx_group_card.dart'; +import 'transaction_v2_list_item.dart'; class TransactionsV2List extends ConsumerStatefulWidget { const TransactionsV2List({ - Key? key, + super.key, required this.walletId, - }) : super(key: key); + }); final String walletId; @@ -72,27 +73,25 @@ class _TransactionsV2ListState extends ConsumerState { @override void initState() { coin = ref.read(pWallets).getWallet(widget.walletId).info.coin; - _query = ref - .read(mainDBProvider) - .isar - .transactionV2s - .buildQuery( - whereClauses: [ - IndexWhereClause.equalTo( - indexName: 'walletId', - value: [widget.walletId], - ) - ], - filter: ref - .read(pWallets) - .getWallet(widget.walletId) - .transactionFilterOperation, - sortBy: [ - const SortProperty( - property: "timestamp", - sort: Sort.desc, - ), - ]); + _query = + ref.read(mainDBProvider).isar.transactionV2s.buildQuery( + whereClauses: [ + IndexWhereClause.equalTo( + indexName: 'walletId', + value: [widget.walletId], + ), + ], + filter: ref + .read(pWallets) + .getWallet(widget.walletId) + .transactionFilterOperation, + sortBy: [ + const SortProperty( + property: "timestamp", + sort: Sort.desc, + ), + ], + ); _subscription = _query.watch().listen((event) { WidgetsBinding.instance.addPostFrameCallback((_) { @@ -158,7 +157,8 @@ class _TransactionsV2ListState extends ConsumerState { if (tx.subType == TransactionSubType.cashFusion) { if (fusions.isNotEmpty) { final prevTime = DateTime.fromMillisecondsSinceEpoch( - fusions.last.timestamp * 1000); + fusions.last.timestamp * 1000, + ); final thisTime = DateTime.fromMillisecondsSinceEpoch(tx.timestamp * 1000); diff --git a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_list_item.dart b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_list_item.dart index ccaae32cc..f51c9f609 100644 --- a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_list_item.dart +++ b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_list_item.dart @@ -2,11 +2,10 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:tuple/tuple.dart'; + import '../../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; import '../../../../models/isar/models/isar_models.dart'; -import '../../../exchange_view/trade_details_view.dart'; -import 'fusion_tx_group_card.dart'; -import 'transaction_v2_card.dart'; import '../../../../providers/global/trades_service_provider.dart'; import '../../../../route_generator.dart'; import '../../../../themes/stack_colors.dart'; @@ -17,7 +16,9 @@ import '../../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../../widgets/desktop/desktop_dialog.dart'; import '../../../../widgets/desktop/desktop_dialog_close_button.dart'; import '../../../../widgets/trade_card.dart'; -import 'package:tuple/tuple.dart'; +import '../../../exchange_view/trade_details_view.dart'; +import 'fusion_tx_group_card.dart'; +import 'transaction_v2_card.dart'; class TxListItem extends ConsumerWidget { const TxListItem({ @@ -56,10 +57,12 @@ class TxListItem extends ConsumerWidget { transaction: _tx, ), TradeCard( - key: Key(_tx.txid + - _tx.type.name + - _tx.hashCode.toString() + - trade.uuid), // + key: Key( + _tx.txid + + _tx.type.name + + _tx.hashCode.toString() + + trade.uuid, + ), // trade: trade, onTap: () async { if (Util.isDesktop) { @@ -136,7 +139,7 @@ class TxListItem extends ConsumerWidget { ); } }, - ) + ), ], ), ); diff --git a/lib/pages/wallet_view/wallet_view.dart b/lib/pages/wallet_view/wallet_view.dart index 2f2eb334f..7147ca299 100644 --- a/lib/pages/wallet_view/wallet_view.dart +++ b/lib/pages/wallet_view/wallet_view.dart @@ -64,9 +64,6 @@ import '../../utilities/enums/sync_type_enum.dart'; import '../../utilities/logger.dart'; import '../../utilities/show_loading.dart'; import '../../utilities/text_styles.dart'; -import '../../wallets/crypto_currency/coins/banano.dart'; -import '../../wallets/crypto_currency/coins/bitcoin.dart'; -import '../../wallets/crypto_currency/coins/firo.dart'; import '../../wallets/crypto_currency/crypto_currency.dart'; import '../../wallets/crypto_currency/intermediate/frost_currency.dart'; import '../../wallets/isar/providers/wallet_info_provider.dart'; diff --git a/lib/pages/wallets_view/sub_widgets/all_wallets.dart b/lib/pages/wallets_view/sub_widgets/all_wallets.dart index a69d2bd13..067fcdcb3 100644 --- a/lib/pages/wallets_view/sub_widgets/all_wallets.dart +++ b/lib/pages/wallets_view/sub_widgets/all_wallets.dart @@ -18,7 +18,7 @@ import '../../../wallets/isar/providers/all_wallets_info_provider.dart'; import '../../../widgets/custom_buttons/blue_text_button.dart'; class AllWallets extends StatelessWidget { - const AllWallets({Key? key}) : super(key: key); + const AllWallets({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/wallets_view/sub_widgets/empty_wallets.dart b/lib/pages/wallets_view/sub_widgets/empty_wallets.dart index 530c27e54..905ee350f 100644 --- a/lib/pages/wallets_view/sub_widgets/empty_wallets.dart +++ b/lib/pages/wallets_view/sub_widgets/empty_wallets.dart @@ -21,7 +21,7 @@ import '../../../utilities/text_styles.dart'; import '../../../utilities/util.dart'; class EmptyWallets extends ConsumerWidget { - const EmptyWallets({Key? key}) : super(key: key); + const EmptyWallets({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -83,9 +83,9 @@ class EmptyWallets extends ConsumerWidget { ), ), if (!isDesktop) - Row( + const Row( mainAxisAlignment: MainAxisAlignment.center, - children: const [ + children: [ AddWalletButton( isDesktop: false, ), @@ -103,7 +103,7 @@ class EmptyWallets extends ConsumerWidget { } class AddWalletButton extends ConsumerWidget { - const AddWalletButton({Key? key, required this.isDesktop}) : super(key: key); + const AddWalletButton({super.key, required this.isDesktop}); final bool isDesktop; diff --git a/lib/pages/wallets_view/sub_widgets/favorite_card.dart b/lib/pages/wallets_view/sub_widgets/favorite_card.dart index 6f3dd84bb..ab91cc759 100644 --- a/lib/pages/wallets_view/sub_widgets/favorite_card.dart +++ b/lib/pages/wallets_view/sub_widgets/favorite_card.dart @@ -274,9 +274,11 @@ class _FavoriteCardState extends ConsumerState { } class CardOverlayStack extends StatelessWidget { - const CardOverlayStack( - {Key? key, required this.background, required this.child}) - : super(key: key); + const CardOverlayStack({ + super.key, + required this.background, + required this.child, + }); final Widget background; final Widget child; diff --git a/lib/pages/wallets_view/sub_widgets/favorite_wallets.dart b/lib/pages/wallets_view/sub_widgets/favorite_wallets.dart index b46e84b87..de03c3fd7 100644 --- a/lib/pages/wallets_view/sub_widgets/favorite_wallets.dart +++ b/lib/pages/wallets_view/sub_widgets/favorite_wallets.dart @@ -13,18 +13,18 @@ import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; -import '../../manage_favorites_view/manage_favorites_view.dart'; -import 'favorite_card.dart'; + import '../../../themes/stack_colors.dart'; import '../../../utilities/assets.dart'; import '../../../utilities/constants.dart'; import '../../../utilities/text_styles.dart'; import '../../../wallets/isar/providers/favourite_wallets_provider.dart'; -import '../../../widgets/custom_page_view/custom_page_view.dart' - as cpv; +import '../../../widgets/custom_page_view/custom_page_view.dart' as cpv; +import '../../manage_favorites_view/manage_favorites_view.dart'; +import 'favorite_card.dart'; class FavoriteWallets extends ConsumerStatefulWidget { - const FavoriteWallets({Key? key}) : super(key: key); + const FavoriteWallets({super.key}); @override ConsumerState createState() => _FavoriteWalletsState(); @@ -55,9 +55,11 @@ class _FavoriteWalletsState extends ConsumerState { _pageController.addListener(() { if (_pageController.position.pixels > (cardWidth * (_favLength - 1))) { - _pageController.animateToPage(_favLength - 1, - duration: const Duration(milliseconds: 1), - curve: Curves.decelerate); + _pageController.animateToPage( + _favLength - 1, + duration: const Duration(milliseconds: 1), + curve: Curves.decelerate, + ); } }); super.initState(); @@ -76,7 +78,7 @@ class _FavoriteWalletsState extends ConsumerState { final favorites = ref.watch(pFavouriteWalletInfos(true)); _favLength = favorites.length; - bool hasFavorites = favorites.isNotEmpty; + final bool hasFavorites = favorites.isNotEmpty; final remaining = ((screenWidth - cardWidth) / cardWidth).ceil(); @@ -100,7 +102,8 @@ class _FavoriteWalletsState extends ConsumerState { TextButton( style: ButtonStyle( backgroundColor: MaterialStateProperty.all( - Theme.of(context).extension()!.background), + Theme.of(context).extension()!.background, + ), ), child: SvgPicture.asset( Assets.svg.ellipsis, @@ -135,7 +138,8 @@ class _FavoriteWalletsState extends ConsumerState { .extension()! .textFieldDefaultBG, borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius), + Constants.size.circularBorderRadius, + ), ), child: MaterialButton( splashColor: @@ -145,7 +149,8 @@ class _FavoriteWalletsState extends ConsumerState { materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius), + Constants.size.circularBorderRadius, + ), ), onPressed: () { Navigator.of(context) diff --git a/lib/pages/wallets_view/sub_widgets/wallet_list_item.dart b/lib/pages/wallets_view/sub_widgets/wallet_list_item.dart index d7ec6037b..5602d2419 100644 --- a/lib/pages/wallets_view/sub_widgets/wallet_list_item.dart +++ b/lib/pages/wallets_view/sub_widgets/wallet_list_item.dart @@ -14,8 +14,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; -import '../../wallet_view/wallet_view.dart'; -import '../wallets_overview.dart'; + import '../../../providers/providers.dart'; import '../../../themes/coin_icon_provider.dart'; import '../../../themes/stack_colors.dart'; @@ -24,11 +23,12 @@ import '../../../utilities/constants.dart'; import '../../../utilities/show_loading.dart'; import '../../../utilities/text_styles.dart'; import '../../../utilities/util.dart'; -import '../../../wallets/crypto_currency/coins/ethereum.dart'; import '../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; import '../../../widgets/dialogs/tor_warning_dialog.dart'; import '../../../widgets/rounded_white_container.dart'; +import '../../wallet_view/wallet_view.dart'; +import '../wallets_overview.dart'; class WalletListItem extends ConsumerWidget { const WalletListItem({ @@ -128,16 +128,19 @@ class WalletListItem extends ConsumerWidget { Expanded( child: Consumer( builder: (_, ref, __) { - final tuple = ref.watch(priceAnd24hChangeNotifierProvider - .select((value) => value.getPrice(coin))); + final tuple = ref.watch( + priceAnd24hChangeNotifierProvider + .select((value) => value.getPrice(coin)), + ); final calls = ref.watch(prefsChangeNotifierProvider).externalCalls; final priceString = tuple.item1.toAmount(fractionDigits: 2).fiatString( locale: ref.watch( - localeServiceChangeNotifierProvider - .select((value) => value.locale)), + localeServiceChangeNotifierProvider + .select((value) => value.locale), + ), ); final double percentChange = tuple.item2; diff --git a/lib/pages/wallets_view/wallets_overview.dart b/lib/pages/wallets_view/wallets_overview.dart index ebd4931f7..f557e6f7e 100644 --- a/lib/pages/wallets_view/wallets_overview.dart +++ b/lib/pages/wallets_view/wallets_overview.dart @@ -12,9 +12,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:isar/isar.dart'; +import 'package:tuple/tuple.dart'; + import '../../models/add_wallet_list_entity/sub_classes/coin_entity.dart'; import '../../models/isar/models/ethereum/eth_contract.dart'; -import '../add_wallet_views/create_or_restore_wallet_view/create_or_restore_wallet_view.dart'; import '../../pages_desktop_specific/my_stack_view/dialogs/desktop_expanding_wallet_card.dart'; import '../../providers/db/main_db_provider.dart'; import '../../providers/providers.dart'; @@ -23,7 +24,6 @@ import '../../utilities/assets.dart'; import '../../utilities/constants.dart'; import '../../utilities/text_styles.dart'; import '../../utilities/util.dart'; -import '../../wallets/crypto_currency/coins/ethereum.dart'; import '../../wallets/crypto_currency/crypto_currency.dart'; import '../../wallets/isar/models/wallet_info.dart'; import '../../wallets/isar/providers/wallet_info_provider.dart'; @@ -37,7 +37,7 @@ import '../../widgets/rounded_white_container.dart'; import '../../widgets/stack_text_field.dart'; import '../../widgets/textfield_icon_button.dart'; import '../../widgets/wallet_card.dart'; -import 'package:tuple/tuple.dart'; +import '../add_wallet_views/create_or_restore_wallet_view/create_or_restore_wallet_view.dart'; class WalletsOverview extends ConsumerStatefulWidget { const WalletsOverview({ @@ -296,7 +296,8 @@ class _EthWalletsOverviewState extends ConsumerState { if (isDesktop) { return DesktopExpandingWalletCard( key: Key( - "${element.item1.info.name}_${element.item2.map((e) => e.address).join()}"), + "${element.item1.info.name}_${element.item2.map((e) => e.address).join()}", + ), data: element, navigatorState: widget.navigatorState!, ); diff --git a/lib/pages/wallets_view/wallets_view.dart b/lib/pages/wallets_view/wallets_view.dart index 76298c595..236e79e4d 100644 --- a/lib/pages/wallets_view/wallets_view.dart +++ b/lib/pages/wallets_view/wallets_view.dart @@ -19,7 +19,7 @@ import '../../themes/theme_providers.dart'; import '../../wallets/isar/providers/all_wallets_info_provider.dart'; class WalletsView extends ConsumerWidget { - const WalletsView({Key? key}) : super(key: key); + const WalletsView({super.key}); static const routeName = "/wallets"; @@ -28,8 +28,9 @@ class WalletsView extends ConsumerWidget { debugPrint("BUILD: $runtimeType"); final hasWallets = ref.watch(pAllWalletsInfo).isNotEmpty; - final showFavorites = ref.watch(prefsChangeNotifierProvider - .select((value) => value.showFavoriteWallets)); + final showFavorites = ref.watch( + prefsChangeNotifierProvider.select((value) => value.showFavoriteWallets), + ); return SafeArea( child: hasWallets diff --git a/lib/pages_desktop_specific/address_book_view/desktop_address_book.dart b/lib/pages_desktop_specific/address_book_view/desktop_address_book.dart index af09985df..c68b9c058 100644 --- a/lib/pages_desktop_specific/address_book_view/desktop_address_book.dart +++ b/lib/pages_desktop_specific/address_book_view/desktop_address_book.dart @@ -22,7 +22,6 @@ import '../../providers/db/main_db_provider.dart'; import '../../providers/global/address_book_service_provider.dart'; import '../../providers/providers.dart'; import '../../providers/ui/address_book_providers/address_book_filter_provider.dart'; -import '../../app_config.dart'; import '../../themes/stack_colors.dart'; import '../../utilities/assets.dart'; import '../../utilities/constants.dart'; diff --git a/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_address_book_scaffold.dart b/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_address_book_scaffold.dart index a7a47e979..ce435797f 100644 --- a/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_address_book_scaffold.dart +++ b/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_address_book_scaffold.dart @@ -12,7 +12,7 @@ import 'package:flutter/widgets.dart'; class DesktopAddressBookScaffold extends StatelessWidget { const DesktopAddressBookScaffold({ - Key? key, + super.key, required this.controlsLeft, required this.controlsRight, required this.filterItems, @@ -21,7 +21,7 @@ class DesktopAddressBookScaffold extends StatelessWidget { required this.favorites, required this.all, required this.details, - }) : super(key: key); + }); final Widget? controlsLeft; final Widget? controlsRight; @@ -115,7 +115,7 @@ class DesktopAddressBookScaffold extends StatelessWidget { ), ], ), - ) + ), ], ); } diff --git a/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_address_card.dart b/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_address_card.dart index 509653aba..b124fa025 100644 --- a/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_address_card.dart +++ b/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_address_card.dart @@ -14,6 +14,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; + import '../../../models/isar/models/contact_entry.dart'; import '../../../notifications/show_flush_bar.dart'; import '../../../pages/address_book_views/subviews/edit_contact_address_view.dart'; @@ -22,7 +23,6 @@ import '../../../themes/coin_icon_provider.dart'; import '../../../themes/stack_colors.dart'; import '../../../utilities/assets.dart'; import '../../../utilities/clipboard_interface.dart'; - import '../../../utilities/text_styles.dart'; import '../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../widgets/custom_buttons/blue_text_button.dart'; @@ -30,11 +30,11 @@ import '../../../widgets/desktop/desktop_dialog.dart'; class DesktopAddressCard extends ConsumerWidget { const DesktopAddressCard({ - Key? key, + super.key, required this.entry, required this.contactId, this.clipboard = const ClipboardWrapper(), - }) : super(key: key); + }); final ContactAddressEntry entry; final String contactId; @@ -103,7 +103,8 @@ class DesktopAddressCard extends ConsumerWidget { text: "Edit", onTap: () async { ref.refresh( - addressEntryDataProviderFamilyRefresher); + addressEntryDataProviderFamilyRefresher, + ); ref.read(addressEntryDataProvider(0)).address = entry.address; ref.read(addressEntryDataProvider(0)).addressLabel = @@ -155,7 +156,7 @@ class DesktopAddressCard extends ConsumerWidget { }, ), ], - ) + ), ], ), ), diff --git a/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_contact_details.dart b/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_contact_details.dart index d38d171d3..f3037f527 100644 --- a/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_contact_details.dart +++ b/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_contact_details.dart @@ -63,12 +63,14 @@ class _DesktopContactDetailsState extends ConsumerState { .transactions .where() .filter() - .anyOf(contact.addresses.map((e) => e.address), - (q, String e) => q.address((q) => q.valueEqualTo(e))) + .anyOf( + contact.addresses.map((e) => e.address), + (q, String e) => q.address((q) => q.valueEqualTo(e)), + ) .sortByTimestampDesc() .findAll(); - List> result = []; + final List> result = []; for (final tx in transactions) { result.add(Tuple2(tx.walletId, tx)); @@ -85,8 +87,10 @@ class _DesktopContactDetailsState extends ConsumerState { // provider hack to prevent trying to update widget with deleted contact ContactEntry? _contact; try { - _contact = ref.watch(addressBookServiceProvider - .select((value) => value.getContactById(widget.contactId))); + _contact = ref.watch( + addressBookServiceProvider + .select((value) => value.getContactById(widget.contactId)), + ); } catch (_) { return Container(); } @@ -186,7 +190,8 @@ class _DesktopContactDetailsState extends ConsumerState { text: "Add new", onTap: () async { ref.refresh( - addressEntryDataProviderFamilyRefresher); + addressEntryDataProviderFamilyRefresher, + ); await showDialog( context: context, @@ -280,9 +285,11 @@ class _DesktopContactDetailsState extends ConsumerState { ), FutureBuilder( future: _filteredTransactionsByContact(), - builder: (_, - AsyncSnapshot>> - snapshot) { + builder: ( + _, + AsyncSnapshot>> + snapshot, + ) { if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) { @@ -300,7 +307,8 @@ class _DesktopContactDetailsState extends ConsumerState { ..._cachedTransactions.map( (e) => TransactionCard( key: Key( - "contactDetailsTransaction_${e.item1}_${e.item2.txid}_cardKey"), + "contactDetailsTransaction_${e.item1}_${e.item2.txid}_cardKey", + ), transaction: e.item2, walletId: e.item1, ), @@ -334,7 +342,8 @@ class _DesktopContactDetailsState extends ConsumerState { ..._cachedTransactions.map( (e) => TransactionCard( key: Key( - "contactDetailsTransaction_${e.item1}_${e.item2.txid}_cardKey"), + "contactDetailsTransaction_${e.item1}_${e.item2.txid}_cardKey", + ), transaction: e.item2, walletId: e.item1, ), diff --git a/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_contact_options_menu_popup.dart b/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_contact_options_menu_popup.dart index 61b0de815..5601cf702 100644 --- a/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_contact_options_menu_popup.dart +++ b/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_contact_options_menu_popup.dart @@ -11,6 +11,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; + import '../../../notifications/show_flush_bar.dart'; import '../../../pages/address_book_views/subviews/edit_contact_name_emoji_view.dart'; import '../../../providers/global/address_book_service_provider.dart'; @@ -23,8 +24,7 @@ import '../../../widgets/desktop/primary_button.dart'; import '../../../widgets/desktop/secondary_button.dart'; class DesktopContactOptionsMenuPopup extends ConsumerStatefulWidget { - const DesktopContactOptionsMenuPopup({Key? key, required this.contactId}) - : super(key: key); + const DesktopContactOptionsMenuPopup({super.key, required this.contactId}); final String contactId; @@ -169,7 +169,7 @@ class _DesktopContactOptionsMenuPopupState ), ), ], - ) + ), ], ), ), @@ -256,20 +256,23 @@ class _DesktopContactOptionsMenuPopupState width: 12, ), Text( - ref.watch(addressBookServiceProvider.select( - (value) => value - .getContactById(widget.contactId) - .isFavorite)) + ref.watch( + addressBookServiceProvider.select( + (value) => value + .getContactById(widget.contactId) + .isFavorite, + ), + ) ? "Remove from favorites" : "Add to favorites", style: STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( + context, + ).copyWith( color: Theme.of(context) .extension()! .textDark, ), - ) + ), ], ), ), @@ -326,13 +329,13 @@ class _DesktopContactOptionsMenuPopupState Text( "Edit contact", style: STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( + context, + ).copyWith( color: Theme.of(context) .extension()! .textDark, ), - ) + ), ], ), ), @@ -389,13 +392,13 @@ class _DesktopContactOptionsMenuPopupState Text( "Delete contact", style: STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( + context, + ).copyWith( color: Theme.of(context) .extension()! .textDark, ), - ) + ), ], ), ), diff --git a/lib/pages_desktop_specific/addresses/desktop_wallet_addresses_view.dart b/lib/pages_desktop_specific/addresses/desktop_wallet_addresses_view.dart index cf6c7c424..9186b8f0b 100644 --- a/lib/pages_desktop_specific/addresses/desktop_wallet_addresses_view.dart +++ b/lib/pages_desktop_specific/addresses/desktop_wallet_addresses_view.dart @@ -12,9 +12,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:isar/isar.dart'; + import '../../models/isar/models/isar_models.dart'; import '../../pages/receive_view/addresses/address_details_view.dart'; -import 'sub_widgets/desktop_address_list.dart'; import '../../providers/db/main_db_provider.dart'; import '../../themes/stack_colors.dart'; import '../../utilities/assets.dart'; @@ -22,14 +22,15 @@ import '../../utilities/text_styles.dart'; import '../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../widgets/desktop/desktop_app_bar.dart'; import '../../widgets/desktop/desktop_scaffold.dart'; +import 'sub_widgets/desktop_address_list.dart'; final desktopSelectedAddressId = StateProvider.autoDispose((ref) => null); class DesktopWalletAddressesView extends ConsumerStatefulWidget { const DesktopWalletAddressesView({ - Key? key, + super.key, required this.walletId, - }) : super(key: key); + }); static const String routeName = "/desktopWalletAddressesView"; @@ -143,7 +144,8 @@ class _DesktopWalletAddressesViewState child: SingleChildScrollView( child: AddressDetailsView( key: Key( - "currentDesktopAddressDetails_key_${ref.watch(desktopSelectedAddressId.state).state}"), + "currentDesktopAddressDetails_key_${ref.watch(desktopSelectedAddressId.state).state}", + ), walletId: widget.walletId, addressId: ref .watch(desktopSelectedAddressId.state) diff --git a/lib/pages_desktop_specific/addresses/sub_widgets/desktop_address_list.dart b/lib/pages_desktop_specific/addresses/sub_widgets/desktop_address_list.dart index 3da33f220..9ac4fab33 100644 --- a/lib/pages_desktop_specific/addresses/sub_widgets/desktop_address_list.dart +++ b/lib/pages_desktop_specific/addresses/sub_widgets/desktop_address_list.dart @@ -12,9 +12,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:isar/isar.dart'; + import '../../../models/isar/models/isar_models.dart'; import '../../../pages/receive_view/addresses/address_card.dart'; -import '../desktop_wallet_addresses_view.dart'; import '../../../providers/db/main_db_provider.dart'; import '../../../themes/stack_colors.dart'; import '../../../utilities/assets.dart'; @@ -26,6 +26,7 @@ import '../../../widgets/icon_widgets/x_icon.dart'; import '../../../widgets/rounded_white_container.dart'; import '../../../widgets/stack_text_field.dart'; import '../../../widgets/textfield_icon_button.dart'; +import '../desktop_wallet_addresses_view.dart'; class DesktopAddressList extends ConsumerStatefulWidget { const DesktopAddressList({ @@ -55,14 +56,16 @@ class _DesktopAddressListState extends ConsumerState { .read(mainDBProvider) .getAddresses(widget.walletId) .filter() - .group((q) => q - .subTypeEqualTo(AddressSubType.change) - .or() - .subTypeEqualTo(AddressSubType.receiving) - .or() - .subTypeEqualTo(AddressSubType.paynymReceive) - .or() - .subTypeEqualTo(AddressSubType.paynymNotification)) + .group( + (q) => q + .subTypeEqualTo(AddressSubType.change) + .or() + .subTypeEqualTo(AddressSubType.receiving) + .or() + .subTypeEqualTo(AddressSubType.paynymReceive) + .or() + .subTypeEqualTo(AddressSubType.paynymNotification), + ) .and() .not() .typeEqualTo(AddressType.nonWallet) @@ -99,15 +102,19 @@ class _DesktopAddressListState extends ConsumerState { .getAddresses(widget.walletId) .filter() .anyOf( - labels, (q, e) => q.valueEqualTo(e.addressString)) - .group((q) => q - .subTypeEqualTo(AddressSubType.change) - .or() - .subTypeEqualTo(AddressSubType.receiving) - .or() - .subTypeEqualTo(AddressSubType.paynymReceive) - .or() - .subTypeEqualTo(AddressSubType.paynymNotification)) + labels, + (q, e) => q.valueEqualTo(e.addressString), + ) + .group( + (q) => q + .subTypeEqualTo(AddressSubType.change) + .or() + .subTypeEqualTo(AddressSubType.receiving) + .or() + .subTypeEqualTo(AddressSubType.paynymReceive) + .or() + .subTypeEqualTo(AddressSubType.paynymNotification), + ) .and() .not() .typeEqualTo(AddressType.nonWallet) diff --git a/lib/pages_desktop_specific/cashfusion/desktop_cashfusion_view.dart b/lib/pages_desktop_specific/cashfusion/desktop_cashfusion_view.dart index 9d674c585..d35da1a31 100644 --- a/lib/pages_desktop_specific/cashfusion/desktop_cashfusion_view.dart +++ b/lib/pages_desktop_specific/cashfusion/desktop_cashfusion_view.dart @@ -17,8 +17,8 @@ import 'package:flutter/services.dart'; import 'package:flutter_native_splash/cli_commands.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../pages/cashfusion/fusion_rounds_selection_sheet.dart'; -import 'sub_widgets/fusion_dialog.dart'; import '../../providers/cash_fusion/fusion_progress_ui_state_provider.dart'; import '../../providers/global/prefs_provider.dart'; import '../../providers/global/wallets_provider.dart'; @@ -38,6 +38,7 @@ import '../../widgets/desktop/desktop_scaffold.dart'; import '../../widgets/desktop/primary_button.dart'; import '../../widgets/rounded_white_container.dart'; import '../../widgets/stack_text_field.dart'; +import 'sub_widgets/fusion_dialog.dart'; class DesktopCashFusionView extends ConsumerStatefulWidget { const DesktopCashFusionView({ @@ -77,7 +78,8 @@ class _DesktopCashFusion extends ConsumerState { ); } catch (e) { if (!e.toString().contains( - "FusionProgressUIState was already set for ${widget.walletId}")) { + "FusionProgressUIState was already set for ${widget.walletId}", + )) { rethrow; } } @@ -256,7 +258,8 @@ class _DesktopCashFusion extends ConsumerState { Text( "What is Fusion?", style: STextStyles.desktopH2( - context), + context, + ), ), DesktopDialogCloseButton( onPressedOverride: () => @@ -276,8 +279,8 @@ class _DesktopCashFusion extends ConsumerState { "companies to track your coins.", style: STextStyles.desktopTextMedium( - context) - .copyWith( + context, + ).copyWith( color: Theme.of(context) .extension()! .textDark3, @@ -337,7 +340,8 @@ class _DesktopCashFusion extends ConsumerState { Text( "Server settings", style: STextStyles.desktopTextExtraExtraSmall( - context), + context, + ), ), CustomTextButton( text: "Default", @@ -396,7 +400,7 @@ class _DesktopCashFusion extends ConsumerState { controller: portController, focusNode: portFocusNode, inputFormatters: [ - FilteringTextInputFormatter.digitsOnly + FilteringTextInputFormatter.digitsOnly, ], onChanged: (value) { setState(() { @@ -544,7 +548,7 @@ class _DesktopCashFusion extends ConsumerState { controller: fusionRoundController, focusNode: fusionRoundFocusNode, inputFormatters: [ - FilteringTextInputFormatter.digitsOnly + FilteringTextInputFormatter.digitsOnly, ], onChanged: (value) { setState(() { @@ -562,8 +566,8 @@ class _DesktopCashFusion extends ConsumerState { context, desktopMed: true, ).copyWith( - labelText: - "Enter number of fusions.."), + labelText: "Enter number of fusions..", + ), ), ), ], diff --git a/lib/pages_desktop_specific/cashfusion/sub_widgets/fusion_dialog.dart b/lib/pages_desktop_specific/cashfusion/sub_widgets/fusion_dialog.dart index 437c61a55..51502bf81 100644 --- a/lib/pages_desktop_specific/cashfusion/sub_widgets/fusion_dialog.dart +++ b/lib/pages_desktop_specific/cashfusion/sub_widgets/fusion_dialog.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'fusion_progress.dart'; + import '../../../providers/cash_fusion/fusion_progress_ui_state_provider.dart'; import '../../../providers/global/prefs_provider.dart'; import '../../../providers/global/wallets_provider.dart'; @@ -18,6 +18,7 @@ import '../../../widgets/desktop/primary_button.dart'; import '../../../widgets/desktop/secondary_button.dart'; import '../../../widgets/rounded_container.dart'; import '../../../widgets/rounded_white_container.dart'; +import 'fusion_progress.dart'; enum CashFusionStatus { waiting, running, success, failed } @@ -303,7 +304,8 @@ class _FusionDialogViewState extends ConsumerState { ); } catch (e) { if (!e.toString().contains( - "FusionProgressUIState was already set for ${widget.walletId}")) { + "FusionProgressUIState was already set for ${widget.walletId}", + )) { rethrow; } } diff --git a/lib/pages_desktop_specific/cashfusion/sub_widgets/fusion_progress.dart b/lib/pages_desktop_specific/cashfusion/sub_widgets/fusion_progress.dart index 8487ce863..3e3f5b44e 100644 --- a/lib/pages_desktop_specific/cashfusion/sub_widgets/fusion_progress.dart +++ b/lib/pages_desktop_specific/cashfusion/sub_widgets/fusion_progress.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; + import '../../../pages/settings_views/global_settings_view/stack_backup_views/sub_widgets/restoring_item_card.dart'; -import 'fusion_dialog.dart'; import '../../../providers/cash_fusion/fusion_progress_ui_state_provider.dart'; import '../../../themes/stack_colors.dart'; import '../../../utilities/assets.dart'; @@ -10,6 +10,7 @@ import '../../../utilities/text_styles.dart'; import '../../../utilities/util.dart'; import '../../../widgets/conditional_parent.dart'; import '../../../widgets/rounded_container.dart'; +import 'fusion_dialog.dart'; class FusionProgress extends ConsumerWidget { const FusionProgress({super.key, required this.walletId}); @@ -22,42 +23,57 @@ class FusionProgress extends ConsumerWidget { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _ProgressItem( - iconAsset: Assets.svg.node, - label: "Connecting to server", - state: ref.watch(fusionProgressUIStateProvider(walletId) - .select((value) => value.connecting))), + iconAsset: Assets.svg.node, + label: "Connecting to server", + state: ref.watch( + fusionProgressUIStateProvider(walletId) + .select((value) => value.connecting), + ), + ), const SizedBox( height: 12, ), _ProgressItem( - iconAsset: Assets.svg.upFromLine, - label: "Allocating outputs", - state: ref.watch(fusionProgressUIStateProvider(walletId) - .select((value) => value.outputs))), + iconAsset: Assets.svg.upFromLine, + label: "Allocating outputs", + state: ref.watch( + fusionProgressUIStateProvider(walletId) + .select((value) => value.outputs), + ), + ), const SizedBox( height: 12, ), _ProgressItem( - iconAsset: Assets.svg.peers, - label: "Waiting for peers", - state: ref.watch(fusionProgressUIStateProvider(walletId) - .select((value) => value.peers))), + iconAsset: Assets.svg.peers, + label: "Waiting for peers", + state: ref.watch( + fusionProgressUIStateProvider(walletId) + .select((value) => value.peers), + ), + ), const SizedBox( height: 12, ), _ProgressItem( - iconAsset: Assets.svg.fusing, - label: "Fusing", - state: ref.watch(fusionProgressUIStateProvider(walletId) - .select((value) => value.fusing))), + iconAsset: Assets.svg.fusing, + label: "Fusing", + state: ref.watch( + fusionProgressUIStateProvider(walletId) + .select((value) => value.fusing), + ), + ), const SizedBox( height: 12, ), _ProgressItem( - iconAsset: Assets.svg.checkCircle, - label: "Complete", - state: ref.watch(fusionProgressUIStateProvider(walletId) - .select((value) => value.complete))), + iconAsset: Assets.svg.checkCircle, + label: "Complete", + state: ref.watch( + fusionProgressUIStateProvider(walletId) + .select((value) => value.complete), + ), + ), ], ); } diff --git a/lib/pages_desktop_specific/coin_control/desktop_coin_control_use_dialog.dart b/lib/pages_desktop_specific/coin_control/desktop_coin_control_use_dialog.dart index a76851278..dce4eeafc 100644 --- a/lib/pages_desktop_specific/coin_control/desktop_coin_control_use_dialog.dart +++ b/lib/pages_desktop_specific/coin_control/desktop_coin_control_use_dialog.dart @@ -14,9 +14,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:isar/isar.dart'; + import '../../db/isar/main_db.dart'; import '../../models/isar/models/blockchain_data/utxo.dart'; -import 'utxo_row.dart'; import '../../themes/coin_icon_provider.dart'; import '../../themes/stack_colors.dart'; import '../../utilities/amount/amount.dart'; @@ -38,6 +38,7 @@ import '../../widgets/rounded_container.dart'; import '../../widgets/stack_text_field.dart'; import '../../widgets/textfield_icon_button.dart'; import '../../widgets/toggle.dart'; +import 'utxo_row.dart'; final desktopUseUTXOs = StateProvider((ref) => {}); @@ -295,7 +296,7 @@ class _DesktopCoinControlUseDialogState } }, displayPrefix: "Sort by", - ) + ), ], ), const SizedBox( @@ -320,7 +321,8 @@ class _DesktopCoinControlUseDialogState return UtxoRow( key: Key( - "${utxo.walletId}_${utxo.id}_${utxo.isBlocked}"), + "${utxo.walletId}_${utxo.id}_${utxo.isBlocked}", + ), data: data, compact: true, walletId: widget.walletId, @@ -391,7 +393,8 @@ class _DesktopCoinControlUseDialogState "output${entry.value.length > 1 ? "s" : ""}", style: STextStyles .desktopTextExtraExtraSmall( - context), + context, + ), ), ), RotateIcon( @@ -422,7 +425,8 @@ class _DesktopCoinControlUseDialogState return UtxoRow( key: Key( - "${utxo.walletId}_${utxo.id}_${utxo.isBlocked}"), + "${utxo.walletId}_${utxo.id}_${utxo.isBlocked}", + ), data: data, compact: true, compactWithBorder: false, @@ -477,8 +481,8 @@ class _DesktopCoinControlUseDialogState "Amount to send", style: STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( + context, + ).copyWith( color: Theme.of(context) .extension()! .textDark, @@ -512,8 +516,8 @@ class _DesktopCoinControlUseDialogState Text( "Selected amount", style: STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( + context, + ).copyWith( color: Theme.of(context) .extension()! .textDark, @@ -524,8 +528,8 @@ class _DesktopCoinControlUseDialogState coin.fractionDigits, )} ${coin.ticker}", style: STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( + context, + ).copyWith( color: widget.amountToSend == null ? Theme.of(context) .extension()! diff --git a/lib/pages_desktop_specific/coin_control/desktop_coin_control_view.dart b/lib/pages_desktop_specific/coin_control/desktop_coin_control_view.dart index 957cfacdb..653da8a68 100644 --- a/lib/pages_desktop_specific/coin_control/desktop_coin_control_view.dart +++ b/lib/pages_desktop_specific/coin_control/desktop_coin_control_view.dart @@ -14,10 +14,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:isar/isar.dart'; + import '../../db/isar/main_db.dart'; import '../../models/isar/models/blockchain_data/utxo.dart'; -import 'freeze_button.dart'; -import 'utxo_row.dart'; import '../../themes/coin_icon_provider.dart'; import '../../themes/stack_colors.dart'; import '../../utilities/assets.dart'; @@ -36,12 +35,14 @@ import '../../widgets/icon_widgets/x_icon.dart'; import '../../widgets/rounded_container.dart'; import '../../widgets/stack_text_field.dart'; import '../../widgets/textfield_icon_button.dart'; +import 'freeze_button.dart'; +import 'utxo_row.dart'; class DesktopCoinControlView extends ConsumerStatefulWidget { const DesktopCoinControlView({ - Key? key, + super.key, required this.walletId, - }) : super(key: key); + }); static const String routeName = "/desktopCoinControl"; @@ -308,7 +309,8 @@ class _DesktopCoinControlViewState return UtxoRow( key: Key( - "${utxo.walletId}_${utxo.id}_${utxo.isBlocked}"), + "${utxo.walletId}_${utxo.id}_${utxo.isBlocked}", + ), data: data, walletId: widget.walletId, onSelectionChanged: (value) { @@ -374,7 +376,8 @@ class _DesktopCoinControlViewState "output${entry.value.length > 1 ? "s" : ""}", style: STextStyles.desktopTextExtraExtraSmall( - context), + context, + ), ), ), RotateIcon( @@ -404,7 +407,8 @@ class _DesktopCoinControlViewState return UtxoRow( key: Key( - "${utxo.walletId}_${utxo.id}_${utxo.isBlocked}"), + "${utxo.walletId}_${utxo.id}_${utxo.isBlocked}", + ), data: data, walletId: widget.walletId, raiseOnSelected: false, diff --git a/lib/pages_desktop_specific/coin_control/freeze_button.dart b/lib/pages_desktop_specific/coin_control/freeze_button.dart index 0f4d6d4e8..dddcc92fa 100644 --- a/lib/pages_desktop_specific/coin_control/freeze_button.dart +++ b/lib/pages_desktop_specific/coin_control/freeze_button.dart @@ -19,9 +19,9 @@ import '../../widgets/desktop/primary_button.dart'; class FreezeButton extends StatefulWidget { const FreezeButton({ - Key? key, + super.key, required this.selectedUTXOs, - }) : super(key: key); + }); final Set selectedUTXOs; @@ -51,7 +51,7 @@ class _FreezeButtonState extends State { } Future _onFreezeStateButtonPressed() async { - List utxosToUpdate = []; + final List utxosToUpdate = []; switch (_freezeLabelCache) { case "Freeze": for (final e in widget.selectedUTXOs) { @@ -95,7 +95,7 @@ class _FreezeButtonState extends State { @override void initState() { - List> streams = []; + final List> streams = []; for (final data in widget.selectedUTXOs) { final stream = MainDB.instance.watchUTXO(id: data.utxoId); diff --git a/lib/pages_desktop_specific/coin_control/utxo_row.dart b/lib/pages_desktop_specific/coin_control/utxo_row.dart index c414f1b4f..ca33fe8f2 100644 --- a/lib/pages_desktop_specific/coin_control/utxo_row.dart +++ b/lib/pages_desktop_specific/coin_control/utxo_row.dart @@ -48,14 +48,14 @@ class UtxoRowData { class UtxoRow extends ConsumerStatefulWidget { const UtxoRow({ - Key? key, + super.key, required this.data, required this.walletId, this.onSelectionChanged, this.compact = false, this.compactWithBorder = true, this.raiseOnSelected = true, - }) : super(key: key); + }); final String walletId; final UtxoRowData data; diff --git a/lib/pages_desktop_specific/desktop_buy/desktop_buy_view.dart b/lib/pages_desktop_specific/desktop_buy/desktop_buy_view.dart index f2d0e9bef..6c5d2dc2d 100644 --- a/lib/pages_desktop_specific/desktop_buy/desktop_buy_view.dart +++ b/lib/pages_desktop_specific/desktop_buy/desktop_buy_view.dart @@ -22,7 +22,7 @@ import '../../widgets/rounded_white_container.dart'; import '../../widgets/tor_subscription.dart'; class DesktopBuyView extends ConsumerStatefulWidget { - const DesktopBuyView({Key? key}) : super(key: key); + const DesktopBuyView({super.key}); static const String routeName = "/desktopBuyView"; diff --git a/lib/pages_desktop_specific/desktop_exchange/desktop_all_trades_view.dart b/lib/pages_desktop_specific/desktop_exchange/desktop_all_trades_view.dart index e4a6129bf..c487ff991 100644 --- a/lib/pages_desktop_specific/desktop_exchange/desktop_all_trades_view.dart +++ b/lib/pages_desktop_specific/desktop_exchange/desktop_all_trades_view.dart @@ -16,6 +16,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:isar/isar.dart'; +import 'package:tuple/tuple.dart'; + import '../../db/isar/main_db.dart'; import '../../models/exchange/change_now/exchange_transaction_status.dart'; import '../../models/exchange/response_objects/trade.dart'; @@ -41,10 +43,9 @@ import '../../widgets/icon_widgets/x_icon.dart'; import '../../widgets/rounded_white_container.dart'; import '../../widgets/stack_text_field.dart'; import '../../widgets/textfield_icon_button.dart'; -import 'package:tuple/tuple.dart'; class DesktopAllTradesView extends ConsumerStatefulWidget { - const DesktopAllTradesView({Key? key}) : super(key: key); + const DesktopAllTradesView({super.key}); static const String routeName = "/desktopAllTrades"; @@ -60,10 +61,11 @@ class _DesktopAllTradesViewState extends ConsumerState { String _searchString = ""; List>> groupTransactionsByMonth( - List trades) { - Map> map = {}; + List trades, + ) { + final Map> map = {}; - for (var trade in trades) { + for (final trade in trades) { final date = trade.timestamp; final monthYear = "${Constants.monthMap[date.month]} ${date.year}"; if (map[monthYear] == null) { @@ -72,7 +74,7 @@ class _DesktopAllTradesViewState extends ConsumerState { map[monthYear]!.add(trade); } - List>> result = []; + final List>> result = []; map.forEach((key, value) { result.add(Tuple2(key, value)); }); @@ -214,7 +216,8 @@ class _DesktopAllTradesViewState extends ConsumerState { child: Consumer( builder: (_, ref, __) { List trades = ref.watch( - tradesServiceProvider.select((value) => value.trades)); + tradesServiceProvider.select((value) => value.trades), + ); if (_searchString.isNotEmpty) { final term = _searchString.toLowerCase(); @@ -261,7 +264,8 @@ class _DesktopAllTradesViewState extends ConsumerState { padding: const EdgeInsets.all(4), child: DesktopTradeRowCard( key: Key( - "transactionCard_key_${month.item2[index].tradeId}"), + "transactionCard_key_${month.item2[index].tradeId}", + ), tradeId: month.item2[index].tradeId, ), ), @@ -284,9 +288,9 @@ class _DesktopAllTradesViewState extends ConsumerState { class DesktopTradeRowCard extends ConsumerStatefulWidget { const DesktopTradeRowCard({ - Key? key, + super.key, required this.tradeId, - }) : super(key: key); + }); final String tradeId; @@ -563,7 +567,8 @@ class _DesktopTradeRowCardState extends ConsumerState { flex: 4, child: Text( Format.extractDateFrom( - trade.timestamp.millisecondsSinceEpoch ~/ 1000), + trade.timestamp.millisecondsSinceEpoch ~/ 1000, + ), style: STextStyles.desktopTextExtraExtraSmall(context), ), ), diff --git a/lib/pages_desktop_specific/desktop_exchange/desktop_exchange_view.dart b/lib/pages_desktop_specific/desktop_exchange/desktop_exchange_view.dart index 1476d5d72..d0d0fda65 100644 --- a/lib/pages_desktop_specific/desktop_exchange/desktop_exchange_view.dart +++ b/lib/pages_desktop_specific/desktop_exchange/desktop_exchange_view.dart @@ -26,7 +26,7 @@ import '../../widgets/desktop/desktop_scaffold.dart'; import '../../widgets/rounded_white_container.dart'; class DesktopExchangeView extends ConsumerStatefulWidget { - const DesktopExchangeView({Key? key}) : super(key: key); + const DesktopExchangeView({super.key}); static const String routeName = "/desktopExchange"; @@ -95,7 +95,7 @@ class _DesktopExchangeViewState extends ConsumerState { subMessage: "This could take a few minutes", eventBus: null, ), - ) + ), ], ); }, @@ -173,9 +173,9 @@ class _DesktopExchangeViewState extends ConsumerState { const SizedBox( width: 16, ), - Expanded( + const Expanded( child: Row( - children: const [ + children: [ Expanded( child: DesktopTradeHistory(), ), diff --git a/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_1.dart b/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_1.dart index afa894500..6d9c61831 100644 --- a/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_1.dart +++ b/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_1.dart @@ -10,18 +10,19 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import '../step_scaffold.dart'; -import 'desktop_step_item.dart'; + import '../../../../providers/providers.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/enums/exchange_rate_type_enum.dart'; import '../../../../utilities/text_styles.dart'; import '../../../../widgets/rounded_white_container.dart'; +import '../step_scaffold.dart'; +import 'desktop_step_item.dart'; class DesktopStep1 extends ConsumerWidget { const DesktopStep1({ - Key? key, - }) : super(key: key); + super.key, + }); @override Widget build(BuildContext context, WidgetRef ref) { @@ -73,13 +74,17 @@ class DesktopStep1 extends ConsumerWidget { color: Theme.of(context).extension()!.background, ), DesktopStepItem( - label: ref.watch(desktopExchangeModelProvider - .select((value) => value!.rateType)) == + label: ref.watch( + desktopExchangeModelProvider + .select((value) => value!.rateType), + ) == ExchangeRateType.estimated ? "Estimated rate" : "Fixed rate", - value: ref.watch(desktopExchangeModelProvider - .select((value) => value!.rateInfo)), + value: ref.watch( + desktopExchangeModelProvider + .select((value) => value!.rateInfo), + ), ), ], ), diff --git a/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_2.dart b/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_2.dart index a1ff7fb8d..64f4e4471 100644 --- a/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_2.dart +++ b/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_2.dart @@ -37,10 +37,10 @@ import '../step_scaffold.dart'; class DesktopStep2 extends ConsumerStatefulWidget { const DesktopStep2({ - Key? key, + super.key, required this.enableNextChanged, this.clipboard = const ClipboardWrapper(), - }) : super(key: key); + }); final ClipboardInterface clipboard; final void Function(bool) enableNextChanged; diff --git a/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_3.dart b/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_3.dart index d60a552ce..8dfb82734 100644 --- a/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_3.dart +++ b/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_3.dart @@ -20,8 +20,8 @@ import '../../../../widgets/rounded_white_container.dart'; class DesktopStep3 extends ConsumerStatefulWidget { const DesktopStep3({ - Key? key, - }) : super(key: key); + super.key, + }); @override ConsumerState createState() => _DesktopStep3State(); @@ -71,13 +71,17 @@ class _DesktopStep3State extends ConsumerState { color: Theme.of(context).extension()!.background, ), DesktopStepItem( - label: ref.watch(desktopExchangeModelProvider - .select((value) => value!.rateType)) == + label: ref.watch( + desktopExchangeModelProvider + .select((value) => value!.rateType), + ) == ExchangeRateType.estimated ? "Estimated rate" : "Fixed rate", - value: ref.watch(desktopExchangeModelProvider - .select((value) => value!.rateInfo)), + value: ref.watch( + desktopExchangeModelProvider + .select((value) => value!.rateInfo), + ), ), Container( height: 1, @@ -87,8 +91,10 @@ class _DesktopStep3State extends ConsumerState { vertical: true, label: "Recipient ${ref.watch(desktopExchangeModelProvider.select((value) => value!.receiveTicker.toUpperCase()))} address", - value: ref.watch(desktopExchangeModelProvider - .select((value) => value!.recipientAddress)) ?? + value: ref.watch( + desktopExchangeModelProvider + .select((value) => value!.recipientAddress), + ) ?? "Error", ), Container( @@ -99,8 +105,10 @@ class _DesktopStep3State extends ConsumerState { vertical: true, label: "Refund ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendTicker.toUpperCase()))} address", - value: ref.watch(desktopExchangeModelProvider - .select((value) => value!.refundAddress)) ?? + value: ref.watch( + desktopExchangeModelProvider + .select((value) => value!.refundAddress), + ) ?? "Error", ), ], diff --git a/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_4.dart b/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_4.dart index b831653a9..dd30adee2 100644 --- a/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_4.dart +++ b/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_4.dart @@ -12,19 +12,20 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../../../app_config.dart'; -import '../step_scaffold.dart'; -import 'desktop_step_item.dart'; import '../../../../providers/providers.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/text_styles.dart'; import '../../../../widgets/rounded_container.dart'; import '../../../../widgets/rounded_white_container.dart'; +import '../step_scaffold.dart'; +import 'desktop_step_item.dart'; class DesktopStep4 extends ConsumerStatefulWidget { const DesktopStep4({ - Key? key, - }) : super(key: key); + super.key, + }); @override ConsumerState createState() => _DesktopStep4State(); @@ -147,26 +148,34 @@ class _DesktopStep4State extends ConsumerState { vertical: true, label: "Send ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendTicker.toUpperCase()))} to this address", - value: ref.watch(desktopExchangeModelProvider - .select((value) => value!.trade?.payInAddress)) ?? + value: ref.watch( + desktopExchangeModelProvider + .select((value) => value!.trade?.payInAddress), + ) ?? "Error", ), Container( height: 1, color: Theme.of(context).extension()!.background, ), - if (ref.watch(desktopExchangeModelProvider - .select((value) => value!.trade?.payInExtraId)) != + if (ref.watch( + desktopExchangeModelProvider + .select((value) => value!.trade?.payInExtraId), + ) != null) DesktopStepItem( vertical: true, label: "Memo", - value: ref.watch(desktopExchangeModelProvider - .select((value) => value!.trade?.payInExtraId)) ?? + value: ref.watch( + desktopExchangeModelProvider + .select((value) => value!.trade?.payInExtraId), + ) ?? "Error", ), - if (ref.watch(desktopExchangeModelProvider - .select((value) => value!.trade?.payInExtraId)) != + if (ref.watch( + desktopExchangeModelProvider + .select((value) => value!.trade?.payInExtraId), + ) != null) Container( height: 1, @@ -183,8 +192,10 @@ class _DesktopStep4State extends ConsumerState { ), DesktopStepItem( label: "Trade ID", - value: ref.watch(desktopExchangeModelProvider - .select((value) => value!.trade?.tradeId)) ?? + value: ref.watch( + desktopExchangeModelProvider + .select((value) => value!.trade?.tradeId), + ) ?? "Error", ), Container( diff --git a/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_item.dart b/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_item.dart index 6b169f573..352e353fb 100644 --- a/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_item.dart +++ b/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_item.dart @@ -9,18 +9,19 @@ */ import 'package:flutter/material.dart'; + import '../../../../themes/stack_colors.dart'; import '../../../../utilities/text_styles.dart'; import '../../../../widgets/conditional_parent.dart'; class DesktopStepItem extends StatelessWidget { - const DesktopStepItem( - {Key? key, - required this.label, - required this.value, - this.padding = const EdgeInsets.all(16), - this.vertical = false}) - : super(key: key); + const DesktopStepItem({ + super.key, + required this.label, + required this.value, + this.padding = const EdgeInsets.all(16), + this.vertical = false, + }); final String label; final String value; diff --git a/lib/pages_desktop_specific/desktop_exchange/subwidgets/desktop_choose_from_stack.dart b/lib/pages_desktop_specific/desktop_exchange/subwidgets/desktop_choose_from_stack.dart index bf9f9ffc6..bbe22138e 100644 --- a/lib/pages_desktop_specific/desktop_exchange/subwidgets/desktop_choose_from_stack.dart +++ b/lib/pages_desktop_specific/desktop_exchange/subwidgets/desktop_choose_from_stack.dart @@ -11,6 +11,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:tuple/tuple.dart'; + import '../../../providers/providers.dart'; import '../../../themes/stack_colors.dart'; import '../../../utilities/amount/amount.dart'; @@ -18,7 +20,6 @@ import '../../../utilities/amount/amount_formatter.dart'; import '../../../utilities/assets.dart'; import '../../../utilities/constants.dart'; import '../../../utilities/text_styles.dart'; -import '../../../wallets/crypto_currency/coins/firo.dart'; import '../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../widgets/custom_buttons/blue_text_button.dart'; @@ -28,7 +29,6 @@ import '../../../widgets/rounded_white_container.dart'; import '../../../widgets/stack_text_field.dart'; import '../../../widgets/textfield_icon_button.dart'; import '../../../widgets/wallet_info_row/sub_widgets/wallet_info_row_coin_icon.dart'; -import 'package:tuple/tuple.dart'; class DesktopChooseFromStack extends ConsumerStatefulWidget { const DesktopChooseFromStack({ @@ -194,8 +194,10 @@ class _DesktopChooseFromStackState height: 5, ), itemBuilder: (context, index) { - final wallet = ref.watch(pWallets - .select((value) => value.getWallet(walletIds[index]))); + final wallet = ref.watch( + pWallets + .select((value) => value.getWallet(walletIds[index])), + ); return RoundedWhiteContainer( borderColor: @@ -216,8 +218,8 @@ class _DesktopChooseFromStackState Text( wallet.info.name, style: STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( + context, + ).copyWith( color: Theme.of(context) .extension()! .textDark, @@ -275,7 +277,7 @@ class _DesktopChooseFromStackState ), ), ], - ) + ), ], ); } diff --git a/lib/pages_desktop_specific/desktop_exchange/subwidgets/desktop_exchange_steps_indicator.dart b/lib/pages_desktop_specific/desktop_exchange/subwidgets/desktop_exchange_steps_indicator.dart index 6d2c677cb..c8d4ba9ff 100644 --- a/lib/pages_desktop_specific/desktop_exchange/subwidgets/desktop_exchange_steps_indicator.dart +++ b/lib/pages_desktop_specific/desktop_exchange/subwidgets/desktop_exchange_steps_indicator.dart @@ -14,8 +14,7 @@ import '../../../utilities/text_styles.dart'; import '../../../widgets/rounded_container.dart'; class DesktopExchangeStepsIndicator extends StatelessWidget { - const DesktopExchangeStepsIndicator({Key? key, required this.currentStep}) - : super(key: key); + const DesktopExchangeStepsIndicator({super.key, required this.currentStep}); final int currentStep; diff --git a/lib/pages_desktop_specific/desktop_exchange/subwidgets/desktop_trade_history.dart b/lib/pages_desktop_specific/desktop_exchange/subwidgets/desktop_trade_history.dart index 86240e37c..d589e2118 100644 --- a/lib/pages_desktop_specific/desktop_exchange/subwidgets/desktop_trade_history.dart +++ b/lib/pages_desktop_specific/desktop_exchange/subwidgets/desktop_trade_history.dart @@ -13,6 +13,8 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:isar/isar.dart'; + +import '../../../db/isar/main_db.dart'; import '../../../models/isar/models/blockchain_data/transaction.dart'; import '../../../pages/exchange_view/trade_details_view.dart'; import '../../../providers/exchange/trade_sent_from_stack_lookup_provider.dart'; @@ -27,10 +29,8 @@ import '../../../widgets/desktop/desktop_dialog_close_button.dart'; import '../../../widgets/rounded_white_container.dart'; import '../../../widgets/trade_card.dart'; -import '../../../db/isar/main_db.dart'; - class DesktopTradeHistory extends ConsumerStatefulWidget { - const DesktopTradeHistory({Key? key}) : super(key: key); + const DesktopTradeHistory({super.key}); @override ConsumerState createState() => @@ -154,7 +154,8 @@ class _DesktopTradeHistoryState extends ConsumerState { Text( "Trade details", style: STextStyles.desktopH3( - context), + context, + ), ), DesktopDialogCloseButton( onPressedOverride: @@ -173,8 +174,10 @@ class _DesktopTradeHistoryState extends ConsumerState { tradeId: tradeId, transactionIfSentFromStack: tx, walletName: ref.read( - pWalletName( - walletIds.first)), + pWalletName( + walletIds.first, + ), + ), walletId: walletIds.first, ), ), @@ -220,7 +223,8 @@ class _DesktopTradeHistoryState extends ConsumerState { Text( "Trade details", style: STextStyles.desktopH3( - context), + context, + ), ), DesktopDialogCloseButton( onPressedOverride: diff --git a/lib/pages_desktop_specific/desktop_home_view.dart b/lib/pages_desktop_specific/desktop_home_view.dart index 80ca02051..980b563a0 100644 --- a/lib/pages_desktop_specific/desktop_home_view.dart +++ b/lib/pages_desktop_specific/desktop_home_view.dart @@ -10,15 +10,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'address_book_view/desktop_address_book.dart'; -import 'desktop_buy/desktop_buy_view.dart'; -import 'desktop_exchange/desktop_exchange_view.dart'; -import 'desktop_menu.dart'; -import 'my_stack_view/my_stack_view.dart'; -import 'notifications/desktop_notifications_view.dart'; -import 'settings/desktop_settings_view.dart'; -import 'settings/settings_menu/desktop_about_view.dart'; -import 'settings/settings_menu/desktop_support_view.dart'; + import '../providers/desktop/current_desktop_menu_item.dart'; import '../providers/global/active_wallet_provider.dart'; import '../providers/global/auto_swb_service_provider.dart'; @@ -31,9 +23,18 @@ import '../route_generator.dart'; import '../themes/stack_colors.dart'; import '../utilities/enums/backup_frequency_type.dart'; import '../widgets/background.dart'; +import 'address_book_view/desktop_address_book.dart'; +import 'desktop_buy/desktop_buy_view.dart'; +import 'desktop_exchange/desktop_exchange_view.dart'; +import 'desktop_menu.dart'; +import 'my_stack_view/my_stack_view.dart'; +import 'notifications/desktop_notifications_view.dart'; +import 'settings/desktop_settings_view.dart'; +import 'settings/settings_menu/desktop_about_view.dart'; +import 'settings/settings_menu/desktop_support_view.dart'; class DesktopHomeView extends ConsumerStatefulWidget { - const DesktopHomeView({Key? key}) : super(key: key); + const DesktopHomeView({super.key}); static const String routeName = "/desktopHome"; @@ -144,11 +145,13 @@ class _DesktopHomeViewState extends ConsumerState { ref.read(unreadNotificationsStateProvider.state).state; if (unreadNotificationIds.isNotEmpty) { - List> futures = []; + final List> futures = []; for (int i = 0; i < unreadNotificationIds.length - 1; i++) { - futures.add(ref - .read(notificationsProvider) - .markAsRead(unreadNotificationIds.elementAt(i), false)); + futures.add( + ref + .read(notificationsProvider) + .markAsRead(unreadNotificationIds.elementAt(i), false), + ); } // wait for multiple to update if any diff --git a/lib/pages_desktop_specific/desktop_menu.dart b/lib/pages_desktop_specific/desktop_menu.dart index 7ff4714a4..868454541 100644 --- a/lib/pages_desktop_specific/desktop_menu.dart +++ b/lib/pages_desktop_specific/desktop_menu.dart @@ -37,10 +37,10 @@ enum DesktopMenuItemId { class DesktopMenu extends ConsumerStatefulWidget { const DesktopMenu({ - Key? key, + super.key, this.onSelectionChanged, this.onSelectionWillChange, - }) : super(key: key); + }); final void Function(DesktopMenuItemId)? onSelectionChanged; final void Function(DesktopMenuItemId)? onSelectionWillChange; @@ -70,7 +70,7 @@ class _DesktopMenuState extends ConsumerState { void toggleMinimize() { final expanded = _width == expandedWidth; - for (var e in controllers) { + for (final e in controllers) { e.toggle?.call(); } @@ -102,7 +102,7 @@ class _DesktopMenuState extends ConsumerState { @override void dispose() { - for (var e in controllers) { + for (final e in controllers) { e.dispose(); } torButtonController.dispose(); diff --git a/lib/pages_desktop_specific/desktop_menu_item.dart b/lib/pages_desktop_specific/desktop_menu_item.dart index c7eee9020..1fde1281c 100644 --- a/lib/pages_desktop_specific/desktop_menu_item.dart +++ b/lib/pages_desktop_specific/desktop_menu_item.dart @@ -13,13 +13,14 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; -import 'desktop_menu.dart'; + import '../providers/desktop/current_desktop_menu_item.dart'; import '../providers/global/notifications_provider.dart'; import '../themes/stack_colors.dart'; import '../themes/theme_providers.dart'; import '../utilities/assets.dart'; import '../utilities/text_styles.dart'; +import 'desktop_menu.dart'; class DMIController { VoidCallback? toggle; @@ -29,7 +30,7 @@ class DMIController { } class DesktopMyStackIcon extends ConsumerWidget { - const DesktopMyStackIcon({Key? key}) : super(key: key); + const DesktopMyStackIcon({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -49,7 +50,7 @@ class DesktopMyStackIcon extends ConsumerWidget { } class DesktopExchangeIcon extends ConsumerWidget { - const DesktopExchangeIcon({Key? key}) : super(key: key); + const DesktopExchangeIcon({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -69,7 +70,7 @@ class DesktopExchangeIcon extends ConsumerWidget { } class DesktopBuyIcon extends ConsumerWidget { - const DesktopBuyIcon({Key? key}) : super(key: key); + const DesktopBuyIcon({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -89,12 +90,13 @@ class DesktopBuyIcon extends ConsumerWidget { } class DesktopNotificationsIcon extends ConsumerWidget { - const DesktopNotificationsIcon({Key? key}) : super(key: key); + const DesktopNotificationsIcon({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - return ref.watch(notificationsProvider - .select((value) => value.hasUnreadNotifications)) + return ref.watch( + notificationsProvider.select((value) => value.hasUnreadNotifications), + ) ? SvgPicture.file( File( ref.watch( @@ -110,8 +112,10 @@ class DesktopNotificationsIcon extends ConsumerWidget { Assets.svg.bell, width: 20, height: 20, - color: ref.watch(notificationsProvider - .select((value) => value.hasUnreadNotifications)) + color: ref.watch( + notificationsProvider + .select((value) => value.hasUnreadNotifications), + ) ? null : DesktopMenuItemId.notifications == ref.watch(currentDesktopMenuItemProvider.state).state @@ -127,7 +131,7 @@ class DesktopNotificationsIcon extends ConsumerWidget { } class DesktopAddressBookIcon extends ConsumerWidget { - const DesktopAddressBookIcon({Key? key}) : super(key: key); + const DesktopAddressBookIcon({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -147,7 +151,7 @@ class DesktopAddressBookIcon extends ConsumerWidget { } class DesktopSettingsIcon extends ConsumerWidget { - const DesktopSettingsIcon({Key? key}) : super(key: key); + const DesktopSettingsIcon({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -167,7 +171,7 @@ class DesktopSettingsIcon extends ConsumerWidget { } class DesktopSupportIcon extends ConsumerWidget { - const DesktopSupportIcon({Key? key}) : super(key: key); + const DesktopSupportIcon({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -187,7 +191,7 @@ class DesktopSupportIcon extends ConsumerWidget { } class DesktopAboutIcon extends ConsumerWidget { - const DesktopAboutIcon({Key? key}) : super(key: key); + const DesktopAboutIcon({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -207,7 +211,7 @@ class DesktopAboutIcon extends ConsumerWidget { } class DesktopExitIcon extends ConsumerWidget { - const DesktopExitIcon({Key? key}) : super(key: key); + const DesktopExitIcon({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -225,7 +229,7 @@ class DesktopExitIcon extends ConsumerWidget { class DesktopMenuItem extends ConsumerStatefulWidget { const DesktopMenuItem({ - Key? key, + super.key, required this.icon, required this.label, required this.value, @@ -233,7 +237,7 @@ class DesktopMenuItem extends ConsumerStatefulWidget { required this.duration, this.labelLength = 125, this.controller, - }) : super(key: key); + }); final Widget icon; final String label; @@ -350,7 +354,7 @@ class _DesktopMenuItemState extends ConsumerState> ), ), ), - ) + ), ], ), ), diff --git a/lib/pages_desktop_specific/lelantus_coins/lelantus_coins_view.dart b/lib/pages_desktop_specific/lelantus_coins/lelantus_coins_view.dart index 12b118cba..61624a272 100644 --- a/lib/pages_desktop_specific/lelantus_coins/lelantus_coins_view.dart +++ b/lib/pages_desktop_specific/lelantus_coins/lelantus_coins_view.dart @@ -24,9 +24,9 @@ import '../../widgets/rounded_white_container.dart'; class LelantusCoinsView extends ConsumerStatefulWidget { const LelantusCoinsView({ - Key? key, + super.key, required this.walletId, - }) : super(key: key); + }); static const String routeName = "/lelantusCoinsView"; diff --git a/lib/pages_desktop_specific/my_stack_view/coin_wallets_table.dart b/lib/pages_desktop_specific/my_stack_view/coin_wallets_table.dart index 70c11546c..e440af8fe 100644 --- a/lib/pages_desktop_specific/my_stack_view/coin_wallets_table.dart +++ b/lib/pages_desktop_specific/my_stack_view/coin_wallets_table.dart @@ -117,9 +117,9 @@ class CoinWalletsTable extends ConsumerWidget { class WalletRowHoverOverlay extends StatefulWidget { const WalletRowHoverOverlay({ - Key? key, + super.key, required this.onPressed, - }) : super(key: key); + }); final VoidCallback onPressed; diff --git a/lib/pages_desktop_specific/my_stack_view/desktop_favorite_wallets.dart b/lib/pages_desktop_specific/my_stack_view/desktop_favorite_wallets.dart index 994a5f949..4ec2e43a4 100644 --- a/lib/pages_desktop_specific/my_stack_view/desktop_favorite_wallets.dart +++ b/lib/pages_desktop_specific/my_stack_view/desktop_favorite_wallets.dart @@ -11,6 +11,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../pages/manage_favorites_view/manage_favorites_view.dart'; import '../../pages/wallets_view/sub_widgets/favorite_card.dart'; import '../../themes/stack_colors.dart'; @@ -21,7 +22,7 @@ import '../../wallets/isar/providers/favourite_wallets_provider.dart'; import '../../widgets/custom_buttons/blue_text_button.dart'; class DesktopFavoriteWallets extends ConsumerWidget { - const DesktopFavoriteWallets({Key? key}) : super(key: key); + const DesktopFavoriteWallets({super.key}); static const cardWidth = 220.0; static const cardHeight = 125.0; @@ -78,7 +79,7 @@ class DesktopFavoriteWallets extends ConsumerWidget { width: cardWidth, height: cardHeight, ); - }) + }), ], ), ) @@ -101,7 +102,8 @@ class DesktopFavoriteWallets extends ConsumerWidget { materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius), + Constants.size.circularBorderRadius, + ), ), onPressed: () { Navigator.of(context) diff --git a/lib/pages_desktop_specific/my_stack_view/dialogs/desktop_expanding_wallet_card.dart b/lib/pages_desktop_specific/my_stack_view/dialogs/desktop_expanding_wallet_card.dart index 22fe2250f..5246a6b16 100644 --- a/lib/pages_desktop_specific/my_stack_view/dialogs/desktop_expanding_wallet_card.dart +++ b/lib/pages_desktop_specific/my_stack_view/dialogs/desktop_expanding_wallet_card.dart @@ -27,10 +27,10 @@ import 'package:tuple/tuple.dart'; class DesktopExpandingWalletCard extends StatefulWidget { const DesktopExpandingWalletCard({ - Key? key, + super.key, required this.data, required this.navigatorState, - }) : super(key: key); + }); final Tuple2> data; final NavigatorState navigatorState; diff --git a/lib/pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart b/lib/pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart index 57848404b..5c43cb131 100644 --- a/lib/pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart +++ b/lib/pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart @@ -16,9 +16,9 @@ import '../../utilities/text_styles.dart'; class ExitToMyStackButton extends StatelessWidget { const ExitToMyStackButton({ - Key? key, + super.key, this.onPressed, - }) : super(key: key); + }); final VoidCallback? onPressed; diff --git a/lib/pages_desktop_specific/my_stack_view/my_stack_view.dart b/lib/pages_desktop_specific/my_stack_view/my_stack_view.dart index e981979db..99a55c8dd 100644 --- a/lib/pages_desktop_specific/my_stack_view/my_stack_view.dart +++ b/lib/pages_desktop_specific/my_stack_view/my_stack_view.dart @@ -124,7 +124,7 @@ class _DesktopMyStackTitleState extends ConsumerState { Text( "My ${AppConfig.prefix}", style: STextStyles.desktopH3(context), - ) + ), ], ); } diff --git a/lib/pages_desktop_specific/my_stack_view/my_wallets.dart b/lib/pages_desktop_specific/my_stack_view/my_wallets.dart index 1e8789239..70a756452 100644 --- a/lib/pages_desktop_specific/my_stack_view/my_wallets.dart +++ b/lib/pages_desktop_specific/my_stack_view/my_wallets.dart @@ -10,16 +10,17 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../pages/add_wallet_views/add_wallet_view/add_wallet_view.dart'; -import 'desktop_favorite_wallets.dart'; -import 'wallet_summary_table.dart'; import '../../providers/providers.dart'; import '../../themes/stack_colors.dart'; import '../../utilities/text_styles.dart'; import '../../widgets/custom_buttons/blue_text_button.dart'; +import 'desktop_favorite_wallets.dart'; +import 'wallet_summary_table.dart'; class MyWallets extends ConsumerStatefulWidget { - const MyWallets({Key? key}) : super(key: key); + const MyWallets({super.key}); @override ConsumerState createState() => _MyWalletsState(); @@ -28,8 +29,9 @@ class MyWallets extends ConsumerStatefulWidget { class _MyWalletsState extends ConsumerState { @override Widget build(BuildContext context) { - final showFavorites = ref.watch(prefsChangeNotifierProvider - .select((value) => value.showFavoriteWallets)); + final showFavorites = ref.watch( + prefsChangeNotifierProvider.select((value) => value.showFavoriteWallets), + ); return Padding( padding: const EdgeInsets.only( diff --git a/lib/pages_desktop_specific/my_stack_view/paynym/desktop_paynym_send_dialog.dart b/lib/pages_desktop_specific/my_stack_view/paynym/desktop_paynym_send_dialog.dart index 4aef6a2ba..023b9fb79 100644 --- a/lib/pages_desktop_specific/my_stack_view/paynym/desktop_paynym_send_dialog.dart +++ b/lib/pages_desktop_specific/my_stack_view/paynym/desktop_paynym_send_dialog.dart @@ -13,9 +13,9 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../../models/paynym/paynym_account_lite.dart'; import '../../../models/send_view_auto_fill_data.dart'; -import '../wallet_view/sub_widgets/desktop_send.dart'; import '../../../providers/global/locale_provider.dart'; import '../../../providers/global/prefs_provider.dart'; import '../../../providers/global/price_provider.dart'; @@ -30,16 +30,17 @@ import '../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../widgets/desktop/desktop_dialog.dart'; import '../../../widgets/desktop/desktop_dialog_close_button.dart'; import '../../../widgets/rounded_white_container.dart'; +import '../wallet_view/sub_widgets/desktop_send.dart'; class DesktopPaynymSendDialog extends ConsumerStatefulWidget { const DesktopPaynymSendDialog({ - Key? key, + super.key, required this.walletId, this.autoFillData, this.clipboard = const ClipboardWrapper(), this.barcodeScanner = const BarcodeScannerWrapper(), this.accountLite, - }) : super(key: key); + }); final String walletId; final SendViewAutoFillData? autoFillData; @@ -57,7 +58,8 @@ class _DesktopPaynymSendDialogState @override Widget build(BuildContext context) { final String locale = ref.watch( - localeServiceChangeNotifierProvider.select((value) => value.locale)); + localeServiceChangeNotifierProvider.select((value) => value.locale), + ); final coin = ref.watch(pWalletCoin(widget.walletId)); @@ -126,9 +128,11 @@ class _DesktopPaynymSendDialogState crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( - ref.watch(pAmountFormatter(coin)).format(ref - .watch(pWalletBalance(widget.walletId)) - .spendable), + ref.watch(pAmountFormatter(coin)).format( + ref + .watch(pWalletBalance(widget.walletId)) + .spendable, + ), style: STextStyles.titleBold12(context), textAlign: TextAlign.right, ), @@ -142,16 +146,18 @@ class _DesktopPaynymSendDialogState ), )).toAmount(fractionDigits: 2).fiatString( locale: locale, - )} ${ref.watch(prefsChangeNotifierProvider.select( - (value) => value.currency, - ))}", + )} ${ref.watch( + prefsChangeNotifierProvider.select( + (value) => value.currency, + ), + )}", style: STextStyles.baseXS(context).copyWith( color: Theme.of(context) .extension()! .textSubtitle1, ), textAlign: TextAlign.right, - ) + ), ], ), ), diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_summary_table.dart b/lib/pages_desktop_specific/my_stack_view/wallet_summary_table.dart index 762e0f58f..f38296cd5 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_summary_table.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_summary_table.dart @@ -186,7 +186,7 @@ class _DesktopWalletSummaryRowState .extension()! .textDark, ), - ) + ), ], ), ), @@ -218,7 +218,7 @@ class _DesktopWalletSummaryRowState } class TablePriceInfo extends ConsumerWidget { - const TablePriceInfo({Key? key, required this.coin}) : super(key: key); + const TablePriceInfo({super.key, required this.coin}); final CryptoCurrency coin; diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_token_view.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_token_view.dart index 9f4c17780..e30245c4d 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_token_view.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_token_view.dart @@ -36,10 +36,10 @@ import '../../../widgets/rounded_white_container.dart'; /// [eventBus] should only be set during testing class DesktopTokenView extends ConsumerStatefulWidget { const DesktopTokenView({ - Key? key, + super.key, required this.walletId, this.eventBus, - }) : super(key: key); + }); static const String routeName = "/desktopTokenView"; diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart index 58ae825ff..83dc57784 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart @@ -16,18 +16,13 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../../pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart'; import '../../../pages/token_view/my_tokens_view.dart'; import '../../../pages/wallet_view/sub_widgets/transactions_list.dart'; import '../../../pages/wallet_view/transaction_views/all_transactions_view.dart'; import '../../../pages/wallet_view/transaction_views/tx_v2/all_transactions_v2_view.dart'; import '../../../pages/wallet_view/transaction_views/tx_v2/transaction_v2_list.dart'; -import 'sub_widgets/desktop_wallet_features.dart'; -import 'sub_widgets/desktop_wallet_summary.dart'; -import 'sub_widgets/my_wallet.dart'; -import 'sub_widgets/network_info_button.dart'; -import 'sub_widgets/wallet_keys_button.dart'; -import 'sub_widgets/wallet_options_button.dart'; import '../../../providers/global/active_wallet_provider.dart'; import '../../../providers/global/auto_swb_service_provider.dart'; import '../../../providers/providers.dart'; @@ -48,14 +43,20 @@ import '../../../widgets/desktop/desktop_app_bar.dart'; import '../../../widgets/desktop/desktop_scaffold.dart'; import '../../../widgets/hover_text_field.dart'; import '../../../widgets/rounded_white_container.dart'; +import 'sub_widgets/desktop_wallet_features.dart'; +import 'sub_widgets/desktop_wallet_summary.dart'; +import 'sub_widgets/my_wallet.dart'; +import 'sub_widgets/network_info_button.dart'; +import 'sub_widgets/wallet_keys_button.dart'; +import 'sub_widgets/wallet_options_button.dart'; /// [eventBus] should only be set during testing class DesktopWalletView extends ConsumerStatefulWidget { const DesktopWalletView({ - Key? key, + super.key, required this.walletId, this.eventBus, - }) : super(key: key); + }); static const String routeName = "/desktopWalletView"; @@ -127,8 +128,9 @@ class _DesktopWalletViewState extends ConsumerState { eventBus = widget.eventBus != null ? widget.eventBus! : GlobalEventBus.instance; - WidgetsBinding.instance.addPostFrameCallback((_) => - ref.read(currentWalletIdProvider.notifier).state = wallet.walletId); + WidgetsBinding.instance.addPostFrameCallback( + (_) => ref.read(currentWalletIdProvider.notifier).state = wallet.walletId, + ); if (!wallet.shouldAutoSync) { // // enable auto sync if it wasn't enabled when loading wallet diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/address_book_address_chooser.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/address_book_address_chooser.dart index a3132262b..5e1252be4 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/address_book_address_chooser.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/address_book_address_chooser.dart @@ -11,8 +11,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; + import '../../../../../models/isar/models/contact_entry.dart'; -import 'sub_widgets/contact_list_item.dart'; import '../../../../../providers/global/address_book_service_provider.dart'; import '../../../../../themes/stack_colors.dart'; import '../../../../../utilities/assets.dart'; @@ -23,6 +23,7 @@ import '../../../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../../../widgets/icon_widgets/x_icon.dart'; import '../../../../../widgets/stack_text_field.dart'; import '../../../../../widgets/textfield_icon_button.dart'; +import 'sub_widgets/contact_list_item.dart'; class AddressBookAddressChooser extends StatefulWidget { const AddressBookAddressChooser({ @@ -69,8 +70,9 @@ class _AddressBookAddressChooserState extends State { List filter(List contacts, String searchTerm) { if (widget.coin != null) { - contacts.removeWhere((e) => - e.addressesSorted.where((a) => a.coin == widget.coin!).isEmpty); + contacts.removeWhere( + (e) => e.addressesSorted.where((a) => a.coin == widget.coin!).isEmpty, + ); } contacts.retainWhere((e) => _matches(searchTerm, e)); @@ -202,8 +204,10 @@ class _AddressBookAddressChooserState extends State { child: Consumer( builder: (context, ref, _) { List contacts = ref - .watch(addressBookServiceProvider - .select((value) => value.contacts)) + .watch( + addressBookServiceProvider + .select((value) => value.contacts), + ) .toList(); contacts = filter(contacts, _searchTerm); @@ -227,7 +231,8 @@ class _AddressBookAddressChooserState extends State { if (index == 0) { return Padding( key: const Key( - "addressBookCAddressChooserFavoritesHeaderItemKey"), + "addressBookCAddressChooserFavoritesHeaderItemKey", + ), padding: const EdgeInsets.only( bottom: 10, ), @@ -247,7 +252,8 @@ class _AddressBookAddressChooserState extends State { } else if (index == favorites.length + 1) { return Padding( key: const Key( - "addressBookCAddressChooserAllContactsHeaderItemKey"), + "addressBookCAddressChooserAllContactsHeaderItemKey", + ), padding: const EdgeInsets.symmetric( vertical: 10, ), diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/sub_widgets/contact_list_item.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/sub_widgets/contact_list_item.dart index be7d7d138..bb7c00cea 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/sub_widgets/contact_list_item.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/sub_widgets/contact_list_item.dart @@ -10,6 +10,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../../../../../providers/global/address_book_service_provider.dart'; import '../../../../../../themes/stack_colors.dart'; import '../../../../../../utilities/text_styles.dart'; @@ -49,8 +50,10 @@ class _ContactListItemState extends ConsumerState { @override Widget build(BuildContext context) { - final contact = ref.watch(addressBookServiceProvider - .select((value) => value.getContactById(contactId))); + final contact = ref.watch( + addressBookServiceProvider + .select((value) => value.getContactById(contactId)), + ); // hack fix until we use a proper database (not Hive) int i = 0; @@ -79,12 +82,14 @@ class _ContactListItemState extends ConsumerState { children: [ // filter addresses by coin is provided before building address list ...contact.addressesSorted - .where((e) => - filterByCoin != null ? e.coin == filterByCoin! : true) + .where( + (e) => filterByCoin != null ? e.coin == filterByCoin! : true, + ) .map( (e) => Column( key: Key( - "contactAddress_${e.address}_${e.label}_${++i}_key"), + "contactAddress_${e.address}_${e.label}_${++i}_key", + ), mainAxisSize: MainAxisSize.min, children: [ Container( @@ -117,9 +122,9 @@ class _ContactListItemState extends ConsumerState { Text( "${contactId == "default" ? e.other! : e.label} (${e.coin.ticker})", style: STextStyles - .desktopTextExtraExtraSmall( - context) - .copyWith( + .desktopTextExtraExtraSmall( + context, + ).copyWith( color: Theme.of(context) .extension()! .textDark, @@ -132,7 +137,8 @@ class _ContactListItemState extends ConsumerState { e.address, style: STextStyles .desktopTextExtraExtraSmall( - context), + context, + ), ), ), ], @@ -151,7 +157,7 @@ class _ContactListItemState extends ConsumerState { ), ], ), - ) + ), ], ), ), diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/delete_wallet_keys_popup.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/delete_wallet_keys_popup.dart index fd5349380..bd6dac13f 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/delete_wallet_keys_popup.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/delete_wallet_keys_popup.dart @@ -30,11 +30,11 @@ import '../../../../widgets/desktop/secondary_button.dart'; class DeleteWalletKeysPopup extends ConsumerStatefulWidget { const DeleteWalletKeysPopup({ - Key? key, + super.key, required this.walletId, required this.words, this.clipboardInterface = const ClipboardWrapper(), - }) : super(key: key); + }); final String walletId; final List words; @@ -188,9 +188,9 @@ class _DeleteWalletKeysPopup extends ConsumerState { class ConfirmDelete extends ConsumerStatefulWidget { const ConfirmDelete({ - Key? key, + super.key, required this.walletId, - }) : super(key: key); + }); final String walletId; diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_auth_send.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_auth_send.dart index f4dffdb00..c38b33a61 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_auth_send.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_auth_send.dart @@ -13,6 +13,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; + import '../../../../providers/desktop/storage_crypto_handler_provider.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/assets.dart'; @@ -170,7 +171,8 @@ class _DesktopAuthSendState extends ConsumerState { ), GestureDetector( key: const Key( - "restoreFromFilePasswordFieldShowPasswordButtonKey"), + "restoreFromFilePasswordFieldShowPasswordButtonKey", + ), onTap: () async { setState(() { hidePassword = !hidePassword; @@ -224,7 +226,7 @@ class _DesktopAuthSendState extends ConsumerState { ), ), ], - ) + ), ], ); } diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_balance_toggle_button.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_balance_toggle_button.dart index e873843b1..abe41055c 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_balance_toggle_button.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_balance_toggle_button.dart @@ -21,9 +21,9 @@ import '../../../../utilities/text_styles.dart'; class DesktopBalanceToggleButton extends ConsumerWidget { const DesktopBalanceToggleButton({ - Key? key, + super.key, this.onPressed, - }) : super(key: key); + }); final VoidCallback? onPressed; @@ -74,9 +74,9 @@ class DesktopBalanceToggleButton extends ConsumerWidget { class DesktopPrivateBalanceToggleButton extends ConsumerWidget { const DesktopPrivateBalanceToggleButton({ - Key? key, + super.key, this.onPressed, - }) : super(key: key); + }); final VoidCallback? onPressed; diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_delete_wallet_dialog.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_delete_wallet_dialog.dart index ddad5fd55..e8c6f89e9 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_delete_wallet_dialog.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_delete_wallet_dialog.dart @@ -13,8 +13,8 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../../../notifications/show_flush_bar.dart'; -import 'desktop_attention_delete_wallet.dart'; import '../../../../providers/desktop/storage_crypto_handler_provider.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/assets.dart'; @@ -26,12 +26,13 @@ import '../../../../widgets/desktop/primary_button.dart'; import '../../../../widgets/desktop/secondary_button.dart'; import '../../../../widgets/loading_indicator.dart'; import '../../../../widgets/stack_text_field.dart'; +import 'desktop_attention_delete_wallet.dart'; class DesktopDeleteWalletDialog extends ConsumerStatefulWidget { const DesktopDeleteWalletDialog({ - Key? key, + super.key, required this.walletId, - }) : super(key: key); + }); final String walletId; @@ -54,10 +55,10 @@ class _DesktopDeleteWalletDialog unawaited( showDialog( context: context, - builder: (context) => Column( + builder: (context) => const Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, - children: const [ + children: [ LoadingIndicator( width: 200, height: 200, @@ -190,7 +191,8 @@ class _DesktopDeleteWalletDialog ), GestureDetector( key: const Key( - "desktopDeleteWalletShowPasswordButtonKey"), + "desktopDeleteWalletShowPasswordButtonKey", + ), onTap: () async { setState(() { hidePassword = !hidePassword; @@ -252,7 +254,7 @@ class _DesktopDeleteWalletDialog : null, ), ], - ) + ), ], ), ), diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart index a592d60bb..3b25f0dad 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart @@ -13,6 +13,7 @@ import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../../../models/models.dart'; import '../../../../pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart'; import '../../../../providers/global/wallets_provider.dart'; @@ -25,10 +26,6 @@ import '../../../../utilities/assets.dart'; import '../../../../utilities/constants.dart'; import '../../../../utilities/enums/fee_rate_type_enum.dart'; import '../../../../utilities/text_styles.dart'; -import '../../../../wallets/crypto_currency/coins/ethereum.dart'; -import '../../../../wallets/crypto_currency/coins/firo.dart'; -import '../../../../wallets/crypto_currency/coins/monero.dart'; -import '../../../../wallets/crypto_currency/coins/wownero.dart'; import '../../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../../wallets/isar/providers/eth/current_token_wallet_provider.dart'; import '../../../../wallets/isar/providers/wallet_info_provider.dart'; @@ -76,9 +73,11 @@ class _DesktopFeeDropDownState extends ConsumerState { switch (feeRateType) { case FeeRateType.fast: if (ref - .read(widget.isToken - ? tokenFeeSessionCacheProvider - : feeSheetSessionCacheProvider) + .read( + widget.isToken + ? tokenFeeSessionCacheProvider + : feeSheetSessionCacheProvider, + ) .fast[amount] == null) { if (widget.isToken == false) { @@ -86,7 +85,9 @@ class _DesktopFeeDropDownState extends ConsumerState { if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( - amount, MoneroTransactionPriority.fast.raw!); + amount, + MoneroTransactionPriority.fast.raw!, + ); ref.read(feeSheetSessionCacheProvider).fast[amount] = fee; } else if (coin is Firo) { final Amount fee; @@ -113,16 +114,20 @@ class _DesktopFeeDropDownState extends ConsumerState { } } return ref - .read(widget.isToken - ? tokenFeeSessionCacheProvider - : feeSheetSessionCacheProvider) + .read( + widget.isToken + ? tokenFeeSessionCacheProvider + : feeSheetSessionCacheProvider, + ) .fast[amount]!; case FeeRateType.average: if (ref - .read(widget.isToken - ? tokenFeeSessionCacheProvider - : feeSheetSessionCacheProvider) + .read( + widget.isToken + ? tokenFeeSessionCacheProvider + : feeSheetSessionCacheProvider, + ) .average[amount] == null) { if (widget.isToken == false) { @@ -130,7 +135,9 @@ class _DesktopFeeDropDownState extends ConsumerState { if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( - amount, MoneroTransactionPriority.regular.raw!); + amount, + MoneroTransactionPriority.regular.raw!, + ); ref.read(feeSheetSessionCacheProvider).average[amount] = fee; } else if (coin is Firo) { final Amount fee; @@ -157,16 +164,20 @@ class _DesktopFeeDropDownState extends ConsumerState { } } return ref - .read(widget.isToken - ? tokenFeeSessionCacheProvider - : feeSheetSessionCacheProvider) + .read( + widget.isToken + ? tokenFeeSessionCacheProvider + : feeSheetSessionCacheProvider, + ) .average[amount]!; case FeeRateType.slow: if (ref - .read(widget.isToken - ? tokenFeeSessionCacheProvider - : feeSheetSessionCacheProvider) + .read( + widget.isToken + ? tokenFeeSessionCacheProvider + : feeSheetSessionCacheProvider, + ) .slow[amount] == null) { if (widget.isToken == false) { @@ -174,7 +185,9 @@ class _DesktopFeeDropDownState extends ConsumerState { if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( - amount, MoneroTransactionPriority.slow.raw!); + amount, + MoneroTransactionPriority.slow.raw!, + ); ref.read(feeSheetSessionCacheProvider).slow[amount] = fee; } else if (coin is Firo) { final Amount fee; @@ -201,9 +214,11 @@ class _DesktopFeeDropDownState extends ConsumerState { } } return ref - .read(widget.isToken - ? tokenFeeSessionCacheProvider - : feeSheetSessionCacheProvider) + .read( + widget.isToken + ? tokenFeeSessionCacheProvider + : feeSheetSessionCacheProvider, + ) .slow[amount]!; default: return Amount.zero; @@ -295,13 +310,13 @@ final sendAmountProvider = class FeeDropDownChild extends ConsumerWidget { const FeeDropDownChild({ - Key? key, + super.key, required this.feeObject, required this.feeRateType, required this.walletId, required this.feeFor, required this.isSelected, - }) : super(key: key); + }); final FeeObject? feeObject; final FeeRateType feeRateType; @@ -322,10 +337,12 @@ class FeeDropDownChild extends ConsumerWidget { ]; String estimatedTimeToBeIncludedInNextBlock( - int targetBlockTime, int estimatedNumberOfBlocks) { - int time = targetBlockTime * estimatedNumberOfBlocks; + int targetBlockTime, + int estimatedNumberOfBlocks, + ) { + final int time = targetBlockTime * estimatedNumberOfBlocks; - int hours = (time / 3600).floor(); + final int hours = (time / 3600).floor(); if (hours > 1) { return "~$hours hours"; } else if (hours == 1) { diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_receive.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_receive.dart index 974e1f7f2..78031971e 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_receive.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_receive.dart @@ -17,6 +17,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:isar/isar.dart'; import 'package:qr_flutter/qr_flutter.dart'; +import 'package:tuple/tuple.dart'; + import '../../../../models/isar/models/isar_models.dart'; import '../../../../notifications/show_flush_bar.dart'; import '../../../../pages/receive_view/generate_receiving_uri_qr_code_view.dart'; @@ -41,7 +43,6 @@ import '../../../../widgets/custom_loading_overlay.dart'; import '../../../../widgets/desktop/desktop_dialog.dart'; import '../../../../widgets/desktop/secondary_button.dart'; import '../../../../widgets/rounded_white_container.dart'; -import 'package:tuple/tuple.dart'; class DesktopReceive extends ConsumerStatefulWidget { const DesktopReceive({ @@ -345,8 +346,8 @@ class _DesktopReceiveState extends ConsumerState { _sparkAddress ?? "Error", style: STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( + context, + ).copyWith( color: Theme.of(context) .extension()! .textDark, @@ -370,7 +371,8 @@ class _DesktopReceiveState extends ConsumerState { onTap: () { clipboard.setData( ClipboardData( - text: ref.watch(pWalletReceivingAddress(walletId))), + text: ref.watch(pWalletReceivingAddress(walletId)), + ), ); showFloatingFlushBar( type: FlushBarType.info, @@ -435,8 +437,8 @@ class _DesktopReceiveState extends ConsumerState { child: Text( ref.watch(pWalletReceivingAddress(walletId)), style: STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( + context, + ).copyWith( color: Theme.of(context) .extension()! .textDark, diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart index afa38b49a..43f5eb557 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart @@ -46,13 +46,6 @@ import '../../../../utilities/logger.dart'; import '../../../../utilities/prefs.dart'; import '../../../../utilities/text_styles.dart'; import '../../../../utilities/util.dart'; -import '../../../../wallets/crypto_currency/coins/epiccash.dart'; -import '../../../../wallets/crypto_currency/coins/ethereum.dart'; -import '../../../../wallets/crypto_currency/coins/firo.dart'; -import '../../../../wallets/crypto_currency/coins/monero.dart'; -import '../../../../wallets/crypto_currency/coins/stellar.dart'; -import '../../../../wallets/crypto_currency/coins/tezos.dart'; -import '../../../../wallets/crypto_currency/coins/wownero.dart'; import '../../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../../wallets/crypto_currency/intermediate/nano_currency.dart'; import '../../../../wallets/isar/providers/wallet_info_provider.dart'; diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_token_send.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_token_send.dart index 8272999c8..490465490 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_token_send.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_token_send.dart @@ -37,7 +37,6 @@ import '../../../../utilities/logger.dart'; import '../../../../utilities/prefs.dart'; import '../../../../utilities/text_styles.dart'; import '../../../../utilities/util.dart'; -import '../../../../wallets/crypto_currency/coins/firo.dart'; import '../../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../../wallets/isar/providers/eth/current_token_wallet_provider.dart'; import '../../../../wallets/isar/providers/eth/token_balance_provider.dart'; diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart index 0f55a7c70..2fa0ee1eb 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart @@ -152,7 +152,7 @@ class _DesktopWalletFeaturesState extends ConsumerState { _attemptAnonymize(), ); }, - ) + ), ], ), ], @@ -261,7 +261,7 @@ class _DesktopWalletFeaturesState extends ConsumerState { ), ), ], - ) + ), ], ), ), diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart index ee2c69156..5f0e0f69b 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart @@ -10,9 +10,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../../../models/balance.dart'; import '../../../../pages/wallet_view/sub_widgets/wallet_refresh_button.dart'; -import 'desktop_balance_toggle_button.dart'; import '../../../../providers/providers.dart'; import '../../../../providers/wallet/public_private_balance_state_provider.dart'; import '../../../../providers/wallet/wallet_balance_toggle_state_provider.dart'; @@ -26,14 +26,15 @@ import '../../../../wallets/crypto_currency/coins/firo.dart'; import '../../../../wallets/isar/providers/eth/current_token_wallet_provider.dart'; import '../../../../wallets/isar/providers/eth/token_balance_provider.dart'; import '../../../../wallets/isar/providers/wallet_info_provider.dart'; +import 'desktop_balance_toggle_button.dart'; class DesktopWalletSummary extends ConsumerStatefulWidget { const DesktopWalletSummary({ - Key? key, + super.key, required this.walletId, required this.initialSyncStatus, this.isToken = false, - }) : super(key: key); + }); final String walletId; final WalletSyncStatus initialSyncStatus; @@ -65,7 +66,8 @@ class _WDesktopWalletSummaryState extends ConsumerState { final coin = ref.watch(pWalletCoin(widget.walletId)); final isFiro = coin is Firo; final locale = ref.watch( - localeServiceChangeNotifierProvider.select((value) => value.locale)); + localeServiceChangeNotifierProvider.select((value) => value.locale), + ); final baseCurrency = ref .watch(prefsChangeNotifierProvider.select((value) => value.currency)); @@ -75,10 +77,14 @@ class _WDesktopWalletSummaryState extends ConsumerState { : null; final priceTuple = widget.isToken - ? ref.watch(priceAnd24hChangeNotifierProvider - .select((value) => value.getTokenPrice(tokenContract!.address))) - : ref.watch(priceAnd24hChangeNotifierProvider - .select((value) => value.getPrice(coin))); + ? ref.watch( + priceAnd24hChangeNotifierProvider + .select((value) => value.getTokenPrice(tokenContract!.address)), + ) + : ref.watch( + priceAnd24hChangeNotifierProvider + .select((value) => value.getPrice(coin)), + ); final _showAvailable = ref.watch(walletBalanceToggleStateProvider.state).state == @@ -103,9 +109,12 @@ class _WDesktopWalletSummaryState extends ConsumerState { break; } } else { - Balance balance = widget.isToken - ? ref.watch(pTokenBalance( - (walletId: walletId, contractAddress: tokenContract!.address))) + final Balance balance = widget.isToken + ? ref.watch( + pTokenBalance( + (walletId: walletId, contractAddress: tokenContract!.address), + ), + ) : ref.watch(pWalletBalance(walletId)); balanceToShow = _showAvailable ? balance.spendable : balance.total; diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart index 29507d545..10366c646 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart @@ -28,7 +28,7 @@ import '../../../../../widgets/rounded_container.dart'; class MoreFeaturesDialog extends ConsumerStatefulWidget { const MoreFeaturesDialog({ - Key? key, + super.key, required this.walletId, required this.onPaynymPressed, required this.onCoinControlPressed, @@ -37,7 +37,7 @@ class MoreFeaturesDialog extends ConsumerStatefulWidget { required this.onOrdinalsPressed, required this.onMonkeyPressed, required this.onFusionPressed, - }) : super(key: key); + }); final String walletId; final VoidCallback? onPaynymPressed; diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/my_wallet.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/my_wallet.dart index df81592c9..092f9884f 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/my_wallet.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/my_wallet.dart @@ -18,7 +18,6 @@ import 'desktop_receive.dart'; import 'desktop_send.dart'; import 'desktop_token_send.dart'; import '../../../../providers/global/wallets_provider.dart'; -import '../../../../wallets/crypto_currency/coins/ethereum.dart'; import '../../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../../wallets/wallet/impl/bitcoin_frost_wallet.dart'; import '../../../../widgets/custom_tab_view.dart'; diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/network_info_button.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/network_info_button.dart index 488f3ac4d..b84332c01 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/network_info_button.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/network_info_button.dart @@ -30,10 +30,10 @@ import 'package:tuple/tuple.dart'; class NetworkInfoButton extends ConsumerStatefulWidget { const NetworkInfoButton({ - Key? key, + super.key, required this.walletId, this.eventBus, - }) : super(key: key); + }); final String walletId; final EventBus? eventBus; diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/qr_code_desktop_popup_content.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/qr_code_desktop_popup_content.dart index af3d18683..9fb269448 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/qr_code_desktop_popup_content.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/qr_code_desktop_popup_content.dart @@ -16,9 +16,9 @@ import '../../../../widgets/desktop/desktop_dialog_close_button.dart'; class QRCodeDesktopPopupContent extends StatelessWidget { const QRCodeDesktopPopupContent({ - Key? key, + super.key, required this.value, - }) : super(key: key); + }); final String value; diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart index dddf74318..24dbe3150 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart @@ -13,8 +13,8 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../../../notifications/show_flush_bar.dart'; -import 'wallet_keys_desktop_popup.dart'; import '../../../../providers/desktop/storage_crypto_handler_provider.dart'; import '../../../../providers/providers.dart'; import '../../../../themes/stack_colors.dart'; @@ -29,12 +29,13 @@ import '../../../../widgets/desktop/primary_button.dart'; import '../../../../widgets/desktop/secondary_button.dart'; import '../../../../widgets/loading_indicator.dart'; import '../../../../widgets/stack_text_field.dart'; +import 'wallet_keys_desktop_popup.dart'; class UnlockWalletKeysDesktop extends ConsumerStatefulWidget { const UnlockWalletKeysDesktop({ - Key? key, + super.key, required this.walletId, - }) : super(key: key); + }); final String walletId; @@ -218,7 +219,8 @@ class _UnlockWalletKeysDesktopState children: [ GestureDetector( key: const Key( - "enterUnlockWalletKeysDesktopFieldShowPasswordButtonKey"), + "enterUnlockWalletKeysDesktopFieldShowPasswordButtonKey", + ), onTap: () async { setState(() { hidePassword = !hidePassword; @@ -305,7 +307,8 @@ class _UnlockWalletKeysDesktopState ); await Future.delayed( - const Duration(seconds: 1)); + const Duration(seconds: 1), + ); final verified = await ref .read(storageCryptoHandlerProvider) @@ -349,7 +352,8 @@ class _UnlockWalletKeysDesktopState Navigator.of(context, rootNavigator: true).pop(); await Future.delayed( - const Duration(milliseconds: 300)); + const Duration(milliseconds: 300), + ); unawaited( showFloatingFlushBar( diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_keys_button.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_keys_button.dart index 838e2c363..d58a66c4f 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_keys_button.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_keys_button.dart @@ -18,9 +18,9 @@ import '../../../../utilities/text_styles.dart'; class WalletKeysButton extends StatelessWidget { const WalletKeysButton({ - Key? key, + super.key, required this.walletId, - }) : super(key: key); + }); final String walletId; @@ -44,7 +44,7 @@ class WalletKeysButton extends StatelessWidget { name: UnlockWalletKeysDesktop.routeName, arguments: walletId, ), - ) + ), ]; }, ), @@ -71,7 +71,7 @@ class WalletKeysButton extends StatelessWidget { Text( "Wallet keys", style: STextStyles.desktopMenuItemSelected(context), - ) + ), ], ), ), diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_keys_desktop_popup.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_keys_desktop_popup.dart index 61bfcfebf..df9252fe5 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_keys_desktop_popup.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_keys_desktop_popup.dart @@ -12,10 +12,10 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; + import '../../../../notifications/show_flush_bar.dart'; import '../../../../pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart'; import '../../../../pages/wallet_view/transaction_views/transaction_details_view.dart'; -import 'qr_code_desktop_popup_content.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/address_utils.dart'; import '../../../../utilities/assets.dart'; @@ -26,14 +26,15 @@ import '../../../../widgets/desktop/desktop_dialog_close_button.dart'; import '../../../../widgets/desktop/primary_button.dart'; import '../../../../widgets/desktop/secondary_button.dart'; import '../../../../widgets/rounded_white_container.dart'; +import 'qr_code_desktop_popup_content.dart'; class WalletKeysDesktopPopup extends StatelessWidget { const WalletKeysDesktopPopup({ - Key? key, + super.key, required this.words, this.frostData, this.clipboardInterface = const ClipboardWrapper(), - }) : super(key: key); + }); final List words; final ({String keys, String config})? frostData; @@ -90,14 +91,17 @@ class WalletKeysDesktopPopup extends StatelessWidget { .extension()! .textFieldDefaultBG, padding: const EdgeInsets.symmetric( - horizontal: 12, vertical: 9), + horizontal: 12, + vertical: 9, + ), child: Row( children: [ Flexible( child: SelectableText( frostData!.keys, style: STextStyles.desktopTextExtraExtraSmall( - context), + context, + ), textAlign: TextAlign.center, ), ), @@ -106,7 +110,7 @@ class WalletKeysDesktopPopup extends StatelessWidget { ), IconCopyButton( data: frostData!.keys, - ) + ), // TODO [prio=low: Add QR code button and dialog. ], ), @@ -133,14 +137,17 @@ class WalletKeysDesktopPopup extends StatelessWidget { .extension()! .textFieldDefaultBG, padding: const EdgeInsets.symmetric( - horizontal: 12, vertical: 9), + horizontal: 12, + vertical: 9, + ), child: Row( children: [ Flexible( child: SelectableText( frostData!.config, style: STextStyles.desktopTextExtraExtraSmall( - context), + context, + ), textAlign: TextAlign.center, ), ), @@ -149,7 +156,7 @@ class WalletKeysDesktopPopup extends StatelessWidget { ), IconCopyButton( data: frostData!.config, - ) + ), // TODO [prio=low: Add QR code button and dialog. ], ), diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart index 23922cd2e..93a084c0e 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart @@ -14,13 +14,10 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../../../pages/settings_views/wallet_settings_view/frost_ms/frost_ms_options_view.dart'; import '../../../../pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/change_representative_view.dart'; import '../../../../pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart'; -import '../../../addresses/desktop_wallet_addresses_view.dart'; -import '../../../lelantus_coins/lelantus_coins_view.dart'; -import 'desktop_delete_wallet_dialog.dart'; -import '../../../spark_coins/spark_coins_view.dart'; import '../../../../route_generator.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/assets.dart'; @@ -30,6 +27,10 @@ import '../../../../wallets/crypto_currency/coins/firo.dart'; import '../../../../wallets/crypto_currency/intermediate/frost_currency.dart'; import '../../../../wallets/crypto_currency/intermediate/nano_currency.dart'; import '../../../../wallets/isar/providers/wallet_info_provider.dart'; +import '../../../addresses/desktop_wallet_addresses_view.dart'; +import '../../../lelantus_coins/lelantus_coins_view.dart'; +import '../../../spark_coins/spark_coins_view.dart'; +import 'desktop_delete_wallet_dialog.dart'; enum _WalletOptions { addressList, @@ -327,8 +328,8 @@ class WalletOptionsPopupMenu extends ConsumerWidget { child: Text( _WalletOptions.addressList.prettyName, style: STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( + context, + ).copyWith( color: Theme.of(context) .extension()! .textDark, @@ -364,8 +365,8 @@ class WalletOptionsPopupMenu extends ConsumerWidget { child: Text( _WalletOptions.changeRepresentative.prettyName, style: STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( + context, + ).copyWith( color: Theme.of(context) .extension()! .textDark, @@ -401,8 +402,8 @@ class WalletOptionsPopupMenu extends ConsumerWidget { child: Text( _WalletOptions.lelantusCoins.prettyName, style: STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( + context, + ).copyWith( color: Theme.of(context) .extension()! .textDark, @@ -438,8 +439,8 @@ class WalletOptionsPopupMenu extends ConsumerWidget { child: Text( _WalletOptions.sparkCoins.prettyName, style: STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( + context, + ).copyWith( color: Theme.of(context) .extension()! .textDark, @@ -475,8 +476,8 @@ class WalletOptionsPopupMenu extends ConsumerWidget { child: Text( _WalletOptions.frostOptions.prettyName, style: STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( + context, + ).copyWith( color: Theme.of(context) .extension()! .textDark, @@ -512,8 +513,8 @@ class WalletOptionsPopupMenu extends ConsumerWidget { child: Text( _WalletOptions.showXpub.prettyName, style: STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( + context, + ).copyWith( color: Theme.of(context) .extension()! .textDark, @@ -547,8 +548,8 @@ class WalletOptionsPopupMenu extends ConsumerWidget { child: Text( _WalletOptions.deleteWallet.prettyName, style: STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( + context, + ).copyWith( color: Theme.of(context) .extension()! .textDark, @@ -571,10 +572,10 @@ class WalletOptionsPopupMenu extends ConsumerWidget { class TransparentButton extends StatelessWidget { const TransparentButton({ - Key? key, + super.key, required this.child, this.onPressed, - }) : super(key: key); + }); final Widget child; final VoidCallback? onPressed; diff --git a/lib/pages_desktop_specific/notifications/desktop_notifications_view.dart b/lib/pages_desktop_specific/notifications/desktop_notifications_view.dart index bc05bafa6..e4c04fa50 100644 --- a/lib/pages_desktop_specific/notifications/desktop_notifications_view.dart +++ b/lib/pages_desktop_specific/notifications/desktop_notifications_view.dart @@ -20,7 +20,7 @@ import '../../widgets/desktop/desktop_scaffold.dart'; import '../../widgets/rounded_white_container.dart'; class DesktopNotificationsView extends ConsumerStatefulWidget { - const DesktopNotificationsView({Key? key}) : super(key: key); + const DesktopNotificationsView({super.key}); static const String routeName = "/desktopNotifications"; diff --git a/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart b/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart index 83a865e1e..4aff4afb0 100644 --- a/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart +++ b/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart @@ -5,6 +5,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; + import '../../models/isar/models/blockchain_data/utxo.dart'; import '../../models/isar/ordinal.dart'; import '../../networking/http.dart'; @@ -29,10 +30,10 @@ import '../../widgets/rounded_white_container.dart'; class DesktopOrdinalDetailsView extends ConsumerStatefulWidget { const DesktopOrdinalDetailsView({ - Key? key, + super.key, required this.walletId, required this.ordinal, - }) : super(key: key); + }); final String walletId; final Ordinal ordinal; @@ -51,7 +52,7 @@ class _DesktopOrdinalDetailsViewState late final UTXO? utxo; Future _savePngToFile() async { - HTTP client = HTTP(); + final HTTP client = HTTP(); final response = await client.get( url: Uri.parse(widget.ordinal.content), @@ -62,7 +63,8 @@ class _DesktopOrdinalDetailsViewState if (response.code != 200) { throw Exception( - "DesktopOrdinalDetailsView _savePngToFile statusCode=${response.code} body=${response.bodyBytes}"); + "DesktopOrdinalDetailsView _savePngToFile statusCode=${response.code} body=${response.bodyBytes}", + ); } final bytes = response.bodyBytes; @@ -78,7 +80,7 @@ class _DesktopOrdinalDetailsViewState final docPath = dir.path; final filePath = "$docPath/ordinal_${widget.ordinal.inscriptionNumber}.png"; - File imgFile = File(filePath); + final File imgFile = File(filePath); if (imgFile.existsSync()) { throw Exception("File already exists"); @@ -284,24 +286,30 @@ class _DesktopOrdinalDetailsViewState // ), // // todo: add utxo status const _Divider(), - Consumer(builder: (context, ref, _) { - final coin = ref - .watch(pWallets) - .getWallet(widget.walletId) - .info - .coin; - return _DetailsItemWCopy( - title: "Amount", - data: utxo == null - ? "ERROR" - : ref.watch(pAmountFormatter(coin)).format( - Amount( - rawValue: BigInt.from(utxo!.value), - fractionDigits: coin.fractionDigits, + Consumer( + builder: (context, ref, _) { + final coin = ref + .watch(pWallets) + .getWallet(widget.walletId) + .info + .coin; + return _DetailsItemWCopy( + title: "Amount", + data: utxo == null + ? "ERROR" + : ref + .watch(pAmountFormatter(coin)) + .format( + Amount( + rawValue: + BigInt.from(utxo!.value), + fractionDigits: + coin.fractionDigits, + ), ), - ), - ); - }), + ); + }, + ), const _Divider(), _DetailsItemWCopy( title: "Owner address", @@ -328,7 +336,7 @@ class _DesktopOrdinalDetailsViewState } class _Divider extends StatelessWidget { - const _Divider({Key? key}) : super(key: key); + const _Divider({super.key}); @override Widget build(BuildContext context) { @@ -346,10 +354,10 @@ class _Divider extends StatelessWidget { class _DetailsItemWCopy extends StatelessWidget { const _DetailsItemWCopy({ - Key? key, + super.key, required this.title, required this.data, - }) : super(key: key); + }); final String title; final String data; diff --git a/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart b/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart index 3ab866f27..29291510e 100644 --- a/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart +++ b/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart @@ -11,6 +11,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../pages/ordinals/widgets/ordinals_list.dart'; import '../../providers/providers.dart'; import '../../themes/stack_colors.dart'; @@ -41,7 +42,7 @@ class _DesktopOrdinals extends ConsumerState { late final TextEditingController searchController; late final FocusNode searchFocusNode; - String _searchTerm = ""; + final String _searchTerm = ""; @override void initState() { @@ -106,7 +107,7 @@ class _DesktopOrdinals extends ConsumerState { Text( "Ordinals", style: STextStyles.desktopH3(context), - ) + ), ], ), ), @@ -210,15 +211,16 @@ class _DesktopOrdinals extends ConsumerState { onPressed: () async { // show loading for a minimum of 2 seconds on refreshing await showLoading( - rootNavigator: true, - whileFuture: Future.wait([ - Future.delayed(const Duration(seconds: 2)), - (ref.read(pWallets).getWallet(widget.walletId) - as OrdinalsInterface) - .refreshInscriptions() - ]), - context: context, - message: "Refreshing..."); + rootNavigator: true, + whileFuture: Future.wait([ + Future.delayed(const Duration(seconds: 2)), + (ref.read(pWallets).getWallet(widget.walletId) + as OrdinalsInterface) + .refreshInscriptions(), + ]), + context: context, + message: "Refreshing...", + ); }, ), ], diff --git a/lib/pages_desktop_specific/password/create_password_view.dart b/lib/pages_desktop_specific/password/create_password_view.dart index 31f070982..5f318a1bc 100644 --- a/lib/pages_desktop_specific/password/create_password_view.dart +++ b/lib/pages_desktop_specific/password/create_password_view.dart @@ -13,9 +13,9 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:zxcvbn/zxcvbn.dart'; + import '../../notifications/show_flush_bar.dart'; -import '../desktop_home_view.dart'; -import 'forgotten_passphrase_restore_from_swb.dart'; import '../../providers/desktop/storage_crypto_handler_provider.dart'; import '../../providers/global/secure_store_provider.dart'; import '../../providers/providers.dart'; @@ -29,13 +29,14 @@ import '../../widgets/desktop/desktop_app_bar.dart'; import '../../widgets/desktop/desktop_scaffold.dart'; import '../../widgets/progress_bar.dart'; import '../../widgets/stack_text_field.dart'; -import 'package:zxcvbn/zxcvbn.dart'; +import '../desktop_home_view.dart'; +import 'forgotten_passphrase_restore_from_swb.dart'; class CreatePasswordView extends ConsumerStatefulWidget { const CreatePasswordView({ - Key? key, + super.key, this.restoreFromSWB = false, - }) : super(key: key); + }); static const String routeName = "/createPasswordDesktop"; final bool restoreFromSWB; @@ -77,20 +78,24 @@ class _CreatePasswordViewState extends ConsumerState { final String repeatPassphrase = passwordRepeatController.text; if (passphrase.isEmpty) { - unawaited(showFloatingFlushBar( - type: FlushBarType.warning, - message: "A password is required", - context: context, - )); + unawaited( + showFloatingFlushBar( + type: FlushBarType.warning, + message: "A password is required", + context: context, + ), + ); _nextLock = false; return; } if (passphrase != repeatPassphrase) { - unawaited(showFloatingFlushBar( - type: FlushBarType.warning, - message: "Password does not match", - context: context, - )); + unawaited( + showFloatingFlushBar( + type: FlushBarType.warning, + message: "Password does not match", + context: context, + ), + ); _nextLock = false; return; } @@ -98,7 +103,8 @@ class _CreatePasswordViewState extends ConsumerState { try { if (await ref.read(storageCryptoHandlerProvider).hasPassword()) { throw Exception( - "Tried creating a new password and attempted to overwrite an existing entry!"); + "Tried creating a new password and attempted to overwrite an existing entry!", + ); } await ref.read(storageCryptoHandlerProvider).initFromNew(passphrase); @@ -110,11 +116,13 @@ class _CreatePasswordViewState extends ConsumerState { await ref.read(nodeServiceChangeNotifierProvider).updateDefaults(); } } catch (e) { - unawaited(showFloatingFlushBar( - type: FlushBarType.warning, - message: "Error: $e", - context: context, - )); + unawaited( + showFloatingFlushBar( + type: FlushBarType.warning, + message: "Error: $e", + context: context, + ), + ); _nextLock = false; return; } @@ -136,11 +144,13 @@ class _CreatePasswordViewState extends ConsumerState { } if (!widget.restoreFromSWB && mounted) { - unawaited(showFloatingFlushBar( - type: FlushBarType.success, - message: "Your password is set up", - context: context, - )); + unawaited( + showFloatingFlushBar( + type: FlushBarType.success, + message: "Your password is set up", + context: context, + ), + ); } _nextLock = false; } @@ -231,7 +241,8 @@ class _CreatePasswordViewState extends ConsumerState { children: [ GestureDetector( key: const Key( - "createDesktopPasswordFieldShowPasswordButtonKey"), + "createDesktopPasswordFieldShowPasswordButtonKey", + ), onTap: () async { setState(() { hidePassword = !hidePassword; @@ -279,7 +290,7 @@ class _CreatePasswordViewState extends ConsumerState { } final result = zxcvbn.evaluate(newValue); String suggestionsAndTips = ""; - for (var sug + for (final sug in result.feedback.suggestions!.toSet()) { suggestionsAndTips += "$sug\n"; } @@ -293,7 +304,9 @@ class _CreatePasswordViewState extends ConsumerState { // hack fix to format back string returned from zxcvbn if (feedback.contains("phrasesNo need")) { feedback = feedback.replaceFirst( - "phrasesNo need", "phrases\nNo need"); + "phrasesNo need", + "phrases\nNo need", + ); } if (feedback.endsWith("\n")) { @@ -385,7 +398,8 @@ class _CreatePasswordViewState extends ConsumerState { children: [ GestureDetector( key: const Key( - "createDesktopPasswordFieldShowPasswordButtonKey2"), + "createDesktopPasswordFieldShowPasswordButtonKey2", + ), onTap: () async { setState(() { hidePassword = !hidePassword; diff --git a/lib/pages_desktop_specific/password/desktop_login_view.dart b/lib/pages_desktop_specific/password/desktop_login_view.dart index 62e3b70fe..54d00c789 100644 --- a/lib/pages_desktop_specific/password/desktop_login_view.dart +++ b/lib/pages_desktop_specific/password/desktop_login_view.dart @@ -63,8 +63,10 @@ class _DesktopLoginViewState extends ConsumerState { Future _checkDesktopMigrate() async { if (Util.isDesktop) { - int dbVersion = DB.instance.get( - boxName: DB.boxNameDBInfo, key: "hive_data_version") as int? ?? + final int dbVersion = DB.instance.get( + boxName: DB.boxNameDBInfo, + key: "hive_data_version", + ) as int? ?? 0; if (dbVersion < Constants.currentDataVersion) { try { @@ -73,8 +75,11 @@ class _DesktopLoginViewState extends ConsumerState { secureStore: ref.read(secureStoreProvider), ); } catch (e, s) { - Logging.instance.log("Cannot migrate desktop database\n$e $s", - level: LogLevel.Error, printFullLength: true); + Logging.instance.log( + "Cannot migrate desktop database\n$e $s", + level: LogLevel.Error, + printFullLength: true, + ); } } } @@ -249,12 +254,16 @@ class _DesktopLoginViewState extends ConsumerState { hoverColor: Colors.transparent, enabledBorder: OutlineInputBorder( borderSide: const BorderSide( - color: Colors.transparent, width: 1), + color: Colors.transparent, + width: 1, + ), borderRadius: BorderRadius.circular(10), ), focusedBorder: OutlineInputBorder( borderSide: const BorderSide( - color: Colors.transparent, width: 1), + color: Colors.transparent, + width: 1, + ), borderRadius: BorderRadius.circular(10), ), contentPadding: const EdgeInsets.only( @@ -273,7 +282,8 @@ class _DesktopLoginViewState extends ConsumerState { ), GestureDetector( key: const Key( - "restoreFromFilePasswordFieldShowPasswordButtonKey"), + "restoreFromFilePasswordFieldShowPasswordButtonKey", + ), onTap: () async { setState(() { hidePassword = !hidePassword; diff --git a/lib/pages_desktop_specific/password/forgotten_passphrase_restore_from_swb.dart b/lib/pages_desktop_specific/password/forgotten_passphrase_restore_from_swb.dart index bb2e6e816..173840528 100644 --- a/lib/pages_desktop_specific/password/forgotten_passphrase_restore_from_swb.dart +++ b/lib/pages_desktop_specific/password/forgotten_passphrase_restore_from_swb.dart @@ -42,7 +42,7 @@ import '../../widgets/stack_text_field.dart'; import 'create_password_view.dart'; class ForgottenPassphraseRestoreFromSWB extends ConsumerStatefulWidget { - const ForgottenPassphraseRestoreFromSWB({Key? key}) : super(key: key); + const ForgottenPassphraseRestoreFromSWB({super.key}); static const String routeName = "/forgottenPassphraseRestoreFromSWB"; @@ -360,7 +360,8 @@ class _ForgottenPassphraseRestoreFromSWBState ), GestureDetector( key: const Key( - "restoreFromFilePasswordFieldShowPasswordButtonKey"), + "restoreFromFilePasswordFieldShowPasswordButtonKey", + ), onTap: () async { setState(() { hidePassword = !hidePassword; diff --git a/lib/pages_desktop_specific/settings/desktop_settings_view.dart b/lib/pages_desktop_specific/settings/desktop_settings_view.dart index 3ecf30375..7ab75026d 100644 --- a/lib/pages_desktop_specific/settings/desktop_settings_view.dart +++ b/lib/pages_desktop_specific/settings/desktop_settings_view.dart @@ -27,7 +27,7 @@ import '../../widgets/desktop/desktop_app_bar.dart'; import '../../widgets/desktop/desktop_scaffold.dart'; class DesktopSettingsView extends ConsumerStatefulWidget { - const DesktopSettingsView({Key? key}) : super(key: key); + const DesktopSettingsView({super.key}); static const String routeName = "/desktopSettings"; @@ -123,7 +123,7 @@ class _DesktopSettingsViewState extends ConsumerState { } class DesktopSettingsTitle extends StatelessWidget { - const DesktopSettingsTitle({Key? key}) : super(key: key); + const DesktopSettingsTitle({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages_desktop_specific/settings/settings_menu.dart b/lib/pages_desktop_specific/settings/settings_menu.dart index d12cdea34..16b1a438b 100644 --- a/lib/pages_desktop_specific/settings/settings_menu.dart +++ b/lib/pages_desktop_specific/settings/settings_menu.dart @@ -11,16 +11,17 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; -import 'settings_menu_item.dart'; + import '../../themes/stack_colors.dart'; import '../../utilities/assets.dart'; +import 'settings_menu_item.dart'; final selectedSettingsMenuItemStateProvider = StateProvider((_) => 0); class SettingsMenu extends ConsumerStatefulWidget { const SettingsMenu({ - Key? key, - }) : super(key: key); + super.key, + }); @override ConsumerState createState() => _SettingsMenuState(); @@ -65,8 +66,10 @@ class _SettingsMenuState extends ConsumerState { width: 11, height: 11, color: ref - .watch(selectedSettingsMenuItemStateProvider - .state) + .watch( + selectedSettingsMenuItemStateProvider + .state, + ) .state == i ? Theme.of(context) diff --git a/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/advanced_settings.dart b/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/advanced_settings.dart index 69661e04c..029c5e294 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/advanced_settings.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/advanced_settings.dart @@ -11,10 +11,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../../../pages/settings_views/global_settings_view/advanced_views/manage_coin_units/manage_coin_units_view.dart'; -import 'debug_info_dialog.dart'; -import 'desktop_manage_block_explorers_dialog.dart'; -import 'stack_privacy_dialog.dart'; import '../../../../providers/global/prefs_provider.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/assets.dart'; @@ -22,9 +20,12 @@ import '../../../../utilities/text_styles.dart'; import '../../../../widgets/custom_buttons/draggable_switch_button.dart'; import '../../../../widgets/desktop/primary_button.dart'; import '../../../../widgets/rounded_white_container.dart'; +import 'debug_info_dialog.dart'; +import 'desktop_manage_block_explorers_dialog.dart'; +import 'stack_privacy_dialog.dart'; class AdvancedSettings extends ConsumerStatefulWidget { - const AdvancedSettings({Key? key}) : super(key: key); + const AdvancedSettings({super.key}); static const String routeName = "/settingsMenuAdvanced"; @@ -73,7 +74,8 @@ class _AdvancedSettings extends ConsumerState { text: "\n\nConfigure these settings only if you know what you are doing!", style: STextStyles.desktopTextExtraExtraSmall( - context), + context, + ), ), ], ), @@ -94,9 +96,10 @@ class _AdvancedSettings extends ConsumerState { "Toggle testnet coins", style: STextStyles.desktopTextExtraSmall(context) .copyWith( - color: Theme.of(context) - .extension()! - .textDark), + color: Theme.of(context) + .extension()! + .textDark, + ), textAlign: TextAlign.left, ), SizedBox( @@ -105,7 +108,8 @@ class _AdvancedSettings extends ConsumerState { child: DraggableSwitchButton( isOn: ref.watch( prefsChangeNotifierProvider.select( - (value) => value.showTestNetCoins), + (value) => value.showTestNetCoins, + ), ), onValueChanged: (newValue) { ref @@ -132,9 +136,10 @@ class _AdvancedSettings extends ConsumerState { "Enable coin control", style: STextStyles.desktopTextExtraSmall(context) .copyWith( - color: Theme.of(context) - .extension()! - .textDark), + color: Theme.of(context) + .extension()! + .textDark, + ), textAlign: TextAlign.left, ), SizedBox( @@ -143,7 +148,8 @@ class _AdvancedSettings extends ConsumerState { child: DraggableSwitchButton( isOn: ref.watch( prefsChangeNotifierProvider.select( - (value) => value.enableCoinControl), + (value) => value.enableCoinControl, + ), ), onValueChanged: (newValue) { ref @@ -163,56 +169,62 @@ class _AdvancedSettings extends ConsumerState { ), /// TODO: Make a dialog popup - Consumer(builder: (_, ref, __) { - final externalCalls = ref.watch( - prefsChangeNotifierProvider - .select((value) => value.externalCalls), - ); - return Padding( - padding: const EdgeInsets.all(10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Stack Experience", - style: STextStyles.desktopTextExtraSmall( - context) - .copyWith( - color: Theme.of(context) - .extension()! - .textDark), - textAlign: TextAlign.left, - ), - Text( - externalCalls ? "Easy crypto" : "Incognito", - style: - STextStyles.desktopTextExtraExtraSmall( - context), - ), - ], - ), - PrimaryButton( - label: "Change", - buttonHeight: ButtonHeight.xs, - width: 101, - onPressed: () async { - await showDialog( - context: context, - useSafeArea: false, - barrierDismissible: true, - builder: (context) { - return const StackPrivacyDialog(); - }, - ); - }, - ) - ], - ), - ); - }), + Consumer( + builder: (_, ref, __) { + final externalCalls = ref.watch( + prefsChangeNotifierProvider + .select((value) => value.externalCalls), + ); + return Padding( + padding: const EdgeInsets.all(10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Stack Experience", + style: STextStyles.desktopTextExtraSmall( + context, + ).copyWith( + color: Theme.of(context) + .extension()! + .textDark, + ), + textAlign: TextAlign.left, + ), + Text( + externalCalls + ? "Easy crypto" + : "Incognito", + style: STextStyles + .desktopTextExtraExtraSmall( + context, + ), + ), + ], + ), + PrimaryButton( + label: "Change", + buttonHeight: ButtonHeight.xs, + width: 101, + onPressed: () async { + await showDialog( + context: context, + useSafeArea: false, + barrierDismissible: true, + builder: (context) { + return const StackPrivacyDialog(); + }, + ); + }, + ), + ], + ), + ); + }, + ), ], ), const Padding( @@ -230,9 +242,10 @@ class _AdvancedSettings extends ConsumerState { "Block explorers", style: STextStyles.desktopTextExtraSmall(context) .copyWith( - color: Theme.of(context) - .extension()! - .textDark), + color: Theme.of(context) + .extension()! + .textDark, + ), textAlign: TextAlign.left, ), PrimaryButton( @@ -268,9 +281,10 @@ class _AdvancedSettings extends ConsumerState { "Units", style: STextStyles.desktopTextExtraSmall(context) .copyWith( - color: Theme.of(context) - .extension()! - .textDark), + color: Theme.of(context) + .extension()! + .textDark, + ), textAlign: TextAlign.left, ), PrimaryButton( @@ -306,9 +320,10 @@ class _AdvancedSettings extends ConsumerState { "Debug info", style: STextStyles.desktopTextExtraSmall(context) .copyWith( - color: Theme.of(context) - .extension()! - .textDark), + color: Theme.of(context) + .extension()! + .textDark, + ), textAlign: TextAlign.left, ), PrimaryButton( diff --git a/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/debug_info_dialog.dart b/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/debug_info_dialog.dart index 177898180..dedfe999d 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/debug_info_dialog.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/debug_info_dialog.dart @@ -37,7 +37,7 @@ import '../../../../widgets/stack_text_field.dart'; import '../../../../widgets/textfield_icon_button.dart'; class DebugInfoDialog extends ConsumerStatefulWidget { - const DebugInfoDialog({Key? key}) : super(key: key); + const DebugInfoDialog({super.key}); @override ConsumerState createState() => _DebugInfoDialog(); diff --git a/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/desktop_manage_block_explorers_dialog.dart b/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/desktop_manage_block_explorers_dialog.dart index 45eed9054..e69f90833 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/desktop_manage_block_explorers_dialog.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/desktop_manage_block_explorers_dialog.dart @@ -13,8 +13,9 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; -import '../../../../providers/global/prefs_provider.dart'; + import '../../../../app_config.dart'; +import '../../../../providers/global/prefs_provider.dart'; import '../../../../themes/coin_icon_provider.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/assets.dart'; @@ -139,8 +140,7 @@ class DesktopManageBlockExplorersDialog extends ConsumerWidget { } class _DesktopEditBlockExplorerDialog extends ConsumerStatefulWidget { - const _DesktopEditBlockExplorerDialog({Key? key, required this.coin}) - : super(key: key); + const _DesktopEditBlockExplorerDialog({super.key, required this.coin}); final CryptoCurrency coin; @@ -157,10 +157,10 @@ class _DesktopEditBlockExplorerDialogState @override void initState() { _textEditingController = TextEditingController( - text: - getBlockExplorerTransactionUrlFor(coin: widget.coin, txid: "[TXID]") - .toString() - .replaceAll("%5BTXID%5D", "[TXID]")); + text: getBlockExplorerTransactionUrlFor(coin: widget.coin, txid: "[TXID]") + .toString() + .replaceAll("%5BTXID%5D", "[TXID]"), + ); _focusNode = FocusNode(); super.initState(); } @@ -268,10 +268,10 @@ class _DesktopEditBlockExplorerDialogState ), ), ], - ) + ), ], ), - ) + ), ], ), ); diff --git a/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/stack_privacy_dialog.dart b/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/stack_privacy_dialog.dart index 8ebf89c70..20a9d9e62 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/stack_privacy_dialog.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/stack_privacy_dialog.dart @@ -14,6 +14,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../../../db/hive/db.dart'; import '../../../../providers/global/prefs_provider.dart'; import '../../../../providers/global/price_provider.dart'; @@ -31,7 +32,7 @@ import '../../../../widgets/desktop/secondary_button.dart'; import '../../../../widgets/rounded_white_container.dart'; class StackPrivacyDialog extends ConsumerStatefulWidget { - const StackPrivacyDialog({Key? key}) : super(key: key); + const StackPrivacyDialog({super.key}); @override ConsumerState createState() => _StackPrivacyDialog(); @@ -80,7 +81,7 @@ class _StackPrivacyDialog extends ConsumerState { height: 35, ), Padding( - padding: EdgeInsets.symmetric(horizontal: 32), + padding: const EdgeInsets.symmetric(horizontal: 32), child: PrivacyToggle( externalCallsEnabled: isEasy, onChanged: (externalCalls) { @@ -110,16 +111,19 @@ class _StackPrivacyDialog extends ConsumerState { ? [ if (Constants.enableExchange) const TextSpan( - text: - "Exchange data preloaded for a seamless experience."), - const TextSpan( text: - "\n\nCoinGecko enabled: (24 hour price change shown in-app, total wallet value shown in USD or other currency)."), + "Exchange data preloaded for a seamless experience.", + ), + const TextSpan( + text: + "\n\nCoinGecko enabled: (24 hour price change shown in-app, total wallet value shown in USD or other currency).", + ), TextSpan( text: "\n\nRecommended for most crypto users.", style: isDesktop ? STextStyles.desktopTextExtraExtraSmall600( - context) + context, + ) : TextStyle( color: Theme.of(context) .extension()! @@ -130,17 +134,20 @@ class _StackPrivacyDialog extends ConsumerState { ] : [ const TextSpan( - text: - "Exchange data not preloaded (slower experience)."), + text: + "Exchange data not preloaded (slower experience).", + ), const TextSpan( - text: - "\n\nCoinGecko disabled (price changes not shown, no wallet value shown in other currencies)."), + text: + "\n\nCoinGecko disabled (price changes not shown, no wallet value shown in other currencies).", + ), TextSpan( text: "\n\nRecommended for the privacy conscious.", style: isDesktop ? STextStyles.desktopTextExtraExtraSmall600( - context) + context, + ) : TextStyle( color: Theme.of(context) .extension()! @@ -179,9 +186,10 @@ class _StackPrivacyDialog extends ConsumerState { DB.instance .put( - boxName: DB.boxNamePrefs, - key: "externalCalls", - value: isEasy) + boxName: DB.boxNamePrefs, + key: "externalCalls", + value: isEasy, + ) .then((_) { if (isEasy) { unawaited( @@ -197,7 +205,7 @@ class _StackPrivacyDialog extends ConsumerState { } }, ), - ) + ), ], ), ), @@ -209,10 +217,10 @@ class _StackPrivacyDialog extends ConsumerState { class PrivacyToggle extends ConsumerStatefulWidget { const PrivacyToggle({ - Key? key, + super.key, required this.externalCallsEnabled, this.onChanged, - }) : super(key: key); + }); final bool externalCallsEnabled; final void Function(bool)? onChanged; diff --git a/lib/pages_desktop_specific/settings/settings_menu/appearance_settings/sub_widgets/desktop_install_theme.dart b/lib/pages_desktop_specific/settings/settings_menu/appearance_settings/sub_widgets/desktop_install_theme.dart index 1126cbbfe..081540c3b 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/appearance_settings/sub_widgets/desktop_install_theme.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/appearance_settings/sub_widgets/desktop_install_theme.dart @@ -25,7 +25,7 @@ import '../../../../../widgets/desktop/primary_button.dart'; import '../../../../../widgets/rounded_container.dart'; class DesktopInstallTheme extends ConsumerStatefulWidget { - const DesktopInstallTheme({Key? key}) : super(key: key); + const DesktopInstallTheme({super.key}); @override ConsumerState createState() => @@ -228,7 +228,7 @@ class _DesktopInstallThemeState extends ConsumerState { ), ], ), - ) + ), ], ), ), diff --git a/lib/pages_desktop_specific/settings/settings_menu/appearance_settings/sub_widgets/desktop_manage_themes.dart b/lib/pages_desktop_specific/settings/settings_menu/appearance_settings/sub_widgets/desktop_manage_themes.dart index bf04e48f1..bf55fe744 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/appearance_settings/sub_widgets/desktop_manage_themes.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/appearance_settings/sub_widgets/desktop_manage_themes.dart @@ -20,7 +20,7 @@ import '../../../../../widgets/desktop/desktop_dialog_close_button.dart'; import '../../../../../widgets/toggle.dart'; class DesktopManageThemesDialog extends ConsumerStatefulWidget { - const DesktopManageThemesDialog({Key? key}) : super(key: key); + const DesktopManageThemesDialog({super.key}); @override ConsumerState createState() => diff --git a/lib/pages_desktop_specific/settings/settings_menu/appearance_settings/sub_widgets/desktop_themes_gallery.dart b/lib/pages_desktop_specific/settings/settings_menu/appearance_settings/sub_widgets/desktop_themes_gallery.dart index 160fd67f5..4571973c4 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/appearance_settings/sub_widgets/desktop_themes_gallery.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/appearance_settings/sub_widgets/desktop_themes_gallery.dart @@ -12,6 +12,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../../../../pages/settings_views/global_settings_view/appearance_settings/manage_themes.dart'; import '../../../../../pages/settings_views/global_settings_view/appearance_settings/sub_widgets/stack_theme_card.dart'; import '../../../../../providers/global/prefs_provider.dart'; @@ -24,9 +25,9 @@ import '../../../../../widgets/rounded_white_container.dart'; class DesktopThemeGallery extends ConsumerStatefulWidget { const DesktopThemeGallery({ - Key? key, + super.key, required this.dialogWidth, - }) : super(key: key); + }); final double dialogWidth; @@ -90,7 +91,8 @@ class _DesktopThemeGalleryState extends ConsumerState { .map( (e) => SizedBox( key: Key( - "_DesktopThemeGalleryState_card_${e.id}_key"), + "_DesktopThemeGalleryState_card_${e.id}_key", + ), width: (widget.dialogWidth - 64 - 32) / 3, child: StackThemeCard( diff --git a/lib/pages_desktop_specific/settings/settings_menu/backup_and_restore/create_auto_backup.dart b/lib/pages_desktop_specific/settings/settings_menu/backup_and_restore/create_auto_backup.dart index 80b6faea3..e7225d628 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/backup_and_restore/create_auto_backup.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/backup_and_restore/create_auto_backup.dart @@ -18,6 +18,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:stack_wallet_backup/stack_wallet_backup.dart'; +import 'package:zxcvbn/zxcvbn.dart'; + import '../../../../notifications/show_flush_bar.dart'; import '../../../../pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart'; import '../../../../pages/settings_views/global_settings_view/stack_backup_views/helpers/swb_file_system.dart'; @@ -39,12 +41,11 @@ import '../../../../widgets/desktop/secondary_button.dart'; import '../../../../widgets/progress_bar.dart'; import '../../../../widgets/stack_dialog.dart'; import '../../../../widgets/stack_text_field.dart'; -import 'package:zxcvbn/zxcvbn.dart'; class CreateAutoBackup extends ConsumerStatefulWidget { const CreateAutoBackup({ - Key? key, - }) : super(key: key); + super.key, + }); @override ConsumerState createState() => _CreateAutoBackup(); @@ -175,72 +176,75 @@ class _CreateAutoBackup extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ if (!Platform.isAndroid && !Platform.isIOS) - Consumer(builder: (context, ref, __) { - return Container( - color: Colors.transparent, - child: TextField( - autocorrect: false, - enableSuggestions: false, - onTap: Platform.isAndroid || Platform.isIOS - ? null - : () async { - try { - await stackFileSystem.prepareStorage(); + Consumer( + builder: (context, ref, __) { + return Container( + color: Colors.transparent, + child: TextField( + autocorrect: false, + enableSuggestions: false, + onTap: Platform.isAndroid || Platform.isIOS + ? null + : () async { + try { + await stackFileSystem.prepareStorage(); - if (mounted) { - await stackFileSystem.pickDir(context); - } + if (mounted) { + await stackFileSystem.pickDir(context); + } - if (mounted) { - setState(() { - fileLocationController.text = - stackFileSystem.dirPath ?? ""; - }); + if (mounted) { + setState(() { + fileLocationController.text = + stackFileSystem.dirPath ?? ""; + }); + } + } catch (e, s) { + Logging.instance + .log("$e\n$s", level: LogLevel.Error); } - } catch (e, s) { - Logging.instance - .log("$e\n$s", level: LogLevel.Error); - } - }, - controller: fileLocationController, - style: STextStyles.field(context), - decoration: InputDecoration( - hintText: "Save to...", - hintStyle: STextStyles.fieldLabel(context), - suffixIcon: UnconstrainedBox( - child: Row( - children: [ - const SizedBox( - width: 16, - ), - SvgPicture.asset( - Assets.svg.folder, - color: Theme.of(context) - .extension()! - .textDark3, - width: 16, - height: 16, - ), - const SizedBox( - width: 12, - ), - ], + }, + controller: fileLocationController, + style: STextStyles.field(context), + decoration: InputDecoration( + hintText: "Save to...", + hintStyle: STextStyles.fieldLabel(context), + suffixIcon: UnconstrainedBox( + child: Row( + children: [ + const SizedBox( + width: 16, + ), + SvgPicture.asset( + Assets.svg.folder, + color: Theme.of(context) + .extension()! + .textDark3, + width: 16, + height: 16, + ), + const SizedBox( + width: 12, + ), + ], + ), ), ), + key: const Key( + "createBackupSaveToFileLocationTextFieldKey", + ), + readOnly: true, + toolbarOptions: const ToolbarOptions( + copy: true, + cut: false, + paste: false, + selectAll: false, + ), + onChanged: (newValue) {}, ), - key: const Key( - "createBackupSaveToFileLocationTextFieldKey"), - readOnly: true, - toolbarOptions: const ToolbarOptions( - copy: true, - cut: false, - paste: false, - selectAll: false, - ), - onChanged: (newValue) {}, - ), - ); - }), + ); + }, + ), if (!Platform.isAndroid && !Platform.isIOS) const SizedBox( height: 24, @@ -250,11 +254,12 @@ class _CreateAutoBackup extends ConsumerState { padding: const EdgeInsets.only(bottom: 10.0), child: Text( "Create a passphrase", - style: STextStyles.desktopTextExtraSmall(context) - .copyWith( - color: Theme.of(context) - .extension()! - .textDark3), + style: + STextStyles.desktopTextExtraSmall(context).copyWith( + color: Theme.of(context) + .extension()! + .textDark3, + ), textAlign: TextAlign.left, ), ), @@ -285,7 +290,8 @@ class _CreateAutoBackup extends ConsumerState { ), GestureDetector( key: const Key( - "createBackupPasswordFieldShowPasswordButtonKey"), + "createBackupPasswordFieldShowPasswordButtonKey", + ), onTap: () async { setState(() { hidePassword = !hidePassword; @@ -318,7 +324,7 @@ class _CreateAutoBackup extends ConsumerState { } final result = zxcvbn.evaluate(newValue); String suggestionsAndTips = ""; - for (var sug in result.feedback.suggestions!.toSet()) { + for (final sug in result.feedback.suggestions!.toSet()) { suggestionsAndTips += "$sug\n"; } suggestionsAndTips += result.feedback.warning!; @@ -331,7 +337,9 @@ class _CreateAutoBackup extends ConsumerState { // hack fix to format back string returned from zxcvbn if (feedback.contains("phrasesNo need")) { feedback = feedback.replaceFirst( - "phrasesNo need", "phrases\nNo need"); + "phrasesNo need", + "phrases\nNo need", + ); } if (feedback.endsWith("\n")) { @@ -420,7 +428,8 @@ class _CreateAutoBackup extends ConsumerState { ), GestureDetector( key: const Key( - "createBackupPasswordFieldShowPasswordButtonKey"), + "createBackupPasswordFieldShowPasswordButtonKey", + ), onTap: () async { setState(() { hidePassword = !hidePassword; @@ -502,7 +511,8 @@ class _CreateAutoBackup extends ConsumerState { child: Text( message, style: STextStyles.desktopTextExtraExtraSmall( - context), + context, + ), ), ); }, @@ -646,7 +656,8 @@ class _CreateAutoBackup extends ConsumerState { Text( "Encrypting initial backup", style: STextStyles.desktopH3( - context), + context, + ), ), const SizedBox( height: 40, @@ -655,7 +666,8 @@ class _CreateAutoBackup extends ConsumerState { "This shouldn't take long", style: STextStyles .desktopTextExtraExtraSmall( - context), + context, + ), ), ], ), @@ -673,7 +685,8 @@ class _CreateAutoBackup extends ConsumerState { // make sure the dialog is able to be displayed for at least some time final fut = Future.delayed( - const Duration(milliseconds: 300)); + const Duration(milliseconds: 300), + ); String adkString; int adkVersion; @@ -683,7 +696,8 @@ class _CreateAutoBackup extends ConsumerState { adkString = Format.uint8listToString(adk.item2); adkVersion = adk.item1; } on Exception catch (e, s) { - String err = getErrorMessageFromSWBException(e); + final String err = + getErrorMessageFromSWBException(e); Logging.instance .log("$err\n$s", level: LogLevel.Error); // pop encryption progress dialog @@ -712,10 +726,13 @@ class _CreateAutoBackup extends ConsumerState { } await secureStore.write( - key: "auto_adk_string", value: adkString); + key: "auto_adk_string", + value: adkString, + ); await secureStore.write( - key: "auto_adk_version_string", - value: adkVersion.toString()); + key: "auto_adk_version_string", + value: adkVersion.toString(), + ); final DateTime now = DateTime.now(); final String fileToSave = @@ -725,7 +742,8 @@ class _CreateAutoBackup extends ConsumerState { secureStorage: secureStore, ); - bool result = await SWB.encryptStackWalletWithADK( + final bool result = + await SWB.encryptStackWalletWithADK( fileToSave, adkString, jsonEncode(backup), @@ -785,7 +803,8 @@ class _CreateAutoBackup extends ConsumerState { "Stack Auto Backup enabled!", style: STextStyles.desktopH3( - context), + context, + ), ), const DesktopDialogCloseButton(), ], @@ -808,7 +827,7 @@ class _CreateAutoBackup extends ConsumerState { ), ), ], - ) + ), ], ), ), @@ -831,13 +850,14 @@ class _CreateAutoBackup extends ConsumerState { context: context, barrierDismissible: false, builder: (_) => const StackOkDialog( - title: "Failed to enable Auto Backup"), + title: "Failed to enable Auto Backup", + ), ); } } }, ), - ) + ), ], ), ), diff --git a/lib/pages_desktop_specific/settings/settings_menu/backup_and_restore/enable_backup_dialog.dart b/lib/pages_desktop_specific/settings/settings_menu/backup_and_restore/enable_backup_dialog.dart index fcd05c57b..dcc164c41 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/backup_and_restore/enable_backup_dialog.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/backup_and_restore/enable_backup_dialog.dart @@ -18,7 +18,7 @@ import '../../../../widgets/desktop/primary_button.dart'; import '../../../../widgets/desktop/secondary_button.dart'; class EnableBackupDialog extends StatelessWidget { - const EnableBackupDialog({Key? key}) : super(key: key); + const EnableBackupDialog({super.key}); @override Widget build(BuildContext context) { @@ -88,7 +88,7 @@ class EnableBackupDialog extends StatelessWidget { createAutoBackup(); }, ), - ) + ), ], ), ), diff --git a/lib/pages_desktop_specific/settings/settings_menu/currency_settings/currency_settings.dart b/lib/pages_desktop_specific/settings/settings_menu/currency_settings/currency_settings.dart index c9faa8182..04d73fd35 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/currency_settings/currency_settings.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/currency_settings/currency_settings.dart @@ -20,7 +20,7 @@ import '../../../../widgets/desktop/primary_button.dart'; import '../../../../widgets/rounded_white_container.dart'; class CurrencySettings extends ConsumerStatefulWidget { - const CurrencySettings({Key? key}) : super(key: key); + const CurrencySettings({super.key}); static const String routeName = "/settingsMenuCurrency"; diff --git a/lib/pages_desktop_specific/settings/settings_menu/desktop_about_view.dart b/lib/pages_desktop_specific/settings/settings_menu/desktop_about_view.dart index 7cd74d3af..34c8a8dba 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/desktop_about_view.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/desktop_about_view.dart @@ -546,7 +546,8 @@ class DesktopAboutView extends ConsumerWidget { onTap: () { launchUrl( Uri.parse( - "https://tzkt.io"), + "https://tzkt.io", + ), mode: LaunchMode .externalApplication, ); diff --git a/lib/pages_desktop_specific/settings/settings_menu/desktop_support_view.dart b/lib/pages_desktop_specific/settings/settings_menu/desktop_support_view.dart index 2d9060e3a..cf211b27e 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/desktop_support_view.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/desktop_support_view.dart @@ -17,7 +17,7 @@ import '../../../widgets/desktop/desktop_app_bar.dart'; import '../../../widgets/desktop/desktop_scaffold.dart'; class DesktopSupportView extends ConsumerStatefulWidget { - const DesktopSupportView({Key? key}) : super(key: key); + const DesktopSupportView({super.key}); static const String routeName = "/desktopSupportView"; @@ -42,17 +42,17 @@ class _DesktopSupportView extends ConsumerState { Text( "Support", style: STextStyles.desktopH3(context), - ) + ), ], ), ), - body: Column( + body: const Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( - padding: const EdgeInsets.fromLTRB(24, 10, 0, 0), + padding: EdgeInsets.fromLTRB(24, 10, 0, 0), child: Row( - children: const [ + children: [ SizedBox( width: 576, child: SupportView(), diff --git a/lib/pages_desktop_specific/settings/settings_menu/language_settings/language_dialog.dart b/lib/pages_desktop_specific/settings/settings_menu/language_settings/language_dialog.dart index c24762da6..072906bff 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/language_settings/language_dialog.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/language_settings/language_dialog.dart @@ -11,6 +11,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../../../providers/global/prefs_provider.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/assets.dart'; @@ -27,7 +28,7 @@ import '../../../../widgets/stack_text_field.dart'; import '../../../../widgets/textfield_icon_button.dart'; class LanguageDialog extends ConsumerStatefulWidget { - const LanguageDialog({Key? key}) : super(key: key); + const LanguageDialog({super.key}); @override ConsumerState createState() => _LanguageDialog(); @@ -80,7 +81,8 @@ class _LanguageDialog extends ConsumerState { List _filtered() { return listWithoutSelected .where( - (element) => element.toLowerCase().contains(filter.toLowerCase())) + (element) => element.toLowerCase().contains(filter.toLowerCase()), + ) .toList(); } @@ -141,11 +143,12 @@ class _LanguageDialog extends ConsumerState { return [ SliverOverlapAbsorber( handle: NestedScrollView.sliverOverlapAbsorberHandleFor( - context), + context, + ), sliver: SliverToBoxAdapter( child: Padding( - padding: - EdgeInsets.symmetric(vertical: 16, horizontal: 32), + padding: const EdgeInsets.symmetric( + vertical: 16, horizontal: 32), child: Column( children: [ Padding( @@ -164,9 +167,11 @@ class _LanguageDialog extends ConsumerState { height: 2, ), textAlign: TextAlign.left, - decoration: standardInputDecoration("Search", - searchLanguageFocusNode, context) - .copyWith( + decoration: standardInputDecoration( + "Search", + searchLanguageFocusNode, + context, + ).copyWith( prefixIcon: Padding( padding: const EdgeInsets.symmetric( horizontal: 10, @@ -234,10 +239,12 @@ class _LanguageDialog extends ConsumerState { child: Padding( padding: const EdgeInsets.all(4), key: Key( - "desktopSelectLanguage_${listWithoutSelected[index]}"), + "desktopSelectLanguage_${listWithoutSelected[index]}", + ), child: Padding( padding: const EdgeInsets.symmetric( - horizontal: 32), + horizontal: 32, + ), child: RoundedContainer( padding: const EdgeInsets.all(0), color: index == 0 @@ -287,11 +294,13 @@ class _LanguageDialog extends ConsumerState { listWithoutSelected[index], key: (index == 0) ? const Key( - "desktopSettingsSelectedLanguageText") + "desktopSettingsSelectedLanguageText", + ) : null, style: STextStyles.largeMedium14( - context), + context, + ), ), const SizedBox( height: 2, @@ -300,11 +309,13 @@ class _LanguageDialog extends ConsumerState { listWithoutSelected[index], key: (index == 0) ? const Key( - "desktopSettingsSelectedLanguageTextDescription") + "desktopSettingsSelectedLanguageTextDescription", + ) : null, style: STextStyles.itemSubtitle( - context), + context, + ), ), ], ), @@ -347,7 +358,7 @@ class _LanguageDialog extends ConsumerState { label: "Save Changes", onPressed: () {}, ), - ) + ), ], ), ), diff --git a/lib/pages_desktop_specific/settings/settings_menu/language_settings/language_settings.dart b/lib/pages_desktop_specific/settings/settings_menu/language_settings/language_settings.dart index 795fbbf8b..e02bf1263 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/language_settings/language_settings.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/language_settings/language_settings.dart @@ -11,14 +11,15 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; -import 'language_dialog.dart'; + import '../../../../utilities/assets.dart'; import '../../../../utilities/text_styles.dart'; import '../../../../widgets/desktop/primary_button.dart'; import '../../../../widgets/rounded_white_container.dart'; +import 'language_dialog.dart'; class LanguageOptionSettings extends ConsumerStatefulWidget { - const LanguageOptionSettings({Key? key}) : super(key: key); + const LanguageOptionSettings({super.key}); static const String routeName = "/settingsMenuLanguage"; @@ -78,7 +79,8 @@ class _LanguageOptionSettings extends ConsumerState { text: "\n\nSelect the language of your wallet. We use your system language by default.", style: STextStyles.desktopTextExtraExtraSmall( - context), + context, + ), ), ], ), diff --git a/lib/pages_desktop_specific/settings/settings_menu/security_settings.dart b/lib/pages_desktop_specific/settings/settings_menu/security_settings.dart index 524686732..190740b3f 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/security_settings.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/security_settings.dart @@ -13,6 +13,8 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:zxcvbn/zxcvbn.dart'; + import '../../../app_config.dart'; import '../../../notifications/show_flush_bar.dart'; import '../../../providers/desktop/storage_crypto_handler_provider.dart'; @@ -24,7 +26,6 @@ import '../../../widgets/desktop/primary_button.dart'; import '../../../widgets/progress_bar.dart'; import '../../../widgets/rounded_white_container.dart'; import '../../../widgets/stack_text_field.dart'; -import 'package:zxcvbn/zxcvbn.dart'; class SecuritySettings extends ConsumerStatefulWidget { const SecuritySettings({super.key}); @@ -204,11 +205,12 @@ class _SecuritySettings extends ConsumerState { "Current password", style: STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( - color: Theme.of(context) - .extension()! - .textDark3), + context, + ).copyWith( + color: Theme.of(context) + .extension()! + .textDark3, + ), textAlign: TextAlign.left, ), const SizedBox(height: 10), @@ -218,7 +220,8 @@ class _SecuritySettings extends ConsumerState { ), child: TextField( key: const Key( - "desktopSecurityRestoreFromFilePasswordFieldKey"), + "desktopSecurityRestoreFromFilePasswordFieldKey", + ), focusNode: passwordCurrentFocusNode, controller: passwordCurrentController, style: STextStyles.field(context), @@ -240,7 +243,8 @@ class _SecuritySettings extends ConsumerState { ), GestureDetector( key: const Key( - "desktopSecurityRestoreFromFilePasswordFieldShowPasswordButtonKey"), + "desktopSecurityRestoreFromFilePasswordFieldShowPasswordButtonKey", + ), onTap: () async { setState(() { hidePassword = @@ -275,11 +279,12 @@ class _SecuritySettings extends ConsumerState { "New password", style: STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( - color: Theme.of(context) - .extension()! - .textDark3), + context, + ).copyWith( + color: Theme.of(context) + .extension()! + .textDark3, + ), textAlign: TextAlign.left, ), const SizedBox(height: 10), @@ -289,7 +294,8 @@ class _SecuritySettings extends ConsumerState { ), child: TextField( key: const Key( - "desktopSecurityCreateNewPasswordFieldKey1"), + "desktopSecurityCreateNewPasswordFieldKey1", + ), focusNode: passwordFocusNode, controller: passwordController, style: STextStyles.field(context), @@ -311,7 +317,8 @@ class _SecuritySettings extends ConsumerState { ), GestureDetector( key: const Key( - "desktopSecurityCreateNewPasswordButtonKey1"), + "desktopSecurityCreateNewPasswordButtonKey1", + ), onTap: () async { setState(() { hidePassword = @@ -346,7 +353,7 @@ class _SecuritySettings extends ConsumerState { final result = zxcvbn.evaluate(newValue); String suggestionsAndTips = ""; - for (var sug in result + for (final sug in result .feedback.suggestions! .toSet()) { suggestionsAndTips += "$sug\n"; @@ -363,13 +370,16 @@ class _SecuritySettings extends ConsumerState { if (feedback .contains("phrasesNo need")) { feedback = feedback.replaceFirst( - "phrasesNo need", - "phrases\nNo need"); + "phrasesNo need", + "phrases\nNo need", + ); } if (feedback.endsWith("\n")) { feedback = feedback.substring( - 0, feedback.length - 2); + 0, + feedback.length - 2, + ); } setState(() { @@ -392,7 +402,8 @@ class _SecuritySettings extends ConsumerState { ? Text( passwordFeedback, style: STextStyles.infoSmall( - context), + context, + ), ) : null, ), @@ -407,7 +418,8 @@ class _SecuritySettings extends ConsumerState { ), child: ProgressBar( key: const Key( - "desktopSecurityCreateStackBackUpProgressBar"), + "desktopSecurityCreateStackBackUpProgressBar", + ), width: 450, height: 5, fillColor: passwordStrength < 0.51 @@ -434,11 +446,12 @@ class _SecuritySettings extends ConsumerState { "Confirm new password", style: STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( - color: Theme.of(context) - .extension()! - .textDark3), + context, + ).copyWith( + color: Theme.of(context) + .extension()! + .textDark3, + ), textAlign: TextAlign.left, ), const SizedBox(height: 10), @@ -448,7 +461,8 @@ class _SecuritySettings extends ConsumerState { ), child: TextField( key: const Key( - "desktopSecurityCreateNewPasswordFieldKey2"), + "desktopSecurityCreateNewPasswordFieldKey2", + ), focusNode: passwordRepeatFocusNode, controller: passwordRepeatController, style: STextStyles.field(context), @@ -470,7 +484,8 @@ class _SecuritySettings extends ConsumerState { ), GestureDetector( key: const Key( - "desktopSecurityCreateNewPasswordButtonKey2"), + "desktopSecurityCreateNewPasswordButtonKey2", + ), onTap: () async { setState(() { hidePassword = @@ -515,7 +530,7 @@ class _SecuritySettings extends ConsumerState { }); } }, - ) + ), ], ), ) diff --git a/lib/pages_desktop_specific/settings/settings_menu/syncing_preferences_settings.dart b/lib/pages_desktop_specific/settings/settings_menu/syncing_preferences_settings.dart index d1b03d83e..a384ffacf 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/syncing_preferences_settings.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/syncing_preferences_settings.dart @@ -11,6 +11,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../../pages/settings_views/global_settings_view/syncing_preferences_views/syncing_options_view.dart'; import '../../../providers/global/prefs_provider.dart'; import '../../../themes/stack_colors.dart'; @@ -22,7 +23,7 @@ import '../../../widgets/rounded_container.dart'; import '../../../widgets/rounded_white_container.dart'; class SyncingPreferencesSettings extends ConsumerStatefulWidget { - const SyncingPreferencesSettings({Key? key}) : super(key: key); + const SyncingPreferencesSettings({super.key}); static const String routeName = "/settingsMenuSyncingPref"; @@ -80,14 +81,18 @@ class _SyncingPreferencesSettings child: Padding( padding: const EdgeInsets.all(8.0), child: Text( - _currentTypeDescription(ref.watch( + _currentTypeDescription( + ref.watch( prefsChangeNotifierProvider - .select((value) => value.syncType))), + .select((value) => value.syncType), + ), + ), style: STextStyles.desktopTextExtraSmall(context) .copyWith( - color: Theme.of(context) - .extension()! - .textDark2), + color: Theme.of(context) + .extension()! + .textDark2, + ), textAlign: TextAlign.left, ), ), @@ -112,7 +117,8 @@ class _SyncingPreferencesSettings text: "\n\nSet up your syncing preferences for all wallets in your Stack.", style: STextStyles.desktopTextExtraExtraSmall( - context), + context, + ), ), ], ), @@ -124,46 +130,47 @@ class _SyncingPreferencesSettings crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( - padding: const EdgeInsets.all( - 10, - ), - child: changePrefs - ? SizedBox( - width: 512, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SyncingOptionsView(), - PrimaryButton( - width: 200, - buttonHeight: ButtonHeight.m, - enabled: true, - label: "Save", - onPressed: () { - setState(() { - changePrefs = false; - }); - }, - ), - ], - ), - ) - : Column( + padding: const EdgeInsets.all( + 10, + ), + child: changePrefs + ? SizedBox( + width: 512, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - const SizedBox(height: 10), + const SyncingOptionsView(), PrimaryButton( width: 200, buttonHeight: ButtonHeight.m, enabled: true, - label: "Change preferences", + label: "Save", onPressed: () { setState(() { - changePrefs = true; + changePrefs = false; }); }, ), ], - )), + ), + ) + : Column( + children: [ + const SizedBox(height: 10), + PrimaryButton( + width: 200, + buttonHeight: ButtonHeight.m, + enabled: true, + label: "Change preferences", + onPressed: () { + setState(() { + changePrefs = true; + }); + }, + ), + ], + ), + ), ], ), ], diff --git a/lib/pages_desktop_specific/settings/settings_menu/tor_settings/tor_settings.dart b/lib/pages_desktop_specific/settings/settings_menu/tor_settings/tor_settings.dart index 4cbce2c5e..d01a5d678 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/tor_settings/tor_settings.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/tor_settings/tor_settings.dart @@ -15,6 +15,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; + import '../../../../providers/global/prefs_provider.dart'; import '../../../../services/event_bus/events/global/tor_connection_status_changed_event.dart'; import '../../../../services/event_bus/global_event_bus.dart'; @@ -32,7 +33,7 @@ import '../../../../widgets/desktop/secondary_button.dart'; import '../../../../widgets/rounded_white_container.dart'; class TorSettings extends ConsumerStatefulWidget { - const TorSettings({Key? key}) : super(key: key); + const TorSettings({super.key}); static const String routeName = "/torDesktopSettings"; @@ -198,7 +199,8 @@ class _TorSettingsState extends ConsumerState { text: "\nConnect to the Tor Network with one click.", style: STextStyles.desktopTextExtraExtraSmall( - context), + context, + ), ), TextSpan( text: "\tWhat is Tor?", @@ -233,7 +235,8 @@ class _TorSettingsState extends ConsumerState { "What is Tor?", style: STextStyles.desktopH2( - context), + context, + ), ), DesktopDialogCloseButton( onPressedOverride: () => @@ -250,9 +253,9 @@ class _TorSettingsState extends ConsumerState { " to remain anonymous by routing internet traffic through a series of layered nodes," " to obscure the origin and destination of data.", style: STextStyles - .desktopTextMedium( - context) - .copyWith( + .desktopTextMedium( + context, + ).copyWith( color: Theme.of(context) .extension()! .textDark3, @@ -295,9 +298,10 @@ class _TorSettingsState extends ConsumerState { style: STextStyles.desktopTextExtraExtraSmall(context) .copyWith( - color: Theme.of(context) - .extension()! - .textDark), + color: Theme.of(context) + .extension()! + .textDark, + ), ), const SizedBox( height: 8, @@ -337,7 +341,8 @@ class _TorSettingsState extends ConsumerState { "What is Tor killswitch?", style: STextStyles.desktopH2( - context), + context, + ), ), DesktopDialogCloseButton( onPressedOverride: () => @@ -354,9 +359,9 @@ class _TorSettingsState extends ConsumerState { " disconnecting your device from the Tor network if the" " connection is disrupted or compromised.", style: STextStyles - .desktopTextMedium( - context) - .copyWith( + .desktopTextMedium( + context, + ).copyWith( color: Theme.of(context) .extension()! .textDark3, diff --git a/lib/pages_desktop_specific/settings/settings_menu_item.dart b/lib/pages_desktop_specific/settings/settings_menu_item.dart index a1cc79d98..a6742fd48 100644 --- a/lib/pages_desktop_specific/settings/settings_menu_item.dart +++ b/lib/pages_desktop_specific/settings/settings_menu_item.dart @@ -14,13 +14,13 @@ import '../../utilities/text_styles.dart'; class SettingsMenuItem extends StatelessWidget { const SettingsMenuItem({ - Key? key, + super.key, required this.icon, required this.label, required this.value, required this.group, required this.onChanged, - }) : super(key: key); + }); final Widget icon; final String label; diff --git a/lib/pages_desktop_specific/spark_coins/spark_coins_view.dart b/lib/pages_desktop_specific/spark_coins/spark_coins_view.dart index 42053b155..43ba62a6a 100644 --- a/lib/pages_desktop_specific/spark_coins/spark_coins_view.dart +++ b/lib/pages_desktop_specific/spark_coins/spark_coins_view.dart @@ -24,9 +24,9 @@ import '../../widgets/rounded_white_container.dart'; class SparkCoinsView extends ConsumerStatefulWidget { const SparkCoinsView({ - Key? key, + super.key, required this.walletId, - }) : super(key: key); + }); static const String routeName = "/sparkCoinsView"; diff --git a/lib/providers/exchange/exchange_form_state_provider.dart b/lib/providers/exchange/exchange_form_state_provider.dart index ab1515653..433555230 100644 --- a/lib/providers/exchange/exchange_form_state_provider.dart +++ b/lib/providers/exchange/exchange_form_state_provider.dart @@ -20,7 +20,6 @@ import '../../utilities/amount/amount.dart'; import '../../utilities/amount/amount_unit.dart'; import '../../utilities/enums/exchange_rate_type_enum.dart'; -import '../../wallets/crypto_currency/coins/nano.dart'; import '../../wallets/crypto_currency/crypto_currency.dart'; import 'package:tuple/tuple.dart'; diff --git a/lib/providers/exchange/trade_sent_from_stack_lookup_provider.dart b/lib/providers/exchange/trade_sent_from_stack_lookup_provider.dart index 5eb808e3c..fc717d104 100644 --- a/lib/providers/exchange/trade_sent_from_stack_lookup_provider.dart +++ b/lib/providers/exchange/trade_sent_from_stack_lookup_provider.dart @@ -9,8 +9,10 @@ */ import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../services/trade_sent_from_stack_service.dart'; final tradeSentFromStackLookupProvider = ChangeNotifierProvider( - (ref) => TradeSentFromStackService()); + (ref) => TradeSentFromStackService(), +); diff --git a/lib/providers/global/base_currencies_provider.dart b/lib/providers/global/base_currencies_provider.dart index 341ebf2c1..1709d86c1 100644 --- a/lib/providers/global/base_currencies_provider.dart +++ b/lib/providers/global/base_currencies_provider.dart @@ -31,7 +31,7 @@ class _BaseCurrencies extends ChangeNotifier { return; } - Map newMap = {}; + final Map newMap = {}; for (final entry in _kCurrencyMap.entries) { if (list.contains(entry.key.toLowerCase())) { diff --git a/lib/providers/global/secure_store_provider.dart b/lib/providers/global/secure_store_provider.dart index 284ee2745..d49d6c552 100644 --- a/lib/providers/global/secure_store_provider.dart +++ b/lib/providers/global/secure_store_provider.dart @@ -10,15 +10,18 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; -import '../desktop/storage_crypto_handler_provider.dart'; + import '../../utilities/flutter_secure_storage_interface.dart'; import '../../utilities/util.dart'; +import '../desktop/storage_crypto_handler_provider.dart'; final secureStoreProvider = Provider((ref) { if (Util.isDesktop) { final handler = ref.read(storageCryptoHandlerProvider).handler; return SecureStorageWrapper( - store: DesktopSecureStore(handler), isDesktop: true); + store: DesktopSecureStore(handler), + isDesktop: true, + ); } else { return const SecureStorageWrapper( store: FlutterSecureStorage(), diff --git a/lib/providers/stack_restore/stack_restoring_ui_state_provider.dart b/lib/providers/stack_restore/stack_restoring_ui_state_provider.dart index a618d34e5..41f0fbe07 100644 --- a/lib/providers/stack_restore/stack_restoring_ui_state_provider.dart +++ b/lib/providers/stack_restore/stack_restoring_ui_state_provider.dart @@ -9,8 +9,10 @@ */ import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../models/stack_restoring_ui_state.dart'; final stackRestoringUIStateProvider = ChangeNotifierProvider( - (ref) => StackRestoringUIState()); + (ref) => StackRestoringUIState(), +); diff --git a/lib/providers/ui/address_book_providers/valid_contact_state_provider.dart b/lib/providers/ui/address_book_providers/valid_contact_state_provider.dart index 1bdba9729..7529484d1 100644 --- a/lib/providers/ui/address_book_providers/valid_contact_state_provider.dart +++ b/lib/providers/ui/address_book_providers/valid_contact_state_provider.dart @@ -9,6 +9,7 @@ */ import 'package:flutter_riverpod/flutter_riverpod.dart'; + import 'address_entry_data_provider.dart'; final validContactStateProvider = @@ -19,10 +20,12 @@ final validContactStateProvider = for (int i = 0; i < ids.length; i++) { final _valid = ref.watch( - addressEntryDataProvider(ids[i]).select((value) => value.isValid)); + addressEntryDataProvider(ids[i]).select((value) => value.isValid), + ); final _isEmpty = ref.watch( - addressEntryDataProvider(ids[i]).select((value) => value.isEmpty)); + addressEntryDataProvider(ids[i]).select((value) => value.isEmpty), + ); isValid = isValid && (_valid || _isEmpty); if (_valid) { diff --git a/lib/providers/ui/preview_tx_button_state_provider.dart b/lib/providers/ui/preview_tx_button_state_provider.dart index 283c7c2c8..89d960743 100644 --- a/lib/providers/ui/preview_tx_button_state_provider.dart +++ b/lib/providers/ui/preview_tx_button_state_provider.dart @@ -11,7 +11,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../wallet/public_private_balance_state_provider.dart'; import '../../utilities/amount/amount.dart'; -import '../../wallets/crypto_currency/coins/firo.dart'; import '../../wallets/crypto_currency/crypto_currency.dart'; final pSendAmount = StateProvider.autoDispose((_) => null); diff --git a/lib/providers/wallet/wallet_balance_toggle_state_provider.dart b/lib/providers/wallet/wallet_balance_toggle_state_provider.dart index 31860c052..eb9dada4f 100644 --- a/lib/providers/wallet/wallet_balance_toggle_state_provider.dart +++ b/lib/providers/wallet/wallet_balance_toggle_state_provider.dart @@ -9,8 +9,10 @@ */ import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../utilities/enums/wallet_balance_toggle_state.dart'; final walletBalanceToggleStateProvider = StateProvider.autoDispose( - (ref) => WalletBalanceToggleState.full); + (ref) => WalletBalanceToggleState.full, +); diff --git a/lib/route_generator.dart b/lib/route_generator.dart index dd22e8ccd..735b8681e 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -2458,11 +2458,11 @@ class RouteGenerator { return PageRouteBuilder( pageBuilder: (context, animation, secondaryAnimation) => viewToInsert, transitionsBuilder: (context, animation, secondaryAnimation, child) { - var begin = const Offset(0.0, 1.0); - var end = Offset.zero; - var curve = Curves.easeInOut; + final begin = const Offset(0.0, 1.0); + final end = Offset.zero; + final curve = Curves.easeInOut; - var tween = + final tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve)); return SlideTransition( @@ -2475,7 +2475,7 @@ class RouteGenerator { static Route _routeError(String message) { // Replace with robust ErrorView page - Widget errorView = Scaffold( + final Widget errorView = Scaffold( appBar: AppBar( backgroundColor: Colors.black, title: const Text('Navigation error'), diff --git a/lib/services/auto_swb_service.dart b/lib/services/auto_swb_service.dart index d1f5d5499..1113b30f1 100644 --- a/lib/services/auto_swb_service.dart +++ b/lib/services/auto_swb_service.dart @@ -13,11 +13,12 @@ import 'dart:convert'; import 'dart:io'; import 'package:flutter/foundation.dart'; +import 'package:tuple/tuple.dart'; + import '../pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart'; import '../utilities/flutter_secure_storage_interface.dart'; import '../utilities/logger.dart'; import '../utilities/prefs.dart'; -import 'package:tuple/tuple.dart'; enum AutoSWBStatus { idle, @@ -42,8 +43,9 @@ class AutoSWBService extends ChangeNotifier { Future doBackup() async { if (_status == AutoSWBStatus.backingUp) { Logging.instance.log( - "AutoSWBService attempted to run doBackup() while a backup is in progress!", - level: LogLevel.Warning); + "AutoSWBService attempted to run doBackup() while a backup is in progress!", + level: LogLevel.Warning, + ); return; } Logging.instance @@ -61,8 +63,9 @@ class AutoSWBService extends ChangeNotifier { final autoBackupDirectoryPath = Prefs.instance.autoBackupLocation; if (autoBackupDirectoryPath == null) { Logging.instance.log( - "AutoSWBService attempted to run doBackup() when no auto backup directory was set!", - level: LogLevel.Error); + "AutoSWBService attempted to run doBackup() when no auto backup directory was set!", + level: LogLevel.Error, + ); // set error backup status and notify listeners _status = AutoSWBStatus.error; notifyListeners(); @@ -70,7 +73,8 @@ class AutoSWBService extends ChangeNotifier { } final json = await SWB.createStackWalletJSON( - secureStorage: secureStorageInterface); + secureStorage: secureStorageInterface, + ); final jsonString = jsonEncode(json); final adkString = @@ -85,7 +89,11 @@ class AutoSWBService extends ChangeNotifier { createAutoBackupFilename(autoBackupDirectoryPath, now); final result = await SWB.encryptStackWalletWithADK( - fileToSave, adkString!, jsonString, adkVersion); + fileToSave, + adkString!, + jsonString, + adkVersion, + ); if (!result) { throw Exception("stack auto backup service failed to create a backup"); @@ -99,7 +107,7 @@ class AutoSWBService extends ChangeNotifier { Logging.instance .log("AutoSWBService.doBackup() succeeded", level: LogLevel.Info); } on Exception catch (e, s) { - String err = getErrorMessageFromSWBException(e); + final String err = getErrorMessageFromSWBException(e); Logging.instance.log("$err\n$s", level: LogLevel.Error); // set error backup status and notify listeners _status = AutoSWBStatus.error; @@ -124,13 +132,13 @@ class AutoSWBService extends ChangeNotifier { final List> files = []; for (final file in dir.listSync()) { - String fileName = file.uri.pathSegments.last; + final String fileName = file.uri.pathSegments.last; // check that its a swb auto backup file if (fileName.startsWith("stackautobackup_") && fileName.endsWith(".swb")) { // get date from filename - int a = fileName.indexOf("_") + 1; - int b = fileName.indexOf(".swb"); + final int a = fileName.indexOf("_") + 1; + final int b = fileName.indexOf(".swb"); final dateString = fileName.substring(a, b); // split date components @@ -148,8 +156,9 @@ class AutoSWBService extends ChangeNotifier { } // sort from newest to oldest - files.sort((a, b) => - b.item1.millisecondsSinceEpoch - a.item1.millisecondsSinceEpoch); + files.sort( + (a, b) => b.item1.millisecondsSinceEpoch - a.item1.millisecondsSinceEpoch, + ); // delete any older backups if there are more than the number we want to keep while (files.length > numberToKeep) { diff --git a/lib/services/coins/bitcoincash/cashtokens.dart b/lib/services/coins/bitcoincash/cashtokens.dart index d35d2838c..d9da0ed01 100644 --- a/lib/services/coins/bitcoincash/cashtokens.dart +++ b/lib/services/coins/bitcoincash/cashtokens.dart @@ -99,7 +99,7 @@ class TokenOutputData { return false; } - int s = bitfield![0] & 0xf0; + final int s = bitfield![0] & 0xf0; if (s >= 0x80 || s == 0x00) { return false; } @@ -122,43 +122,43 @@ class TokenOutputData { // and pack outputs. These are called by the wrap and unwrap functions. int deserialize(Uint8List buffer, {int cursor = 0, bool strict = false}) { try { - this.id = buffer.sublist(cursor, cursor + 32); + id = buffer.sublist(cursor, cursor + 32); cursor += 32; - this.bitfield = Uint8List.fromList([buffer[cursor]]); + bitfield = Uint8List.fromList([buffer[cursor]]); cursor += 1; - if (this.hasCommitmentLength()) { + if (hasCommitmentLength()) { // Read the first byte to determine the length of the commitment data - int commitmentLength = buffer[cursor]; + final int commitmentLength = buffer[cursor]; // Move cursor to the next byte cursor += 1; // Read 'commitmentLength' bytes for the commitment data - this.commitment = buffer.sublist(cursor, cursor + commitmentLength); + commitment = buffer.sublist(cursor, cursor + commitmentLength); // Adjust the cursor by the length of the commitment data cursor += commitmentLength; } else { - this.commitment = null; + commitment = null; } - if (this.hasAmount()) { + if (hasAmount()) { // Use readCompactSize that returns CompactSizeResult - CompactSizeResult result = + final CompactSizeResult result = readCompactSize(buffer, cursor, strict: strict); - this.amount = result.amount; + amount = result.amount; cursor += result.bytesRead; } else { - this.amount = 0; + amount = 0; } - if (!this.isValidBitfield() || - (this.hasAmount() && this.amount == 0) || - (this.amount! < 0 || this.amount! > (1 << 63) - 1) || - (this.hasCommitmentLength() && this.commitment!.isEmpty) || - (this.amount! == 0 && !this.hasNFT())) { + if (!isValidBitfield() || + (hasAmount() && amount == 0) || + (amount! < 0 || amount! > (1 << 63) - 1) || + (hasCommitmentLength() && commitment!.isEmpty) || + (amount! == 0 && !hasNFT())) { throw Exception('Unable to parse token data or token data is invalid'); } @@ -170,19 +170,19 @@ class TokenOutputData { // Serialize method Uint8List serialize() { - var buffer = BytesBuilder(); + final buffer = BytesBuilder(); // write ID and bitfield - buffer.add(this.id!); - buffer.addByte(this.bitfield![0]); + buffer.add(id!); + buffer.addByte(bitfield![0]); // Write optional fields - if (this.hasCommitmentLength()) { - buffer.add(this.commitment!); + if (hasCommitmentLength()) { + buffer.add(commitment!); } - if (this.hasAmount()) { - List compactSizeBytes = writeCompactSize(this.amount!); + if (hasAmount()) { + final List compactSizeBytes = writeCompactSize(amount!); buffer.add(compactSizeBytes); } @@ -195,7 +195,7 @@ final List PREFIX_BYTE = [0xef]; // This function wraps a "normal" output together with token data. ParsedOutput wrap_spk(TokenOutputData? token_data, Uint8List script_pub_key) { - ParsedOutput parsedOutput = ParsedOutput(); + final ParsedOutput parsedOutput = ParsedOutput(); if (token_data == null) { parsedOutput.script_pub_key = script_pub_key; @@ -220,7 +220,7 @@ ParsedOutput wrap_spk(TokenOutputData? token_data, Uint8List script_pub_key) { // Note that the data returend in both cases in of ParsedOutput type, which // holds both the script pub key and token data. ParsedOutput unwrap_spk(Uint8List wrapped_spk) { - ParsedOutput parsedOutput = ParsedOutput(); + final ParsedOutput parsedOutput = ParsedOutput(); if (wrapped_spk.isEmpty || wrapped_spk[0] != PREFIX_BYTE[0]) { parsedOutput.script_pub_key = wrapped_spk; @@ -228,14 +228,15 @@ ParsedOutput unwrap_spk(Uint8List wrapped_spk) { } int read_cursor = 1; // Start after the PREFIX_BYTE - TokenOutputData token_data = TokenOutputData(); + final TokenOutputData token_data = TokenOutputData(); Uint8List wrapped_spk_without_prefix_byte; try { // Deserialize updates read_cursor by the number of bytes read wrapped_spk_without_prefix_byte = wrapped_spk.sublist(read_cursor); - int bytesRead = token_data.deserialize(wrapped_spk_without_prefix_byte); + final int bytesRead = + token_data.deserialize(wrapped_spk_without_prefix_byte); read_cursor += bytesRead; parsedOutput.token_data = token_data; @@ -294,7 +295,7 @@ CompactSizeResult readCompactSize( } Uint8List writeCompactSize(int size) { - var buffer = ByteData(9); // Maximum needed size for compact size is 9 bytes + final buffer = ByteData(9); // Maximum needed size for compact size is 9 bytes if (size < 0) { throw Exception("attempt to write size < 0"); } else if (size < 253) { diff --git a/lib/services/coins/tezos/api/tezos_api.dart b/lib/services/coins/tezos/api/tezos_api.dart index bf102ef34..e646019ee 100644 --- a/lib/services/coins/tezos/api/tezos_api.dart +++ b/lib/services/coins/tezos/api/tezos_api.dart @@ -1,11 +1,11 @@ import 'dart:convert'; import '../../../../networking/http.dart'; -import 'tezos_account.dart'; -import 'tezos_transaction.dart'; -import '../../../tor_service.dart'; import '../../../../utilities/logger.dart'; import '../../../../utilities/prefs.dart'; +import '../../../tor_service.dart'; +import 'tezos_account.dart'; +import 'tezos_transaction.dart'; abstract final class TezosAPI { static final HTTP _client = HTTP(); @@ -33,8 +33,10 @@ abstract final class TezosAPI { } } - static Future getAccount(String address, - {String type = "user"}) async { + static Future getAccount( + String address, { + String type = "user", + }) async { try { final uriString = "$_baseURL/v1/accounts/$address?legacy=false"; final response = await _client.get( @@ -74,8 +76,8 @@ abstract final class TezosAPI { final result = jsonDecode(response.body) as List; - List txs = []; - for (var tx in result) { + final List txs = []; + for (final tx in result) { if (tx["type"] == "transaction") { final theTx = TezosTransaction( id: tx["id"] as int, diff --git a/lib/services/coins/tezos/api/tezos_rpc_api.dart b/lib/services/coins/tezos/api/tezos_rpc_api.dart index fe6f1ddea..59ca98045 100644 --- a/lib/services/coins/tezos/api/tezos_rpc_api.dart +++ b/lib/services/coins/tezos/api/tezos_rpc_api.dart @@ -13,7 +13,7 @@ abstract final class TezosRpcAPI { required String address, }) async { try { - String balanceCall = + final String balanceCall = "${nodeInfo.host}:${nodeInfo.port}/chains/main/blocks/head/context/contracts/$address/balance"; final response = await _client.get( diff --git a/lib/services/coins/tezos/api/tezos_transaction.dart b/lib/services/coins/tezos/api/tezos_transaction.dart index 3ffcd4adf..e1e6bc88b 100644 --- a/lib/services/coins/tezos/api/tezos_transaction.dart +++ b/lib/services/coins/tezos/api/tezos_transaction.dart @@ -40,4 +40,4 @@ class TezosTransaction { required this.senderAddress, required this.receiverAddress, }); -} \ No newline at end of file +} diff --git a/lib/services/debug_service.dart b/lib/services/debug_service.dart index a8d7dd963..189c4f19f 100644 --- a/lib/services/debug_service.dart +++ b/lib/services/debug_service.dart @@ -14,6 +14,7 @@ import 'dart:io'; import 'package:event_bus/event_bus.dart'; import 'package:flutter/material.dart'; import 'package:isar/isar.dart'; + import '../models/isar/models/log.dart'; import '../utilities/logger.dart'; @@ -44,7 +45,11 @@ class DebugService extends ChangeNotifier { // }); } - List get recentLogs => isar.logs.where().sortByTimestampInMillisUTCDesc().limit(100).findAllSync(); + List get recentLogs => isar.logs + .where() + .sortByTimestampInMillisUTCDesc() + .limit(100) + .findAllSync(); // Future updateRecentLogs() async { // int totalCount = await isar.logs.count(); @@ -95,7 +100,7 @@ class DebugService extends ChangeNotifier { final filename = "Stack_Wallet_logs_${now.year}_${now.month}_${now.day}_${now.hour}_${now.minute}_${now.second}.txt"; final filepath = "$directory/$filename"; - File file = await File(filepath).create(); + final File file = await File(filepath).create(); final sink = file.openWrite(); final logs = await isar.logs.where().anyTimestampInMillisUTC().findAll(); diff --git a/lib/services/ethereum/ethereum_api.dart b/lib/services/ethereum/ethereum_api.dart index d2ead5aeb..faacec22e 100644 --- a/lib/services/ethereum/ethereum_api.dart +++ b/lib/services/ethereum/ethereum_api.dart @@ -11,6 +11,8 @@ import 'dart:convert'; import 'package:http/http.dart'; +import 'package:tuple/tuple.dart'; + import '../../dto/ethereum/eth_token_tx_dto.dart'; import '../../dto/ethereum/eth_token_tx_extra_dto.dart'; import '../../dto/ethereum/eth_tx_dto.dart'; @@ -18,15 +20,13 @@ import '../../dto/ethereum/pending_eth_tx_dto.dart'; import '../../models/isar/models/ethereum/eth_contract.dart'; import '../../models/paymint/fee_object_model.dart'; import '../../networking/http.dart'; -import '../tor_service.dart'; import '../../utilities/amount/amount.dart'; import '../../utilities/eth_commons.dart'; import '../../utilities/extensions/extensions.dart'; import '../../utilities/logger.dart'; import '../../utilities/prefs.dart'; -import '../../wallets/crypto_currency/coins/ethereum.dart'; import '../../wallets/crypto_currency/crypto_currency.dart'; -import 'package:tuple/tuple.dart'; +import '../tor_service.dart'; class EthApiException implements Exception { EthApiException(this.message); @@ -117,7 +117,8 @@ abstract class EthereumAPI { } static Future> getEthTransactionByHash( - String txid) async { + String txid, + ) async { try { final response = await post( Uri.parse( @@ -600,16 +601,18 @@ abstract class EthereumAPI { final feesSlow = fees.slow.shift(9).toBigInt(); return FeeObject( - numberOfBlocksFast: fees.numberOfBlocksFast, - numberOfBlocksAverage: fees.numberOfBlocksAverage, - numberOfBlocksSlow: fees.numberOfBlocksSlow, - fast: feesFast.toInt(), - medium: feesStandard.toInt(), - slow: feesSlow.toInt()); + numberOfBlocksFast: fees.numberOfBlocksFast, + numberOfBlocksAverage: fees.numberOfBlocksAverage, + numberOfBlocksSlow: fees.numberOfBlocksSlow, + fast: feesFast.toInt(), + medium: feesStandard.toInt(), + slow: feesSlow.toInt(), + ); } static Future> getTokenContractInfoByAddress( - String contractAddress) async { + String contractAddress, + ) async { try { final response = await client.get( url: Uri.parse( @@ -648,7 +651,8 @@ abstract class EthereumAPI { ); } else { throw EthApiException( - "Unsupported token type found: ${map["type"]}"); + "Unsupported token type found: ${map["type"]}", + ); } return EthereumResponse( @@ -732,7 +736,8 @@ abstract class EthereumAPI { try { final response = await client.get( url: Uri.parse( - "$stackBaseServer/state?addrs=$contractAddress&parts=proxy"), + "$stackBaseServer/state?addrs=$contractAddress&parts=proxy", + ), proxyInfo: Prefs.instance.useTor ? TorService.sharedInstance.getProxyInfo() : null, diff --git a/lib/services/event_bus/events/global/blocks_remaining_event.dart b/lib/services/event_bus/events/global/blocks_remaining_event.dart index 9a9c71c81..008d39440 100644 --- a/lib/services/event_bus/events/global/blocks_remaining_event.dart +++ b/lib/services/event_bus/events/global/blocks_remaining_event.dart @@ -16,7 +16,8 @@ class BlocksRemainingEvent { BlocksRemainingEvent(this.blocksRemaining, this.walletId) { Logging.instance.log( - "RefreshPercentChangedEvent fired on $walletId with blocks remaining = $blocksRemaining", - level: LogLevel.Info); + "RefreshPercentChangedEvent fired on $walletId with blocks remaining = $blocksRemaining", + level: LogLevel.Info, + ); } } diff --git a/lib/services/event_bus/events/global/refresh_percent_changed_event.dart b/lib/services/event_bus/events/global/refresh_percent_changed_event.dart index bbe9027b1..7429b828e 100644 --- a/lib/services/event_bus/events/global/refresh_percent_changed_event.dart +++ b/lib/services/event_bus/events/global/refresh_percent_changed_event.dart @@ -16,7 +16,8 @@ class RefreshPercentChangedEvent { RefreshPercentChangedEvent(this.percent, this.walletId) { Logging.instance.log( - "RefreshPercentChangedEvent fired on $walletId with percent (range of 0.0-1.0)= $percent", - level: LogLevel.Info); + "RefreshPercentChangedEvent fired on $walletId with percent (range of 0.0-1.0)= $percent", + level: LogLevel.Info, + ); } } diff --git a/lib/services/event_bus/events/global/tor_connection_status_changed_event.dart b/lib/services/event_bus/events/global/tor_connection_status_changed_event.dart index 640bdc1fb..9087d37e2 100644 --- a/lib/services/event_bus/events/global/tor_connection_status_changed_event.dart +++ b/lib/services/event_bus/events/global/tor_connection_status_changed_event.dart @@ -17,7 +17,8 @@ class TorConnectionStatusChangedEvent { TorConnectionStatusChangedEvent(this.newStatus, this.message) { Logging.instance.log( - "TorSyncStatusChangedEvent fired with arg newStatus = $newStatus ($message)", - level: LogLevel.Info); + "TorSyncStatusChangedEvent fired with arg newStatus = $newStatus ($message)", + level: LogLevel.Info, + ); } } diff --git a/lib/services/event_bus/events/global/updated_in_background_event.dart b/lib/services/event_bus/events/global/updated_in_background_event.dart index c492a837f..582978357 100644 --- a/lib/services/event_bus/events/global/updated_in_background_event.dart +++ b/lib/services/event_bus/events/global/updated_in_background_event.dart @@ -16,7 +16,8 @@ class UpdatedInBackgroundEvent { UpdatedInBackgroundEvent(this.message, this.walletId) { Logging.instance.log( - "UpdatedInBackgroundEvent fired with message: $message", - level: LogLevel.Info); + "UpdatedInBackgroundEvent fired with message: $message", + level: LogLevel.Info, + ); } } diff --git a/lib/services/exchange/change_now/change_now_api.dart b/lib/services/exchange/change_now/change_now_api.dart index 4ee3e5502..2882f9d4d 100644 --- a/lib/services/exchange/change_now/change_now_api.dart +++ b/lib/services/exchange/change_now/change_now_api.dart @@ -12,6 +12,8 @@ import 'dart:convert'; import 'package:decimal/decimal.dart'; import 'package:flutter/foundation.dart'; +import 'package:tuple/tuple.dart'; + import '../../../exceptions/exchange/exchange_exception.dart'; import '../../../exceptions/exchange/pair_unavailable_exception.dart'; import '../../../exceptions/exchange/unsupported_currency_exception.dart'; @@ -26,12 +28,11 @@ import '../../../models/exchange/response_objects/range.dart'; import '../../../models/isar/exchange_cache/currency.dart'; import '../../../models/isar/exchange_cache/pair.dart'; import '../../../networking/http.dart'; -import 'change_now_exchange.dart'; -import '../exchange_response.dart'; -import '../../tor_service.dart'; import '../../../utilities/logger.dart'; import '../../../utilities/prefs.dart'; -import 'package:tuple/tuple.dart'; +import '../../tor_service.dart'; +import '../exchange_response.dart'; +import 'change_now_exchange.dart'; class ChangeNowAPI { static const String scheme = "https"; @@ -172,8 +173,10 @@ class ChangeNowAPI { ); return result; } catch (e, s) { - Logging.instance.log("getAvailableCurrencies exception: $e\n$s", - level: LogLevel.Error); + Logging.instance.log( + "getAvailableCurrencies exception: $e\n$s", + level: LogLevel.Error, + ); return ExchangeResponse( exception: ExchangeException( "Error: $jsonArray", @@ -182,8 +185,10 @@ class ChangeNowAPI { ); } } catch (e, s) { - Logging.instance.log("getAvailableCurrencies exception: $e\n$s", - level: LogLevel.Error); + Logging.instance.log( + "getAvailableCurrencies exception: $e\n$s", + level: LogLevel.Error, + ); return ExchangeResponse( exception: ExchangeException( e.toString(), @@ -197,7 +202,7 @@ class ChangeNowAPI { Tuple2, bool> args, ) { try { - List currencies = []; + final List currencies = []; for (final json in args.item1) { try { @@ -213,8 +218,11 @@ class ChangeNowAPI { ); } catch (_) { return ExchangeResponse( - exception: ExchangeException("Failed to serialize $json", - ExchangeExceptionType.serializeResponseError)); + exception: ExchangeException( + "Failed to serialize $json", + ExchangeExceptionType.serializeResponseError, + ), + ); } } @@ -255,8 +263,10 @@ class ChangeNowAPI { ); return result; } catch (e, s) { - Logging.instance.log("getAvailableCurrencies exception: $e\n$s", - level: LogLevel.Error); + Logging.instance.log( + "getAvailableCurrencies exception: $e\n$s", + level: LogLevel.Error, + ); return ExchangeResponse( exception: ExchangeException( "Error: $jsonArray", @@ -265,8 +275,10 @@ class ChangeNowAPI { ); } } catch (e, s) { - Logging.instance.log("getAvailableCurrencies exception: $e\n$s", - level: LogLevel.Error); + Logging.instance.log( + "getAvailableCurrencies exception: $e\n$s", + level: LogLevel.Error, + ); return ExchangeResponse( exception: ExchangeException( e.toString(), @@ -280,7 +292,7 @@ class ChangeNowAPI { List args, ) { try { - List currencies = []; + final List currencies = []; for (final json in args) { try { @@ -296,8 +308,11 @@ class ChangeNowAPI { ); } catch (_) { return ExchangeResponse( - exception: ExchangeException("Failed to serialize $json", - ExchangeExceptionType.serializeResponseError)); + exception: ExchangeException( + "Failed to serialize $json", + ExchangeExceptionType.serializeResponseError, + ), + ); } } @@ -342,7 +357,7 @@ class ChangeNowAPI { final jsonArray = response as List; - List currencies = []; + final List currencies = []; try { for (final json in jsonArray) { try { @@ -366,11 +381,16 @@ class ChangeNowAPI { } } } catch (e, s) { - Logging.instance.log("getPairedCurrencies exception: $e\n$s", - level: LogLevel.Error); + Logging.instance.log( + "getPairedCurrencies exception: $e\n$s", + level: LogLevel.Error, + ); return ExchangeResponse( - exception: ExchangeException("Error: $jsonArray", - ExchangeExceptionType.serializeResponseError)); + exception: ExchangeException( + "Error: $jsonArray", + ExchangeExceptionType.serializeResponseError, + ), + ); } return ExchangeResponse(value: currencies); } catch (e, s) { @@ -393,7 +413,9 @@ class ChangeNowAPI { required String toTicker, String? apiKey, }) async { - Map? params = {"api_key": apiKey ?? kChangeNowApiKey}; + final Map params = { + "api_key": apiKey ?? kChangeNowApiKey, + }; final uri = _buildUri("/min-amount/${fromTicker}_$toTicker", params); @@ -413,8 +435,10 @@ class ChangeNowAPI { ); } } catch (e, s) { - Logging.instance.log("getMinimalExchangeAmount exception: $e\n$s", - level: LogLevel.Error); + Logging.instance.log( + "getMinimalExchangeAmount exception: $e\n$s", + level: LogLevel.Error, + ); return ExchangeResponse( exception: ExchangeException( e.toString(), @@ -434,11 +458,14 @@ class ChangeNowAPI { required bool isFixedRate, String? apiKey, }) async { - Map? params = {"api_key": apiKey ?? kChangeNowApiKey}; + final Map params = { + "api_key": apiKey ?? kChangeNowApiKey, + }; final uri = _buildUri( - "/exchange-range${isFixedRate ? "/fixed-rate" : ""}/${fromTicker}_$toTicker", - params); + "/exchange-range${isFixedRate ? "/fixed-rate" : ""}/${fromTicker}_$toTicker", + params, + ); try { final jsonObject = await _makeGetRequest(uri); @@ -472,7 +499,7 @@ class ChangeNowAPI { required Decimal fromAmount, String? apiKey, }) async { - Map params = {"api_key": apiKey ?? kChangeNowApiKey}; + final Map params = {"api_key": apiKey ?? kChangeNowApiKey}; final uri = _buildUri( "/exchange-amount/${fromAmount.toString()}/${fromTicker}_$toTicker", @@ -524,8 +551,10 @@ class ChangeNowAPI { ); } } catch (e, s) { - Logging.instance.log("getEstimatedExchangeAmount exception: $e\n$s", - level: LogLevel.Error); + Logging.instance.log( + "getEstimatedExchangeAmount exception: $e\n$s", + level: LogLevel.Error, + ); return ExchangeResponse( exception: ExchangeException( e.toString(), @@ -545,7 +574,7 @@ class ChangeNowAPI { bool useRateId = true, String? apiKey, }) async { - Map params = { + final Map params = { "api_key": apiKey ?? kChangeNowApiKey, "useRateId": useRateId.toString(), }; @@ -608,8 +637,10 @@ class ChangeNowAPI { ); } } catch (e, s) { - Logging.instance.log("getEstimatedExchangeAmount exception: $e\n$s", - level: LogLevel.Error); + Logging.instance.log( + "getEstimatedExchangeAmount exception: $e\n$s", + level: LogLevel.Error, + ); return ExchangeResponse( exception: ExchangeException( e.toString(), @@ -685,7 +716,7 @@ class ChangeNowAPI { CNFlowType flow = CNFlowType.standard, String? apiKey, }) async { - Map? params = { + final Map params = { "fromCurrency": fromTicker, "toCurrency": toTicker, "flow": flow.value, @@ -732,8 +763,10 @@ class ChangeNowAPI { ); } } catch (e, s) { - Logging.instance.log("getEstimatedExchangeAmountV2 exception: $e\n$s", - level: LogLevel.Error); + Logging.instance.log( + "getEstimatedExchangeAmountV2 exception: $e\n$s", + level: LogLevel.Error, + ); return ExchangeResponse( exception: ExchangeException( e.toString(), @@ -751,7 +784,9 @@ class ChangeNowAPI { String? apiKey, }) async { final uri = _buildUri( - "/market-info/fixed-rate/${apiKey ?? kChangeNowApiKey}", null); + "/market-info/fixed-rate/${apiKey ?? kChangeNowApiKey}", + null, + ); try { // json array is expected here @@ -762,8 +797,10 @@ class ChangeNowAPI { await compute(_parseFixedRateMarketsJson, jsonArray as List); return result; } catch (e, s) { - Logging.instance.log("getAvailableFixedRateMarkets exception: $e\n$s", - level: LogLevel.Error); + Logging.instance.log( + "getAvailableFixedRateMarkets exception: $e\n$s", + level: LogLevel.Error, + ); return ExchangeResponse( exception: ExchangeException( "Error: $jsonArray", @@ -772,8 +809,10 @@ class ChangeNowAPI { ); } } catch (e, s) { - Logging.instance.log("getAvailableFixedRateMarkets exception: $e\n$s", - level: LogLevel.Error); + Logging.instance.log( + "getAvailableFixedRateMarkets exception: $e\n$s", + level: LogLevel.Error, + ); return ExchangeResponse( exception: ExchangeException( e.toString(), @@ -784,17 +823,22 @@ class ChangeNowAPI { } ExchangeResponse> _parseFixedRateMarketsJson( - List jsonArray) { + List jsonArray, + ) { try { - List markets = []; + final List markets = []; for (final json in jsonArray) { try { markets.add( - FixedRateMarket.fromMap(Map.from(json as Map))); + FixedRateMarket.fromMap(Map.from(json as Map)), + ); } catch (_) { return ExchangeResponse( - exception: ExchangeException("Failed to serialize $json", - ExchangeExceptionType.serializeResponseError)); + exception: ExchangeException( + "Failed to serialize $json", + ExchangeExceptionType.serializeResponseError, + ), + ); } } return ExchangeResponse(value: markets); @@ -842,7 +886,8 @@ class ChangeNowAPI { try { final value = ExchangeTransaction.fromJson( - Map.from(json as Map)); + Map.from(json as Map), + ); return ExchangeResponse(value: value); } catch (_) { return ExchangeResponse( @@ -854,8 +899,9 @@ class ChangeNowAPI { } } catch (e, s) { Logging.instance.log( - "createStandardExchangeTransaction exception: $e\n$s", - level: LogLevel.Error); + "createStandardExchangeTransaction exception: $e\n$s", + level: LogLevel.Error, + ); return ExchangeResponse( exception: ExchangeException( e.toString(), @@ -915,7 +961,8 @@ class ChangeNowAPI { try { final value = ExchangeTransaction.fromJson( - Map.from(json as Map)); + Map.from(json as Map), + ); return ExchangeResponse(value: value); } catch (_) { return ExchangeResponse( @@ -927,8 +974,9 @@ class ChangeNowAPI { } } catch (e, s) { Logging.instance.log( - "createFixedRateExchangeTransaction exception: $e\n$s", - level: LogLevel.Error); + "createFixedRateExchangeTransaction exception: $e\n$s", + level: LogLevel.Error, + ); return ExchangeResponse( exception: ExchangeException( e.toString(), @@ -951,7 +999,8 @@ class ChangeNowAPI { try { final value = ExchangeTransactionStatus.fromJson( - Map.from(json as Map)); + Map.from(json as Map), + ); return ExchangeResponse(value: value); } catch (_) { return ExchangeResponse( @@ -976,8 +1025,10 @@ class ChangeNowAPI { Future>> getAvailableFloatingRatePairs({ bool includePartners = false, }) async { - final uri = _buildUri("/market-info/available-pairs", - {"includePartners": includePartners.toString()}); + final uri = _buildUri( + "/market-info/available-pairs", + {"includePartners": includePartners.toString()}, + ); try { // json array is expected here @@ -985,11 +1036,15 @@ class ChangeNowAPI { try { final result = await compute( - _parseAvailableFloatingRatePairsJson, jsonArray as List); + _parseAvailableFloatingRatePairsJson, + jsonArray as List, + ); return result; } catch (e, s) { - Logging.instance.log("getAvailableFloatingRatePairs exception: $e\n$s", - level: LogLevel.Error); + Logging.instance.log( + "getAvailableFloatingRatePairs exception: $e\n$s", + level: LogLevel.Error, + ); return ExchangeResponse( exception: ExchangeException( "Error: $jsonArray", @@ -998,8 +1053,10 @@ class ChangeNowAPI { ); } } catch (e, s) { - Logging.instance.log("getAvailableFloatingRatePairs exception: $e\n$s", - level: LogLevel.Error); + Logging.instance.log( + "getAvailableFloatingRatePairs exception: $e\n$s", + level: LogLevel.Error, + ); return ExchangeResponse( exception: ExchangeException( e.toString(), @@ -1010,9 +1067,10 @@ class ChangeNowAPI { } ExchangeResponse> _parseAvailableFloatingRatePairsJson( - List jsonArray) { + List jsonArray, + ) { try { - List pairs = []; + final List pairs = []; for (final json in jsonArray) { try { final List stringPair = (json as String).split("_"); @@ -1026,8 +1084,11 @@ class ChangeNowAPI { ); } catch (_) { return ExchangeResponse( - exception: ExchangeException("Failed to serialize $json", - ExchangeExceptionType.serializeResponseError)); + exception: ExchangeException( + "Failed to serialize $json", + ExchangeExceptionType.serializeResponseError, + ), + ); } } return ExchangeResponse(value: pairs); diff --git a/lib/services/exchange/exchange_data_loading_service.dart b/lib/services/exchange/exchange_data_loading_service.dart index 40d1e16bf..e500ed5cf 100644 --- a/lib/services/exchange/exchange_data_loading_service.dart +++ b/lib/services/exchange/exchange_data_loading_service.dart @@ -10,19 +10,20 @@ import 'package:flutter/foundation.dart'; import 'package:isar/isar.dart'; +import 'package:tuple/tuple.dart'; + import '../../db/hive/db.dart'; import '../../models/exchange/active_pair.dart'; import '../../models/exchange/aggregate_currency.dart'; import '../../models/isar/exchange_cache/currency.dart'; import '../../models/isar/exchange_cache/pair.dart'; -import 'change_now/change_now_exchange.dart'; -import 'majestic_bank/majestic_bank_exchange.dart'; -import 'trocador/trocador_exchange.dart'; import '../../utilities/enums/exchange_rate_type_enum.dart'; import '../../utilities/logger.dart'; import '../../utilities/prefs.dart'; import '../../utilities/stack_file_system.dart'; -import 'package:tuple/tuple.dart'; +import 'change_now/change_now_exchange.dart'; +import 'majestic_bank/majestic_bank_exchange.dart'; +import 'trocador/trocador_exchange.dart'; class ExchangeDataLoadingService { ExchangeDataLoadingService._(); @@ -40,8 +41,9 @@ class ExchangeDataLoadingService { static int get currentCacheVersion => DB.instance.get( - boxName: DB.boxNameDBInfo, - key: "exchange_data_cache_version") as int? ?? + boxName: DB.boxNameDBInfo, + key: "exchange_data_cache_version", + ) as int? ?? 0; Future _updateCurrentCacheVersion(int version) async { @@ -102,15 +104,17 @@ class ExchangeDataLoadingService { ) async { final currencies = await ExchangeDataLoadingService.instance.isar.currencies .filter() - .group((q) => rateType == ExchangeRateType.fixed - ? q - .rateTypeEqualTo(SupportedRateType.both) - .or() - .rateTypeEqualTo(SupportedRateType.fixed) - : q - .rateTypeEqualTo(SupportedRateType.both) - .or() - .rateTypeEqualTo(SupportedRateType.estimated)) + .group( + (q) => rateType == ExchangeRateType.fixed + ? q + .rateTypeEqualTo(SupportedRateType.both) + .or() + .rateTypeEqualTo(SupportedRateType.fixed) + : q + .rateTypeEqualTo(SupportedRateType.both) + .or() + .rateTypeEqualTo(SupportedRateType.estimated), + ) .and() .tickerEqualTo( ticker, @@ -213,8 +217,9 @@ class ExchangeDataLoadingService { }); } else { Logging.instance.log( - "Failed to load changeNOW currencies: ${responseCurrencies.exception?.message}", - level: LogLevel.Error); + "Failed to load changeNOW currencies: ${responseCurrencies.exception?.message}", + level: LogLevel.Error, + ); return; } } diff --git a/lib/services/exchange/majestic_bank/majestic_bank_api.dart b/lib/services/exchange/majestic_bank/majestic_bank_api.dart index c69d6ebe7..0dc46343b 100644 --- a/lib/services/exchange/majestic_bank/majestic_bank_api.dart +++ b/lib/services/exchange/majestic_bank/majestic_bank_api.dart @@ -227,8 +227,9 @@ class MajesticBankAPI { return ExchangeResponse(value: result); } catch (e, s) { Logging.instance.log( - "calculateOrder $fromCurrency-$receiveCurrency exception: $e\n$s", - level: LogLevel.Error); + "calculateOrder $fromCurrency-$receiveCurrency exception: $e\n$s", + level: LogLevel.Error, + ); return ExchangeResponse( exception: ExchangeException( e.toString(), @@ -342,9 +343,12 @@ class MajesticBankAPI { Future> trackOrder({ required String orderId, }) async { - final uri = _buildUri(endpoint: "track", params: { - "trx": orderId, - }); + final uri = _buildUri( + endpoint: "track", + params: { + "trx": orderId, + }, + ); try { final jsonObject = await _makeGetRequest(uri); diff --git a/lib/services/exchange/majestic_bank/majestic_bank_exchange.dart b/lib/services/exchange/majestic_bank/majestic_bank_exchange.dart index 351608759..e23490e0a 100644 --- a/lib/services/exchange/majestic_bank/majestic_bank_exchange.dart +++ b/lib/services/exchange/majestic_bank/majestic_bank_exchange.dart @@ -153,7 +153,9 @@ class MajesticBankExchange extends Exchange { @override Future>> getPairedCurrencies( - String forCurrency, bool fixedRate) { + String forCurrency, + bool fixedRate, + ) { // TODO: change this if the api changes to allow getting by paired currency return getAllCurrencies(fixedRate); } diff --git a/lib/services/exchange/simpleswap/simpleswap_api.dart b/lib/services/exchange/simpleswap/simpleswap_api.dart index 509977bb1..0653ff7c6 100644 --- a/lib/services/exchange/simpleswap/simpleswap_api.dart +++ b/lib/services/exchange/simpleswap/simpleswap_api.dart @@ -12,6 +12,9 @@ import 'dart:convert'; import 'package:decimal/decimal.dart'; import 'package:flutter/foundation.dart'; +import 'package:tuple/tuple.dart'; +import 'package:uuid/uuid.dart'; + import '../../../exceptions/exchange/exchange_exception.dart'; import '../../../external_api_keys.dart'; import '../../../models/exchange/response_objects/fixed_rate_market.dart'; @@ -20,13 +23,11 @@ import '../../../models/exchange/response_objects/trade.dart'; import '../../../models/exchange/simpleswap/sp_currency.dart'; import '../../../models/isar/exchange_cache/pair.dart'; import '../../../networking/http.dart'; -import '../exchange_response.dart'; -import 'simpleswap_exchange.dart'; -import '../../tor_service.dart'; import '../../../utilities/logger.dart'; import '../../../utilities/prefs.dart'; -import 'package:tuple/tuple.dart'; -import 'package:uuid/uuid.dart'; +import '../../tor_service.dart'; +import '../exchange_response.dart'; +import 'simpleswap_exchange.dart'; class SimpleSwapAPI { static const String scheme = "https"; @@ -104,7 +105,7 @@ class SimpleSwapAPI { String? extraIdTo, String? apiKey, }) async { - Map body = { + final Map body = { "fixed": isFixedRate, "currency_from": currencyFrom, "currency_to": currencyTo, @@ -148,8 +149,10 @@ class SimpleSwapAPI { ); return ExchangeResponse(value: trade, exception: null); } catch (e, s) { - Logging.instance.log("getAvailableCurrencies exception: $e\n$s", - level: LogLevel.Error); + Logging.instance.log( + "getAvailableCurrencies exception: $e\n$s", + level: LogLevel.Error, + ); return ExchangeResponse( exception: ExchangeException( e.toString(), @@ -165,15 +168,19 @@ class SimpleSwapAPI { required bool fixedRate, }) async { final uri = _buildUri( - "/get_all_currencies", {"api_key": apiKey ?? kSimpleSwapApiKey}); + "/get_all_currencies", + {"api_key": apiKey ?? kSimpleSwapApiKey}, + ); try { final jsonArray = await _makeGetRequest(uri); return await compute(_parseAvailableCurrenciesJson, jsonArray as List); } catch (e, s) { - Logging.instance.log("getAvailableCurrencies exception: $e\n$s", - level: LogLevel.Error); + Logging.instance.log( + "getAvailableCurrencies exception: $e\n$s", + level: LogLevel.Error, + ); return ExchangeResponse( exception: ExchangeException( e.toString(), @@ -184,9 +191,10 @@ class SimpleSwapAPI { } ExchangeResponse> _parseAvailableCurrenciesJson( - List jsonArray) { + List jsonArray, + ) { try { - List currencies = []; + final List currencies = []; for (final json in jsonArray) { try { @@ -194,15 +202,20 @@ class SimpleSwapAPI { .add(SPCurrency.fromJson(Map.from(json as Map))); } catch (_) { return ExchangeResponse( - exception: ExchangeException("Failed to serialize $json", - ExchangeExceptionType.serializeResponseError)); + exception: ExchangeException( + "Failed to serialize $json", + ExchangeExceptionType.serializeResponseError, + ), + ); } } return ExchangeResponse(value: currencies); } catch (e, s) { - Logging.instance.log("_parseAvailableCurrenciesJson exception: $e\n$s", - level: LogLevel.Error); + Logging.instance.log( + "_parseAvailableCurrenciesJson exception: $e\n$s", + level: LogLevel.Error, + ); return ExchangeResponse( exception: ExchangeException( e.toString(), @@ -228,8 +241,10 @@ class SimpleSwapAPI { final jsonObject = await _makeGetRequest(uri); return ExchangeResponse( - value: SPCurrency.fromJson( - Map.from(jsonObject as Map))); + value: SPCurrency.fromJson( + Map.from(jsonObject as Map), + ), + ); } catch (e, s) { Logging.instance .log("getCurrency exception: $e\n$s", level: LogLevel.Error); @@ -279,7 +294,7 @@ class SimpleSwapAPI { Tuple2, bool> args, ) { try { - List pairs = []; + final List pairs = []; for (final entry in args.item1.entries) { try { @@ -296,15 +311,20 @@ class SimpleSwapAPI { } } catch (_) { return ExchangeResponse( - exception: ExchangeException("Failed to serialize $json", - ExchangeExceptionType.serializeResponseError)); + exception: ExchangeException( + "Failed to serialize $json", + ExchangeExceptionType.serializeResponseError, + ), + ); } } return ExchangeResponse(value: pairs); } catch (e, s) { - Logging.instance.log("_parseAvailableCurrenciesJson exception: $e\n$s", - level: LogLevel.Error); + Logging.instance.log( + "_parseAvailableCurrenciesJson exception: $e\n$s", + level: LogLevel.Error, + ); return ExchangeResponse( exception: ExchangeException( e.toString(), @@ -468,8 +488,10 @@ class SimpleSwapAPI { ); return result; } catch (e, s) { - Logging.instance.log("getAvailableFixedRateMarkets exception: $e\n$s", - level: LogLevel.Error); + Logging.instance.log( + "getAvailableFixedRateMarkets exception: $e\n$s", + level: LogLevel.Error, + ); return ExchangeResponse( exception: ExchangeException( "Error: $jsonArray", @@ -478,8 +500,10 @@ class SimpleSwapAPI { ); } } catch (e, s) { - Logging.instance.log("getAvailableFixedRateMarkets exception: $e\n$s", - level: LogLevel.Error); + Logging.instance.log( + "getAvailableFixedRateMarkets exception: $e\n$s", + level: LogLevel.Error, + ); return ExchangeResponse( exception: ExchangeException( e.toString(), @@ -490,24 +514,30 @@ class SimpleSwapAPI { } ExchangeResponse> _parseFixedRateMarketsJson( - List jsonArray) { + List jsonArray, + ) { try { final List markets = []; for (final json in jsonArray) { try { final map = Map.from(json as Map); - markets.add(FixedRateMarket( - from: map["currency_from"] as String, - to: map["currency_to"] as String, - min: Decimal.parse(map["min"] as String), - max: Decimal.parse(map["max"] as String), - rate: Decimal.parse(map["rate"] as String), - minerFee: null, - )); + markets.add( + FixedRateMarket( + from: map["currency_from"] as String, + to: map["currency_to"] as String, + min: Decimal.parse(map["min"] as String), + max: Decimal.parse(map["max"] as String), + rate: Decimal.parse(map["rate"] as String), + minerFee: null, + ), + ); } catch (_) { return ExchangeResponse( - exception: ExchangeException("Failed to serialize $json", - ExchangeExceptionType.serializeResponseError)); + exception: ExchangeException( + "Failed to serialize $json", + ExchangeExceptionType.serializeResponseError, + ), + ); } } return ExchangeResponse(value: markets); diff --git a/lib/services/exchange/simpleswap/simpleswap_exchange.dart b/lib/services/exchange/simpleswap/simpleswap_exchange.dart index 13d8029d5..dae14cbf7 100644 --- a/lib/services/exchange/simpleswap/simpleswap_exchange.dart +++ b/lib/services/exchange/simpleswap/simpleswap_exchange.dart @@ -175,7 +175,9 @@ class SimpleSwapExchange extends Exchange { @override Future>> getPairedCurrencies( - String forCurrency, bool fixedRate) { + String forCurrency, + bool fixedRate, + ) { // TODO: implement getPairedCurrencies throw UnimplementedError(); } diff --git a/lib/services/litescribe_api.dart b/lib/services/litescribe_api.dart index a4d9a8a42..9a41f4f2b 100644 --- a/lib/services/litescribe_api.dart +++ b/lib/services/litescribe_api.dart @@ -3,8 +3,8 @@ import 'dart:convert'; import '../dto/ordinals/inscription_data.dart'; import '../dto/ordinals/litescribe_response.dart'; import '../networking/http.dart'; -import 'tor_service.dart'; import '../utilities/prefs.dart'; +import 'tor_service.dart'; class LitescribeAPI { static final LitescribeAPI _instance = LitescribeAPI._internal(); @@ -30,7 +30,8 @@ class LitescribeAPI { return LitescribeResponse(data: _validateJson(response.body)); } else { throw Exception( - 'LitescribeAPI _getResponse exception: Failed to load data'); + 'LitescribeAPI _getResponse exception: Failed to load data', + ); } } @@ -40,18 +41,23 @@ class LitescribeAPI { return parsed; } else { throw const FormatException( - 'LitescribeAPI _validateJson exception: Invalid JSON format'); + 'LitescribeAPI _validateJson exception: Invalid JSON format', + ); } } - Future> getInscriptionsByAddress(String address, - {int cursor = 0, int size = 1000}) async { + Future> getInscriptionsByAddress( + String address, { + int cursor = 0, + int size = 1000, + }) async { // size param determines how many inscriptions are returned per response // default of 1000 is used to cover most addresses (I assume) // if the total number of inscriptions at the address exceeds the length of the list of inscriptions returned, another call with a higher size is made final int defaultLimit = 1000; final response = await _getResponse( - '/address/inscriptions?address=$address&cursor=$cursor&size=$size'); + '/address/inscriptions?address=$address&cursor=$cursor&size=$size', + ); // Check if the number of returned inscriptions equals the limit final int total = response.data['result']['total'] as int; @@ -73,14 +79,16 @@ class LitescribeAPI { try { // Iterate through the list and create InscriptionData objects from each element final List inscriptions = (list as List) - .map((json) => - InscriptionData.fromJson(json as Map)) + .map( + (json) => InscriptionData.fromJson(json as Map), + ) .toList(); return inscriptions; } catch (e) { throw const FormatException( - 'LitescribeAPI getInscriptionsByAddress exception: AddressInscriptionResponse.fromJson failure'); + 'LitescribeAPI getInscriptionsByAddress exception: AddressInscriptionResponse.fromJson failure', + ); } } } diff --git a/lib/services/nano_api.dart b/lib/services/nano_api.dart index c364f1f4a..bc2e2b84d 100644 --- a/lib/services/nano_api.dart +++ b/lib/services/nano_api.dart @@ -18,7 +18,7 @@ class NanoAPI { NAccountInfo? accountInfo; Exception? exception; - HTTP client = HTTP(); + final HTTP client = HTTP(); try { final response = await client.post( @@ -65,7 +65,7 @@ class NanoAPI { required String privateKey, required String work, }) async { - Map block = { + final Map block = { "type": "state", "account": account, "previous": previousBlock, @@ -112,7 +112,7 @@ class NanoAPI { required Uri server, required Map block, }) async { - HTTP client = HTTP(); + final HTTP client = HTTP(); final response = await client.post( url: server, diff --git a/lib/services/notifications_api.dart b/lib/services/notifications_api.dart index f614952fc..a5a62c188 100644 --- a/lib/services/notifications_api.dart +++ b/lib/services/notifications_api.dart @@ -9,9 +9,10 @@ */ import 'package:flutter_local_notifications/flutter_local_notifications.dart'; + import '../models/notification_model.dart'; -import 'notifications_service.dart'; import '../utilities/prefs.dart'; +import 'notifications_service.dart'; class NotificationApi { static final _notifications = FlutterLocalNotificationsPlugin(); @@ -19,11 +20,13 @@ class NotificationApi { static Future _notificationDetails() async { return const NotificationDetails( - android: AndroidNotificationDetails('channel id', 'channel name', - channelDescription: 'channel description', - // importance: Importance.max, - priority: Priority.high, - ticker: 'ticker'), + android: AndroidNotificationDetails( + 'channel id', 'channel name', + channelDescription: 'channel description', + // importance: Importance.max, + priority: Priority.high, + ticker: 'ticker', + ), iOS: DarwinNotificationDetails(), macOS: DarwinNotificationDetails(), ); @@ -33,7 +36,8 @@ class NotificationApi { const android = AndroidInitializationSettings('app_icon_alpha'); const iOS = DarwinInitializationSettings(); const linux = LinuxInitializationSettings( - defaultActionName: "temporary_stack_wallet"); + defaultActionName: "temporary_stack_wallet", + ); const macOS = DarwinInitializationSettings(); const settings = InitializationSettings( android: android, diff --git a/lib/services/trade_sent_from_stack_service.dart b/lib/services/trade_sent_from_stack_service.dart index 84832a345..8c04f4714 100644 --- a/lib/services/trade_sent_from_stack_service.dart +++ b/lib/services/trade_sent_from_stack_service.dart @@ -9,6 +9,7 @@ */ import 'package:flutter/material.dart'; + import '../db/hive/db.dart'; import '../models/trade_wallet_lookup.dart'; @@ -56,9 +57,10 @@ class TradeSentFromStackService extends ChangeNotifier { required TradeWalletLookup tradeWalletLookup, }) async { await DB.instance.put( - boxName: DB.boxNameTradeLookup, - key: tradeWalletLookup.uuid, - value: tradeWalletLookup); + boxName: DB.boxNameTradeLookup, + key: tradeWalletLookup.uuid, + value: tradeWalletLookup, + ); notifyListeners(); } @@ -66,6 +68,8 @@ class TradeSentFromStackService extends ChangeNotifier { required TradeWalletLookup tradeWalletLookup, }) async { await DB.instance.delete( - key: tradeWalletLookup.uuid, boxName: DB.boxNameTradeLookup); + key: tradeWalletLookup.uuid, + boxName: DB.boxNameTradeLookup, + ); } } diff --git a/lib/services/trade_service.dart b/lib/services/trade_service.dart index 7fd3dcf05..6207b102e 100644 --- a/lib/services/trade_service.dart +++ b/lib/services/trade_service.dart @@ -9,15 +9,18 @@ */ import 'package:flutter/cupertino.dart'; + import '../db/hive/db.dart'; import '../models/exchange/response_objects/trade.dart'; class TradesService extends ChangeNotifier { List get trades { final list = DB.instance.values(boxName: DB.boxNameTradesV2); - list.sort((a, b) => - b.timestamp.millisecondsSinceEpoch - - a.timestamp.millisecondsSinceEpoch); + list.sort( + (a, b) => + b.timestamp.millisecondsSinceEpoch - + a.timestamp.millisecondsSinceEpoch, + ); return list; } @@ -61,7 +64,9 @@ class TradesService extends ChangeNotifier { required bool shouldNotifyListeners, }) async { await deleteByUuid( - uuid: trade.uuid, shouldNotifyListeners: shouldNotifyListeners); + uuid: trade.uuid, + shouldNotifyListeners: shouldNotifyListeners, + ); } Future deleteByUuid({ diff --git a/lib/services/transaction_notification_tracker.dart b/lib/services/transaction_notification_tracker.dart index 328ca8753..a100b476e 100644 --- a/lib/services/transaction_notification_tracker.dart +++ b/lib/services/transaction_notification_tracker.dart @@ -17,72 +17,92 @@ class TransactionNotificationTracker { List get pendings { final notifiedPendingTransactions = DB.instance.get( - boxName: walletId, key: "notifiedPendingTransactions") as Map? ?? + boxName: walletId, + key: "notifiedPendingTransactions", + ) as Map? ?? {}; return List.from(notifiedPendingTransactions.keys); } bool wasNotifiedPending(String txid) { final notifiedPendingTransactions = DB.instance.get( - boxName: walletId, key: "notifiedPendingTransactions") as Map? ?? + boxName: walletId, + key: "notifiedPendingTransactions", + ) as Map? ?? {}; return notifiedPendingTransactions[txid] as bool? ?? false; } Future addNotifiedPending(String txid) async { final notifiedPendingTransactions = DB.instance.get( - boxName: walletId, key: "notifiedPendingTransactions") as Map? ?? + boxName: walletId, + key: "notifiedPendingTransactions", + ) as Map? ?? {}; notifiedPendingTransactions[txid] = true; await DB.instance.put( - boxName: walletId, - key: "notifiedPendingTransactions", - value: notifiedPendingTransactions); + boxName: walletId, + key: "notifiedPendingTransactions", + value: notifiedPendingTransactions, + ); } List get confirmeds { final notifiedConfirmedTransactions = DB.instance.get( - boxName: walletId, key: "notifiedConfirmedTransactions") as Map? ?? + boxName: walletId, + key: "notifiedConfirmedTransactions", + ) as Map? ?? {}; return List.from(notifiedConfirmedTransactions.keys); } bool wasNotifiedConfirmed(String txid) { final notifiedConfirmedTransactions = DB.instance.get( - boxName: walletId, key: "notifiedConfirmedTransactions") as Map? ?? + boxName: walletId, + key: "notifiedConfirmedTransactions", + ) as Map? ?? {}; return notifiedConfirmedTransactions[txid] as bool? ?? false; } Future addNotifiedConfirmed(String txid) async { final notifiedConfirmedTransactions = DB.instance.get( - boxName: walletId, key: "notifiedConfirmedTransactions") as Map? ?? + boxName: walletId, + key: "notifiedConfirmedTransactions", + ) as Map? ?? {}; notifiedConfirmedTransactions[txid] = true; await DB.instance.put( - boxName: walletId, - key: "notifiedConfirmedTransactions", - value: notifiedConfirmedTransactions); + boxName: walletId, + key: "notifiedConfirmedTransactions", + value: notifiedConfirmedTransactions, + ); } Future deleteTransaction(String txid) async { final notifiedPendingTransactions = DB.instance.get( - boxName: walletId, key: "notifiedPendingTransactions") as Map? ?? + boxName: walletId, + key: "notifiedPendingTransactions", + ) as Map? ?? {}; final notifiedConfirmedTransactions = DB.instance.get( - boxName: walletId, key: "notifiedConfirmedTransactions") as Map? ?? + boxName: walletId, + key: "notifiedConfirmedTransactions", + ) as Map? ?? {}; notifiedPendingTransactions.remove(txid); notifiedConfirmedTransactions.remove(txid); await DB.instance.put( - boxName: walletId, - key: "notifiedConfirmedTransactions", - value: notifiedConfirmedTransactions); + boxName: walletId, + key: "notifiedConfirmedTransactions", + value: notifiedConfirmedTransactions, + ); await DB.instance.put( - boxName: walletId, - key: "notifiedPendingTransactions", - value: notifiedPendingTransactions); + boxName: walletId, + key: "notifiedPendingTransactions", + value: notifiedPendingTransactions, + ); } } diff --git a/lib/themes/coin_icon_provider.dart b/lib/themes/coin_icon_provider.dart index afb47672f..4a789ad76 100644 --- a/lib/themes/coin_icon_provider.dart +++ b/lib/themes/coin_icon_provider.dart @@ -11,17 +11,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../models/isar/stack_theme.dart'; import 'theme_providers.dart'; -import '../wallets/crypto_currency/coins/bitcoin.dart'; -import '../wallets/crypto_currency/coins/bitcoincash.dart'; -import '../wallets/crypto_currency/coins/dogecoin.dart'; -import '../wallets/crypto_currency/coins/epiccash.dart'; -import '../wallets/crypto_currency/coins/ethereum.dart'; -import '../wallets/crypto_currency/coins/firo.dart'; -import '../wallets/crypto_currency/coins/litecoin.dart'; -import '../wallets/crypto_currency/coins/monero.dart'; -import '../wallets/crypto_currency/coins/namecoin.dart'; -import '../wallets/crypto_currency/coins/particl.dart'; -import '../wallets/crypto_currency/coins/wownero.dart'; import '../wallets/crypto_currency/crypto_currency.dart'; final coinIconProvider = Provider.family((ref, coin) { diff --git a/lib/themes/stack_colors.dart b/lib/themes/stack_colors.dart index 20b60e3b1..29804aa0c 100644 --- a/lib/themes/stack_colors.dart +++ b/lib/themes/stack_colors.dart @@ -9,6 +9,7 @@ */ import 'package:flutter/material.dart'; + import '../models/isar/stack_theme.dart'; class StackColors extends ThemeExtension { @@ -1429,18 +1430,20 @@ class StackColors extends ThemeExtension { t, )!, textFieldDefaultSearchIconRight: Color.lerp( - textFieldDefaultSearchIconRight, - other.textFieldDefaultSearchIconRight, - t)!, + textFieldDefaultSearchIconRight, + other.textFieldDefaultSearchIconRight, + t, + )!, textFieldErrorSearchIconRight: Color.lerp( textFieldErrorSearchIconRight, other.textFieldErrorSearchIconRight, t, )!, textFieldSuccessSearchIconRight: Color.lerp( - textFieldSuccessSearchIconRight, - other.textFieldSuccessSearchIconRight, - t)!, + textFieldSuccessSearchIconRight, + other.textFieldSuccessSearchIconRight, + t, + )!, settingsItem2ActiveBG: Color.lerp( settingsItem2ActiveBG, other.settingsItem2ActiveBG, diff --git a/lib/utilities/address_utils.dart b/lib/utilities/address_utils.dart index 2bb721fe0..97f2ec965 100644 --- a/lib/utilities/address_utils.dart +++ b/lib/utilities/address_utils.dart @@ -11,8 +11,6 @@ import 'dart:convert'; import 'logger.dart'; -import '../wallets/crypto_currency/coins/bitcoincash.dart'; -import '../wallets/crypto_currency/coins/ecash.dart'; import '../wallets/crypto_currency/crypto_currency.dart'; class AddressUtils { @@ -217,8 +215,10 @@ class AddressUtils { try { result = Map.from(jsonDecode(data) as Map); } catch (e) { - Logging.instance.log("Exception caught in parseQRSeedData($data): $e", - level: LogLevel.Error); + Logging.instance.log( + "Exception caught in parseQRSeedData($data): $e", + level: LogLevel.Error, + ); } return result; } diff --git a/lib/utilities/amount/amount.dart b/lib/utilities/amount/amount.dart index e388020e1..a1a68576f 100644 --- a/lib/utilities/amount/amount.dart +++ b/lib/utilities/amount/amount.dart @@ -11,6 +11,7 @@ import 'dart:convert'; import 'package:decimal/decimal.dart'; + import '../util.dart'; class Amount { @@ -168,7 +169,8 @@ class Amount { Amount operator +(Amount other) { if (fractionDigits != other.fractionDigits) { throw ArgumentError( - "fractionDigits do not match: this=$this, other=$other"); + "fractionDigits do not match: this=$this, other=$other", + ); } return Amount( rawValue: raw + other.raw, @@ -179,7 +181,8 @@ class Amount { Amount operator -(Amount other) { if (fractionDigits != other.fractionDigits) { throw ArgumentError( - "fractionDigits do not match: this=$this, other=$other"); + "fractionDigits do not match: this=$this, other=$other", + ); } return Amount( rawValue: raw - other.raw, @@ -190,7 +193,8 @@ class Amount { Amount operator *(Amount other) { if (fractionDigits != other.fractionDigits) { throw ArgumentError( - "fractionDigits do not match: this=$this, other=$other"); + "fractionDigits do not match: this=$this, other=$other", + ); } return Amount( rawValue: raw * other.raw, diff --git a/lib/utilities/amount/amount_input_formatter.dart b/lib/utilities/amount/amount_input_formatter.dart index f09100ad7..2ecd9fe54 100644 --- a/lib/utilities/amount/amount_input_formatter.dart +++ b/lib/utilities/amount/amount_input_formatter.dart @@ -1,8 +1,9 @@ import 'dart:math'; import 'package:flutter/services.dart'; -import 'amount_unit.dart'; + import '../util.dart'; +import 'amount_unit.dart'; class AmountInputFormatter extends TextInputFormatter { final int decimals; @@ -17,7 +18,9 @@ class AmountInputFormatter extends TextInputFormatter { @override TextEditingValue formatEditUpdate( - TextEditingValue oldValue, TextEditingValue newValue) { + TextEditingValue oldValue, + TextEditingValue newValue, + ) { // get number symbols for decimal place and group separator final numberSymbols = Util.getSymbolsFor(locale: locale); diff --git a/lib/utilities/amount/amount_unit.dart b/lib/utilities/amount/amount_unit.dart index 9911e5961..a8cbfa701 100644 --- a/lib/utilities/amount/amount_unit.dart +++ b/lib/utilities/amount/amount_unit.dart @@ -14,9 +14,6 @@ import 'package:decimal/decimal.dart'; import '../../models/isar/models/ethereum/eth_contract.dart'; import 'amount.dart'; import '../util.dart'; -import '../../wallets/crypto_currency/coins/ethereum.dart'; -import '../../wallets/crypto_currency/coins/monero.dart'; -import '../../wallets/crypto_currency/coins/wownero.dart'; import '../../wallets/crypto_currency/crypto_currency.dart'; import '../../wallets/crypto_currency/intermediate/nano_currency.dart'; diff --git a/lib/utilities/biometrics.dart b/lib/utilities/biometrics.dart index 8be9edb24..18e27cbfb 100644 --- a/lib/utilities/biometrics.dart +++ b/lib/utilities/biometrics.dart @@ -10,9 +10,9 @@ import 'dart:io'; -import 'package:flutter/cupertino.dart'; import 'package:local_auth/auth_strings.dart'; import 'package:local_auth/local_auth.dart'; + import 'logger.dart'; class Biometrics { @@ -28,14 +28,16 @@ class Biometrics { }) async { if (!(Platform.isIOS || Platform.isAndroid)) { Logging.instance.log( - "Tried to use Biometrics.authenticate() on a platform that is not Android or iOS! ...returning false.", - level: LogLevel.Error); + "Tried to use Biometrics.authenticate() on a platform that is not Android or iOS! ...returning false.", + level: LogLevel.Error, + ); return false; } if (integrationTestFlag) { Logging.instance.log( - "Tried to use Biometrics.authenticate() during integration testing. Returning false.", - level: LogLevel.Warning); + "Tried to use Biometrics.authenticate() during integration testing. Returning false.", + level: LogLevel.Warning, + ); return false; } @@ -49,7 +51,7 @@ class Biometrics { // debugPrint("isDeviceSupported: $isDeviceSupported"); if (canCheckBiometrics && isDeviceSupported) { - List availableSystems = + final List availableSystems = await localAuth.getAvailableBiometrics(); //todo: check if print needed @@ -59,7 +61,7 @@ class Biometrics { if (Platform.isIOS) { if (availableSystems.contains(BiometricType.face)) { try { - bool didAuthenticate = await localAuth.authenticate( + final bool didAuthenticate = await localAuth.authenticate( biometricOnly: true, localizedReason: localizedReason, stickyAuth: true, @@ -71,12 +73,13 @@ class Biometrics { } } catch (e) { Logging.instance.log( - "local_auth exception caught in Biometrics.authenticate(), e: $e", - level: LogLevel.Error); + "local_auth exception caught in Biometrics.authenticate(), e: $e", + level: LogLevel.Error, + ); } } else if (availableSystems.contains(BiometricType.fingerprint)) { try { - bool didAuthenticate = await localAuth.authenticate( + final bool didAuthenticate = await localAuth.authenticate( biometricOnly: true, localizedReason: localizedReason, stickyAuth: true, @@ -88,14 +91,15 @@ class Biometrics { } } catch (e) { Logging.instance.log( - "local_auth exception caught in Biometrics.authenticate(), e: $e", - level: LogLevel.Error); + "local_auth exception caught in Biometrics.authenticate(), e: $e", + level: LogLevel.Error, + ); } } } else if (Platform.isAndroid) { if (availableSystems.contains(BiometricType.fingerprint)) { try { - bool didAuthenticate = await localAuth.authenticate( + final bool didAuthenticate = await localAuth.authenticate( biometricOnly: true, localizedReason: localizedReason, stickyAuth: true, @@ -111,8 +115,9 @@ class Biometrics { } } catch (e) { Logging.instance.log( - "local_auth exception caught in Biometrics.authenticate(), e: $e", - level: LogLevel.Error); + "local_auth exception caught in Biometrics.authenticate(), e: $e", + level: LogLevel.Error, + ); } } } diff --git a/lib/utilities/bip47_utils.dart b/lib/utilities/bip47_utils.dart index 80b393dff..3d2cdfb41 100644 --- a/lib/utilities/bip47_utils.dart +++ b/lib/utilities/bip47_utils.dart @@ -11,6 +11,7 @@ import 'dart:typed_data'; import 'package:bip47/src/util.dart'; + import '../models/isar/models/blockchain_data/v2/output_v2.dart'; import '../models/isar/models/blockchain_data/v2/transaction_v2.dart'; @@ -19,7 +20,8 @@ abstract class Bip47Utils { static Uint8List? getBlindedPaymentCodeBytesFrom(TransactionV2 transaction) { for (int i = 0; i < transaction.outputs.length; i++) { final bytes = getBlindedPaymentCodeBytesFromOutput( - transaction.outputs.elementAt(i)); + transaction.outputs.elementAt(i), + ); if (bytes != null) { return bytes; } @@ -31,7 +33,7 @@ abstract class Bip47Utils { static Uint8List? getBlindedPaymentCodeBytesFromOutput(OutputV2 output) { Uint8List? blindedCodeBytes; - List? scriptChunks = output.scriptPubKeyAsm?.split(" "); + final List? scriptChunks = output.scriptPubKeyAsm?.split(" "); if (scriptChunks?.length == 2 && scriptChunks?[0] == "OP_RETURN") { final blindedPaymentCode = scriptChunks![1]; final bytes = blindedPaymentCode.fromHex; diff --git a/lib/utilities/connection_check/electrum_connection_check.dart b/lib/utilities/connection_check/electrum_connection_check.dart index a78075246..480434ce3 100644 --- a/lib/utilities/connection_check/electrum_connection_check.dart +++ b/lib/utilities/connection_check/electrum_connection_check.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:electrum_adapter/electrum_adapter.dart'; + import '../../services/event_bus/events/global/tor_connection_status_changed_event.dart'; import '../../services/tor_service.dart'; import '../logger.dart'; @@ -33,7 +34,8 @@ Future checkElectrumServer({ } else { // ... But if the killswitch is set, then we throw an exception. throw Exception( - "Tor preference and killswitch set but Tor is not enabled, not connecting to Electrum adapter"); + "Tor preference and killswitch set but Tor is not enabled, not connecting to Electrum adapter", + ); // TODO [prio=low]: Try to start Tor. } } else { diff --git a/lib/utilities/desktop_password_service.dart b/lib/utilities/desktop_password_service.dart index e563d453e..323be414b 100644 --- a/lib/utilities/desktop_password_service.dart +++ b/lib/utilities/desktop_password_service.dart @@ -10,6 +10,7 @@ import 'package:hive/hive.dart'; import 'package:stack_wallet_backup/secure_storage.dart'; + import 'logger.dart'; const String kBoxNameDesktopData = "desktopData"; @@ -44,7 +45,8 @@ class DPS { StorageCryptoHandler get handler { if (_handler == null) { throw Exception( - "DPS: attempted to access handler without proper authentication"); + "DPS: attempted to access handler without proper authentication", + ); } return _handler!; } @@ -76,7 +78,8 @@ class DPS { Future initFromExisting(String passphrase) async { if (_handler != null) { throw Exception( - "DPS: attempted to re initialize with existing passphrase"); + "DPS: attempted to re initialize with existing passphrase", + ); } try { @@ -84,7 +87,8 @@ class DPS { if (keyBlob == null) { throw Exception( - "DPS: failed to find keyBlob while attempting to initialize with existing passphrase"); + "DPS: failed to find keyBlob while attempting to initialize with existing passphrase", + ); } final blobVersion = await _getStoredKeyBlobVersion(); _handler = await StorageCryptoHandler.fromExisting( diff --git a/lib/utilities/enums/fiat_enum.dart b/lib/utilities/enums/fiat_enum.dart index a563112cf..914a97da4 100644 --- a/lib/utilities/enums/fiat_enum.dart +++ b/lib/utilities/enums/fiat_enum.dart @@ -1161,6 +1161,9 @@ Fiats fiatFromTickerCaseInsensitive(String ticker) { return Fiats.ZWD; default: throw ArgumentError.value( - ticker, "name", "No Fiat enum value with that ticker"); + ticker, + "name", + "No Fiat enum value with that ticker", + ); } } diff --git a/lib/utilities/eth_commons.dart b/lib/utilities/eth_commons.dart index b7f936c58..c105412db 100644 --- a/lib/utilities/eth_commons.dart +++ b/lib/utilities/eth_commons.dart @@ -12,7 +12,6 @@ import 'package:bip32/bip32.dart' as bip32; import 'package:bip39/bip39.dart' as bip39; import 'package:decimal/decimal.dart'; import "package:hex/hex.dart"; -import '../wallets/crypto_currency/coins/ethereum.dart'; import '../wallets/crypto_currency/crypto_currency.dart'; class GasTracker { diff --git a/lib/utilities/extensions/impl/big_int.dart b/lib/utilities/extensions/impl/big_int.dart index d0b84ff98..866c5103d 100644 --- a/lib/utilities/extensions/impl/big_int.dart +++ b/lib/utilities/extensions/impl/big_int.dart @@ -31,7 +31,7 @@ extension BigIntExtensions on BigInt { throw Exception("BigInt value is negative"); } BigInt number = this; - int bytes = (number.bitLength + 7) >> 3; + final int bytes = (number.bitLength + 7) >> 3; final b256 = BigInt.from(256); final result = Uint8List(bytes); for (int i = 0; i < bytes; i++) { diff --git a/lib/utilities/extensions/impl/contract_abi.dart b/lib/utilities/extensions/impl/contract_abi.dart index f34e33970..b067c552e 100644 --- a/lib/utilities/extensions/impl/contract_abi.dart +++ b/lib/utilities/extensions/impl/contract_abi.dart @@ -10,9 +10,10 @@ import 'dart:convert'; -import '../../logger.dart'; import 'package:web3dart/web3dart.dart'; +import '../../logger.dart'; + extension ContractAbiExtensions on ContractAbi { static ContractAbi fromJsonList({ required String name, @@ -112,12 +113,17 @@ extension ContractAbiExtensions on ContractAbi { } } - static CompositeFunctionParameter _parseTuple(String name, String typeName, - List> components) { + static CompositeFunctionParameter _parseTuple( + String name, + String typeName, + List> components, + ) { // The type will have the form tuple[3][]...[1], where the indices after the // tuple indicate that the type is part of an array. - assert(RegExp(r'^tuple(?:\[\d*\])*$').hasMatch(typeName), - '$typeName is an invalid tuple type'); + assert( + RegExp(r'^tuple(?:\[\d*\])*$').hasMatch(typeName), + '$typeName is an invalid tuple type', + ); final arrayLengths = []; var remainingName = typeName; diff --git a/lib/utilities/flutter_secure_storage_interface.dart b/lib/utilities/flutter_secure_storage_interface.dart index 26adefaaa..1c81119ab 100644 --- a/lib/utilities/flutter_secure_storage_interface.dart +++ b/lib/utilities/flutter_secure_storage_interface.dart @@ -11,6 +11,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:isar/isar.dart'; import 'package:stack_wallet_backup/secure_storage.dart'; + import '../models/isar/models/encrypted_string_value.dart'; import 'stack_file_system.dart'; @@ -140,9 +141,11 @@ class SecureStorageWrapper implements SecureStorageInterface { const SecureStorageWrapper({ required dynamic store, required bool isDesktop, - }) : assert(isDesktop - ? store is DesktopSecureStore - : store is FlutterSecureStorage), + }) : assert( + isDesktop + ? store is DesktopSecureStore + : store is FlutterSecureStorage, + ), _store = store, _isDesktop = isDesktop; diff --git a/lib/utilities/format.dart b/lib/utilities/format.dart index b11e98e1e..7a9fe1b69 100644 --- a/lib/utilities/format.dart +++ b/lib/utilities/format.dart @@ -113,7 +113,7 @@ abstract class Format { static String uint8listToString(Uint8List list) { String result = ""; - for (var n in list) { + for (final n in list) { result += (n.toRadixString(16).length == 1 ? "0" : "") + n.toRadixString(16); } @@ -121,7 +121,7 @@ abstract class Format { } static Uint8List stringToUint8List(String string) { - List list = []; + final List list = []; for (var leg = 0; leg < string.length; leg = leg + 2) { list.add(int.parse(string.substring(leg, leg + 2), radix: 16)); } diff --git a/lib/utilities/logger.dart b/lib/utilities/logger.dart index 9574db009..6af09473a 100644 --- a/lib/utilities/logger.dart +++ b/lib/utilities/logger.dart @@ -105,17 +105,17 @@ abstract class Logger { return; } final utcTime = withTimeStamp ? "${core.DateTime.now().toUtc()}: " : ""; - core.int defaultPrintLength = 1020 - utcTime.length; + final core.int defaultPrintLength = 1020 - utcTime.length; if (normalLength) { debugPrint("$utcTime$object"); } else if (object == null || object.toString().length <= defaultPrintLength) { debugPrint("$utcTime$object"); } else { - core.String log = object.toString(); + final core.String log = object.toString(); core.int start = 0; core.int endIndex = defaultPrintLength; - core.int logLength = log.length; + final core.int logLength = log.length; core.int tmpLogLength = log.length; while (endIndex < logLength) { debugPrint(utcTime + log.substring(start, endIndex)); diff --git a/lib/utilities/paynym_is_api.dart b/lib/utilities/paynym_is_api.dart index 1ed94d751..8b42e6143 100644 --- a/lib/utilities/paynym_is_api.dart +++ b/lib/utilities/paynym_is_api.dart @@ -10,6 +10,8 @@ import 'dart:convert'; +import 'package:tuple/tuple.dart'; + import '../models/paynym/created_paynym.dart'; import '../models/paynym/paynym_account.dart'; import '../models/paynym/paynym_claim.dart'; @@ -19,7 +21,6 @@ import '../models/paynym/paynym_unfollow.dart'; import '../networking/http.dart'; import '../services/tor_service.dart'; import 'prefs.dart'; -import 'package:tuple/tuple.dart'; // todo: better error message parsing (from response itself?) @@ -34,13 +35,13 @@ class PaynymIsApi { Map body, [ Map additionalHeaders = const {}, ]) async { - String url = baseURL + + final String url = baseURL + version + (endpoint.startsWith("/") ? endpoint : "/$endpoint"); final uri = Uri.parse(url); // Calculate the body length. - int contentLength = utf8.encode(jsonEncode(body)).length; + final int contentLength = utf8.encode(jsonEncode(body)).length; final headers = { 'Content-Type': 'application/json; charset=UTF-8', @@ -248,8 +249,10 @@ class PaynymIsApi { // | 200 | Nym found and returned | // | 404 | Nym not found | // | 400 | Bad request | - Future> nym(String code, - [bool compact = false]) async { + Future> nym( + String code, [ + bool compact = false, + ]) async { final Map requestBody = {"nym": code}; if (compact) { requestBody["compact"] = true; diff --git a/lib/utilities/stack_file_system.dart b/lib/utilities/stack_file_system.dart index b5f28e48a..3675813d9 100644 --- a/lib/utilities/stack_file_system.dart +++ b/lib/utilities/stack_file_system.dart @@ -42,7 +42,8 @@ abstract class StackFileSystem { appDirectory = Directory(_overrideDesktopDirPath!); } else { appDirectory = Directory( - "${Platform.environment['HOME']}/.${AppConfig.appDefaultDataDirName}"); + "${Platform.environment['HOME']}/.${AppConfig.appDefaultDataDirName}", + ); } } else if (Platform.isWindows) { if (_overrideDesktopDirPath != null) { @@ -56,7 +57,8 @@ abstract class StackFileSystem { } else { appDirectory = await getLibraryDirectory(); appDirectory = Directory( - "${appDirectory.path}/${AppConfig.appDefaultDataDirName}"); + "${appDirectory.path}/${AppConfig.appDefaultDataDirName}", + ); } } else if (Platform.isIOS) { // todo: check if we need different behaviour here diff --git a/lib/utilities/test_epic_box_connection.dart b/lib/utilities/test_epic_box_connection.dart index 471d6fcce..039fa7ec8 100644 --- a/lib/utilities/test_epic_box_connection.dart +++ b/lib/utilities/test_epic_box_connection.dart @@ -17,7 +17,7 @@ import 'logger.dart'; import 'prefs.dart'; Future _testEpicBoxNodeConnection(Uri uri) async { - HTTP client = HTTP(); + final HTTP client = HTTP(); try { final response = await client .get( @@ -27,8 +27,10 @@ Future _testEpicBoxNodeConnection(Uri uri) async { ? TorService.sharedInstance.getProxyInfo() : null, ) - .timeout(const Duration(milliseconds: 2000), - onTimeout: () async => Response(utf8.encode('Error'), 408)); + .timeout( + const Duration(milliseconds: 2000), + onTimeout: () async => Response(utf8.encode('Error'), 408), + ); final json = jsonDecode(response.body); diff --git a/lib/utilities/test_eth_node_connection.dart b/lib/utilities/test_eth_node_connection.dart index f90ae1353..be95a19ce 100644 --- a/lib/utilities/test_eth_node_connection.dart +++ b/lib/utilities/test_eth_node_connection.dart @@ -2,7 +2,7 @@ import 'package:http/http.dart'; import 'package:web3dart/web3dart.dart' as web3; Future testEthNodeConnection(String host) async { - web3.Web3Client client = web3.Web3Client(host, Client()); + final web3.Web3Client client = web3.Web3Client(host, Client()); try { await client.getBlockNumber(); return true; diff --git a/lib/utilities/test_node_connection.dart b/lib/utilities/test_node_connection.dart index c47fb49ac..7dd432140 100644 --- a/lib/utilities/test_node_connection.dart +++ b/lib/utilities/test_node_connection.dart @@ -13,12 +13,6 @@ import 'test_eth_node_connection.dart'; import 'test_monero_node_connection.dart'; import 'test_stellar_node_connection.dart'; import '../wallets/api/tezos/tezos_rpc_api.dart'; -import '../wallets/crypto_currency/coins/bitcoin_frost.dart'; -import '../wallets/crypto_currency/coins/epiccash.dart'; -import '../wallets/crypto_currency/coins/ethereum.dart'; -import '../wallets/crypto_currency/coins/solana.dart'; -import '../wallets/crypto_currency/coins/stellar.dart'; -import '../wallets/crypto_currency/coins/tezos.dart'; import '../wallets/crypto_currency/crypto_currency.dart'; import '../wallets/crypto_currency/interfaces/electrumx_currency_interface.dart'; import '../wallets/crypto_currency/intermediate/cryptonote_currency.dart'; diff --git a/lib/utilities/test_stellar_node_connection.dart b/lib/utilities/test_stellar_node_connection.dart index 7b580e85c..2fd2f9fcc 100644 --- a/lib/utilities/test_stellar_node_connection.dart +++ b/lib/utilities/test_stellar_node_connection.dart @@ -1,13 +1,14 @@ import 'dart:convert'; +import 'package:stellar_flutter_sdk/stellar_flutter_sdk.dart'; + import '../networking/http.dart' as http; import '../services/tor_service.dart'; import 'prefs.dart'; -import 'package:stellar_flutter_sdk/stellar_flutter_sdk.dart'; Future testStellarNodeConnection(String host, int port) async { - http.HTTP client = http.HTTP(); - Uri uri = Uri.parse("$host:$port"); + final http.HTTP client = http.HTTP(); + final Uri uri = Uri.parse("$host:$port"); final response = await client .get( @@ -17,12 +18,14 @@ Future testStellarNodeConnection(String host, int port) async { ? TorService.sharedInstance.getProxyInfo() : null, ) - .timeout(const Duration(milliseconds: 2000), - onTimeout: () async => http.Response(utf8.encode('Error'), 408)); + .timeout( + const Duration(milliseconds: 2000), + onTimeout: () async => http.Response(utf8.encode('Error'), 408), + ); if (response.code == 200) { //Get chain height for sdk - StellarSDK stellarSdk = StellarSDK(host); + final StellarSDK stellarSdk = StellarSDK(host); final height = await stellarSdk.ledgers .order(RequestBuilderOrder.DESC) .limit(1) diff --git a/lib/utilities/util.dart b/lib/utilities/util.dart index cdfc1bfb7..d573d9b90 100644 --- a/lib/utilities/util.dart +++ b/lib/utilities/util.dart @@ -54,14 +54,14 @@ abstract class Util { } static MaterialColor createMaterialColor(Color color) { - List strengths = [.05]; + final List strengths = [.05]; final swatch = {}; final int r = color.red, g = color.green, b = color.blue; for (int i = 1; i < 10; i++) { strengths.add(0.1 * i); } - for (var strength in strengths) { + for (final strength in strengths) { final double ds = 0.5 - strength; swatch[(strength * 1000).round()] = Color.fromRGBO( r + ((ds < 0 ? r : (255 - r)) * ds).round(), diff --git a/lib/wallets/api/lelantus_ffi_wrapper.dart b/lib/wallets/api/lelantus_ffi_wrapper.dart index e6037bedb..d43f504c4 100644 --- a/lib/wallets/api/lelantus_ffi_wrapper.dart +++ b/lib/wallets/api/lelantus_ffi_wrapper.dart @@ -2,6 +2,7 @@ import 'package:bip32/bip32.dart'; import 'package:bitcoindart/bitcoindart.dart' as bitcoindart; import 'package:flutter/foundation.dart'; import 'package:lelantus/lelantus.dart' as lelantus; + import '../../models/isar/models/isar_models.dart' as isar_models; import '../../models/isar/models/isar_models.dart'; import '../../models/lelantus_fee_data.dart'; @@ -70,8 +71,8 @@ abstract final class LelantusFfiWrapper { String partialDerivationPath, }) args, ) async { - List jindexes = []; - List lelantusCoins = []; + final List jindexes = []; + final List lelantusCoins = []; final List spendTxIds = []; int lastFoundIndex = 0; @@ -121,10 +122,12 @@ abstract final class LelantusFfiWrapper { ); final bool isUsed = args.usedSerialNumbers.contains(serialNumber); - lelantusCoins.removeWhere((e) => - e.txid == txId && - e.mintIndex == currentIndex && - e.anonymitySetId != setId); + lelantusCoins.removeWhere( + (e) => + e.txid == txId && + e.mintIndex == currentIndex && + e.anonymitySetId != setId, + ); lelantusCoins.add( isar_models.LelantusCoin( @@ -164,10 +167,12 @@ abstract final class LelantusFfiWrapper { ); final bool isUsed = args.usedSerialNumbers.contains(serialNumber); - lelantusCoins.removeWhere((e) => - e.txid == txId && - e.mintIndex == currentIndex && - e.anonymitySetId != setId); + lelantusCoins.removeWhere( + (e) => + e.txid == txId && + e.mintIndex == currentIndex && + e.anonymitySetId != setId, + ); lelantusCoins.add( isar_models.LelantusCoin( @@ -218,12 +223,13 @@ abstract final class LelantusFfiWrapper { } static Future _estimateJoinSplitFee( - ({ - int spendAmount, - bool subtractFeeFromAmount, - List lelantusEntries, - bool isTestNet, - }) data) async { + ({ + int spendAmount, + bool subtractFeeFromAmount, + List lelantusEntries, + bool isTestNet, + }) data, + ) async { debugPrint("estimateJoinSplit fee"); // for (int i = 0; i < lelantusEntries.length; i++) { // Logging.instance.log(lelantusEntries[i], addToDebugMessagesDB: false); @@ -232,8 +238,8 @@ abstract final class LelantusFfiWrapper { "${data.spendAmount} ${data.subtractFeeFromAmount}", ); - List changeToMint = List.empty(growable: true); - List spendCoinIndexes = List.empty(growable: true); + final List changeToMint = List.empty(growable: true); + final List spendCoinIndexes = List.empty(growable: true); // Logging.instance.log(lelantusEntries, addToDebugMessagesDB: false); final fee = lelantus.estimateFee( data.spendAmount, @@ -286,18 +292,19 @@ abstract final class LelantusFfiWrapper { } static Future _createJoinSplitTransaction( - ({ - TxData txData, - bool subtractFeeFromAmount, - int index, - List lelantusEntries, - int locktime, - Bip39HDCurrency cryptoCurrency, - List> anonymitySetsArg, - String partialDerivationPath, - String hexRootPrivateKey, - Uint8List chaincode, - }) arg) async { + ({ + TxData txData, + bool subtractFeeFromAmount, + int index, + List lelantusEntries, + int locktime, + Bip39HDCurrency cryptoCurrency, + List> anonymitySetsArg, + String partialDerivationPath, + String hexRootPrivateKey, + Uint8List chaincode, + }) arg, + ) async { final spendAmount = arg.txData.recipients!.first.amount.raw.toInt(); final address = arg.txData.recipients!.first.address; final isChange = arg.txData.recipients!.first.isChange; @@ -401,12 +408,13 @@ abstract final class LelantusFfiWrapper { if (!setIds.contains(anonymitySetId)) { setIds.add(anonymitySetId); final anonymitySet = arg.anonymitySetsArg.firstWhere( - (element) => element["setId"] == anonymitySetId, - orElse: () => {}); + (element) => element["setId"] == anonymitySetId, + orElse: () => {}, + ); if (anonymitySet.isNotEmpty) { anonymitySetHashes.add(anonymitySet['setHash'] as String); groupBlockHashes.add(anonymitySet['blockHash'] as String); - List list = []; + final List list = []; for (int i = 0; i < (anonymitySet['coins'] as List).length; i++) { list.add(anonymitySet['coins'][i][0] as String); } @@ -447,7 +455,8 @@ abstract final class LelantusFfiWrapper { final extTx = finalTx.buildIncomplete(); extTx.addInput( Format.stringToUint8List( - '0000000000000000000000000000000000000000000000000000000000000000'), + '0000000000000000000000000000000000000000000000000000000000000000', + ), 4294967295, 4294967295, Format.stringToUint8List("c9"), @@ -467,7 +476,7 @@ abstract final class LelantusFfiWrapper { txid: txId, raw: txHex, recipients: [ - (address: address, amount: amountAmount, isChange: isChange) + (address: address, amount: amountAmount, isChange: isChange), ], fee: Amount( rawValue: BigInt.from(fee), @@ -509,14 +518,15 @@ abstract final class LelantusFfiWrapper { // =========================================================================== static Future _getMintScriptWrapper( - ({ - int amount, - String privateKeyHex, - int index, - String seedId, - bool isTestNet - }) data) async { - String mintHex = lelantus.getMintScript( + ({ + int amount, + String privateKeyHex, + int index, + String seedId, + bool isTestNet + }) data, + ) async { + final String mintHex = lelantus.getMintScript( data.amount, data.privateKeyHex, data.index, diff --git a/lib/wallets/api/tezos/tezos_api.dart b/lib/wallets/api/tezos/tezos_api.dart index 754fc17aa..abe126ac3 100644 --- a/lib/wallets/api/tezos/tezos_api.dart +++ b/lib/wallets/api/tezos/tezos_api.dart @@ -33,8 +33,10 @@ abstract final class TezosAPI { } } - static Future getAccount(String address, - {String type = "user"}) async { + static Future getAccount( + String address, { + String type = "user", + }) async { try { final uriString = "$_baseURL/v1/accounts/$address?legacy=false"; final response = await _client.get( @@ -76,8 +78,8 @@ abstract final class TezosAPI { final result = jsonDecode(response.body) as List; - List txs = []; - for (var tx in result) { + final List txs = []; + for (final tx in result) { if (tx["type"] == "transaction") { final theTx = TezosTransaction( id: tx["id"] as int, diff --git a/lib/wallets/api/tezos/tezos_rpc_api.dart b/lib/wallets/api/tezos/tezos_rpc_api.dart index 201cd3b9b..1497a46c0 100644 --- a/lib/wallets/api/tezos/tezos_rpc_api.dart +++ b/lib/wallets/api/tezos/tezos_rpc_api.dart @@ -13,7 +13,7 @@ abstract final class TezosRpcAPI { required String address, }) async { try { - String balanceCall = + final String balanceCall = "${nodeInfo.host}:${nodeInfo.port}/chains/main/blocks/head/context/contracts/$address/balance"; final response = await _client.get( diff --git a/lib/wallets/crypto_currency/coins/bitcoincash.dart b/lib/wallets/crypto_currency/coins/bitcoincash.dart index e713dd417..25e18f6a3 100644 --- a/lib/wallets/crypto_currency/coins/bitcoincash.dart +++ b/lib/wallets/crypto_currency/coins/bitcoincash.dart @@ -4,6 +4,7 @@ import 'package:bech32/bech32.dart'; import 'package:bitbox/bitbox.dart' as bitbox; import 'package:bs58check/bs58check.dart' as bs58check; import 'package:coinlib_flutter/coinlib_flutter.dart' as coinlib; + import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/node_model.dart'; import '../../../utilities/amount/amount.dart'; @@ -133,7 +134,8 @@ class Bitcoincash extends Bip39HDCurrency with ElectrumXCurrencyInterface { final addr = coinlib.Address.fromString(address, networkParams); return Bip39HDCurrency.convertBytesToScriptHash( - addr.program.script.compiled); + addr.program.script.compiled, + ); } catch (e) { rethrow; } @@ -159,7 +161,8 @@ class Bitcoincash extends Bip39HDCurrency with ElectrumXCurrencyInterface { break; default: throw Exception( - "DerivePathType $derivePathType not supported for coinType"); + "DerivePathType $derivePathType not supported for coinType", + ); } break; case 0xef: diff --git a/lib/wallets/crypto_currency/coins/ecash.dart b/lib/wallets/crypto_currency/coins/ecash.dart index bf173fe67..f20b9a52c 100644 --- a/lib/wallets/crypto_currency/coins/ecash.dart +++ b/lib/wallets/crypto_currency/coins/ecash.dart @@ -4,6 +4,7 @@ import 'package:bech32/bech32.dart'; import 'package:bitbox/bitbox.dart' as bitbox; import 'package:bs58check/bs58check.dart' as bs58check; import 'package:coinlib_flutter/coinlib_flutter.dart' as coinlib; + import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/node_model.dart'; import '../../../utilities/amount/amount.dart'; @@ -128,7 +129,8 @@ class Ecash extends Bip39HDCurrency with ElectrumXCurrencyInterface { final addr = coinlib.Address.fromString(address, networkParams); return Bip39HDCurrency.convertBytesToScriptHash( - addr.program.script.compiled); + addr.program.script.compiled, + ); } catch (e) { rethrow; } @@ -153,12 +155,14 @@ class Ecash extends Bip39HDCurrency with ElectrumXCurrencyInterface { break; default: throw Exception( - "DerivePathType $derivePathType not supported for coinType"); + "DerivePathType $derivePathType not supported for coinType", + ); } break; case 0xef: // testnet wif throw Exception( - "DerivePathType $derivePathType not supported for coinType"); + "DerivePathType $derivePathType not supported for coinType", + ); default: throw Exception("Invalid ECash network wif used!"); } diff --git a/lib/wallets/crypto_currency/coins/namecoin.dart b/lib/wallets/crypto_currency/coins/namecoin.dart index 557644b84..daca04916 100644 --- a/lib/wallets/crypto_currency/coins/namecoin.dart +++ b/lib/wallets/crypto_currency/coins/namecoin.dart @@ -1,4 +1,5 @@ import 'package:coinlib_flutter/coinlib_flutter.dart' as coinlib; + import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/node_model.dart'; import '../../../utilities/amount/amount.dart'; @@ -130,9 +131,10 @@ class Namecoin extends Bip39HDCurrency with ElectrumXCurrencyInterface { } @override - ({coinlib.Address address, AddressType addressType}) getAddressForPublicKey( - {required coinlib.ECPublicKey publicKey, - required DerivePathType derivePathType}) { + ({coinlib.Address address, AddressType addressType}) getAddressForPublicKey({ + required coinlib.ECPublicKey publicKey, + required DerivePathType derivePathType, + }) { switch (derivePathType) { // case DerivePathType.bip16: diff --git a/lib/wallets/crypto_currency/coins/particl.dart b/lib/wallets/crypto_currency/coins/particl.dart index 9fb53ab1f..382602e7a 100644 --- a/lib/wallets/crypto_currency/coins/particl.dart +++ b/lib/wallets/crypto_currency/coins/particl.dart @@ -1,4 +1,5 @@ import 'package:coinlib_flutter/coinlib_flutter.dart' as coinlib; + import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/node_model.dart'; import '../../../utilities/amount/amount.dart'; @@ -51,11 +52,12 @@ class Particl extends Bip39HDCurrency with ElectrumXCurrencyInterface { @override // See https://github.com/cypherstack/stack_wallet/blob/d08b5c9b22b58db800ad07b2ceeb44c6d05f9cf3/lib/services/coins/particl/particl_wallet.dart#L68 - String constructDerivePath( - {required DerivePathType derivePathType, - int account = 0, - required int chain, - required int index}) { + String constructDerivePath({ + required DerivePathType derivePathType, + int account = 0, + required int chain, + required int index, + }) { String coinType; switch (networkParams.wifPrefix) { case 0x6c: // PART mainnet wif. diff --git a/lib/wallets/crypto_currency/coins/peercoin.dart b/lib/wallets/crypto_currency/coins/peercoin.dart index 3287b38ed..0edc1cbfa 100644 --- a/lib/wallets/crypto_currency/coins/peercoin.dart +++ b/lib/wallets/crypto_currency/coins/peercoin.dart @@ -1,5 +1,6 @@ import 'package:coinlib/src/network.dart'; import 'package:coinlib_flutter/coinlib_flutter.dart' as coinlib; + import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/node_model.dart'; import '../../../utilities/amount/amount.dart'; @@ -142,9 +143,10 @@ class Peercoin extends Bip39HDCurrency with ElectrumXCurrencyInterface { } @override - ({coinlib.Address address, AddressType addressType}) getAddressForPublicKey( - {required coinlib.ECPublicKey publicKey, - required DerivePathType derivePathType}) { + ({coinlib.Address address, AddressType addressType}) getAddressForPublicKey({ + required coinlib.ECPublicKey publicKey, + required DerivePathType derivePathType, + }) { switch (derivePathType) { // case DerivePathType.bip16: diff --git a/lib/wallets/crypto_currency/coins/solana.dart b/lib/wallets/crypto_currency/coins/solana.dart index 96ff3f2ae..7ae69ed43 100644 --- a/lib/wallets/crypto_currency/coins/solana.dart +++ b/lib/wallets/crypto_currency/coins/solana.dart @@ -1,4 +1,5 @@ import 'package:solana/solana.dart'; + import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/node_model.dart'; import '../../../utilities/default_nodes.dart'; @@ -70,7 +71,8 @@ class Solana extends Bip39Currency { @override bool validateAddress(String address) { return isPointOnEd25519Curve( - Ed25519HDPublicKey.fromBase58(address).toByteArray()); + Ed25519HDPublicKey.fromBase58(address).toByteArray(), + ); } @override diff --git a/lib/wallets/crypto_currency/coins/stellar.dart b/lib/wallets/crypto_currency/coins/stellar.dart index dfcb37fce..e9749c515 100644 --- a/lib/wallets/crypto_currency/coins/stellar.dart +++ b/lib/wallets/crypto_currency/coins/stellar.dart @@ -120,7 +120,8 @@ class Stellar extends Bip39Currency { @override DerivePathType get primaryDerivePathType => throw UnsupportedError( - "$runtimeType does not use bitcoin style derivation paths",); + "$runtimeType does not use bitcoin style derivation paths", + ); @override Uri defaultBlockExplorer(String txid) { diff --git a/lib/wallets/crypto_currency/coins/tezos.dart b/lib/wallets/crypto_currency/coins/tezos.dart index 0cf344993..96f2acc0d 100644 --- a/lib/wallets/crypto_currency/coins/tezos.dart +++ b/lib/wallets/crypto_currency/coins/tezos.dart @@ -3,14 +3,15 @@ import 'dart:typed_data'; import 'package:bip39/bip39.dart' as bip39; import 'package:coinlib_flutter/coinlib_flutter.dart'; +import 'package:tezart/src/crypto/crypto.dart'; +import 'package:tezart/tezart.dart'; + import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/node_model.dart'; import '../../../utilities/default_nodes.dart'; import '../../../utilities/enums/derive_path_type_enum.dart'; import '../crypto_currency.dart'; import '../intermediate/bip39_currency.dart'; -import 'package:tezart/src/crypto/crypto.dart'; -import 'package:tezart/tezart.dart'; class Tezos extends Bip39Currency { Tezos(super.network) { @@ -128,12 +129,15 @@ class Tezos extends Bip39Currency { // =========== Private ======================================================= static ({Uint8List privateKey, Uint8List chainCode}) _deriveRootNode( - Uint8List seed) { + Uint8List seed, + ) { return _deriveNode(seed, Uint8List.fromList(utf8.encode("ed25519 seed"))); } static ({Uint8List privateKey, Uint8List chainCode}) _deriveNode( - Uint8List msg, Uint8List key) { + Uint8List msg, + Uint8List key, + ) { final hMac = hmacSha512(key, msg); final privateKey = hMac.sublist(0, 32); final chainCode = hMac.sublist(32); @@ -141,11 +145,13 @@ class Tezos extends Bip39Currency { } static ({Uint8List privateKey, Uint8List chainCode}) _deriveChildNode( - ({Uint8List privateKey, Uint8List chainCode}) node, int index) { - Uint8List indexBuf = Uint8List(4); + ({Uint8List privateKey, Uint8List chainCode}) node, + int index, + ) { + final Uint8List indexBuf = Uint8List(4); ByteData.view(indexBuf.buffer).setUint32(0, index, Endian.big); - Uint8List message = Uint8List.fromList([ + final Uint8List message = Uint8List.fromList([ Uint8List(1)[0], ...node.privateKey, ...indexBuf, diff --git a/lib/wallets/isar/models/wallet_info.dart b/lib/wallets/isar/models/wallet_info.dart index 60a7a1b30..a84fe4348 100644 --- a/lib/wallets/isar/models/wallet_info.dart +++ b/lib/wallets/isar/models/wallet_info.dart @@ -81,7 +81,9 @@ class WalletInfo implements IsarId { return Balance.zeroFor(currency: coin); } else { return Balance.fromJson( - cachedBalanceSecondaryString!, coin.fractionDigits); + cachedBalanceSecondaryString!, + coin.fractionDigits, + ); } } @@ -92,7 +94,9 @@ class WalletInfo implements IsarId { return Balance.zeroFor(currency: coin); } else { return Balance.fromJson( - cachedBalanceTertiaryString!, coin.fractionDigits); + cachedBalanceTertiaryString!, + coin.fractionDigits, + ); } } diff --git a/lib/wallets/isar/providers/eth/token_balance_provider.dart b/lib/wallets/isar/providers/eth/token_balance_provider.dart index 741092387..9a4f054a9 100644 --- a/lib/wallets/isar/providers/eth/token_balance_provider.dart +++ b/lib/wallets/isar/providers/eth/token_balance_provider.dart @@ -1,5 +1,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:isar/isar.dart'; + import '../../../../models/balance.dart'; import '../../../../models/isar/models/isar_models.dart'; import '../../../../providers/db/main_db_provider.dart'; @@ -53,7 +54,10 @@ final pTokenWalletInfo = Provider.family( (ref, data) { - return ref.watch(_twiProvider(data).select( - (value) => (value.value as TokenWalletInfo).getCachedBalance())); + return ref.watch( + _twiProvider(data).select( + (value) => (value.value as TokenWalletInfo).getCachedBalance(), + ), + ); }, ); diff --git a/lib/wallets/isar/providers/wallet_info_provider.dart b/lib/wallets/isar/providers/wallet_info_provider.dart index a3ec30e49..b4247dc6f 100644 --- a/lib/wallets/isar/providers/wallet_info_provider.dart +++ b/lib/wallets/isar/providers/wallet_info_provider.dart @@ -1,5 +1,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:isar/isar.dart'; + import '../../../models/balance.dart'; import '../../../providers/db/main_db_provider.dart'; import '../../crypto_currency/crypto_currency.dart'; @@ -48,7 +49,8 @@ final pWalletBalanceSecondary = Provider.family( (ref, walletId) { return ref.watch( _wiProvider(walletId).select( - (value) => (value.value as WalletInfo).cachedBalanceSecondary), + (value) => (value.value as WalletInfo).cachedBalanceSecondary, + ), ); }, ); @@ -92,7 +94,8 @@ final pWalletReceivingAddress = Provider.family( (ref, walletId) { return ref.watch( _wiProvider(walletId).select( - (value) => (value.value as WalletInfo).cachedReceivingAddress), + (value) => (value.value as WalletInfo).cachedReceivingAddress, + ), ); }, ); @@ -101,7 +104,8 @@ final pWalletTokenAddresses = Provider.family, String>( (ref, walletId) { return ref.watch( _wiProvider(walletId).select( - (value) => (value.value as WalletInfo).tokenContractAddresses), + (value) => (value.value as WalletInfo).tokenContractAddresses, + ), ); }, ); diff --git a/lib/wallets/wallet/impl/banano_wallet.dart b/lib/wallets/wallet/impl/banano_wallet.dart index 044e31efd..6f6017322 100644 --- a/lib/wallets/wallet/impl/banano_wallet.dart +++ b/lib/wallets/wallet/impl/banano_wallet.dart @@ -1,4 +1,3 @@ -import '../../crypto_currency/coins/banano.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../crypto_currency/intermediate/nano_currency.dart'; import '../../isar/models/wallet_info.dart'; diff --git a/lib/wallets/wallet/impl/bitcoin_frost_wallet.dart b/lib/wallets/wallet/impl/bitcoin_frost_wallet.dart index 953a1f0cf..d41d3d4c2 100644 --- a/lib/wallets/wallet/impl/bitcoin_frost_wallet.dart +++ b/lib/wallets/wallet/impl/bitcoin_frost_wallet.dart @@ -21,7 +21,6 @@ import '../../../services/frost.dart'; import '../../../utilities/amount/amount.dart'; import '../../../utilities/extensions/extensions.dart'; import '../../../utilities/logger.dart'; -import '../../crypto_currency/coins/bitcoin_frost.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../crypto_currency/intermediate/frost_currency.dart'; import '../../isar/models/frost_wallet_info.dart'; diff --git a/lib/wallets/wallet/impl/bitcoin_wallet.dart b/lib/wallets/wallet/impl/bitcoin_wallet.dart index 207ca30fc..5487d8083 100644 --- a/lib/wallets/wallet/impl/bitcoin_wallet.dart +++ b/lib/wallets/wallet/impl/bitcoin_wallet.dart @@ -1,7 +1,7 @@ import 'package:isar/isar.dart'; + import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../utilities/amount/amount.dart'; -import '../../crypto_currency/coins/bitcoin.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../crypto_currency/interfaces/paynym_currency_interface.dart'; import '../intermediate/bip39_hd_wallet.dart'; @@ -48,8 +48,9 @@ class BitcoinWallet extends Bip39HDWallet Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) { return Amount( rawValue: BigInt.from( - ((42 + (272 * inputCount) + (128 * outputCount)) / 4).ceil() * - (feeRatePerKB / 1000).ceil()), + ((42 + (272 * inputCount) + (128 * outputCount)) / 4).ceil() * + (feeRatePerKB / 1000).ceil(), + ), fractionDigits: cryptoCurrency.fractionDigits, ); } diff --git a/lib/wallets/wallet/impl/bitcoincash_wallet.dart b/lib/wallets/wallet/impl/bitcoincash_wallet.dart index 45a81afd2..b00f650f2 100644 --- a/lib/wallets/wallet/impl/bitcoincash_wallet.dart +++ b/lib/wallets/wallet/impl/bitcoincash_wallet.dart @@ -1,18 +1,17 @@ import 'package:bitbox/bitbox.dart' as bitbox; import 'package:isar/isar.dart'; + import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/isar/models/blockchain_data/transaction.dart'; import '../../../models/isar/models/blockchain_data/v2/input_v2.dart'; import '../../../models/isar/models/blockchain_data/v2/output_v2.dart'; import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; import '../../../services/coins/bitcoincash/bch_utils.dart'; -import '../../../services/coins/bitcoincash/cashtokens.dart' - as cash_tokens; +import '../../../services/coins/bitcoincash/cashtokens.dart' as cash_tokens; import '../../../utilities/amount/amount.dart'; import '../../../utilities/enums/derive_path_type_enum.dart'; import '../../../utilities/extensions/extensions.dart'; import '../../../utilities/logger.dart'; -import '../../crypto_currency/coins/bitcoincash.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../crypto_currency/interfaces/electrumx_currency_interface.dart'; import '../intermediate/bip39_hd_wallet.dart'; @@ -76,10 +75,12 @@ class BitcoincashWallet .not() .typeEqualTo(AddressType.nonWallet) .and() - .group((q) => q - .subTypeEqualTo(AddressSubType.receiving) - .or() - .subTypeEqualTo(AddressSubType.change)) + .group( + (q) => q + .subTypeEqualTo(AddressSubType.receiving) + .or() + .subTypeEqualTo(AddressSubType.change), + ) .findAll(); return allAddresses; } @@ -100,14 +101,15 @@ class BitcoincashWallet @override Future updateTransactions() async { - List
allAddressesOld = await fetchAddressesForElectrumXScan(); + final List
allAddressesOld = + await fetchAddressesForElectrumXScan(); - Set receivingAddresses = allAddressesOld + final Set receivingAddresses = allAddressesOld .where((e) => e.subType == AddressSubType.receiving) .map((e) => convertAddressString(e.value)) .toSet(); - Set changeAddresses = allAddressesOld + final Set changeAddresses = allAddressesOld .where((e) => e.subType == AddressSubType.change) .map((e) => convertAddressString(e.value)) .toSet(); @@ -117,7 +119,7 @@ class BitcoincashWallet final List> allTxHashes = await fetchHistory(allAddressesSet); - List> allTransactions = []; + final List> allTransactions = []; for (final txHash in allTxHashes) { // final storedTx = await mainDB.isar.transactionV2s @@ -177,8 +179,9 @@ class BitcoincashWallet try { final prevOutJson = Map.from( - (inputTx["vout"] as List).firstWhere((e) => e["n"] == vout) - as Map); + (inputTx["vout"] as List).firstWhere((e) => e["n"] == vout) + as Map, + ); final prevOut = OutputV2.fromElectrumXJson( prevOutJson, decimalPlaces: cryptoCurrency.fractionDigits, @@ -194,8 +197,9 @@ class BitcoincashWallet addresses.addAll(prevOut.addresses); } catch (e, s) { Logging.instance.log( - "Error getting prevOutJson: $e\nStack trace: $s", - level: LogLevel.Warning); + "Error getting prevOutJson: $e\nStack trace: $s", + level: LogLevel.Warning, + ); } } @@ -360,8 +364,10 @@ class BitcoincashWallet @override Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) { return Amount( - rawValue: BigInt.from(((181 * inputCount) + (34 * outputCount) + 10) * - (feeRatePerKB / 1000).ceil()), + rawValue: BigInt.from( + ((181 * inputCount) + (34 * outputCount) + 10) * + (feeRatePerKB / 1000).ceil(), + ), fractionDigits: info.coin.fractionDigits, ); } diff --git a/lib/wallets/wallet/impl/dogecoin_wallet.dart b/lib/wallets/wallet/impl/dogecoin_wallet.dart index f91cda4e5..2c2dcd309 100644 --- a/lib/wallets/wallet/impl/dogecoin_wallet.dart +++ b/lib/wallets/wallet/impl/dogecoin_wallet.dart @@ -1,4 +1,5 @@ import 'package:isar/isar.dart'; + import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/isar/models/blockchain_data/transaction.dart'; import '../../../models/isar/models/blockchain_data/v2/input_v2.dart'; @@ -7,7 +8,6 @@ import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; import '../../../utilities/amount/amount.dart'; import '../../../utilities/extensions/extensions.dart'; import '../../../utilities/logger.dart'; -import '../../crypto_currency/coins/dogecoin.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../crypto_currency/interfaces/electrumx_currency_interface.dart'; import '../intermediate/bip39_hd_wallet.dart'; @@ -55,14 +55,15 @@ class DogecoinWallet @override Future updateTransactions() async { // Get all addresses. - List
allAddressesOld = await fetchAddressesForElectrumXScan(); + final List
allAddressesOld = + await fetchAddressesForElectrumXScan(); // Separate receiving and change addresses. - Set receivingAddresses = allAddressesOld + final Set receivingAddresses = allAddressesOld .where((e) => e.subType == AddressSubType.receiving) .map((e) => e.value) .toSet(); - Set changeAddresses = allAddressesOld + final Set changeAddresses = allAddressesOld .where((e) => e.subType == AddressSubType.change) .map((e) => e.value) .toSet(); @@ -75,7 +76,7 @@ class DogecoinWallet await fetchHistory(allAddressesSet); // Only parse new txs (not in db yet). - List> allTransactions = []; + final List> allTransactions = []; for (final txHash in allTxHashes) { // Check for duplicates by searching for tx by tx_hash in db. final storedTx = await mainDB.isar.transactionV2s @@ -136,8 +137,8 @@ class DogecoinWallet ); final prevOutJson = Map.from( - (inputTx["vout"] as List).firstWhere((e) => e["n"] == vout) - as Map); + (inputTx["vout"] as List).firstWhere((e) => e["n"] == vout) as Map, + ); final prevOut = OutputV2.fromElectrumXJson( prevOutJson, @@ -211,7 +212,7 @@ class DogecoinWallet .fold(BigInt.zero, (value, element) => value + element); TransactionType type; - TransactionSubType subType = TransactionSubType.none; + final TransactionSubType subType = TransactionSubType.none; // At least one input was owned by this wallet. if (wasSentFromThisWallet) { @@ -277,7 +278,7 @@ class DogecoinWallet // check for bip47 notification final outputs = jsonTX["vout"] as List; for (final output in outputs) { - List? scriptChunks = + final List? scriptChunks = (output['scriptPubKey']?['asm'] as String?)?.split(" "); if (scriptChunks?.length == 2 && scriptChunks?[0] == "OP_RETURN") { final blindedPaymentCode = scriptChunks![1]; @@ -300,8 +301,10 @@ class DogecoinWallet @override Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) { return Amount( - rawValue: BigInt.from(((181 * inputCount) + (34 * outputCount) + 10) * - (feeRatePerKB / 1000).ceil()), + rawValue: BigInt.from( + ((181 * inputCount) + (34 * outputCount) + 10) * + (feeRatePerKB / 1000).ceil(), + ), fractionDigits: cryptoCurrency.fractionDigits, ); } diff --git a/lib/wallets/wallet/impl/ecash_wallet.dart b/lib/wallets/wallet/impl/ecash_wallet.dart index 2a0eeb1e8..53c0f895b 100644 --- a/lib/wallets/wallet/impl/ecash_wallet.dart +++ b/lib/wallets/wallet/impl/ecash_wallet.dart @@ -1,18 +1,17 @@ import 'package:bitbox/bitbox.dart' as bitbox; import 'package:isar/isar.dart'; + import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/isar/models/blockchain_data/transaction.dart'; import '../../../models/isar/models/blockchain_data/v2/input_v2.dart'; import '../../../models/isar/models/blockchain_data/v2/output_v2.dart'; import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; import '../../../services/coins/bitcoincash/bch_utils.dart'; -import '../../../services/coins/bitcoincash/cashtokens.dart' - as cash_tokens; +import '../../../services/coins/bitcoincash/cashtokens.dart' as cash_tokens; import '../../../utilities/amount/amount.dart'; import '../../../utilities/enums/derive_path_type_enum.dart'; import '../../../utilities/extensions/extensions.dart'; import '../../../utilities/logger.dart'; -import '../../crypto_currency/coins/ecash.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../crypto_currency/interfaces/electrumx_currency_interface.dart'; import '../intermediate/bip39_hd_wallet.dart'; @@ -92,14 +91,15 @@ class EcashWallet extends Bip39HDWallet @override Future updateTransactions() async { - List
allAddressesOld = await fetchAddressesForElectrumXScan(); + final List
allAddressesOld = + await fetchAddressesForElectrumXScan(); - Set receivingAddresses = allAddressesOld + final Set receivingAddresses = allAddressesOld .where((e) => e.subType == AddressSubType.receiving) .map((e) => convertAddressString(e.value)) .toSet(); - Set changeAddresses = allAddressesOld + final Set changeAddresses = allAddressesOld .where((e) => e.subType == AddressSubType.change) .map((e) => convertAddressString(e.value)) .toSet(); @@ -109,7 +109,7 @@ class EcashWallet extends Bip39HDWallet final List> allTxHashes = await fetchHistory(allAddressesSet); - List> allTransactions = []; + final List> allTransactions = []; for (final txHash in allTxHashes) { final storedTx = await mainDB.isar.transactionV2s @@ -168,8 +168,8 @@ class EcashWallet extends Bip39HDWallet ); final prevOutJson = Map.from( - (inputTx["vout"] as List).firstWhere((e) => e["n"] == vout) - as Map); + (inputTx["vout"] as List).firstWhere((e) => e["n"] == vout) as Map, + ); final prevOut = OutputV2.fromElectrumXJson( prevOutJson, @@ -351,8 +351,10 @@ class EcashWallet extends Bip39HDWallet @override Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) { return Amount( - rawValue: BigInt.from(((181 * inputCount) + (34 * outputCount) + 10) * - (feeRatePerKB / 1000).ceil()), + rawValue: BigInt.from( + ((181 * inputCount) + (34 * outputCount) + 10) * + (feeRatePerKB / 1000).ceil(), + ), fractionDigits: info.coin.fractionDigits, ); } diff --git a/lib/wallets/wallet/impl/epiccash_wallet.dart b/lib/wallets/wallet/impl/epiccash_wallet.dart index 62b43ff1d..9df6a936a 100644 --- a/lib/wallets/wallet/impl/epiccash_wallet.dart +++ b/lib/wallets/wallet/impl/epiccash_wallet.dart @@ -9,6 +9,8 @@ import 'package:flutter_libepiccash/models/transaction.dart' as epic_models; import 'package:isar/isar.dart'; import 'package:mutex/mutex.dart'; import 'package:stack_wallet_backup/generate_password.dart'; +import 'package:web_socket_channel/web_socket_channel.dart'; + import '../../../models/balance.dart'; import '../../../models/epicbox_config_model.dart'; import '../../../models/isar/models/blockchain_data/address.dart'; @@ -30,12 +32,10 @@ import '../../../utilities/flutter_secure_storage_interface.dart'; import '../../../utilities/logger.dart'; import '../../../utilities/stack_file_system.dart'; import '../../../utilities/test_epic_box_connection.dart'; -import '../../crypto_currency/coins/epiccash.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../models/tx_data.dart'; import '../intermediate/bip39_wallet.dart'; import '../supporting/epiccash_wallet_info_extension.dart'; -import 'package:web_socket_channel/web_socket_channel.dart'; // // refactor of https://github.com/cypherstack/stack_wallet/blob/1d9fb4cd069f22492ece690ac788e05b8f8b1209/lib/services/coins/epiccash/epiccash_wallet.dart @@ -493,11 +493,17 @@ class EpiccashWallet extends Bip39Wallet { final EpicBoxConfigModel epicboxConfig = await getEpicBoxConfig(); await secureStorageInterface.write( - key: '${walletId}_config', value: stringConfig); + key: '${walletId}_config', + value: stringConfig, + ); await secureStorageInterface.write( - key: '${walletId}_password', value: password); + key: '${walletId}_password', + value: password, + ); await secureStorageInterface.write( - key: '${walletId}_epicboxConfig', value: epicboxConfig.toString()); + key: '${walletId}_epicboxConfig', + value: epicboxConfig.toString(), + ); final String name = walletId; @@ -523,7 +529,8 @@ class EpiccashWallet extends Bip39Wallet { // subtract a couple days to ensure we have a buffer for SWB final bufferedCreateHeight = _calculateRestoreHeightFrom( - date: DateTime.now().subtract(const Duration(days: 2))); + date: DateTime.now().subtract(const Duration(days: 2)), + ); final epicData = ExtraEpiccashWalletInfo( receivingIndex: 0, @@ -542,8 +549,9 @@ class EpiccashWallet extends Bip39Wallet { } else { try { Logging.instance.log( - "initializeExisting() ${cryptoCurrency.prettyName} wallet", - level: LogLevel.Info); + "initializeExisting() ${cryptoCurrency.prettyName} wallet", + level: LogLevel.Info, + ); final config = await _getRealConfig(); final password = @@ -554,7 +562,9 @@ class EpiccashWallet extends Bip39Wallet { password: password!, ); await secureStorageInterface.write( - key: '${walletId}_wallet', value: walletOpen); + key: '${walletId}_wallet', + value: walletOpen, + ); await updateNode(); } catch (e, s) { @@ -650,7 +660,8 @@ class EpiccashWallet extends Bip39Wallet { if (feeAmount > info.cachedBalance.spendable) { throw Exception( - "Epic cash prepare send fee is greater than available balance!"); + "Epic cash prepare send fee is greater than available balance!", + ); } if (info.cachedBalance.spendable == recipient.amount) { @@ -742,15 +753,17 @@ class EpiccashWallet extends Bip39Wallet { ); await _generateAndStoreReceivingAddressForIndex( - epicData.receivingIndex); + epicData.receivingIndex, + ); } }); unawaited(refresh()); } catch (e, s) { Logging.instance.log( - "Exception rethrown from electrumx_mixin recover(): $e\n$s", - level: LogLevel.Info); + "Exception rethrown from electrumx_mixin recover(): $e\n$s", + level: LogLevel.Info, + ); rethrow; } @@ -1117,12 +1130,13 @@ class EpiccashWallet extends Bip39Wallet { // this wasn't done before the refactor either so... // TODO: implement _getFees return FeeObject( - numberOfBlocksFast: 10, - numberOfBlocksAverage: 10, - numberOfBlocksSlow: 10, - fast: 1, - medium: 1, - slow: 1); + numberOfBlocksFast: 10, + numberOfBlocksAverage: 10, + numberOfBlocksSlow: 10, + fast: 1, + medium: 1, + slow: 1, + ); } @override diff --git a/lib/wallets/wallet/impl/ethereum_wallet.dart b/lib/wallets/wallet/impl/ethereum_wallet.dart index 05b075868..736f0fb05 100644 --- a/lib/wallets/wallet/impl/ethereum_wallet.dart +++ b/lib/wallets/wallet/impl/ethereum_wallet.dart @@ -5,6 +5,8 @@ import 'package:decimal/decimal.dart'; import 'package:ethereum_addresses/ethereum_addresses.dart'; import 'package:http/http.dart'; import 'package:isar/isar.dart'; +import 'package:web3dart/web3dart.dart' as web3; + import '../../../models/balance.dart'; import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/isar/models/blockchain_data/transaction.dart'; @@ -19,12 +21,10 @@ import '../../../utilities/amount/amount.dart'; import '../../../utilities/enums/fee_rate_type_enum.dart'; import '../../../utilities/eth_commons.dart'; import '../../../utilities/logger.dart'; -import '../../crypto_currency/coins/ethereum.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../models/tx_data.dart'; import '../intermediate/bip39_wallet.dart'; import '../wallet_mixin_interfaces/private_key_interface.dart'; -import 'package:web3dart/web3dart.dart' as web3; // Eth can not use tor with web3dart @@ -142,7 +142,7 @@ class EthereumWallet extends Bip39Wallet with PrivateKeyInterface { @override Future pingCheck() async { - web3.Web3Client client = getEthClient(); + final web3.Web3Client client = getEthClient(); try { await client.getBlockNumber(); return true; @@ -158,7 +158,7 @@ class EthereumWallet extends Bip39Wallet with PrivateKeyInterface { final addressHex = (await getCurrentReceivingAddress())!.value; final address = web3.EthereumAddress.fromHex(addressHex); - web3.EtherAmount ethBalance = await client.getBalance(address); + final web3.EtherAmount ethBalance = await client.getBalance(address); final balance = Balance( total: Amount( rawValue: ethBalance.getInWei, @@ -287,7 +287,7 @@ class EthereumWallet extends Bip39Wallet with PrivateKeyInterface { final List outputs = []; final List inputs = []; - OutputV2 output = OutputV2.isarCantDoRequiredInDefaultConstructor( + final OutputV2 output = OutputV2.isarCantDoRequiredInDefaultConstructor( scriptPubKeyHex: "00", valueStringSats: transactionAmount.raw.toString(), addresses: [ @@ -295,7 +295,7 @@ class EthereumWallet extends Bip39Wallet with PrivateKeyInterface { ], walletOwns: addressTo == thisAddress, ); - InputV2 input = InputV2.isarCantDoRequiredInDefaultConstructor( + final InputV2 input = InputV2.isarCantDoRequiredInDefaultConstructor( scriptSigHex: null, scriptSigAsm: null, sequence: null, @@ -413,8 +413,10 @@ class EthereumWallet extends Bip39Wallet with PrivateKeyInterface { // ); final nonce = txData.nonce ?? - await client.getTransactionCount(myWeb3Address, - atBlock: const web3.BlockNum.pending()); + await client.getTransactionCount( + myWeb3Address, + atBlock: const web3.BlockNum.pending(), + ); // final nResponse = await EthereumAPI.getAddressNonce(address: myAddress); // print("=============================================================="); diff --git a/lib/wallets/wallet/impl/firo_wallet.dart b/lib/wallets/wallet/impl/firo_wallet.dart index 0be178086..dce8cd9fa 100644 --- a/lib/wallets/wallet/impl/firo_wallet.dart +++ b/lib/wallets/wallet/impl/firo_wallet.dart @@ -5,6 +5,7 @@ import 'dart:math'; import 'package:decimal/decimal.dart'; import 'package:flutter_libsparkmobile/flutter_libsparkmobile.dart'; import 'package:isar/isar.dart'; + import '../../../models/isar/models/blockchain_data/v2/input_v2.dart'; import '../../../models/isar/models/blockchain_data/v2/output_v2.dart'; import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; @@ -13,7 +14,6 @@ import '../../../utilities/amount/amount.dart'; import '../../../utilities/extensions/extensions.dart'; import '../../../utilities/logger.dart'; import '../../../utilities/util.dart'; -import '../../crypto_currency/coins/firo.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../crypto_currency/interfaces/electrumx_currency_interface.dart'; import '../../isar/models/spark_coin.dart'; @@ -259,9 +259,9 @@ class FiroWallet extends Bip39HDWallet .toUint8ListFromHex .first; if (opByte == OP_SPARKMINT || opByte == OP_SPARKSMINT) { - final serCoin = base64Encode(output.scriptPubKeyHex - .substring(2, 488) - .toUint8ListFromHex); + final serCoin = base64Encode( + output.scriptPubKeyHex.substring(2, 488).toUint8ListFromHex, + ); final coin = sparkCoinsInvolved .where((e) => e.serializedCoinB64!.startsWith(serCoin)) .firstOrNull; @@ -384,8 +384,8 @@ class FiroWallet extends Bip39HDWallet ); final prevOutJson = Map.from( - (inputTx["vout"] as List).firstWhere((e) => e["n"] == vout) - as Map); + (inputTx["vout"] as List).firstWhere((e) => e["n"] == vout) as Map, + ); final prevOut = OutputV2.fromElectrumXJson( prevOutJson, @@ -604,7 +604,8 @@ class FiroWallet extends Bip39HDWallet if (isRescan) { // clear cache await electrumXCachedClient.clearSharedTransactionCache( - cryptoCurrency: info.coin); + cryptoCurrency: info.coin, + ); // clear blockchain info await mainDB.deleteWalletBlockchainData(walletId); } @@ -717,12 +718,16 @@ class FiroWallet extends Bip39HDWallet } // remove extra addresses to help minimize risk of creating a large gap - addressesToStore.removeWhere((e) => - e.subType == AddressSubType.change && - e.derivationIndex > highestChangeIndexWithHistory); - addressesToStore.removeWhere((e) => - e.subType == AddressSubType.receiving && - e.derivationIndex > highestReceivingIndexWithHistory); + addressesToStore.removeWhere( + (e) => + e.subType == AddressSubType.change && + e.derivationIndex > highestChangeIndexWithHistory, + ); + addressesToStore.removeWhere( + (e) => + e.subType == AddressSubType.receiving && + e.derivationIndex > highestReceivingIndexWithHistory, + ); await mainDB.updateOrPutAddresses(addressesToStore); @@ -774,8 +779,9 @@ class FiroWallet extends Bip39HDWallet unawaited(refresh()); } catch (e, s) { Logging.instance.log( - "Exception rethrown from electrumx_mixin recover(): $e\n$s", - level: LogLevel.Info); + "Exception rethrown from electrumx_mixin recover(): $e\n$s", + level: LogLevel.Info, + ); rethrow; } @@ -784,8 +790,10 @@ class FiroWallet extends Bip39HDWallet @override Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) { return Amount( - rawValue: BigInt.from(((181 * inputCount) + (34 * outputCount) + 10) * - (feeRatePerKB / 1000).ceil()), + rawValue: BigInt.from( + ((181 * inputCount) + (34 * outputCount) + 10) * + (feeRatePerKB / 1000).ceil(), + ), fractionDigits: cryptoCurrency.fractionDigits, ); } diff --git a/lib/wallets/wallet/impl/litecoin_wallet.dart b/lib/wallets/wallet/impl/litecoin_wallet.dart index 0cce6fd8d..97c9bd42c 100644 --- a/lib/wallets/wallet/impl/litecoin_wallet.dart +++ b/lib/wallets/wallet/impl/litecoin_wallet.dart @@ -1,4 +1,5 @@ import 'package:isar/isar.dart'; + import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/isar/models/blockchain_data/transaction.dart'; import '../../../models/isar/models/blockchain_data/v2/input_v2.dart'; @@ -7,7 +8,6 @@ import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; import '../../../models/isar/ordinal.dart'; import '../../../utilities/amount/amount.dart'; import '../../../utilities/logger.dart'; -import '../../crypto_currency/coins/litecoin.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../crypto_currency/interfaces/electrumx_currency_interface.dart'; import '../intermediate/bip39_hd_wallet.dart'; @@ -54,14 +54,15 @@ class LitecoinWallet @override Future updateTransactions() async { // Get all addresses. - List
allAddressesOld = await fetchAddressesForElectrumXScan(); + final List
allAddressesOld = + await fetchAddressesForElectrumXScan(); // Separate receiving and change addresses. - Set receivingAddresses = allAddressesOld + final Set receivingAddresses = allAddressesOld .where((e) => e.subType == AddressSubType.receiving) .map((e) => e.value) .toSet(); - Set changeAddresses = allAddressesOld + final Set changeAddresses = allAddressesOld .where((e) => e.subType == AddressSubType.change) .map((e) => e.value) .toSet(); @@ -78,7 +79,7 @@ class LitecoinWallet await fetchHistory(allAddressesSet); // Only parse new txs (not in db yet). - List> allTransactions = []; + final List> allTransactions = []; for (final txHash in allTxHashes) { // Check for duplicates by searching for tx by tx_hash in db. final storedTx = await mainDB.isar.transactionV2s @@ -141,8 +142,8 @@ class LitecoinWallet ); final prevOutJson = Map.from( - (inputTx["vout"] as List).firstWhere((e) => e["n"] == vout) - as Map); + (inputTx["vout"] as List).firstWhere((e) => e["n"] == vout) as Map, + ); final prevOut = OutputV2.fromElectrumXJson( prevOutJson, @@ -310,8 +311,9 @@ class LitecoinWallet Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) { return Amount( rawValue: BigInt.from( - ((42 + (272 * inputCount) + (128 * outputCount)) / 4).ceil() * - (feeRatePerKB / 1000).ceil()), + ((42 + (272 * inputCount) + (128 * outputCount)) / 4).ceil() * + (feeRatePerKB / 1000).ceil(), + ), fractionDigits: cryptoCurrency.fractionDigits, ); } diff --git a/lib/wallets/wallet/impl/namecoin_wallet.dart b/lib/wallets/wallet/impl/namecoin_wallet.dart index 48321a888..a401d6395 100644 --- a/lib/wallets/wallet/impl/namecoin_wallet.dart +++ b/lib/wallets/wallet/impl/namecoin_wallet.dart @@ -1,4 +1,5 @@ import 'package:isar/isar.dart'; + import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/isar/models/blockchain_data/transaction.dart'; import '../../../models/isar/models/blockchain_data/v2/input_v2.dart'; @@ -6,14 +7,14 @@ import '../../../models/isar/models/blockchain_data/v2/output_v2.dart'; import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; import '../../../utilities/amount/amount.dart'; import '../../../utilities/logger.dart'; -import '../../crypto_currency/coins/namecoin.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../crypto_currency/interfaces/electrumx_currency_interface.dart'; import '../intermediate/bip39_hd_wallet.dart'; import '../wallet_mixin_interfaces/coin_control_interface.dart'; import '../wallet_mixin_interfaces/electrumx_interface.dart'; -class NamecoinWallet extends Bip39HDWallet +class NamecoinWallet + extends Bip39HDWallet with ElectrumXInterface, CoinControlInterface { @override int get isarTransactionVersion => 2; @@ -74,8 +75,9 @@ class NamecoinWallet extends Bip39HDWallet Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) { return Amount( rawValue: BigInt.from( - ((42 + (272 * inputCount) + (128 * outputCount)) / 4).ceil() * - (feeRatePerKB / 1000).ceil()), + ((42 + (272 * inputCount) + (128 * outputCount)) / 4).ceil() * + (feeRatePerKB / 1000).ceil(), + ), fractionDigits: cryptoCurrency.fractionDigits, ); } @@ -83,14 +85,15 @@ class NamecoinWallet extends Bip39HDWallet @override Future updateTransactions() async { // Get all addresses. - List
allAddressesOld = await fetchAddressesForElectrumXScan(); + final List
allAddressesOld = + await fetchAddressesForElectrumXScan(); // Separate receiving and change addresses. - Set receivingAddresses = allAddressesOld + final Set receivingAddresses = allAddressesOld .where((e) => e.subType == AddressSubType.receiving) .map((e) => e.value) .toSet(); - Set changeAddresses = allAddressesOld + final Set changeAddresses = allAddressesOld .where((e) => e.subType == AddressSubType.change) .map((e) => e.value) .toSet(); @@ -103,7 +106,7 @@ class NamecoinWallet extends Bip39HDWallet await fetchHistory(allAddressesSet); // Only parse new txs (not in db yet). - List> allTransactions = []; + final List> allTransactions = []; for (final txHash in allTxHashes) { // Check for duplicates by searching for tx by tx_hash in db. final storedTx = await mainDB.isar.transactionV2s @@ -164,8 +167,8 @@ class NamecoinWallet extends Bip39HDWallet ); final prevOutJson = Map.from( - (inputTx["vout"] as List).firstWhere((e) => e["n"] == vout) - as Map); + (inputTx["vout"] as List).firstWhere((e) => e["n"] == vout) as Map, + ); final prevOut = OutputV2.fromElectrumXJson( prevOutJson, @@ -239,7 +242,7 @@ class NamecoinWallet extends Bip39HDWallet .fold(BigInt.zero, (value, element) => value + element); TransactionType type; - TransactionSubType subType = TransactionSubType.none; + final TransactionSubType subType = TransactionSubType.none; // At least one input was owned by this wallet. if (wasSentFromThisWallet) { diff --git a/lib/wallets/wallet/impl/nano_wallet.dart b/lib/wallets/wallet/impl/nano_wallet.dart index e541ad1f7..db66769cf 100644 --- a/lib/wallets/wallet/impl/nano_wallet.dart +++ b/lib/wallets/wallet/impl/nano_wallet.dart @@ -1,4 +1,3 @@ -import '../../crypto_currency/coins/nano.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../crypto_currency/intermediate/nano_currency.dart'; import '../intermediate/bip39_wallet.dart'; diff --git a/lib/wallets/wallet/impl/particl_wallet.dart b/lib/wallets/wallet/impl/particl_wallet.dart index 760c88e97..0ce72b393 100644 --- a/lib/wallets/wallet/impl/particl_wallet.dart +++ b/lib/wallets/wallet/impl/particl_wallet.dart @@ -2,6 +2,7 @@ import 'dart:typed_data'; import 'package:bitcoindart/bitcoindart.dart' as bitcoindart; import 'package:isar/isar.dart'; + import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/isar/models/blockchain_data/transaction.dart'; import '../../../models/isar/models/blockchain_data/v2/input_v2.dart'; @@ -12,7 +13,6 @@ import '../../../utilities/amount/amount.dart'; import '../../../utilities/enums/derive_path_type_enum.dart'; import '../../../utilities/extensions/impl/uint8_list.dart'; import '../../../utilities/logger.dart'; -import '../../crypto_currency/coins/particl.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../crypto_currency/interfaces/electrumx_currency_interface.dart'; import '../../models/tx_data.dart'; @@ -120,8 +120,9 @@ class ParticlWallet Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) { return Amount( rawValue: BigInt.from( - ((42 + (272 * inputCount) + (128 * outputCount)) / 4).ceil() * - (feeRatePerKB / 1000).ceil()), + ((42 + (272 * inputCount) + (128 * outputCount)) / 4).ceil() * + (feeRatePerKB / 1000).ceil(), + ), fractionDigits: cryptoCurrency.fractionDigits, ); } @@ -129,14 +130,15 @@ class ParticlWallet @override Future updateTransactions() async { // Get all addresses. - List
allAddressesOld = await fetchAddressesForElectrumXScan(); + final List
allAddressesOld = + await fetchAddressesForElectrumXScan(); // Separate receiving and change addresses. - Set receivingAddresses = allAddressesOld + final Set receivingAddresses = allAddressesOld .where((e) => e.subType == AddressSubType.receiving) .map((e) => e.value) .toSet(); - Set changeAddresses = allAddressesOld + final Set changeAddresses = allAddressesOld .where((e) => e.subType == AddressSubType.change) .map((e) => e.value) .toSet(); @@ -149,7 +151,7 @@ class ParticlWallet await fetchHistory(allAddressesSet); // Only parse new txs (not in db yet). - List> allTransactions = []; + final List> allTransactions = []; for (final txHash in allTxHashes) { // Check for duplicates by searching for tx by tx_hash in db. final storedTx = await mainDB.isar.transactionV2s @@ -210,8 +212,8 @@ class ParticlWallet ); final prevOutJson = Map.from( - (inputTx["vout"] as List).firstWhere((e) => e["n"] == vout) - as Map); + (inputTx["vout"] as List).firstWhere((e) => e["n"] == vout) as Map, + ); final prevOut = _parseOutput( prevOutJson, @@ -285,7 +287,7 @@ class ParticlWallet .fold(BigInt.zero, (value, element) => value + element); TransactionType type; - TransactionSubType subType = TransactionSubType.none; + final TransactionSubType subType = TransactionSubType.none; // Particl has special outputs like confidential amounts. We can check // for them here. They're also checked in checkBlockUTXO. @@ -342,8 +344,10 @@ class ParticlWallet required TxData txData, required List utxoSigningData, }) async { - Logging.instance.log("Starting Particl buildTransaction ----------", - level: LogLevel.Info); + Logging.instance.log( + "Starting Particl buildTransaction ----------", + level: LogLevel.Info, + ); // TODO: use coinlib (For this we need coinlib to support particl) @@ -515,8 +519,10 @@ class ParticlWallet ); } } catch (e, s) { - Logging.instance.log("Caught exception while signing transaction: $e\n$s", - level: LogLevel.Error); + Logging.instance.log( + "Caught exception while signing transaction: $e\n$s", + level: LogLevel.Error, + ); rethrow; } @@ -530,8 +536,10 @@ class ParticlWallet String hexString = builtTx.toHex(isParticl: true).toString(); if (hexString.length % 2 != 0) { // Ensure the string has an even length. - Logging.instance.log("Hex string has odd length, which is unexpected.", - level: LogLevel.Error); + Logging.instance.log( + "Hex string has odd length, which is unexpected.", + level: LogLevel.Error, + ); throw Exception("Invalid hex string length."); } // int maxStrips = 3; // Strip up to 3 0x00s (match previous particl_wallet). diff --git a/lib/wallets/wallet/impl/peercoin_wallet.dart b/lib/wallets/wallet/impl/peercoin_wallet.dart index b7297dd47..2567e841d 100644 --- a/lib/wallets/wallet/impl/peercoin_wallet.dart +++ b/lib/wallets/wallet/impl/peercoin_wallet.dart @@ -6,7 +6,6 @@ import '../../../models/isar/models/blockchain_data/v2/output_v2.dart'; import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; import '../../../utilities/amount/amount.dart'; import '../../../utilities/logger.dart'; -import '../../crypto_currency/coins/peercoin.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../crypto_currency/interfaces/electrumx_currency_interface.dart'; import '../intermediate/bip39_hd_wallet.dart'; diff --git a/lib/wallets/wallet/impl/solana_wallet.dart b/lib/wallets/wallet/impl/solana_wallet.dart index f194dcfee..55ecafade 100644 --- a/lib/wallets/wallet/impl/solana_wallet.dart +++ b/lib/wallets/wallet/impl/solana_wallet.dart @@ -7,9 +7,10 @@ import 'package:isar/isar.dart'; import 'package:socks5_proxy/socks_client.dart'; import 'package:solana/dto.dart'; import 'package:solana/solana.dart'; +import 'package:tuple/tuple.dart'; + import '../../../models/balance.dart'; -import '../../../models/isar/models/blockchain_data/transaction.dart' - as isar; +import '../../../models/isar/models/blockchain_data/transaction.dart' as isar; import '../../../models/isar/models/isar_models.dart'; import '../../../models/node_model.dart'; import '../../../models/paymint/fee_object_model.dart'; @@ -17,11 +18,9 @@ import '../../../services/node_service.dart'; import '../../../services/tor_service.dart'; import '../../../utilities/amount/amount.dart'; import '../../../utilities/logger.dart'; -import '../../crypto_currency/coins/solana.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../models/tx_data.dart'; import '../intermediate/bip39_wallet.dart'; -import 'package:tuple/tuple.dart'; class SolanaWallet extends Bip39Wallet { SolanaWallet(CryptoCurrencyNetwork network) : super(Solana(network)); @@ -63,13 +62,15 @@ class SolanaWallet extends Bip39Wallet { final latestBlockhash = await _rpcClient?.getLatestBlockhash(); final pubKey = (await _getKeyPair()).publicKey; - final compiledMessage = Message(instructions: [ - SystemInstruction.transfer( - fundingAccount: pubKey, - recipientAccount: pubKey, - lamports: transferAmount.raw.toInt(), - ) - ]).compile( + final compiledMessage = Message( + instructions: [ + SystemInstruction.transfer( + fundingAccount: pubKey, + recipientAccount: pubKey, + lamports: transferAmount.raw.toInt(), + ), + ], + ).compile( recentBlockhash: latestBlockhash!.value.blockhash, feePayer: pubKey, ); @@ -119,7 +120,8 @@ class SolanaWallet extends Bip39Wallet { final feeAmount = await _getEstimatedNetworkFee(sendAmount); if (feeAmount == null) { throw Exception( - "Failed to get fees, please check your node connection."); + "Failed to get fees, please check your node connection.", + ); } final address = await getCurrentReceivingAddress(); @@ -128,7 +130,8 @@ class SolanaWallet extends Bip39Wallet { final accInfo = await _rpcClient?.getAccountInfo(address!.value); final int minimumRent = await _rpcClient?.getMinimumBalanceForRentExemption( - accInfo!.value!.data.toString().length) ?? + accInfo!.value!.data.toString().length, + ) ?? 0; // TODO revisit null condition. if (minimumRent > ((await _getCurrentBalanceInLamports()) - @@ -167,11 +170,13 @@ class SolanaWallet extends Bip39Wallet { final message = Message( instructions: [ SystemInstruction.transfer( - fundingAccount: keyPair.publicKey, - recipientAccount: recipientPubKey, - lamports: txData.amount!.raw.toInt()), + fundingAccount: keyPair.publicKey, + recipientAccount: recipientPubKey, + lamports: txData.amount!.raw.toInt(), + ), ComputeBudgetInstruction.setComputeUnitPrice( - microLamports: txData.fee!.raw.toInt() - 5000), + microLamports: txData.fee!.raw.toInt() - 5000, + ), // 5000 lamports is the base fee for a transaction. This instruction adds the necessary fee on top of base fee if it is needed. ComputeBudgetInstruction.setComputeUnitLimit(units: 1000000), // 1000000 is the multiplication number to turn the compute unit price of microLamports to lamports. @@ -230,12 +235,13 @@ class SolanaWallet extends Bip39Wallet { } return FeeObject( - numberOfBlocksFast: 1, - numberOfBlocksAverage: 1, - numberOfBlocksSlow: 1, - fast: fee, - medium: fee, - slow: fee); + numberOfBlocksFast: 1, + numberOfBlocksAverage: 1, + numberOfBlocksSlow: 1, + fast: fee, + medium: fee, + slow: fee, + ); } @override @@ -292,7 +298,8 @@ class SolanaWallet extends Bip39Wallet { // TODO [prio=low]: handle null account info. final int minimumRent = await _rpcClient?.getMinimumBalanceForRentExemption( - accInfo!.value!.data.toString().length) ?? + accInfo!.value!.data.toString().length, + ) ?? 0; // TODO [prio=low]: revisit null condition. final spendableBalance = balance!.value - minimumRent; @@ -366,8 +373,9 @@ class SolanaWallet extends Bip39Wallet { _checkClient(); final transactionsList = await _rpcClient?.getTransactionsList( - (await _getKeyPair()).publicKey, - encoding: Encoding.jsonParsed); + (await _getKeyPair()).publicKey, + encoding: Encoding.jsonParsed, + ); final txsList = List>.empty(growable: true); diff --git a/lib/wallets/wallet/impl/stellar_wallet.dart b/lib/wallets/wallet/impl/stellar_wallet.dart index 3742bc992..09d5d48fa 100644 --- a/lib/wallets/wallet/impl/stellar_wallet.dart +++ b/lib/wallets/wallet/impl/stellar_wallet.dart @@ -5,6 +5,8 @@ import 'dart:io'; import 'package:isar/isar.dart'; import 'package:mutex/mutex.dart'; import 'package:socks5_proxy/socks.dart'; +import 'package:stellar_flutter_sdk/stellar_flutter_sdk.dart' as stellar; + import '../../../models/balance.dart'; import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/isar/models/blockchain_data/transaction.dart'; @@ -20,11 +22,9 @@ import '../../../utilities/amount/amount.dart'; import '../../../utilities/enums/fee_rate_type_enum.dart'; import '../../../utilities/logger.dart'; import '../../../utilities/test_stellar_node_connection.dart'; -import '../../crypto_currency/coins/stellar.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../models/tx_data.dart'; import '../intermediate/bip39_wallet.dart'; -import 'package:stellar_flutter_sdk/stellar_flutter_sdk.dart' as stellar; class StellarWallet extends Bip39Wallet { StellarWallet(CryptoCurrencyNetwork network) : super(Stellar(network)) { @@ -143,8 +143,9 @@ class StellarWallet extends Bip39Wallet { } } catch (e, s) { Logging.instance.log( - "Error getting account ${e.toString()} - ${s.toString()}", - level: LogLevel.Error); + "Error getting account ${e.toString()} - ${s.toString()}", + level: LogLevel.Error, + ); } return exists; } @@ -235,8 +236,10 @@ class StellarWallet extends Bip39Wallet { ), ); } catch (e, s) { - Logging.instance.log("$runtimeType prepareSend() failed: $e\n$s", - level: LogLevel.Error); + Logging.instance.log( + "$runtimeType prepareSend() failed: $e\n$s", + level: LogLevel.Error, + ); rethrow; } } @@ -478,10 +481,12 @@ class StellarWallet extends Bip39Wallet { type = TransactionType.incoming; } final amount = Amount( - rawValue: BigInt.parse(float - .parse(por.amount!) - .toStringAsFixed(cryptoCurrency.fractionDigits) - .replaceAll(".", "")), + rawValue: BigInt.parse( + float + .parse(por.amount!) + .toStringAsFixed(cryptoCurrency.fractionDigits) + .replaceAll(".", ""), + ), fractionDigits: cryptoCurrency.fractionDigits, ); @@ -560,10 +565,12 @@ class StellarWallet extends Bip39Wallet { type = TransactionType.incoming; } final amount = Amount( - rawValue: BigInt.parse(float - .parse(caor.startingBalance!) - .toStringAsFixed(cryptoCurrency.fractionDigits) - .replaceAll(".", "")), + rawValue: BigInt.parse( + float + .parse(caor.startingBalance!) + .toStringAsFixed(cryptoCurrency.fractionDigits) + .replaceAll(".", ""), + ), fractionDigits: cryptoCurrency.fractionDigits, ); @@ -640,8 +647,9 @@ class StellarWallet extends Bip39Wallet { await mainDB.updateOrPutTransactionV2s(transactionList); } catch (e, s) { Logging.instance.log( - "Exception rethrown from updateTransactions(): $e\n$s", - level: LogLevel.Error); + "Exception rethrown from updateTransactions(): $e\n$s", + level: LogLevel.Error, + ); rethrow; } } diff --git a/lib/wallets/wallet/impl/tezos_wallet.dart b/lib/wallets/wallet/impl/tezos_wallet.dart index ead0247aa..f4c8c0ebe 100644 --- a/lib/wallets/wallet/impl/tezos_wallet.dart +++ b/lib/wallets/wallet/impl/tezos_wallet.dart @@ -14,7 +14,6 @@ import '../../../utilities/logger.dart'; import '../../api/tezos/tezos_account.dart'; import '../../api/tezos/tezos_api.dart'; import '../../api/tezos/tezos_rpc_api.dart'; -import '../../crypto_currency/coins/tezos.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../isar/models/wallet_info.dart'; import '../../models/tx_data.dart'; diff --git a/lib/wallets/wallet/intermediate/cryptonote_wallet.dart b/lib/wallets/wallet/intermediate/cryptonote_wallet.dart index 95790a169..aabee237b 100644 --- a/lib/wallets/wallet/intermediate/cryptonote_wallet.dart +++ b/lib/wallets/wallet/intermediate/cryptonote_wallet.dart @@ -7,7 +7,7 @@ import '../wallet_mixin_interfaces/mnemonic_interface.dart'; abstract class CryptonoteWallet extends Wallet with MnemonicInterface { - CryptonoteWallet(T currency) : super(currency); + CryptonoteWallet(super.currency); Completer? walletOpenCompleter; diff --git a/lib/wallets/wallet/wallet.dart b/lib/wallets/wallet/wallet.dart index 36d9130b3..8fa0b9ddf 100644 --- a/lib/wallets/wallet/wallet.dart +++ b/lib/wallets/wallet/wallet.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:isar/isar.dart'; import 'package:meta/meta.dart'; import 'package:mutex/mutex.dart'; + import '../../db/isar/main_db.dart'; import '../../models/isar/models/blockchain_data/address.dart'; import '../../models/isar/models/ethereum/eth_contract.dart'; @@ -20,25 +21,6 @@ import '../../utilities/flutter_secure_storage_interface.dart'; import '../../utilities/logger.dart'; import '../../utilities/paynym_is_api.dart'; import '../../utilities/prefs.dart'; -import '../crypto_currency/coins/banano.dart'; -import '../crypto_currency/coins/bitcoin.dart'; -import '../crypto_currency/coins/bitcoin_frost.dart'; -import '../crypto_currency/coins/bitcoincash.dart'; -import '../crypto_currency/coins/dogecoin.dart'; -import '../crypto_currency/coins/ecash.dart'; -import '../crypto_currency/coins/epiccash.dart'; -import '../crypto_currency/coins/ethereum.dart'; -import '../crypto_currency/coins/firo.dart'; -import '../crypto_currency/coins/litecoin.dart'; -import '../crypto_currency/coins/monero.dart'; -import '../crypto_currency/coins/namecoin.dart'; -import '../crypto_currency/coins/nano.dart'; -import '../crypto_currency/coins/particl.dart'; -import '../crypto_currency/coins/peercoin.dart'; -import '../crypto_currency/coins/solana.dart'; -import '../crypto_currency/coins/stellar.dart'; -import '../crypto_currency/coins/tezos.dart'; -import '../crypto_currency/coins/wownero.dart'; import '../crypto_currency/crypto_currency.dart'; import '../isar/models/wallet_info.dart'; import '../models/tx_data.dart'; @@ -122,7 +104,8 @@ abstract class Wallet { bool _isConnected = false; void xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture( - bool flag) { + bool flag, + ) { _isConnected = flag; } diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/bcash_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/bcash_interface.dart index a7a75e397..9559fa848 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/bcash_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/bcash_interface.dart @@ -1,6 +1,7 @@ import 'package:bitbox/bitbox.dart' as bitbox; import 'package:bitbox/src/utils/network.dart' as bitbox_utils; import 'package:isar/isar.dart'; + import '../../../models/isar/models/blockchain_data/v2/input_v2.dart'; import '../../../models/isar/models/blockchain_data/v2/output_v2.dart'; import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; @@ -112,8 +113,10 @@ mixin BCashInterface ); } } catch (e, s) { - Logging.instance.log("Caught exception while signing transaction: $e\n$s", - level: LogLevel.Error); + Logging.instance.log( + "Caught exception while signing transaction: $e\n$s", + level: LogLevel.Error, + ); rethrow; } diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart index 4858c8bdc..c22064d03 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart @@ -5,6 +5,7 @@ import 'package:bitbox/bitbox.dart' as bitbox; import 'package:flutter/foundation.dart'; import 'package:fusiondart/fusiondart.dart' as fusion; import 'package:isar/isar.dart'; + import '../../../models/fusion_progress_ui_state.dart'; import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/isar/models/blockchain_data/transaction.dart'; @@ -13,8 +14,6 @@ import '../../../pages_desktop_specific/cashfusion/sub_widgets/fusion_dialog.dar import '../../../services/fusion_tor_service.dart'; import '../../../utilities/logger.dart'; import '../../../utilities/stack_file_system.dart'; -import '../../crypto_currency/coins/bitcoincash.dart'; -import '../../crypto_currency/coins/ecash.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../crypto_currency/interfaces/electrumx_currency_interface.dart'; import 'coin_control_interface.dart'; @@ -152,88 +151,113 @@ mixin CashFusionInterface switch (status) { case fusion.FusionStatus.connecting: _uiState?.setConnecting( - CashFusionState(status: CashFusionStatus.running, info: null), - shouldNotify: false); + CashFusionState(status: CashFusionStatus.running, info: null), + shouldNotify: false, + ); _uiState?.setOutputs( - CashFusionState(status: CashFusionStatus.waiting, info: null), - shouldNotify: false); + CashFusionState(status: CashFusionStatus.waiting, info: null), + shouldNotify: false, + ); _uiState?.setPeers( - CashFusionState(status: CashFusionStatus.waiting, info: null), - shouldNotify: false); + CashFusionState(status: CashFusionStatus.waiting, info: null), + shouldNotify: false, + ); _uiState?.setFusing( - CashFusionState(status: CashFusionStatus.waiting, info: null), - shouldNotify: false); + CashFusionState(status: CashFusionStatus.waiting, info: null), + shouldNotify: false, + ); _uiState?.setComplete( - CashFusionState(status: CashFusionStatus.waiting, info: null), - shouldNotify: true); + CashFusionState(status: CashFusionStatus.waiting, info: null), + shouldNotify: true, + ); break; case fusion.FusionStatus.setup: _uiState?.setConnecting( - CashFusionState(status: CashFusionStatus.success, info: null), - shouldNotify: false); + CashFusionState(status: CashFusionStatus.success, info: null), + shouldNotify: false, + ); _uiState?.setOutputs( - CashFusionState(status: CashFusionStatus.running, info: null), - shouldNotify: false); + CashFusionState(status: CashFusionStatus.running, info: null), + shouldNotify: false, + ); _uiState?.setPeers( - CashFusionState(status: CashFusionStatus.waiting, info: null), - shouldNotify: false); + CashFusionState(status: CashFusionStatus.waiting, info: null), + shouldNotify: false, + ); _uiState?.setFusing( - CashFusionState(status: CashFusionStatus.waiting, info: null), - shouldNotify: false); + CashFusionState(status: CashFusionStatus.waiting, info: null), + shouldNotify: false, + ); _uiState?.setComplete( - CashFusionState(status: CashFusionStatus.waiting, info: null), - shouldNotify: true); + CashFusionState(status: CashFusionStatus.waiting, info: null), + shouldNotify: true, + ); break; case fusion.FusionStatus.waiting: _uiState?.setConnecting( - CashFusionState(status: CashFusionStatus.success, info: null), - shouldNotify: false); + CashFusionState(status: CashFusionStatus.success, info: null), + shouldNotify: false, + ); _uiState?.setOutputs( - CashFusionState(status: CashFusionStatus.success, info: null), - shouldNotify: false); + CashFusionState(status: CashFusionStatus.success, info: null), + shouldNotify: false, + ); _uiState?.setPeers( - CashFusionState(status: CashFusionStatus.running, info: null), - shouldNotify: false); + CashFusionState(status: CashFusionStatus.running, info: null), + shouldNotify: false, + ); _uiState?.setFusing( - CashFusionState(status: CashFusionStatus.waiting, info: null), - shouldNotify: false); + CashFusionState(status: CashFusionStatus.waiting, info: null), + shouldNotify: false, + ); _uiState?.setComplete( - CashFusionState(status: CashFusionStatus.waiting, info: null), - shouldNotify: true); + CashFusionState(status: CashFusionStatus.waiting, info: null), + shouldNotify: true, + ); break; case fusion.FusionStatus.running: _uiState?.setConnecting( - CashFusionState(status: CashFusionStatus.success, info: null), - shouldNotify: false); + CashFusionState(status: CashFusionStatus.success, info: null), + shouldNotify: false, + ); _uiState?.setOutputs( - CashFusionState(status: CashFusionStatus.success, info: null), - shouldNotify: false); + CashFusionState(status: CashFusionStatus.success, info: null), + shouldNotify: false, + ); _uiState?.setPeers( - CashFusionState(status: CashFusionStatus.success, info: null), - shouldNotify: false); + CashFusionState(status: CashFusionStatus.success, info: null), + shouldNotify: false, + ); _uiState?.setFusing( - CashFusionState(status: CashFusionStatus.running, info: null), - shouldNotify: false); + CashFusionState(status: CashFusionStatus.running, info: null), + shouldNotify: false, + ); _uiState?.setComplete( - CashFusionState(status: CashFusionStatus.waiting, info: null), - shouldNotify: true); + CashFusionState(status: CashFusionStatus.waiting, info: null), + shouldNotify: true, + ); break; case fusion.FusionStatus.complete: _uiState?.setConnecting( - CashFusionState(status: CashFusionStatus.success, info: null), - shouldNotify: false); + CashFusionState(status: CashFusionStatus.success, info: null), + shouldNotify: false, + ); _uiState?.setOutputs( - CashFusionState(status: CashFusionStatus.success, info: null), - shouldNotify: false); + CashFusionState(status: CashFusionStatus.success, info: null), + shouldNotify: false, + ); _uiState?.setPeers( - CashFusionState(status: CashFusionStatus.success, info: null), - shouldNotify: false); + CashFusionState(status: CashFusionStatus.success, info: null), + shouldNotify: false, + ); _uiState?.setFusing( - CashFusionState(status: CashFusionStatus.success, info: null), - shouldNotify: false); + CashFusionState(status: CashFusionStatus.success, info: null), + shouldNotify: false, + ); _uiState?.setComplete( - CashFusionState(status: CashFusionStatus.success, info: null), - shouldNotify: true); + CashFusionState(status: CashFusionStatus.success, info: null), + shouldNotify: true, + ); break; case fusion.FusionStatus.failed: failCurrentUiState(info); @@ -243,24 +267,30 @@ mixin CashFusionInterface break; case fusion.FusionStatus.reset: _uiState?.setConnecting( - CashFusionState(status: CashFusionStatus.waiting, info: info), - shouldNotify: false); + CashFusionState(status: CashFusionStatus.waiting, info: info), + shouldNotify: false, + ); _uiState?.setOutputs( - CashFusionState(status: CashFusionStatus.waiting, info: info), - shouldNotify: false); + CashFusionState(status: CashFusionStatus.waiting, info: info), + shouldNotify: false, + ); _uiState?.setPeers( - CashFusionState(status: CashFusionStatus.waiting, info: info), - shouldNotify: false); + CashFusionState(status: CashFusionStatus.waiting, info: info), + shouldNotify: false, + ); _uiState?.setFusing( - CashFusionState(status: CashFusionStatus.waiting, info: info), - shouldNotify: false); + CashFusionState(status: CashFusionStatus.waiting, info: info), + shouldNotify: false, + ); _uiState?.setComplete( - CashFusionState(status: CashFusionStatus.waiting, info: info), - shouldNotify: false); + CashFusionState(status: CashFusionStatus.waiting, info: info), + shouldNotify: false, + ); _uiState?.setFusionState( - CashFusionState(status: CashFusionStatus.waiting, info: info), - shouldNotify: false); + CashFusionState(status: CashFusionStatus.waiting, info: info), + shouldNotify: false, + ); _uiState?.setFailed(false, shouldNotify: true); break; @@ -271,32 +301,37 @@ mixin CashFusionInterface // Check each _uiState value to see if it is running. If so, set it to failed. if (_uiState?.connecting.status == CashFusionStatus.running) { _uiState?.setConnecting( - CashFusionState(status: CashFusionStatus.failed, info: info), - shouldNotify: true); + CashFusionState(status: CashFusionStatus.failed, info: info), + shouldNotify: true, + ); return; } if (_uiState?.outputs.status == CashFusionStatus.running) { _uiState?.setOutputs( - CashFusionState(status: CashFusionStatus.failed, info: info), - shouldNotify: true); + CashFusionState(status: CashFusionStatus.failed, info: info), + shouldNotify: true, + ); return; } if (_uiState?.peers.status == CashFusionStatus.running) { _uiState?.setPeers( - CashFusionState(status: CashFusionStatus.failed, info: info), - shouldNotify: true); + CashFusionState(status: CashFusionStatus.failed, info: info), + shouldNotify: true, + ); return; } if (_uiState?.fusing.status == CashFusionStatus.running) { _uiState?.setFusing( - CashFusionState(status: CashFusionStatus.failed, info: info), - shouldNotify: true); + CashFusionState(status: CashFusionStatus.failed, info: info), + shouldNotify: true, + ); return; } if (_uiState?.complete.status == CashFusionStatus.running) { _uiState?.setComplete( - CashFusionState(status: CashFusionStatus.failed, info: info), - shouldNotify: true); + CashFusionState(status: CashFusionStatus.failed, info: info), + shouldNotify: true, + ); return; } } @@ -329,10 +364,12 @@ mixin CashFusionInterface .and() .derivationPathIsNotNull() .and() - .group((q) => q - .subTypeEqualTo(AddressSubType.receiving) - .or() - .subTypeEqualTo(AddressSubType.change)) + .group( + (q) => q + .subTypeEqualTo(AddressSubType.receiving) + .or() + .subTypeEqualTo(AddressSubType.change), + ) .findAll()) .firstWhere((e) => e.publicKey.toString() == pubKey.toString()) .derivationPath! @@ -406,8 +443,11 @@ mixin CashFusionInterface .where((e) => e.otherData == kReservedFusionAddress) .toList(); - unusedReservedAddresses.addAll(await _reserveAddresses( - unusedChangeAddresses.where((e) => e.otherData == null))); + unusedReservedAddresses.addAll( + await _reserveAddresses( + unusedChangeAddresses.where((e) => e.otherData == null), + ), + ); // Return the list of unused reserved change addresses. return unusedReservedAddresses @@ -570,7 +610,7 @@ mixin CashFusionInterface ); // Use server host and port which ultimately come from text fields. - fusion.FusionParams serverParams = fusion.FusionParams( + final fusion.FusionParams serverParams = fusion.FusionParams( serverHost: fusionInfo.host, serverPort: fusionInfo.port, serverSsl: fusionInfo.ssl, @@ -682,13 +722,17 @@ mixin CashFusionInterface .getAddresses(walletId) .filter() .anyOf>( - possibleAddresses, (q, e) => q.valueEqualTo(e)) + QueryBuilder>( + possibleAddresses, + (q, e) => q.valueEqualTo(e), + ) .and() - .group((q) => q - .subTypeEqualTo(AddressSubType.change) - .or() - .subTypeEqualTo(AddressSubType.receiving)) + .group( + (q) => q + .subTypeEqualTo(AddressSubType.change) + .or() + .subTypeEqualTo(AddressSubType.receiving), + ) .and() .typeEqualTo(AddressType.p2pkh) .findFirst(); @@ -753,8 +797,9 @@ mixin CashFusionInterface if (coinList.isEmpty || e.toString().contains("Started with no coins")) { _updateStatus( - status: fusion.FusionStatus.failed, - info: "Started with no coins, stopping."); + status: fusion.FusionStatus.failed, + info: "Started with no coins, stopping.", + ); _stopRequested = true; _uiState?.setFailed(true, shouldNotify: true); } @@ -762,8 +807,9 @@ mixin CashFusionInterface // If we fail too many times in a row, stop trying. if (_failedFuseCount >= maxFailedFuseCount) { _updateStatus( - status: fusion.FusionStatus.failed, - info: "Failed $maxFailedFuseCount times in a row, stopping."); + status: fusion.FusionStatus.failed, + info: "Failed $maxFailedFuseCount times in a row, stopping.", + ); _stopRequested = true; _uiState?.setFailed(true, shouldNotify: true); } diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart index 80a7f6d31..069571ead 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart @@ -11,6 +11,7 @@ import 'package:cw_core/wallet_type.dart'; import 'package:flutter_libmonero/core/key_service.dart'; import 'package:isar/isar.dart'; import 'package:mutex/mutex.dart'; + import '../../../models/balance.dart'; import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/paymint/fee_object_model.dart'; @@ -344,7 +345,7 @@ mixin CwBasedInterface on CryptonoteWallet if (entries != null) { for (final element in entries) { if (element.value.direction == TransactionDirection.incoming) { - int curAddressIndex = + final int curAddressIndex = element.value.additionalInfo!['addressIndex'] as int; if (curAddressIndex > highestIndex) { highestIndex = curAddressIndex; @@ -381,13 +382,15 @@ mixin CwBasedInterface on CryptonoteWallet } } on SocketException catch (se, s) { Logging.instance.log( - "SocketException caught in _checkReceivingAddressForTransactions(): $se\n$s", - level: LogLevel.Error); + "SocketException caught in _checkReceivingAddressForTransactions(): $se\n$s", + level: LogLevel.Error, + ); return; } catch (e, s) { Logging.instance.log( - "Exception rethrown from _checkReceivingAddressForTransactions(): $e\n$s", - level: LogLevel.Error); + "Exception rethrown from _checkReceivingAddressForTransactions(): $e\n$s", + level: LogLevel.Error, + ); rethrow; } } diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/lelantus_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/lelantus_interface.dart index 26410c526..9d536197a 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/lelantus_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/lelantus_interface.dart @@ -718,7 +718,7 @@ mixin LelantusInterface ); Logging.instance.log(spendTxs, level: LogLevel.Info); - for (var element in spendTxs.entries) { + for (final element in spendTxs.entries) { final address = element.value.address.value ?? data[element.value.txid]?.item1 ?? element.key; @@ -840,7 +840,7 @@ mixin LelantusInterface amount += signingData[i].utxo.value; } - for (var mintsElement in txData.mintsMapLelantus!) { + for (final mintsElement in txData.mintsMapLelantus!) { Logging.instance.log("using $mintsElement", level: LogLevel.Info); final Uint8List mintu8 = Format.stringToUint8List(mintsElement['script'] as String); @@ -952,7 +952,7 @@ mixin LelantusInterface for (final value in data) { if (value.inputs.isNotEmpty) { - for (var element in value.inputs) { + for (final element in value.inputs) { if (lelantusCoins.any((e) => e.txid == value.txid) && spendableOutputs.firstWhere( (output) => output?.txid == element.txid, diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/ordinals_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/ordinals_interface.dart index da0a36c70..4ca21ef1a 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/ordinals_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/ordinals_interface.dart @@ -1,4 +1,5 @@ import 'package:isar/isar.dart'; + import '../../../dto/ordinals/inscription_data.dart'; import '../../../models/isar/models/blockchain_data/utxo.dart'; import '../../../models/isar/ordinal.dart'; @@ -37,7 +38,8 @@ mixin OrdinalsInterface .addressProperty() .findAll(); final inscriptions = await _getInscriptionDataFromAddresses( - uniqueAddresses.cast()); + uniqueAddresses.cast(), + ); final ords = inscriptions .map((e) => Ordinal.fromInscriptionData(e, walletId)) @@ -111,11 +113,12 @@ mixin OrdinalsInterface // ===================== Private ============================================= Future> _getInscriptionDataFromAddresses( - List addresses) async { - List allInscriptions = []; - for (String address in addresses) { + List addresses, + ) async { + final List allInscriptions = []; + for (final String address in addresses) { try { - var inscriptions = + final inscriptions = await _litescribeAPI.getInscriptionsByAddress(address); allInscriptions.addAll(inscriptions); } catch (e) { diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart index b2114b09b..b9759d474 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart @@ -24,7 +24,6 @@ import '../../../utilities/enums/derive_path_type_enum.dart'; import '../../../utilities/extensions/extensions.dart'; import '../../../utilities/format.dart'; import '../../../utilities/logger.dart'; -import '../../crypto_currency/coins/dogecoin.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../crypto_currency/interfaces/paynym_currency_interface.dart'; import '../../models/tx_data.dart'; diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart index 08131fabf..591869cfe 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart @@ -6,6 +6,7 @@ import 'package:decimal/decimal.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_libsparkmobile/flutter_libsparkmobile.dart'; import 'package:isar/isar.dart'; + import '../../../models/balance.dart'; import '../../../models/isar/models/blockchain_data/v2/input_v2.dart'; import '../../../models/isar/models/blockchain_data/v2/output_v2.dart'; @@ -626,7 +627,8 @@ mixin SparkInterface ); final spentCoinTagsFuture = electrumXCachedClient.getSparkUsedCoinsTags( - cryptoCurrency: info.coin); + cryptoCurrency: info.coin, + ); final futureResults = await Future.wait([ anonymitySetFuture, diff --git a/lib/widgets/address_book_card.dart b/lib/widgets/address_book_card.dart index 51a9fe134..9c9254080 100644 --- a/lib/widgets/address_book_card.dart +++ b/lib/widgets/address_book_card.dart @@ -61,8 +61,10 @@ class _AddressBookCardState extends ConsumerState { // provider hack to prevent trying to update widget with deleted contact ContactEntry? _contact; try { - _contact = ref.watch(addressBookServiceProvider - .select((value) => value.getContactById(contactId))); + _contact = ref.watch( + addressBookServiceProvider + .select((value) => value.getContactById(contactId)), + ); } catch (_) { return Container(); } diff --git a/lib/widgets/animated_text.dart b/lib/widgets/animated_text.dart index 136da21c6..428cb8544 100644 --- a/lib/widgets/animated_text.dart +++ b/lib/widgets/animated_text.dart @@ -14,11 +14,11 @@ import 'package:flutter/cupertino.dart'; class AnimatedText extends StatefulWidget { const AnimatedText({ - Key? key, + super.key, required this.stringsToLoopThrough, required this.style, this.duration = const Duration(milliseconds: 700), - }) : super(key: key); + }); final List stringsToLoopThrough; final TextStyle style; diff --git a/lib/widgets/animated_widgets/rotate_icon.dart b/lib/widgets/animated_widgets/rotate_icon.dart index 2b54b1367..92b428d9e 100644 --- a/lib/widgets/animated_widgets/rotate_icon.dart +++ b/lib/widgets/animated_widgets/rotate_icon.dart @@ -18,13 +18,13 @@ class RotateIconController { class RotateIcon extends StatefulWidget { const RotateIcon({ - Key? key, + super.key, required this.icon, required this.curve, this.controller, this.animationDurationMultiplier = 1.0, this.rotationPercent = 0.5, - }) : super(key: key); + }); final Widget icon; final Curve curve; diff --git a/lib/widgets/animated_widgets/rotating_arrows.dart b/lib/widgets/animated_widgets/rotating_arrows.dart index c0025a45a..3da54f63a 100644 --- a/lib/widgets/animated_widgets/rotating_arrows.dart +++ b/lib/widgets/animated_widgets/rotating_arrows.dart @@ -23,13 +23,13 @@ class RotatingArrowsController { class RotatingArrows extends StatefulWidget { const RotatingArrows({ - Key? key, + super.key, required this.height, required this.width, this.controller, this.color, this.spinByDefault = true, - }) : super(key: key); + }); final double height; final double width; diff --git a/lib/widgets/app_bar_field.dart b/lib/widgets/app_bar_field.dart index d48b20ee9..8499748a9 100644 --- a/lib/widgets/app_bar_field.dart +++ b/lib/widgets/app_bar_field.dart @@ -13,10 +13,10 @@ import '../utilities/text_styles.dart'; class AppBarSearchField extends StatefulWidget { const AppBarSearchField({ - Key? key, + super.key, required this.controller, this.focusNode, - }) : super(key: key); + }); final TextEditingController? controller; final FocusNode? focusNode; diff --git a/lib/widgets/background.dart b/lib/widgets/background.dart index 3d27b24b1..11290a9df 100644 --- a/lib/widgets/background.dart +++ b/lib/widgets/background.dart @@ -19,9 +19,9 @@ import 'conditional_parent.dart'; class Background extends ConsumerWidget { const Background({ - Key? key, + super.key, required this.child, - }) : super(key: key); + }); final Widget child; diff --git a/lib/widgets/choose_coin_view.dart b/lib/widgets/choose_coin_view.dart index b1238fb6a..b44f7152a 100644 --- a/lib/widgets/choose_coin_view.dart +++ b/lib/widgets/choose_coin_view.dart @@ -148,7 +148,7 @@ class _ChooseCoinViewState extends ConsumerState { style: STextStyles.titleBold12(context), ), ], - ) + ), ], ), ), diff --git a/lib/widgets/conditional_parent.dart b/lib/widgets/conditional_parent.dart index e664c581e..64a3f8876 100644 --- a/lib/widgets/conditional_parent.dart +++ b/lib/widgets/conditional_parent.dart @@ -12,11 +12,11 @@ import 'package:flutter/material.dart'; class ConditionalParent extends StatelessWidget { const ConditionalParent({ - Key? key, + super.key, required this.condition, required this.builder, required this.child, - }) : super(key: key); + }); final bool condition; final Widget Function(Widget) builder; @@ -34,12 +34,12 @@ class ConditionalParent extends StatelessWidget { class BranchedParent extends StatelessWidget { const BranchedParent({ - Key? key, + super.key, required this.condition, required this.conditionBranchBuilder, required this.otherBranchBuilder, required this.children, - }) : super(key: key); + }); final bool condition; final Widget Function(List) conditionBranchBuilder; diff --git a/lib/widgets/custom_buttons/app_bar_icon_button.dart b/lib/widgets/custom_buttons/app_bar_icon_button.dart index 5327d298f..5147f132d 100644 --- a/lib/widgets/custom_buttons/app_bar_icon_button.dart +++ b/lib/widgets/custom_buttons/app_bar_icon_button.dart @@ -10,13 +10,14 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; + import '../../themes/stack_colors.dart'; import '../../utilities/assets.dart'; import '../../utilities/util.dart'; class AppBarIconButton extends StatelessWidget { const AppBarIconButton({ - Key? key, + super.key, required this.icon, required this.onPressed, this.color, @@ -24,7 +25,7 @@ class AppBarIconButton extends StatelessWidget { this.size = 36.0, this.shadows = const [], this.semanticsLabel = "Button", - }) : super(key: key); + }); final Widget icon; final VoidCallback? onPressed; @@ -37,40 +38,40 @@ class AppBarIconButton extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - height: size, - width: size, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(1000), - color: - color ?? Theme.of(context).extension()!.background, - boxShadow: shadows, - ), - child: Semantics( - excludeSemantics: true, - label: semanticsLabel, - child: MaterialButton( - splashColor: Theme.of(context).extension()!.highlight, - padding: EdgeInsets.zero, - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(1000), - ), - onPressed: onPressed, - child: icon, + height: size, + width: size, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(1000), + color: color ?? Theme.of(context).extension()!.background, + boxShadow: shadows, + ), + child: Semantics( + excludeSemantics: true, + label: semanticsLabel, + child: MaterialButton( + splashColor: Theme.of(context).extension()!.highlight, + padding: EdgeInsets.zero, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(1000), ), - )); + onPressed: onPressed, + child: icon, + ), + ), + ); } } class AppBarBackButton extends StatelessWidget { const AppBarBackButton({ - Key? key, + super.key, this.onPressed, this.isCompact = false, this.size, this.iconSize, this.semanticsLabel = "Back Button. Takes Back To Previous Page.", - }) : super(key: key); + }); final VoidCallback? onPressed; final bool isCompact; @@ -82,32 +83,32 @@ class AppBarBackButton extends StatelessWidget { Widget build(BuildContext context) { final isDesktop = Util.isDesktop; return Padding( - padding: isDesktop - ? const EdgeInsets.symmetric( - vertical: 20, - horizontal: 24, - ) - : const EdgeInsets.all(10), - child: AppBarIconButton( - semanticsLabel: semanticsLabel, - size: size ?? - (isDesktop - ? isCompact - ? 42 - : 56 - : 32), - color: isDesktop - ? Theme.of(context).extension()!.textFieldDefaultBG - : Theme.of(context).extension()!.background, - shadows: const [], - icon: SvgPicture.asset( - Assets.svg.arrowLeft, - width: iconSize ?? (isCompact ? 18 : 24), - height: iconSize ?? (isCompact ? 18 : 24), - color: - Theme.of(context).extension()!.topNavIconPrimary, - ), - onPressed: onPressed ?? Navigator.of(context).pop, - )); + padding: isDesktop + ? const EdgeInsets.symmetric( + vertical: 20, + horizontal: 24, + ) + : const EdgeInsets.all(10), + child: AppBarIconButton( + semanticsLabel: semanticsLabel, + size: size ?? + (isDesktop + ? isCompact + ? 42 + : 56 + : 32), + color: isDesktop + ? Theme.of(context).extension()!.textFieldDefaultBG + : Theme.of(context).extension()!.background, + shadows: const [], + icon: SvgPicture.asset( + Assets.svg.arrowLeft, + width: iconSize ?? (isCompact ? 18 : 24), + height: iconSize ?? (isCompact ? 18 : 24), + color: Theme.of(context).extension()!.topNavIconPrimary, + ), + onPressed: onPressed ?? Navigator.of(context).pop, + ), + ); } } diff --git a/lib/widgets/custom_buttons/blue_text_button.dart b/lib/widgets/custom_buttons/blue_text_button.dart index 8639fc1b1..eee64d8e0 100644 --- a/lib/widgets/custom_buttons/blue_text_button.dart +++ b/lib/widgets/custom_buttons/blue_text_button.dart @@ -18,14 +18,14 @@ import '../rounded_container.dart'; class _CustomTextButton extends StatefulWidget { const _CustomTextButton({ - Key? key, + super.key, required this.text, required this.enabledColor, required this.disabledColor, this.onTap, this.enabled = true, this.textSize, - }) : super(key: key); + }); final String text; final VoidCallback? onTap; @@ -128,12 +128,12 @@ class _CustomTextButtonState extends State<_CustomTextButton> class CustomTextButton extends StatelessWidget { const CustomTextButton({ - Key? key, + super.key, required this.text, this.onTap, this.enabled = true, this.textSize, - }) : super(key: key); + }); final String text; final VoidCallback? onTap; diff --git a/lib/widgets/custom_buttons/draggable_switch_button.dart b/lib/widgets/custom_buttons/draggable_switch_button.dart index 6d77e720c..47e706704 100644 --- a/lib/widgets/custom_buttons/draggable_switch_button.dart +++ b/lib/widgets/custom_buttons/draggable_switch_button.dart @@ -9,18 +9,19 @@ */ import 'package:flutter/material.dart'; + import '../../themes/stack_colors.dart'; class DraggableSwitchButton extends StatefulWidget { const DraggableSwitchButton({ - Key? key, + super.key, this.onItem, this.offItem, this.onValueChanged, required this.isOn, this.enabled = true, this.controller, - }) : super(key: key); + }); final Widget? onItem; final Widget? offItem; @@ -131,7 +132,7 @@ class DraggableSwitchButtonState extends State { .clamp(0.0, 1.0); }, onHorizontalDragEnd: (details) { - bool oldValue = _isOn; + final bool oldValue = _isOn; if (valueListener.value > 0.5) { valueListener.value = 1.0; _isOn = true; @@ -158,7 +159,10 @@ class DraggableSwitchButtonState extends State { constraint.maxHeight / 2, ), color: _colorFG( - _isOn, _enabled, valueListener.value), + _isOn, + _enabled, + valueListener.value, + ), ), ); }, diff --git a/lib/widgets/custom_buttons/dropdown_button.dart b/lib/widgets/custom_buttons/dropdown_button.dart index 6ee148a22..b2044376a 100644 --- a/lib/widgets/custom_buttons/dropdown_button.dart +++ b/lib/widgets/custom_buttons/dropdown_button.dart @@ -10,18 +10,19 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; + import '../../themes/stack_colors.dart'; import '../../utilities/assets.dart'; import '../../utilities/constants.dart'; import '../../utilities/text_styles.dart'; import '../animated_widgets/rotate_icon.dart'; -import 'app_bar_icon_button.dart'; import '../desktop/secondary_button.dart'; import '../rounded_white_container.dart'; +import 'app_bar_icon_button.dart'; class JDropdownButton extends StatefulWidget { const JDropdownButton({ - Key? key, + super.key, this.label, required this.items, this.width, @@ -29,7 +30,7 @@ class JDropdownButton extends StatefulWidget { this.groupValue, this.redrawOnScreenSizeChanged = false, this.showIcon = false, - }) : super(key: key); + }); final String? label; final double? width; @@ -137,14 +138,14 @@ class _JDropdownButtonState extends State> { class JDropdownIconButton extends StatefulWidget { const JDropdownIconButton({ - Key? key, + super.key, required this.items, required this.displayPrefix, this.onSelectionChanged, this.groupValue, this.redrawOnScreenSizeChanged = false, this.mobileAppBar = false, - }) : super(key: key); + }); final String displayPrefix; final void Function(T?)? onSelectionChanged; @@ -275,12 +276,12 @@ class _JDropdownIconButtonState extends State> { // ============================================================================= class _JDropdownButtonMenu extends StatefulWidget { - const _JDropdownButtonMenu( - {Key? key, - required this.items, - required this.size, - required this.position}) - : super(key: key); + const _JDropdownButtonMenu({ + super.key, + required this.items, + required this.size, + required this.position, + }); final List<_JDropdownButtonItem> items; final Size size; @@ -335,13 +336,13 @@ class _JDropdownButtonMenuState extends State<_JDropdownButtonMenu> { class _JDropdownButtonItem extends StatelessWidget { const _JDropdownButtonItem({ - Key? key, + super.key, required this.value, required this.groupValue, required this.onSelected, this.height = 53, this.displayPrefix, - }) : super(key: key); + }); final T value; final T? groupValue; diff --git a/lib/widgets/custom_buttons/favorite_toggle.dart b/lib/widgets/custom_buttons/favorite_toggle.dart index 6014767c6..5d79cb0da 100644 --- a/lib/widgets/custom_buttons/favorite_toggle.dart +++ b/lib/widgets/custom_buttons/favorite_toggle.dart @@ -17,14 +17,14 @@ import '../../utilities/assets.dart'; class FavoriteToggle extends ConsumerStatefulWidget { const FavoriteToggle({ - Key? key, + super.key, this.backGround, this.borderRadius = BorderRadius.zero, this.initialState = false, this.on, this.off, required this.onChanged, - }) : super(key: key); + }); final Color? backGround; final Color? on; diff --git a/lib/widgets/custom_buttons/paynym_follow_toggle_button.dart b/lib/widgets/custom_buttons/paynym_follow_toggle_button.dart index ad5e80e30..5bc6143d1 100644 --- a/lib/widgets/custom_buttons/paynym_follow_toggle_button.dart +++ b/lib/widgets/custom_buttons/paynym_follow_toggle_button.dart @@ -13,6 +13,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../models/paynym/paynym_account_lite.dart'; import '../../models/paynym/paynym_response.dart'; import '../../notifications/show_flush_bar.dart'; @@ -35,11 +36,11 @@ enum PaynymFollowToggleButtonStyle { class PaynymFollowToggleButton extends ConsumerStatefulWidget { const PaynymFollowToggleButton({ - Key? key, + super.key, required this.walletId, required this.paymentCodeStringToFollow, this.style = PaynymFollowToggleButtonStyle.primary, - }) : super(key: key); + }); final String walletId; final String paymentCodeStringToFollow; @@ -83,8 +84,11 @@ class _PaynymFollowToggleButtonState // sign token with notification private key String signature = await wallet.signStringWithNotificationKey(token.value!); - var result = await ref.read(paynymAPIProvider).follow(token.value!, - signature, followedAccount.value!.nonSegwitPaymentCode.code); + var result = await ref.read(paynymAPIProvider).follow( + token.value!, + signature, + followedAccount.value!.nonSegwitPaymentCode.code, + ); int i = 0; for (; @@ -96,8 +100,11 @@ class _PaynymFollowToggleButtonState // sign token with notification private key signature = await wallet.signStringWithNotificationKey(token.value!); - result = await ref.read(paynymAPIProvider).follow(token.value!, signature, - followedAccount.value!.nonSegwitPaymentCode.code); + result = await ref.read(paynymAPIProvider).follow( + token.value!, + signature, + followedAccount.value!.nonSegwitPaymentCode.code, + ); await Future.delayed(const Duration(milliseconds: 200)); print("RRR result: $result"); @@ -181,8 +188,11 @@ class _PaynymFollowToggleButtonState // sign token with notification private key String signature = await wallet.signStringWithNotificationKey(token.value!); - var result = await ref.read(paynymAPIProvider).unfollow(token.value!, - signature, followedAccount.value!.nonSegwitPaymentCode.code); + var result = await ref.read(paynymAPIProvider).unfollow( + token.value!, + signature, + followedAccount.value!.nonSegwitPaymentCode.code, + ); int i = 0; for (; @@ -194,8 +204,11 @@ class _PaynymFollowToggleButtonState // sign token with notification private key signature = await wallet.signStringWithNotificationKey(token.value!); - result = await ref.read(paynymAPIProvider).unfollow(token.value!, - signature, followedAccount.value!.nonSegwitPaymentCode.code); + result = await ref.read(paynymAPIProvider).unfollow( + token.value!, + signature, + followedAccount.value!.nonSegwitPaymentCode.code, + ); await Future.delayed(const Duration(milliseconds: 200)); print("unfollow RRR result: $result"); } diff --git a/lib/widgets/custom_buttons/simple_copy_button.dart b/lib/widgets/custom_buttons/simple_copy_button.dart index e8bcc560c..794f0cdb1 100644 --- a/lib/widgets/custom_buttons/simple_copy_button.dart +++ b/lib/widgets/custom_buttons/simple_copy_button.dart @@ -20,9 +20,9 @@ import '../../utilities/text_styles.dart'; class SimpleCopyButton extends StatelessWidget { const SimpleCopyButton({ - Key? key, + super.key, required this.data, - }) : super(key: key); + }); final String data; diff --git a/lib/widgets/custom_loading_overlay.dart b/lib/widgets/custom_loading_overlay.dart index f5c326d01..45d02a5d1 100644 --- a/lib/widgets/custom_loading_overlay.dart +++ b/lib/widgets/custom_loading_overlay.dart @@ -21,13 +21,13 @@ import 'loading_indicator.dart'; class CustomLoadingOverlay extends ConsumerStatefulWidget { const CustomLoadingOverlay({ - Key? key, + super.key, required this.message, this.subMessage, required this.eventBus, this.textColor, this.actionButton, - }) : super(key: key); + }); final String message; final String? subMessage; diff --git a/lib/widgets/custom_page_view/custom_page_view.dart b/lib/widgets/custom_page_view/custom_page_view.dart index 5540820fe..6e26139d2 100644 --- a/lib/widgets/custom_page_view/custom_page_view.dart +++ b/lib/widgets/custom_page_view/custom_page_view.dart @@ -18,6 +18,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart' show precisionErrorTolerance; import 'package:flutter/gestures.dart' show DragStartBehavior; import 'package:flutter/rendering.dart'; + import 'custom_sliver_fill_viewport.dart'; /// A controller for [CustomPageView]. @@ -227,14 +228,19 @@ class PageController extends ScrollController { /// The returned [Future] resolves when the animation completes. /// /// The `duration` and `curve` arguments must not be null. - Future previousPage( - {required Duration duration, required Curve curve}) { + Future previousPage({ + required Duration duration, + required Curve curve, + }) { return animateToPage(page!.round() - 1, duration: duration, curve: curve); } @override - ScrollPosition createScrollPosition(ScrollPhysics physics, - ScrollContext context, ScrollPosition? oldPosition) { + ScrollPosition createScrollPosition( + ScrollPhysics physics, + ScrollContext context, + ScrollPosition? oldPosition, + ) { return _PagePosition( physics: physics, context: context, @@ -260,21 +266,14 @@ class PageController extends ScrollController { class PageMetrics extends FixedScrollMetrics { /// Creates an immutable snapshot of values associated with a [CustomPageView]. PageMetrics({ - required double? minScrollExtent, - required double? maxScrollExtent, - required double? pixels, - required double? viewportDimension, - required AxisDirection axisDirection, - required double devicePixelRatio, + required super.minScrollExtent, + required super.maxScrollExtent, + required super.pixels, + required super.viewportDimension, + required super.axisDirection, + required super.devicePixelRatio, required this.viewportFraction, - }) : super( - minScrollExtent: minScrollExtent, - maxScrollExtent: maxScrollExtent, - pixels: pixels, - viewportDimension: viewportDimension, - axisDirection: axisDirection, - devicePixelRatio: devicePixelRatio, - ); + }); @override PageMetrics copyWith({ @@ -315,21 +314,18 @@ class PageMetrics extends FixedScrollMetrics { class _PagePosition extends ScrollPositionWithSingleContext implements PageMetrics { _PagePosition({ - required ScrollPhysics physics, - required ScrollContext context, + required super.physics, + required super.context, this.initialPage = 0, bool keepPage = true, double viewportFraction = 1.0, - ScrollPosition? oldPosition, + super.oldPosition, }) : assert(viewportFraction > 0.0), _viewportFraction = viewportFraction, _pageToUseOnStartup = initialPage.toDouble(), super( - physics: physics, - context: context, initialPixels: null, keepScrollOffset: keepPage, - oldPosition: oldPosition, ); final int initialPage; @@ -405,14 +401,18 @@ class _PagePosition extends ScrollPositionWithSingleContext return !hasPixels || !hasContentDimensions ? null : _cachedPage ?? - getPageFromPixels(pixels.clamp(minScrollExtent, maxScrollExtent), - viewportDimension); + getPageFromPixels( + pixels.clamp(minScrollExtent, maxScrollExtent), + viewportDimension, + ); } @override void saveScrollOffset() { - PageStorage.of(context.storageContext).writeState(context.storageContext, - _cachedPage ?? getPageFromPixels(pixels, viewportDimension)); + PageStorage.of(context.storageContext).writeState( + context.storageContext, + _cachedPage ?? getPageFromPixels(pixels, viewportDimension), + ); } @override @@ -427,7 +427,8 @@ class _PagePosition extends ScrollPositionWithSingleContext @override void saveOffset() { context.saveOffset( - _cachedPage ?? getPageFromPixels(pixels, viewportDimension)); + _cachedPage ?? getPageFromPixels(pixels, viewportDimension), + ); } @override @@ -507,8 +508,8 @@ class _PagePosition extends ScrollPositionWithSingleContext class _ForceImplicitScrollPhysics extends ScrollPhysics { const _ForceImplicitScrollPhysics({ required this.allowImplicitScrolling, - ScrollPhysics? parent, - }) : super(parent: parent); + super.parent, + }); @override _ForceImplicitScrollPhysics applyTo(ScrollPhysics? ancestor) { @@ -533,7 +534,7 @@ class _ForceImplicitScrollPhysics extends ScrollPhysics { /// * [CustomPageView.physics], which can override the physics used by a page view. class PageScrollPhysics extends ScrollPhysics { /// Creates physics for a [CustomPageView]. - const PageScrollPhysics({ScrollPhysics? parent}) : super(parent: parent); + const PageScrollPhysics({super.parent}); @override PageScrollPhysics applyTo(ScrollPhysics? ancestor) { @@ -551,7 +552,10 @@ class PageScrollPhysics extends ScrollPhysics { } double _getTargetPixels( - ScrollMetrics position, Tolerance tolerance, double velocity) { + ScrollMetrics position, + Tolerance tolerance, + double velocity, + ) { double page = _getPage(position); if (velocity < -tolerance.velocity) { page -= 0.5; @@ -563,7 +567,9 @@ class PageScrollPhysics extends ScrollPhysics { @override Simulation? createBallisticSimulation( - ScrollMetrics position, double velocity) { + ScrollMetrics position, + double velocity, + ) { // If we're out of range and not headed back in range, defer to the parent // ballistics, which should put us back in range at a page boundary. if ((velocity <= 0.0 && position.pixels <= position.minScrollExtent) || @@ -573,8 +579,13 @@ class PageScrollPhysics extends ScrollPhysics { final Tolerance tolerance = this.tolerance; final double target = _getTargetPixels(position, tolerance, velocity); if (target != position.pixels) { - return ScrollSpringSimulation(spring, position.pixels, target, velocity, - tolerance: tolerance); + return ScrollSpringSimulation( + spring, + position.pixels, + target, + velocity, + tolerance: tolerance, + ); } return null; } @@ -641,7 +652,7 @@ class CustomPageView extends StatefulWidget { /// rather than into the contents of the [CustomPageView]. /// {@endtemplate} CustomPageView({ - Key? key, + super.key, this.scrollDirection = Axis.horizontal, this.reverse = false, PageController? controller, @@ -657,8 +668,7 @@ class CustomPageView extends StatefulWidget { this.scrollBehavior, this.padEnds = true, }) : controller = controller ?? _defaultPageController, - childrenDelegate = SliverChildListDelegate(children), - super(key: key); + childrenDelegate = SliverChildListDelegate(children); /// Creates a scrollable list that works page by page using widgets that are /// created on demand. @@ -684,7 +694,7 @@ class CustomPageView extends StatefulWidget { /// /// {@macro flutter.widgets.PageView.allowImplicitScrolling} CustomPageView.builder({ - Key? key, + super.key, this.scrollDirection = Axis.horizontal, this.reverse = false, PageController? controller, @@ -706,8 +716,7 @@ class CustomPageView extends StatefulWidget { itemBuilder, findChildIndexCallback: findChildIndexCallback, childCount: itemCount, - ), - super(key: key); + ); /// Creates a scrollable list that works page by page with a custom child /// model. @@ -794,7 +803,7 @@ class CustomPageView extends StatefulWidget { /// /// {@macro flutter.widgets.PageView.allowImplicitScrolling} CustomPageView.custom({ - Key? key, + super.key, this.scrollDirection = Axis.horizontal, this.reverse = false, PageController? controller, @@ -809,8 +818,7 @@ class CustomPageView extends StatefulWidget { this.scrollBehavior, this.viewportFractionalPadding = 0.25, this.padEnds = true, - }) : controller = controller ?? _defaultPageController, - super(key: key); + }) : controller = controller ?? _defaultPageController; final double viewportFractionalPadding; @@ -951,8 +959,10 @@ class _CustomPageViewState extends State { allowImplicitScrolling: widget.allowImplicitScrolling, ).applyTo( widget.pageSnapping - ? _kPagePhysics.applyTo(widget.physics ?? - widget.scrollBehavior?.getScrollPhysics(context)) + ? _kPagePhysics.applyTo( + widget.physics ?? + widget.scrollBehavior?.getScrollPhysics(context), + ) : widget.physics ?? widget.scrollBehavior?.getScrollPhysics(context), ); @@ -1008,17 +1018,35 @@ class _CustomPageViewState extends State { description .add(EnumProperty('scrollDirection', widget.scrollDirection)); description.add( - FlagProperty('reverse', value: widget.reverse, ifTrue: 'reversed')); - description.add(DiagnosticsProperty( - 'controller', widget.controller, - showName: false)); - description.add(DiagnosticsProperty( - 'physics', widget.physics, - showName: false)); - description.add(FlagProperty('pageSnapping', - value: widget.pageSnapping, ifFalse: 'snapping disabled')); - description.add(FlagProperty('allowImplicitScrolling', + FlagProperty('reverse', value: widget.reverse, ifTrue: 'reversed'), + ); + description.add( + DiagnosticsProperty( + 'controller', + widget.controller, + showName: false, + ), + ); + description.add( + DiagnosticsProperty( + 'physics', + widget.physics, + showName: false, + ), + ); + description.add( + FlagProperty( + 'pageSnapping', + value: widget.pageSnapping, + ifFalse: 'snapping disabled', + ), + ); + description.add( + FlagProperty( + 'allowImplicitScrolling', value: widget.allowImplicitScrolling, - ifTrue: 'allow implicit scrolling')); + ifTrue: 'allow implicit scrolling', + ), + ); } } diff --git a/lib/widgets/custom_page_view/custom_sliver_fill_viewport.dart b/lib/widgets/custom_page_view/custom_sliver_fill_viewport.dart index 94f6a2e35..54e99fa2b 100644 --- a/lib/widgets/custom_page_view/custom_sliver_fill_viewport.dart +++ b/lib/widgets/custom_page_view/custom_sliver_fill_viewport.dart @@ -34,15 +34,14 @@ import 'package:flutter/rendering.dart'; class CustomSliverFillViewport extends StatelessWidget { /// Creates a sliver whose box children that each fill the viewport. const CustomSliverFillViewport({ - Key? key, + super.key, required this.delegate, this.viewportFraction = 1.0, this.viewportFractionalPadding = 0.25, this.padEnds = true, }) : assert(viewportFraction > 0.0), assert(viewportFractionalPadding > 0.0), - assert(viewportFractionalPadding <= 0.25), - super(key: key); + assert(viewportFractionalPadding <= 0.25); /// The fraction of the viewport that each child should fill in the main axis. /// @@ -84,11 +83,10 @@ class CustomSliverFillViewport extends StatelessWidget { class _SliverFillViewportRenderObjectWidget extends SliverMultiBoxAdaptorWidget { const _SliverFillViewportRenderObjectWidget({ - Key? key, - required SliverChildDelegate delegate, + super.key, + required super.delegate, this.viewportFraction = 1.0, - }) : assert(viewportFraction > 0.0), - super(key: key, delegate: delegate); + }) : assert(viewportFraction > 0.0); final double viewportFraction; @@ -97,12 +95,16 @@ class _SliverFillViewportRenderObjectWidget final SliverMultiBoxAdaptorElement element = context as SliverMultiBoxAdaptorElement; return RenderSliverFillViewport( - childManager: element, viewportFraction: viewportFraction); + childManager: element, + viewportFraction: viewportFraction, + ); } @override void updateRenderObject( - BuildContext context, RenderSliverFillViewport renderObject) { + BuildContext context, + RenderSliverFillViewport renderObject, + ) { renderObject.viewportFraction = viewportFraction; } } @@ -123,7 +125,9 @@ class _SliverFractionalPadding extends SingleChildRenderObjectWidget { @override void updateRenderObject( - BuildContext context, _RenderSliverFractionalPadding renderObject) { + BuildContext context, + _RenderSliverFractionalPadding renderObject, + ) { renderObject.viewportFraction = viewportFraction; } } @@ -274,11 +278,11 @@ class _RenderSliverFractionalPadding extends RenderSliverEdgeInsetsPadding { class SliverFillRemaining extends StatelessWidget { /// Creates a sliver that fills the remaining space in the viewport. const SliverFillRemaining({ - Key? key, + super.key, this.child, this.hasScrollBody = true, this.fillOverscroll = false, - }) : super(key: key); + }); /// Box child widget that fills the remaining space in the viewport. /// @@ -336,22 +340,23 @@ class SliverFillRemaining extends StatelessWidget { class _SliverFillRemainingWithScrollable extends SingleChildRenderObjectWidget { const _SliverFillRemainingWithScrollable({ - Key? key, - Widget? child, - }) : super(key: key, child: child); + super.key, + super.child, + }); @override RenderSliverFillRemainingWithScrollable createRenderObject( - BuildContext context) => + BuildContext context, + ) => RenderSliverFillRemainingWithScrollable(); } class _SliverFillRemainingWithoutScrollable extends SingleChildRenderObjectWidget { const _SliverFillRemainingWithoutScrollable({ - Key? key, - Widget? child, - }) : super(key: key, child: child); + super.key, + super.child, + }); @override RenderSliverFillRemaining createRenderObject(BuildContext context) => @@ -360,12 +365,13 @@ class _SliverFillRemainingWithoutScrollable class _SliverFillRemainingAndOverscroll extends SingleChildRenderObjectWidget { const _SliverFillRemainingAndOverscroll({ - Key? key, - Widget? child, - }) : super(key: key, child: child); + super.key, + super.child, + }); @override RenderSliverFillRemainingAndOverscroll createRenderObject( - BuildContext context) => + BuildContext context, + ) => RenderSliverFillRemainingAndOverscroll(); } diff --git a/lib/widgets/custom_pin_put/custom_pin_put.dart b/lib/widgets/custom_pin_put/custom_pin_put.dart index 7d978076b..999775b07 100644 --- a/lib/widgets/custom_pin_put/custom_pin_put.dart +++ b/lib/widgets/custom_pin_put/custom_pin_put.dart @@ -15,7 +15,7 @@ import 'pin_keyboard.dart'; class CustomPinPut extends StatefulWidget { const CustomPinPut({ - Key? key, + super.key, required this.fieldsCount, required this.isRandom, this.height, @@ -64,7 +64,7 @@ class CustomPinPut extends StatefulWidget { this.autofillHints, this.customKey, this.onPinLengthChanged, - }) : super(key: key); + }); final void Function(int)? onPinLengthChanged; diff --git a/lib/widgets/custom_pin_put/pin_keyboard.dart b/lib/widgets/custom_pin_put/pin_keyboard.dart index 274287a17..9eb44ae4d 100644 --- a/lib/widgets/custom_pin_put/pin_keyboard.dart +++ b/lib/widgets/custom_pin_put/pin_keyboard.dart @@ -12,16 +12,17 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; + import '../../themes/stack_colors.dart'; import '../../utilities/assets.dart'; import '../../utilities/text_styles.dart'; class NumberKey extends StatefulWidget { const NumberKey({ - Key? key, + super.key, required this.number, required this.onPressed, - }) : super(key: key); + }); final String number; final ValueSetter onPressed; @@ -94,9 +95,9 @@ class _NumberKeyState extends State { class BackspaceKey extends StatefulWidget { const BackspaceKey({ - Key? key, + super.key, required this.onPressed, - }) : super(key: key); + }); final VoidCallback onPressed; @@ -129,51 +130,51 @@ class _BackspaceKeyState extends State { shadows: const [], ), child: MaterialButton( - // splashColor: Theme.of(context).extension()!.highlight, - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - shape: const StadiumBorder(), - onPressed: () { - onPressed.call(); - setState(() { - _color = Theme.of(context) - .extension()! - .numpadBackDefault - .withOpacity(0.8); - }); + // splashColor: Theme.of(context).extension()!.highlight, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + shape: const StadiumBorder(), + onPressed: () { + onPressed.call(); + setState(() { + _color = Theme.of(context) + .extension()! + .numpadBackDefault + .withOpacity(0.8); + }); - Future.delayed(const Duration(milliseconds: 200), () { - if (mounted) { - setState(() { - _color = Theme.of(context) - .extension()! - .numpadBackDefault; - }); - } - }); - }, - child: Semantics( - label: "Backspace Button. Deletes The Last Digit.", - excludeSemantics: true, - child: Center( - child: SvgPicture.asset( - Assets.svg.delete, - width: 20, - height: 20, - color: Theme.of(context) + Future.delayed(const Duration(milliseconds: 200), () { + if (mounted) { + setState(() { + _color = Theme.of(context) .extension()! - .numpadTextDefault, - ), + .numpadBackDefault; + }); + } + }); + }, + child: Semantics( + label: "Backspace Button. Deletes The Last Digit.", + excludeSemantics: true, + child: Center( + child: SvgPicture.asset( + Assets.svg.delete, + width: 20, + height: 20, + color: + Theme.of(context).extension()!.numpadTextDefault, ), - )), + ), + ), + ), ); } } class SubmitKey extends StatelessWidget { const SubmitKey({ - Key? key, + super.key, required this.onPressed, - }) : super(key: key); + }); final VoidCallback onPressed; @@ -210,11 +211,11 @@ class SubmitKey extends StatelessWidget { class CustomKey extends StatelessWidget { const CustomKey({ - Key? key, + super.key, required this.onPressed, this.iconAssetName, this.semanticsLabel = "Button", - }) : super(key: key); + }); final VoidCallback onPressed; final String? iconAssetName; @@ -223,43 +224,44 @@ class CustomKey extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - height: 72, - width: 72, - decoration: ShapeDecoration( + height: 72, + width: 72, + decoration: ShapeDecoration( + shape: const StadiumBorder(), + color: Theme.of(context).extension()!.numpadBackDefault, + shadows: const [], + ), + child: Semantics( + label: semanticsLabel, + excludeSemantics: true, + child: MaterialButton( + // splashColor: Theme.of(context).extension()!.highlight, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, shape: const StadiumBorder(), - color: Theme.of(context).extension()!.numpadBackDefault, - shadows: const [], - ), - child: Semantics( - label: semanticsLabel, - excludeSemantics: true, - child: MaterialButton( - // splashColor: Theme.of(context).extension()!.highlight, - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - shape: const StadiumBorder(), - onPressed: () { - onPressed.call(); - }, - child: Center( - child: iconAssetName == null - ? null - : SvgPicture.asset( - iconAssetName!, - width: 20, - height: 20, - color: Theme.of(context) - .extension()! - .numpadTextDefault, - ), - ), + onPressed: () { + onPressed.call(); + }, + child: Center( + child: iconAssetName == null + ? null + : SvgPicture.asset( + iconAssetName!, + width: 20, + height: 20, + color: Theme.of(context) + .extension()! + .numpadTextDefault, + ), ), - )); + ), + ), + ); } } class PinKeyboard extends ConsumerWidget { const PinKeyboard({ - Key? key, + super.key, required this.onNumberKeyPressed, required this.onBackPressed, required this.onSubmitPressed, @@ -268,7 +270,7 @@ class PinKeyboard extends ConsumerWidget { this.width = 264, this.height = 360, this.customKey, - }) : super(key: key); + }); final ValueSetter onNumberKeyPressed; final VoidCallback onBackPressed; @@ -411,7 +413,7 @@ class PinKeyboard extends ConsumerWidget { onPressed: _submitHandler, ), ], - ) + ), ], ), ); @@ -420,7 +422,7 @@ class PinKeyboard extends ConsumerWidget { class RandomKeyboard extends StatelessWidget { const RandomKeyboard({ - Key? key, + super.key, required this.onNumberKeyPressed, required this.onBackPressed, required this.onSubmitPressed, @@ -428,7 +430,7 @@ class RandomKeyboard extends StatelessWidget { this.width = 264, this.height = 360, this.customKey, - }) : super(key: key); + }); final ValueSetter onNumberKeyPressed; final VoidCallback onBackPressed; @@ -567,7 +569,7 @@ class RandomKeyboard extends StatelessWidget { onPressed: _submitHandler, ), ], - ) + ), ], ), ); diff --git a/lib/widgets/custom_tab_view.dart b/lib/widgets/custom_tab_view.dart index db69a47a5..181f67341 100644 --- a/lib/widgets/custom_tab_view.dart +++ b/lib/widgets/custom_tab_view.dart @@ -9,18 +9,18 @@ */ import 'package:flutter/material.dart'; + import '../themes/stack_colors.dart'; import '../utilities/text_styles.dart'; class CustomTabView extends StatefulWidget { const CustomTabView({ - Key? key, + super.key, required this.titles, required this.children, this.initialIndex = 0, this.childPadding, - }) : assert(titles.length == children.length), - super(key: key); + }) : assert(titles.length == children.length); final List titles; final List children; diff --git a/lib/widgets/desktop/custom_text_button.dart b/lib/widgets/desktop/custom_text_button.dart index a868ff9ff..e526ba9af 100644 --- a/lib/widgets/desktop/custom_text_button.dart +++ b/lib/widgets/desktop/custom_text_button.dart @@ -23,11 +23,11 @@ enum ButtonHeight { class CustomTextButtonBase extends StatelessWidget { const CustomTextButtonBase({ - Key? key, + super.key, this.width, this.height, this.textButton, - }) : super(key: key); + }); final double? width; final double? height; diff --git a/lib/widgets/desktop/delete_button.dart b/lib/widgets/desktop/delete_button.dart index 897a4f8fe..80eaa79b6 100644 --- a/lib/widgets/desktop/delete_button.dart +++ b/lib/widgets/desktop/delete_button.dart @@ -10,6 +10,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; + import '../../themes/stack_colors.dart'; import '../../utilities/assets.dart'; import '../../utilities/text_styles.dart'; @@ -18,14 +19,14 @@ import 'custom_text_button.dart'; class DeleteButton extends StatelessWidget { const DeleteButton({ - Key? key, + super.key, this.width, this.height, this.label, this.onPressed, this.enabled = true, this.desktopMed = false, - }) : super(key: key); + }); final double? width; final double? height; @@ -48,7 +49,8 @@ class DeleteButton extends StatelessWidget { return enabled ? STextStyles.desktopButtonSecondaryEnabled(context).copyWith( color: - Theme.of(context).extension()!.accentColorRed) + Theme.of(context).extension()!.accentColorRed, + ) : STextStyles.desktopButtonSecondaryDisabled(context); } } else { diff --git a/lib/widgets/desktop/desktop_app_bar.dart b/lib/widgets/desktop/desktop_app_bar.dart index 2c6b39763..2cb365153 100644 --- a/lib/widgets/desktop/desktop_app_bar.dart +++ b/lib/widgets/desktop/desktop_app_bar.dart @@ -16,7 +16,7 @@ const double kDesktopAppBarHeightCompact = 82.0; class DesktopAppBar extends StatelessWidget { const DesktopAppBar({ - Key? key, + super.key, this.leading, this.center, this.overlayCenter, @@ -24,7 +24,7 @@ class DesktopAppBar extends StatelessWidget { this.background = Colors.transparent, required this.isCompactHeight, this.useSpacers = true, - }) : super(key: key); + }); final Widget? leading; final Widget? center; diff --git a/lib/widgets/desktop/desktop_dialog.dart b/lib/widgets/desktop/desktop_dialog.dart index d01fb3d77..e550627df 100644 --- a/lib/widgets/desktop/desktop_dialog.dart +++ b/lib/widgets/desktop/desktop_dialog.dart @@ -13,11 +13,11 @@ import '../../themes/stack_colors.dart'; class DesktopDialog extends StatelessWidget { const DesktopDialog({ - Key? key, + super.key, this.child, this.maxWidth = 641, this.maxHeight = 474, - }) : super(key: key); + }); final Widget? child; final double maxWidth; diff --git a/lib/widgets/desktop/desktop_dialog_close_button.dart b/lib/widgets/desktop/desktop_dialog_close_button.dart index a9b258737..4ec2ee3d8 100644 --- a/lib/widgets/desktop/desktop_dialog_close_button.dart +++ b/lib/widgets/desktop/desktop_dialog_close_button.dart @@ -16,9 +16,9 @@ import '../custom_buttons/app_bar_icon_button.dart'; class DesktopDialogCloseButton extends StatelessWidget { const DesktopDialogCloseButton({ - Key? key, + super.key, this.onPressedOverride, - }) : super(key: key); + }); final VoidCallback? onPressedOverride; diff --git a/lib/widgets/desktop/desktop_fee_dialog.dart b/lib/widgets/desktop/desktop_fee_dialog.dart index 08597b9eb..90304e80c 100644 --- a/lib/widgets/desktop/desktop_fee_dialog.dart +++ b/lib/widgets/desktop/desktop_fee_dialog.dart @@ -11,10 +11,6 @@ import '../../utilities/amount/amount.dart'; import '../../utilities/amount/amount_formatter.dart'; import '../../utilities/enums/fee_rate_type_enum.dart'; import '../../utilities/text_styles.dart'; -import '../../wallets/crypto_currency/coins/ethereum.dart'; -import '../../wallets/crypto_currency/coins/firo.dart'; -import '../../wallets/crypto_currency/coins/monero.dart'; -import '../../wallets/crypto_currency/coins/wownero.dart'; import '../../wallets/crypto_currency/crypto_currency.dart'; import '../../wallets/isar/providers/eth/current_token_wallet_provider.dart'; import '../../wallets/wallet/impl/firo_wallet.dart'; @@ -311,9 +307,9 @@ class _DesktopFeeItemState extends ConsumerState { int targetBlockTime, int estimatedNumberOfBlocks, ) { - int time = targetBlockTime * estimatedNumberOfBlocks; + final int time = targetBlockTime * estimatedNumberOfBlocks; - int hours = (time / 3600).floor(); + final int hours = (time / 3600).floor(); if (hours > 1) { return "~$hours hours"; } else if (hours == 1) { diff --git a/lib/widgets/desktop/desktop_scaffold.dart b/lib/widgets/desktop/desktop_scaffold.dart index f3913b094..25b18963b 100644 --- a/lib/widgets/desktop/desktop_scaffold.dart +++ b/lib/widgets/desktop/desktop_scaffold.dart @@ -14,11 +14,11 @@ import '../background.dart'; class DesktopScaffold extends StatelessWidget { const DesktopScaffold({ - Key? key, + super.key, this.background, this.appBar, this.body, - }) : super(key: key); + }); final Color? background; final Widget? appBar; @@ -47,12 +47,12 @@ class DesktopScaffold extends StatelessWidget { class MasterScaffold extends StatelessWidget { const MasterScaffold({ - Key? key, + super.key, required this.isDesktop, required this.appBar, required this.body, this.background, - }) : super(key: key); + }); final bool isDesktop; final Widget appBar; diff --git a/lib/widgets/desktop/outline_blue_button.dart b/lib/widgets/desktop/outline_blue_button.dart index 6073db347..e0b4c0a20 100644 --- a/lib/widgets/desktop/outline_blue_button.dart +++ b/lib/widgets/desktop/outline_blue_button.dart @@ -19,7 +19,7 @@ export 'custom_text_button.dart'; class OutlineBlueButton extends StatelessWidget { const OutlineBlueButton({ - Key? key, + super.key, this.width, this.height, this.label, @@ -28,7 +28,7 @@ class OutlineBlueButton extends StatelessWidget { this.enabled = true, this.buttonHeight, this.iconSpacing = 10, - }) : super(key: key); + }); final double? width; final double? height; diff --git a/lib/widgets/desktop/paynym_search_button.dart b/lib/widgets/desktop/paynym_search_button.dart index 48d8134a3..d1a757d98 100644 --- a/lib/widgets/desktop/paynym_search_button.dart +++ b/lib/widgets/desktop/paynym_search_button.dart @@ -16,9 +16,9 @@ import '../rounded_container.dart'; class PaynymSearchButton extends StatefulWidget { const PaynymSearchButton({ - Key? key, + super.key, required this.onPressed, - }) : super(key: key); + }); final VoidCallback onPressed; diff --git a/lib/widgets/desktop/primary_button.dart b/lib/widgets/desktop/primary_button.dart index e9d555700..c2f126998 100644 --- a/lib/widgets/desktop/primary_button.dart +++ b/lib/widgets/desktop/primary_button.dart @@ -19,7 +19,7 @@ export 'custom_text_button.dart'; class PrimaryButton extends StatelessWidget { const PrimaryButton({ - Key? key, + super.key, this.width, this.height, this.label, @@ -28,7 +28,7 @@ class PrimaryButton extends StatelessWidget { this.enabled = true, this.buttonHeight, this.iconSpacing = 10, - }) : super(key: key); + }); final double? width; final double? height; diff --git a/lib/widgets/desktop/secondary_button.dart b/lib/widgets/desktop/secondary_button.dart index eba9a59f3..f9e18ec0a 100644 --- a/lib/widgets/desktop/secondary_button.dart +++ b/lib/widgets/desktop/secondary_button.dart @@ -19,7 +19,7 @@ export 'custom_text_button.dart'; class SecondaryButton extends StatelessWidget { const SecondaryButton({ - Key? key, + super.key, this.width, this.height, this.label, @@ -30,7 +30,7 @@ class SecondaryButton extends StatelessWidget { this.buttonHeight, this.iconSpacing = 10, this.padding = EdgeInsets.zero, - }) : super(key: key); + }); final double? width; final double? height; diff --git a/lib/widgets/desktop/simple_desktop_dialog.dart b/lib/widgets/desktop/simple_desktop_dialog.dart index 4b342c060..80cad79b0 100644 --- a/lib/widgets/desktop/simple_desktop_dialog.dart +++ b/lib/widgets/desktop/simple_desktop_dialog.dart @@ -16,10 +16,10 @@ import 'primary_button.dart'; class SimpleDesktopDialog extends StatelessWidget { const SimpleDesktopDialog({ - Key? key, + super.key, required this.title, required this.message, - }) : super(key: key); + }); final String title; final String message; @@ -80,7 +80,7 @@ class SimpleDesktopDialog extends StatelessWidget { ), ], ), - ) + ), ], ), ); diff --git a/lib/widgets/dialogs/basic_dialog.dart b/lib/widgets/dialogs/basic_dialog.dart index fcb54d2eb..8071ec53c 100644 --- a/lib/widgets/dialogs/basic_dialog.dart +++ b/lib/widgets/dialogs/basic_dialog.dart @@ -17,7 +17,7 @@ import '../stack_dialog.dart'; class BasicDialog extends StatelessWidget { const BasicDialog({ - Key? key, + super.key, this.leftButton, this.rightButton, this.icon, @@ -27,7 +27,7 @@ class BasicDialog extends StatelessWidget { this.desktopWidth = 641, this.canPopWithBackButton = false, this.flex = false, - }) : super(key: key); + }); final Widget? leftButton; final Widget? rightButton; @@ -103,7 +103,7 @@ class BasicDialog extends StatelessWidget { : const Spacer(), ], ), - ) + ), ], ), ); diff --git a/lib/widgets/dialogs/frost/frost_step_explanation_dialog.dart b/lib/widgets/dialogs/frost/frost_step_explanation_dialog.dart index fc8be900c..2b3dffa81 100644 --- a/lib/widgets/dialogs/frost/frost_step_explanation_dialog.dart +++ b/lib/widgets/dialogs/frost/frost_step_explanation_dialog.dart @@ -9,6 +9,7 @@ */ import 'package:flutter/material.dart'; + import '../../../utilities/text_styles.dart'; import '../../desktop/secondary_button.dart'; import '../../stack_dialog.dart'; @@ -16,7 +17,8 @@ import '../../stack_dialog.dart'; class FrostStepExplanationDialog extends StatelessWidget { final String title; final String body; - const FrostStepExplanationDialog({super.key, required this.title, required this.body}); + const FrostStepExplanationDialog( + {super.key, required this.title, required this.body}); @override Widget build(BuildContext context) { diff --git a/lib/widgets/dialogs/frost/frost_step_qr_dialog.dart b/lib/widgets/dialogs/frost/frost_step_qr_dialog.dart index 2de302720..c19eb2b2c 100644 --- a/lib/widgets/dialogs/frost/frost_step_qr_dialog.dart +++ b/lib/widgets/dialogs/frost/frost_step_qr_dialog.dart @@ -9,6 +9,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:path_provider/path_provider.dart'; import 'package:qr_flutter/qr_flutter.dart'; import 'package:share_plus/share_plus.dart'; + import '../../../notifications/show_flush_bar.dart'; import '../../../themes/stack_colors.dart'; import '../../../utilities/assets.dart'; @@ -16,9 +17,9 @@ import '../../../utilities/text_styles.dart'; import '../../../utilities/util.dart'; import '../../conditional_parent.dart'; import '../../desktop/secondary_button.dart'; -import '../simple_mobile_dialog.dart'; import '../../rounded_container.dart'; import '../../rounded_white_container.dart'; +import '../simple_mobile_dialog.dart'; class FrostStepQrDialog extends StatefulWidget { const FrostStepQrDialog({ @@ -52,7 +53,8 @@ class _FrostStepQrDialogState extends State { final dir = Directory("${Platform.environment['HOME']}"); if (!dir.existsSync()) { throw Exception( - "Home dir not found while trying to open filepicker on QR image save"); + "Home dir not found while trying to open filepicker on QR image save", + ); } final path = await FilePicker.platform.saveFile( fileName: "qrcode.png", @@ -91,8 +93,10 @@ class _FrostStepQrDialogState extends State { final file = await File("${tempDir.path}/qrcode.png").create(); await file.writeAsBytes(pngBytes); - await Share.shareFiles(["${tempDir.path}/qrcode.png"], - text: "Receive URI QR Code"); + await Share.shareFiles( + ["${tempDir.path}/qrcode.png"], + text: "Receive URI QR Code", + ); } } catch (e) { //todo: comeback to this @@ -111,7 +115,7 @@ class _FrostStepQrDialogState extends State { key: _qrKey, child: RoundedWhiteContainer( boxShadow: [ - Theme.of(context).extension()!.standardBoxShadow + Theme.of(context).extension()!.standardBoxShadow, ], child: Column( mainAxisSize: MainAxisSize.min, diff --git a/lib/widgets/emoji_select_sheet.dart b/lib/widgets/emoji_select_sheet.dart index b4330a232..608aa30a3 100644 --- a/lib/widgets/emoji_select_sheet.dart +++ b/lib/widgets/emoji_select_sheet.dart @@ -12,6 +12,7 @@ import 'package:emojis/emoji.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; + import '../themes/stack_colors.dart'; import '../utilities/assets.dart'; import '../utilities/constants.dart'; @@ -25,8 +26,8 @@ import 'textfield_icon_button.dart'; class EmojiSelectSheet extends ConsumerStatefulWidget { const EmojiSelectSheet({ - Key? key, - }) : super(key: key); + super.key, + }); final double horizontalPadding = 24; final double emojiSize = 24; @@ -55,11 +56,13 @@ class _EmojiSelectSheetState extends ConsumerState { text = text.toLowerCase(); return Emoji.all() - .where((e) => e.keywords - .where( - (e) => e.contains(text), - ) - .isNotEmpty) + .where( + (e) => e.keywords + .where( + (e) => e.contains(text), + ) + .isNotEmpty, + ) .toList(growable: false); } @@ -239,7 +242,7 @@ class _EmojiSelectSheetState extends ConsumerState { ); }, ), - ) + ), ], ), ), diff --git a/lib/widgets/eth_wallet_radio.dart b/lib/widgets/eth_wallet_radio.dart index a164d85ba..fe1da6efa 100644 --- a/lib/widgets/eth_wallet_radio.dart +++ b/lib/widgets/eth_wallet_radio.dart @@ -18,10 +18,10 @@ import 'wallet_info_row/sub_widgets/wallet_info_row_coin_icon.dart'; class EthWalletRadio extends ConsumerStatefulWidget { const EthWalletRadio({ - Key? key, + super.key, required this.walletId, this.selectedWalletId, - }) : super(key: key); + }); final String walletId; final String? selectedWalletId; diff --git a/lib/widgets/exchange/trocador/trocador_kyc_icon.dart b/lib/widgets/exchange/trocador/trocador_kyc_icon.dart index dc7b75a26..17161cac8 100644 --- a/lib/widgets/exchange/trocador/trocador_kyc_icon.dart +++ b/lib/widgets/exchange/trocador/trocador_kyc_icon.dart @@ -16,11 +16,11 @@ import 'trocador_rating_type_enum.dart'; class TrocadorKYCIcon extends StatelessWidget { const TrocadorKYCIcon({ - Key? key, + super.key, required this.kycType, this.width = 18, this.height = 18, - }) : super(key: key); + }); final TrocadorKYCType kycType; final double width; diff --git a/lib/widgets/exchange/trocador/trocador_kyc_info_button.dart b/lib/widgets/exchange/trocador/trocador_kyc_info_button.dart index 91fc2f732..2c1c288ce 100644 --- a/lib/widgets/exchange/trocador/trocador_kyc_info_button.dart +++ b/lib/widgets/exchange/trocador/trocador_kyc_info_button.dart @@ -15,9 +15,9 @@ import '../../trocador_kyc_rating_info.dart'; class TrocadorKYCInfoButton extends StatelessWidget { const TrocadorKYCInfoButton({ - Key? key, + super.key, required this.kycType, - }) : super(key: key); + }); final TrocadorKYCType kycType; diff --git a/lib/widgets/expandable.dart b/lib/widgets/expandable.dart index 2d84e29a7..2a3060d1b 100644 --- a/lib/widgets/expandable.dart +++ b/lib/widgets/expandable.dart @@ -22,7 +22,7 @@ class ExpandableController { class Expandable extends StatefulWidget { const Expandable({ - Key? key, + super.key, required this.header, required this.body, this.animationController, @@ -34,7 +34,7 @@ class Expandable extends StatefulWidget { this.expandOverride, this.curve = Curves.easeInOut, this.initialState = ExpandableState.collapsed, - }) : super(key: key); + }); final Widget header; final Widget body; diff --git a/lib/widgets/expandable2.dart b/lib/widgets/expandable2.dart index c806217df..b8dad923a 100644 --- a/lib/widgets/expandable2.dart +++ b/lib/widgets/expandable2.dart @@ -24,7 +24,7 @@ class Expandable2Controller { class Expandable2 extends StatefulWidget { const Expandable2({ - Key? key, + super.key, required this.header, required this.children, this.background = Colors.white, @@ -36,7 +36,7 @@ class Expandable2 extends StatefulWidget { this.onExpandChanged, this.controller, this.expandOverride, - }) : super(key: key); + }); final Widget header; final List children; diff --git a/lib/widgets/fee_slider.dart b/lib/widgets/fee_slider.dart index 806e9306f..aff7fc131 100644 --- a/lib/widgets/fee_slider.dart +++ b/lib/widgets/fee_slider.dart @@ -2,7 +2,6 @@ import 'dart:math'; import 'package:flutter/material.dart'; import '../utilities/text_styles.dart'; -import '../wallets/crypto_currency/coins/dogecoin.dart'; import '../wallets/crypto_currency/crypto_currency.dart'; class FeeSlider extends StatefulWidget { diff --git a/lib/widgets/frost_scaffold.dart b/lib/widgets/frost_scaffold.dart index 6ce59ce1d..9a527eb57 100644 --- a/lib/widgets/frost_scaffold.dart +++ b/lib/widgets/frost_scaffold.dart @@ -186,7 +186,7 @@ class _FrostScaffoldState extends ConsumerState { width: 500, child: child, ), - ) + ), ], ), child: ConditionalParent( diff --git a/lib/widgets/hover_text_field.dart b/lib/widgets/hover_text_field.dart index 9ade4d63d..f4b9528ff 100644 --- a/lib/widgets/hover_text_field.dart +++ b/lib/widgets/hover_text_field.dart @@ -22,9 +22,9 @@ import '../utilities/util.dart'; class DesktopWalletNameField extends ConsumerStatefulWidget { const DesktopWalletNameField({ - Key? key, + super.key, required this.walletId, - }) : super(key: key); + }); final String walletId; diff --git a/lib/widgets/icon_widgets/addressbook_icon.dart b/lib/widgets/icon_widgets/addressbook_icon.dart index f373c9dbf..62b54d333 100644 --- a/lib/widgets/icon_widgets/addressbook_icon.dart +++ b/lib/widgets/icon_widgets/addressbook_icon.dart @@ -15,11 +15,11 @@ import '../../utilities/assets.dart'; class AddressBookIcon extends StatelessWidget { const AddressBookIcon({ - Key? key, + super.key, this.width = 16, this.height = 16, this.color, - }) : super(key: key); + }); final double width; final double height; diff --git a/lib/widgets/icon_widgets/clipboard_icon.dart b/lib/widgets/icon_widgets/clipboard_icon.dart index 062b3c70d..b8701d707 100644 --- a/lib/widgets/icon_widgets/clipboard_icon.dart +++ b/lib/widgets/icon_widgets/clipboard_icon.dart @@ -15,11 +15,11 @@ import '../../utilities/assets.dart'; class ClipboardIcon extends StatelessWidget { const ClipboardIcon({ - Key? key, + super.key, this.width = 18, this.height = 18, this.color, - }) : super(key: key); + }); final double width; final double height; diff --git a/lib/widgets/icon_widgets/copy_icon.dart b/lib/widgets/icon_widgets/copy_icon.dart index 7d5bd4af9..ceb5074ce 100644 --- a/lib/widgets/icon_widgets/copy_icon.dart +++ b/lib/widgets/icon_widgets/copy_icon.dart @@ -15,11 +15,11 @@ import '../../utilities/assets.dart'; class CopyIcon extends StatelessWidget { const CopyIcon({ - Key? key, + super.key, this.width = 18, this.height = 18, this.color, - }) : super(key: key); + }); final double width; final double height; diff --git a/lib/widgets/icon_widgets/dice_icon.dart b/lib/widgets/icon_widgets/dice_icon.dart index 27b88a684..1d688a2b9 100644 --- a/lib/widgets/icon_widgets/dice_icon.dart +++ b/lib/widgets/icon_widgets/dice_icon.dart @@ -15,11 +15,11 @@ import '../../utilities/assets.dart'; class DiceIcon extends StatelessWidget { const DiceIcon({ - Key? key, + super.key, this.width = 17, this.height = 17, this.color, - }) : super(key: key); + }); final double width; final double height; diff --git a/lib/widgets/icon_widgets/eth_token_icon.dart b/lib/widgets/icon_widgets/eth_token_icon.dart index 2e0e83a2a..5908270f6 100644 --- a/lib/widgets/icon_widgets/eth_token_icon.dart +++ b/lib/widgets/icon_widgets/eth_token_icon.dart @@ -15,7 +15,6 @@ import 'package:isar/isar.dart'; import '../../models/isar/exchange_cache/currency.dart'; import '../../services/exchange/exchange_data_loading_service.dart'; import '../../themes/coin_icon_provider.dart'; -import '../../wallets/crypto_currency/coins/ethereum.dart'; import '../../wallets/crypto_currency/crypto_currency.dart'; class EthTokenIcon extends ConsumerStatefulWidget { diff --git a/lib/widgets/icon_widgets/pencil_icon.dart b/lib/widgets/icon_widgets/pencil_icon.dart index 1389207a2..0cf1b30e8 100644 --- a/lib/widgets/icon_widgets/pencil_icon.dart +++ b/lib/widgets/icon_widgets/pencil_icon.dart @@ -15,11 +15,11 @@ import '../../utilities/assets.dart'; class PencilIcon extends StatelessWidget { const PencilIcon({ - Key? key, + super.key, this.width = 18, this.height = 18, this.color, - }) : super(key: key); + }); final double width; final double height; diff --git a/lib/widgets/icon_widgets/qrcode_icon.dart b/lib/widgets/icon_widgets/qrcode_icon.dart index cc84ff39a..fa3a28749 100644 --- a/lib/widgets/icon_widgets/qrcode_icon.dart +++ b/lib/widgets/icon_widgets/qrcode_icon.dart @@ -15,11 +15,11 @@ import '../../utilities/assets.dart'; class QrCodeIcon extends StatelessWidget { const QrCodeIcon({ - Key? key, + super.key, this.width = 17, this.height = 17, this.color, - }) : super(key: key); + }); final double width; final double height; diff --git a/lib/widgets/icon_widgets/share_icon.dart b/lib/widgets/icon_widgets/share_icon.dart index 449f04ae1..2e6efac3c 100644 --- a/lib/widgets/icon_widgets/share_icon.dart +++ b/lib/widgets/icon_widgets/share_icon.dart @@ -15,11 +15,11 @@ import '../../utilities/assets.dart'; class ShareIcon extends StatelessWidget { const ShareIcon({ - Key? key, + super.key, this.width = 18, this.height = 18, this.color, - }) : super(key: key); + }); final double width; final double height; diff --git a/lib/widgets/icon_widgets/utxo_status_icon.dart b/lib/widgets/icon_widgets/utxo_status_icon.dart index 3c78308df..30a39b1fc 100644 --- a/lib/widgets/icon_widgets/utxo_status_icon.dart +++ b/lib/widgets/icon_widgets/utxo_status_icon.dart @@ -22,14 +22,14 @@ enum UTXOStatusIconStatus { class UTXOStatusIcon extends StatelessWidget { const UTXOStatusIcon({ - Key? key, + super.key, required this.width, required this.height, required this.blocked, required this.selected, required this.status, required this.background, - }) : super(key: key); + }); final double width; final double height; diff --git a/lib/widgets/icon_widgets/x_icon.dart b/lib/widgets/icon_widgets/x_icon.dart index 03a536e51..b77a967d2 100644 --- a/lib/widgets/icon_widgets/x_icon.dart +++ b/lib/widgets/icon_widgets/x_icon.dart @@ -15,11 +15,11 @@ import '../../utilities/assets.dart'; class XIcon extends StatelessWidget { const XIcon({ - Key? key, + super.key, this.width = 18, this.height = 18, this.color, - }) : super(key: key); + }); final double width; final double height; diff --git a/lib/widgets/loading_indicator.dart b/lib/widgets/loading_indicator.dart index e42216fae..1e9db2809 100644 --- a/lib/widgets/loading_indicator.dart +++ b/lib/widgets/loading_indicator.dart @@ -18,10 +18,10 @@ import '../utilities/assets.dart'; class LoadingIndicator extends ConsumerWidget { const LoadingIndicator({ - Key? key, + super.key, this.width, this.height, - }) : super(key: key); + }); final double? width; final double? height; diff --git a/lib/widgets/master_wallet_card.dart b/lib/widgets/master_wallet_card.dart index 3eb07a6db..33427a752 100644 --- a/lib/widgets/master_wallet_card.dart +++ b/lib/widgets/master_wallet_card.dart @@ -24,10 +24,10 @@ import 'wallet_info_row/wallet_info_row.dart'; class MasterWalletCard extends ConsumerStatefulWidget { const MasterWalletCard({ - Key? key, + super.key, required this.walletId, this.popPrevious = false, - }) : super(key: key); + }); final String walletId; final bool popPrevious; diff --git a/lib/widgets/node_options_sheet.dart b/lib/widgets/node_options_sheet.dart index 37316dd05..82137ac80 100644 --- a/lib/widgets/node_options_sheet.dart +++ b/lib/widgets/node_options_sheet.dart @@ -14,6 +14,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:solana/solana.dart'; +import 'package:tuple/tuple.dart'; + import '../models/node_model.dart'; import '../notifications/show_flush_bar.dart'; import '../pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart'; @@ -34,7 +36,6 @@ import '../utilities/test_monero_node_connection.dart'; import '../utilities/text_styles.dart'; import '../wallets/crypto_currency/crypto_currency.dart'; import 'rounded_white_container.dart'; -import 'package:tuple/tuple.dart'; class NodeOptionsSheet extends ConsumerWidget { const NodeOptionsSheet({ @@ -228,7 +229,8 @@ class NodeOptionsSheet extends ConsumerWidget { final status = ref .watch( nodeServiceChangeNotifierProvider.select( - (value) => value.getPrimaryNodeFor(currency: coin)), + (value) => value.getPrimaryNodeFor(currency: coin), + ), ) ?.id != nodeId diff --git a/lib/widgets/onetime_popups/tor_has_been_add_dialog.dart b/lib/widgets/onetime_popups/tor_has_been_add_dialog.dart index 0c189cf63..ab03f5ae0 100644 --- a/lib/widgets/onetime_popups/tor_has_been_add_dialog.dart +++ b/lib/widgets/onetime_popups/tor_has_been_add_dialog.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:hive_flutter/hive_flutter.dart'; + import '../../db/hive/db.dart'; import '../../utilities/assets.dart'; import '../../utilities/text_styles.dart'; @@ -14,7 +15,8 @@ const _kOneTimeTorHasBeenAddedDialogWasShown = "oneTimeTorHasBeenAddedDialogWasShown"; Future showOneTimeTorHasBeenAddedDialogIfRequired( - BuildContext context) async { + BuildContext context, +) async { final box = await Hive.openBox(DB.boxNameOneTimeDialogsShown); if (!box.get( @@ -102,7 +104,7 @@ class _TorHasBeenAddedDialogState extends State<_TorHasBeenAddedDialog> { ), ], ), - ) + ), ], ), ), diff --git a/lib/widgets/progress_bar.dart b/lib/widgets/progress_bar.dart index a58c42c69..212167dae 100644 --- a/lib/widgets/progress_bar.dart +++ b/lib/widgets/progress_bar.dart @@ -12,13 +12,13 @@ import 'package:flutter/cupertino.dart'; class ProgressBar extends StatelessWidget { const ProgressBar({ - Key? key, + super.key, required this.width, required this.height, required this.fillColor, required this.backgroundColor, required this.percent, - }) : super(key: key); + }); final double width; final double height; @@ -49,7 +49,7 @@ class ProgressBar extends StatelessWidget { color: fillColor, borderRadius: BorderRadius.circular(height / 2), ), - ) + ), ], ), ), diff --git a/lib/widgets/rounded_container.dart b/lib/widgets/rounded_container.dart index d75dc6966..fa8bc3f27 100644 --- a/lib/widgets/rounded_container.dart +++ b/lib/widgets/rounded_container.dart @@ -14,7 +14,7 @@ import 'conditional_parent.dart'; class RoundedContainer extends StatelessWidget { const RoundedContainer({ - Key? key, + super.key, this.child, required this.color, this.padding = const EdgeInsets.all(12), @@ -25,7 +25,7 @@ class RoundedContainer extends StatelessWidget { this.hoverColor, this.boxShadow, this.onPressed, - }) : super(key: key); + }); final Widget? child; final Color color; diff --git a/lib/widgets/rounded_white_container.dart b/lib/widgets/rounded_white_container.dart index 0097397e8..a24059c8c 100644 --- a/lib/widgets/rounded_white_container.dart +++ b/lib/widgets/rounded_white_container.dart @@ -14,7 +14,7 @@ import 'rounded_container.dart'; class RoundedWhiteContainer extends StatelessWidget { const RoundedWhiteContainer({ - Key? key, + super.key, this.child, this.padding = const EdgeInsets.all(12), this.radiusMultiplier = 1.0, @@ -24,7 +24,7 @@ class RoundedWhiteContainer extends StatelessWidget { this.hoverColor, this.boxShadow, this.onPressed, - }) : super(key: key); + }); final Widget? child; final EdgeInsets padding; diff --git a/lib/widgets/shake/shake.dart b/lib/widgets/shake/shake.dart index c19241ee1..798368368 100644 --- a/lib/widgets/shake/shake.dart +++ b/lib/widgets/shake/shake.dart @@ -12,12 +12,12 @@ import 'package:flutter/cupertino.dart'; class Shake extends StatefulWidget { const Shake({ - Key? key, + super.key, required this.child, required this.animationRange, required this.controller, required this.animationDuration, - }) : super(key: key); + }); final Widget child; final double animationRange; diff --git a/lib/widgets/stack_dialog.dart b/lib/widgets/stack_dialog.dart index 58fb69ea2..147e83bbd 100644 --- a/lib/widgets/stack_dialog.dart +++ b/lib/widgets/stack_dialog.dart @@ -15,11 +15,11 @@ import '../utilities/util.dart'; class StackDialogBase extends StatelessWidget { const StackDialogBase({ - Key? key, + super.key, this.child, this.padding = const EdgeInsets.all(24), this.keyboardPaddingAmount = 0, - }) : super(key: key); + }); final EdgeInsets padding; final Widget? child; @@ -67,13 +67,13 @@ class StackDialogBase extends StatelessWidget { class StackDialog extends StatelessWidget { const StackDialog({ - Key? key, + super.key, this.leftButton, this.rightButton, this.icon, required this.title, this.message, - }) : super(key: key); + }); final Widget? leftButton; final Widget? rightButton; @@ -132,7 +132,7 @@ class StackDialog extends StatelessWidget { ? const Spacer() : Expanded(child: rightButton!), ], - ) + ), ], ), ); @@ -141,14 +141,14 @@ class StackDialog extends StatelessWidget { class StackOkDialog extends StatelessWidget { const StackOkDialog({ - Key? key, + super.key, this.leftButton, this.onOkPressed, this.icon, required this.title, this.message, this.desktopPopRootNavigator = false, - }) : super(key: key); + }); final bool desktopPopRootNavigator; final Widget? leftButton; @@ -228,7 +228,7 @@ class StackOkDialog extends StatelessWidget { ), ), ], - ) + ), ], ), ); diff --git a/lib/widgets/stack_text_field.dart b/lib/widgets/stack_text_field.dart index 359f5c0d3..c64c6a5e8 100644 --- a/lib/widgets/stack_text_field.dart +++ b/lib/widgets/stack_text_field.dart @@ -9,6 +9,7 @@ */ import 'package:flutter/material.dart'; + import '../themes/stack_colors.dart'; import '../utilities/text_styles.dart'; import '../utilities/util.dart'; @@ -31,7 +32,8 @@ InputDecoration standardInputDecoration( ? STextStyles.desktopTextExtraSmall(context).copyWith( color: Theme.of(context) .extension()! - .textFieldDefaultText) + .textFieldDefaultText, + ) : STextStyles.desktopTextFieldLabel(context) : STextStyles.fieldLabel(context), hintStyle: isDesktop @@ -39,7 +41,8 @@ InputDecoration standardInputDecoration( ? STextStyles.desktopTextExtraSmall(context).copyWith( color: Theme.of(context) .extension()! - .textFieldDefaultText) + .textFieldDefaultText, + ) : STextStyles.desktopTextFieldLabel(context) : STextStyles.fieldLabel(context), enabledBorder: InputBorder.none, diff --git a/lib/widgets/table_view/table_view.dart b/lib/widgets/table_view/table_view.dart index 4fedb067a..ee208716a 100644 --- a/lib/widgets/table_view/table_view.dart +++ b/lib/widgets/table_view/table_view.dart @@ -12,11 +12,11 @@ import 'package:flutter/material.dart'; class TableView extends StatelessWidget { const TableView({ - Key? key, + super.key, required this.rows, this.rowSpacing = 10.0, this.shrinkWrap = false, - }) : super(key: key); + }); final List rows; final double rowSpacing; @@ -42,7 +42,7 @@ class TableView extends StatelessWidget { ), rows[i], ], - ) + ), ], ); } diff --git a/lib/widgets/table_view/table_view_cell.dart b/lib/widgets/table_view/table_view_cell.dart index fb87ce86d..d0543a2c2 100644 --- a/lib/widgets/table_view/table_view_cell.dart +++ b/lib/widgets/table_view/table_view_cell.dart @@ -12,10 +12,10 @@ import 'package:flutter/material.dart'; class TableViewCell extends StatelessWidget { const TableViewCell({ - Key? key, + super.key, required this.flex, required this.child, - }) : super(key: key); + }); final int flex; final Widget child; diff --git a/lib/widgets/table_view/table_view_row.dart b/lib/widgets/table_view/table_view_row.dart index 3f8bd826d..6d2ddb6c3 100644 --- a/lib/widgets/table_view/table_view_row.dart +++ b/lib/widgets/table_view/table_view_row.dart @@ -15,7 +15,7 @@ import 'table_view_cell.dart'; class TableViewRow extends StatefulWidget { const TableViewRow({ - Key? key, + super.key, required this.cells, required this.expandingChild, this.decoration, @@ -24,7 +24,7 @@ class TableViewRow extends StatefulWidget { this.padding = const EdgeInsets.all(0), this.spacing = 0.0, this.crossAxisAlignment = CrossAxisAlignment.center, - }) : super(key: key); + }); final List cells; final Widget? expandingChild; diff --git a/lib/widgets/textfield_icon_button.dart b/lib/widgets/textfield_icon_button.dart index b600e98cc..8faea5495 100644 --- a/lib/widgets/textfield_icon_button.dart +++ b/lib/widgets/textfield_icon_button.dart @@ -12,14 +12,14 @@ import 'package:flutter/material.dart'; class TextFieldIconButton extends StatefulWidget { const TextFieldIconButton({ - Key? key, + super.key, this.width = 40, this.height = 40, this.onTap, required this.child, this.color = Colors.transparent, this.semanticsLabel = "Button", - }) : super(key: key); + }); final double width; final double height; @@ -66,7 +66,7 @@ class _TextFieldIconButtonState extends State { ), ), ), - ) + ), ), ); } diff --git a/lib/widgets/textfields/exchange_textfield.dart b/lib/widgets/textfields/exchange_textfield.dart index 0266d6637..3f6dfc3ca 100644 --- a/lib/widgets/textfields/exchange_textfield.dart +++ b/lib/widgets/textfields/exchange_textfield.dart @@ -25,7 +25,7 @@ import '../loading_indicator.dart'; class ExchangeTextField extends ConsumerStatefulWidget { const ExchangeTextField({ - Key? key, + super.key, this.borderRadius = 0, this.background, required this.controller, @@ -40,7 +40,7 @@ class ExchangeTextField extends ConsumerStatefulWidget { required this.isWalletCoin, this.currency, this.readOnly = false, - }) : super(key: key); + }); final double borderRadius; final Color? background; @@ -136,8 +136,10 @@ class _ExchangeTextFieldState extends ConsumerState { inputFormatters: [ AmountInputFormatter( decimals: 8, // todo change this - locale: ref.watch(localeServiceChangeNotifierProvider - .select((value) => value.locale)), + locale: ref.watch( + localeServiceChangeNotifierProvider + .select((value) => value.locale), + ), ), // // regex to validate a crypto amount with 8 decimal places // TextInputFormatter.withFunction((oldValue, newValue) => @@ -176,7 +178,8 @@ class _ExchangeTextFieldState extends ConsumerState { child: Builder( builder: (context) { if (AppConfig.isStackCoin( - widget.currency?.ticker)) { + widget.currency?.ticker, + )) { return Center( child: CoinIconForTicker( size: 18, diff --git a/lib/widgets/textfields/frost_step_field.dart b/lib/widgets/textfields/frost_step_field.dart index 8ce64b089..f805dc64a 100644 --- a/lib/widgets/textfields/frost_step_field.dart +++ b/lib/widgets/textfields/frost_step_field.dart @@ -1,6 +1,7 @@ import 'package:barcode_scan2/barcode_scan2.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; + import '../../themes/stack_colors.dart'; import '../../utilities/constants.dart'; import '../../utilities/logger.dart'; @@ -154,7 +155,8 @@ class _FrostStepFieldState extends State { if (FocusScope.of(context).hasFocus) { FocusScope.of(context).unfocus(); await Future.delayed( - const Duration(milliseconds: 75)); + const Duration(milliseconds: 75), + ); } final qrResult = await BarcodeScanner.scan(); diff --git a/lib/widgets/toggle.dart b/lib/widgets/toggle.dart index dec082d5f..eec7cec4c 100644 --- a/lib/widgets/toggle.dart +++ b/lib/widgets/toggle.dart @@ -10,13 +10,14 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; + import '../themes/stack_colors.dart'; import '../utilities/text_styles.dart'; import '../utilities/util.dart'; class Toggle extends StatefulWidget { const Toggle({ - Key? key, + super.key, this.onIcon, this.onText, this.offIcon, @@ -28,7 +29,7 @@ class Toggle extends StatefulWidget { required this.onColor, required this.offColor, this.decoration, - }) : super(key: key); + }); final String? onIcon; final String? onText; @@ -142,7 +143,7 @@ class ToggleState extends State { .clamp(0.0, 1.0); }, onHorizontalDragEnd: (details) { - bool oldValue = _isOn; + final bool oldValue = _isOn; if (valueListener.value > 0.5) { valueListener.value = 1.0; _isOn = true; @@ -225,8 +226,8 @@ class ToggleState extends State { widget.onText ?? "", style: isDesktop ? STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( + context, + ).copyWith( color: !_isOn ? Theme.of(context) .extension()! @@ -284,8 +285,8 @@ class ToggleState extends State { widget.offText ?? "", style: isDesktop ? STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( + context, + ).copyWith( color: _isOn ? Theme.of(context) .extension()! diff --git a/lib/widgets/trade_card.dart b/lib/widgets/trade_card.dart index 5305ad584..28a05f9ae 100644 --- a/lib/widgets/trade_card.dart +++ b/lib/widgets/trade_card.dart @@ -14,6 +14,7 @@ import 'package:decimal/decimal.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../models/exchange/change_now/exchange_transaction_status.dart'; import '../models/exchange/response_objects/trade.dart'; import '../models/isar/stack_theme.dart'; @@ -26,10 +27,10 @@ import 'rounded_white_container.dart'; class TradeCard extends ConsumerWidget { const TradeCard({ - Key? key, + super.key, required this.trade, required this.onTap, - }) : super(key: key); + }); final Trade trade; final VoidCallback onTap; @@ -139,7 +140,8 @@ class TradeCard extends ConsumerWidget { ), Text( Format.extractDateFrom( - trade.timestamp.millisecondsSinceEpoch ~/ 1000), + trade.timestamp.millisecondsSinceEpoch ~/ 1000, + ), style: STextStyles.label(context), ), if (isDesktop) @@ -151,7 +153,7 @@ class TradeCard extends ConsumerWidget { ), ], ), - ) + ), ], ), ), diff --git a/lib/widgets/transaction_card.dart b/lib/widgets/transaction_card.dart index 323fb7ebe..b83b09696 100644 --- a/lib/widgets/transaction_card.dart +++ b/lib/widgets/transaction_card.dart @@ -12,6 +12,8 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:tuple/tuple.dart'; + import '../models/isar/models/isar_models.dart'; import '../notifications/show_flush_bar.dart'; import '../pages/wallet_view/sub_widgets/tx_icon.dart'; @@ -25,18 +27,15 @@ import '../utilities/constants.dart'; import '../utilities/format.dart'; import '../utilities/text_styles.dart'; import '../utilities/util.dart'; -import '../wallets/crypto_currency/coins/epiccash.dart'; -import '../wallets/crypto_currency/coins/ethereum.dart'; import '../wallets/crypto_currency/crypto_currency.dart'; import 'desktop/desktop_dialog.dart'; -import 'package:tuple/tuple.dart'; class TransactionCard extends ConsumerStatefulWidget { const TransactionCard({ - Key? key, + super.key, required this.transaction, required this.walletId, - }) : super(key: key); + }); final Transaction transaction; final String walletId; @@ -141,19 +140,26 @@ class _TransactionCardState extends ConsumerState { @override Widget build(BuildContext context) { final locale = ref.watch( - localeServiceChangeNotifierProvider.select((value) => value.locale)); + localeServiceChangeNotifierProvider.select((value) => value.locale), + ); final baseCurrency = ref .watch(prefsChangeNotifierProvider.select((value) => value.currency)); final price = ref - .watch(priceAnd24hChangeNotifierProvider.select((value) => isTokenTx - ? value.getTokenPrice(_transaction.otherData!) - : value.getPrice(coin))) + .watch( + priceAnd24hChangeNotifierProvider.select( + (value) => isTokenTx + ? value.getTokenPrice(_transaction.otherData!) + : value.getPrice(coin), + ), + ) .item1; - final currentHeight = ref.watch(pWallets - .select((value) => value.getWallet(walletId).info.cachedChainHeight)); + final currentHeight = ref.watch( + pWallets + .select((value) => value.getWallet(walletId).info.cachedChainHeight), + ); return Material( color: Theme.of(context).extension()!.popupBG, @@ -172,13 +178,15 @@ class _TransactionCardState extends ConsumerState { ), onPressed: () async { if (coin is Epiccash && _transaction.slateId == null) { - unawaited(showFloatingFlushBar( - context: context, - message: - "Restored Epic funds from your Seed have no Data.\nUse Stack Backup to keep your transaction history.", - type: FlushBarType.warning, - duration: const Duration(seconds: 5), - )); + unawaited( + showFloatingFlushBar( + context: context, + message: + "Restored Epic funds from your Seed have no Data.\nUse Stack Backup to keep your transaction history.", + type: FlushBarType.warning, + duration: const Duration(seconds: 5), + ), + ); return; } if (Util.isDesktop) { @@ -280,13 +288,17 @@ class _TransactionCardState extends ConsumerState { ), ), ), - if (ref.watch(prefsChangeNotifierProvider - .select((value) => value.externalCalls))) + if (ref.watch( + prefsChangeNotifierProvider + .select((value) => value.externalCalls), + )) const SizedBox( width: 10, ), - if (ref.watch(prefsChangeNotifierProvider - .select((value) => value.externalCalls))) + if (ref.watch( + prefsChangeNotifierProvider + .select((value) => value.externalCalls), + )) Flexible( child: FittedBox( fit: BoxFit.scaleDown, diff --git a/lib/widgets/trocador_kyc_rating_info.dart b/lib/widgets/trocador_kyc_rating_info.dart index 9c5b04c9f..826c1bc5c 100644 --- a/lib/widgets/trocador_kyc_rating_info.dart +++ b/lib/widgets/trocador_kyc_rating_info.dart @@ -20,7 +20,7 @@ import 'exchange/trocador/trocador_rating_type_enum.dart'; import 'stack_dialog.dart'; class TrocadorKYCRatingInfo extends StatelessWidget { - const TrocadorKYCRatingInfo({Key? key}) : super(key: key); + const TrocadorKYCRatingInfo({super.key}); @override Widget build(BuildContext context) { @@ -71,7 +71,7 @@ class TrocadorKYCRatingInfo extends StatelessWidget { ), ], ), - ) + ), ], ), ), @@ -138,7 +138,7 @@ class TrocadorKYCRatingInfo extends StatelessWidget { label: "Close", onPressed: Navigator.of(context).pop, ), - ) + ), ], ), ), @@ -151,10 +151,10 @@ class TrocadorKYCRatingInfo extends StatelessWidget { class _Rating extends StatelessWidget { const _Rating({ - Key? key, + super.key, required this.kycType, required this.text, - }) : super(key: key); + }); final TrocadorKYCType kycType; final String text; diff --git a/lib/widgets/wallet_card.dart b/lib/widgets/wallet_card.dart index 98c5fbeac..a2185186c 100644 --- a/lib/widgets/wallet_card.dart +++ b/lib/widgets/wallet_card.dart @@ -12,6 +12,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../models/isar/models/ethereum/eth_contract.dart'; import '../pages/token_view/token_view.dart'; import '../pages/wallet_view/wallet_view.dart'; @@ -36,12 +37,12 @@ import 'wallet_info_row/wallet_info_row.dart'; class SimpleWalletCard extends ConsumerWidget { const SimpleWalletCard({ - Key? key, + super.key, required this.walletId, this.contractAddress, this.popPrevious = false, this.desktopNavigatorState, - }) : super(key: key); + }); final String walletId; final String? contractAddress; @@ -131,7 +132,11 @@ class SimpleWalletCard extends ConsumerWidget { final success = await showLoading( whileFuture: _loadTokenWallet( - desktopNavigatorState?.context ?? context, ref, wallet, contract), + desktopNavigatorState?.context ?? context, + ref, + wallet, + contract, + ), context: desktopNavigatorState?.context ?? context, opaqueBG: true, message: "Loading ${contract.name}", diff --git a/lib/widgets/wallet_info_row/wallet_info_row.dart b/lib/widgets/wallet_info_row/wallet_info_row.dart index 3510e389f..2cc35b7aa 100644 --- a/lib/widgets/wallet_info_row/wallet_info_row.dart +++ b/lib/widgets/wallet_info_row/wallet_info_row.dart @@ -10,6 +10,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../models/isar/models/ethereum/eth_contract.dart'; import '../../pages/token_view/sub_widgets/token_summary.dart'; import '../../providers/db/main_db_provider.dart'; @@ -23,12 +24,12 @@ import 'sub_widgets/wallet_info_row_coin_icon.dart'; class WalletInfoRow extends ConsumerWidget { const WalletInfoRow({ - Key? key, + super.key, required this.walletId, this.onPressedDesktop, this.contractAddress, this.padding = const EdgeInsets.all(0), - }) : super(key: key); + }); final String walletId; final String? contractAddress; @@ -41,8 +42,10 @@ class WalletInfoRow extends ConsumerWidget { EthContract? contract; if (contractAddress != null) { - contract = ref.watch(mainDBProvider - .select((value) => value.getEthContractSync(contractAddress!))); + contract = ref.watch( + mainDBProvider + .select((value) => value.getEthContractSync(contractAddress!)), + ); } if (Util.isDesktop) { @@ -114,7 +117,7 @@ class WalletInfoRow extends ConsumerWidget { ), ], ), - ) + ), ], ), ), diff --git a/lib/widgets/wallet_navigation_bar/components/icons/buy_nav_icon.dart b/lib/widgets/wallet_navigation_bar/components/icons/buy_nav_icon.dart index 586fae900..70e34a109 100644 --- a/lib/widgets/wallet_navigation_bar/components/icons/buy_nav_icon.dart +++ b/lib/widgets/wallet_navigation_bar/components/icons/buy_nav_icon.dart @@ -16,7 +16,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import '../../../../themes/theme_providers.dart'; class BuyNavIcon extends ConsumerWidget { - const BuyNavIcon({Key? key}) : super(key: key); + const BuyNavIcon({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { diff --git a/lib/widgets/wallet_navigation_bar/components/icons/coin_control_nav_icon.dart b/lib/widgets/wallet_navigation_bar/components/icons/coin_control_nav_icon.dart index 94c5bae23..fde47bcfa 100644 --- a/lib/widgets/wallet_navigation_bar/components/icons/coin_control_nav_icon.dart +++ b/lib/widgets/wallet_navigation_bar/components/icons/coin_control_nav_icon.dart @@ -14,7 +14,7 @@ import '../../../../themes/stack_colors.dart'; import '../../../../utilities/assets.dart'; class CoinControlNavIcon extends StatelessWidget { - const CoinControlNavIcon({Key? key}) : super(key: key); + const CoinControlNavIcon({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/widgets/wallet_navigation_bar/components/icons/exchange_nav_icon.dart b/lib/widgets/wallet_navigation_bar/components/icons/exchange_nav_icon.dart index 25e4c0122..f17aabbda 100644 --- a/lib/widgets/wallet_navigation_bar/components/icons/exchange_nav_icon.dart +++ b/lib/widgets/wallet_navigation_bar/components/icons/exchange_nav_icon.dart @@ -16,7 +16,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import '../../../../themes/theme_providers.dart'; class ExchangeNavIcon extends ConsumerWidget { - const ExchangeNavIcon({Key? key}) : super(key: key); + const ExchangeNavIcon({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { diff --git a/lib/widgets/wallet_navigation_bar/components/icons/fusion_nav_icon.dart b/lib/widgets/wallet_navigation_bar/components/icons/fusion_nav_icon.dart index 07b0b16bc..7d46653da 100644 --- a/lib/widgets/wallet_navigation_bar/components/icons/fusion_nav_icon.dart +++ b/lib/widgets/wallet_navigation_bar/components/icons/fusion_nav_icon.dart @@ -14,7 +14,7 @@ import '../../../../themes/stack_colors.dart'; import '../../../../utilities/assets.dart'; class FusionNavIcon extends StatelessWidget { - const FusionNavIcon({Key? key}) : super(key: key); + const FusionNavIcon({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/widgets/wallet_navigation_bar/components/icons/ordinals_nav_icon.dart b/lib/widgets/wallet_navigation_bar/components/icons/ordinals_nav_icon.dart index 98bfecb4f..e347b372a 100644 --- a/lib/widgets/wallet_navigation_bar/components/icons/ordinals_nav_icon.dart +++ b/lib/widgets/wallet_navigation_bar/components/icons/ordinals_nav_icon.dart @@ -14,7 +14,7 @@ import '../../../../themes/stack_colors.dart'; import '../../../../utilities/assets.dart'; class OrdinalsNavIcon extends StatelessWidget { - const OrdinalsNavIcon({Key? key}) : super(key: key); + const OrdinalsNavIcon({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/widgets/wallet_navigation_bar/components/icons/paynym_nav_icon.dart b/lib/widgets/wallet_navigation_bar/components/icons/paynym_nav_icon.dart index 9f11d4136..5236bfec8 100644 --- a/lib/widgets/wallet_navigation_bar/components/icons/paynym_nav_icon.dart +++ b/lib/widgets/wallet_navigation_bar/components/icons/paynym_nav_icon.dart @@ -14,7 +14,7 @@ import '../../../../themes/stack_colors.dart'; import '../../../../utilities/assets.dart'; class PaynymNavIcon extends StatelessWidget { - const PaynymNavIcon({Key? key}) : super(key: key); + const PaynymNavIcon({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/widgets/wallet_navigation_bar/components/icons/receive_nav_icon.dart b/lib/widgets/wallet_navigation_bar/components/icons/receive_nav_icon.dart index 96be35a96..bfd10f900 100644 --- a/lib/widgets/wallet_navigation_bar/components/icons/receive_nav_icon.dart +++ b/lib/widgets/wallet_navigation_bar/components/icons/receive_nav_icon.dart @@ -14,7 +14,7 @@ import '../../../../themes/stack_colors.dart'; import '../../../../utilities/assets.dart'; class ReceiveNavIcon extends StatelessWidget { - const ReceiveNavIcon({Key? key}) : super(key: key); + const ReceiveNavIcon({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/widgets/wallet_navigation_bar/components/icons/send_nav_icon.dart b/lib/widgets/wallet_navigation_bar/components/icons/send_nav_icon.dart index 3d388f3a2..64d0f861f 100644 --- a/lib/widgets/wallet_navigation_bar/components/icons/send_nav_icon.dart +++ b/lib/widgets/wallet_navigation_bar/components/icons/send_nav_icon.dart @@ -14,7 +14,7 @@ import '../../../../themes/stack_colors.dart'; import '../../../../utilities/assets.dart'; class SendNavIcon extends StatelessWidget { - const SendNavIcon({Key? key}) : super(key: key); + const SendNavIcon({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/widgets/wallet_navigation_bar/components/icons/whirlpool_nav_icon.dart b/lib/widgets/wallet_navigation_bar/components/icons/whirlpool_nav_icon.dart index 02c9aaf80..579ea148d 100644 --- a/lib/widgets/wallet_navigation_bar/components/icons/whirlpool_nav_icon.dart +++ b/lib/widgets/wallet_navigation_bar/components/icons/whirlpool_nav_icon.dart @@ -14,7 +14,7 @@ import '../../../../themes/stack_colors.dart'; import '../../../../utilities/assets.dart'; class WhirlpoolNavIcon extends StatelessWidget { - const WhirlpoolNavIcon({Key? key}) : super(key: key); + const WhirlpoolNavIcon({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/widgets/wallet_navigation_bar/components/wallet_navigation_bar_item.dart b/lib/widgets/wallet_navigation_bar/components/wallet_navigation_bar_item.dart index 6cf3c2601..3f5bddc08 100644 --- a/lib/widgets/wallet_navigation_bar/components/wallet_navigation_bar_item.dart +++ b/lib/widgets/wallet_navigation_bar/components/wallet_navigation_bar_item.dart @@ -33,10 +33,10 @@ class WalletNavigationBarItemData { class WalletNavigationBarItem extends ConsumerWidget { const WalletNavigationBarItem({ - Key? key, + super.key, required this.data, required this.disableDuration, - }) : super(key: key); + }); final WalletNavigationBarItemData data; final Duration disableDuration; @@ -95,9 +95,9 @@ class WalletNavigationBarItem extends ConsumerWidget { class WalletNavigationBarMoreItem extends ConsumerWidget { const WalletNavigationBarMoreItem({ - Key? key, + super.key, required this.data, - }) : super(key: key); + }); final WalletNavigationBarItemData data; diff --git a/lib/widgets/wallet_navigation_bar/wallet_navigation_bar.dart b/lib/widgets/wallet_navigation_bar/wallet_navigation_bar.dart index b272048c3..8c93a1c74 100644 --- a/lib/widgets/wallet_navigation_bar/wallet_navigation_bar.dart +++ b/lib/widgets/wallet_navigation_bar/wallet_navigation_bar.dart @@ -21,10 +21,10 @@ final walletNavBarMore = StateProvider.autoDispose((ref) => false); class WalletNavigationBar extends ConsumerStatefulWidget { const WalletNavigationBar({ - Key? key, + super.key, required this.items, required this.moreItems, - }) : super(key: key); + }); final List items; final List moreItems; @@ -131,7 +131,7 @@ class _WalletNavigationBarState extends ConsumerState { boxShadow: [ Theme.of(context) .extension()! - .standardBoxShadow + .standardBoxShadow, ], borderRadius: BorderRadius.circular( 1000, diff --git a/test/address_utils_test.dart b/test/address_utils_test.dart index 42ab1a45b..560962256 100644 --- a/test/address_utils_test.dart +++ b/test/address_utils_test.dart @@ -1,6 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:stackwallet/utilities/address_utils.dart'; -import 'package:stackwallet/wallets/crypto_currency/coins/firo.dart'; import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart'; void main() { diff --git a/test/cached_electrumx_test.dart b/test/cached_electrumx_test.dart index 5f0ae68f1..26213175f 100644 --- a/test/cached_electrumx_test.dart +++ b/test/cached_electrumx_test.dart @@ -5,7 +5,6 @@ import 'package:mockito/mockito.dart'; import 'package:stackwallet/electrumx_rpc/cached_electrumx_client.dart'; import 'package:stackwallet/electrumx_rpc/electrumx_client.dart'; import 'package:stackwallet/utilities/prefs.dart'; -import 'package:stackwallet/wallets/crypto_currency/coins/firo.dart'; import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart'; import 'cached_electrumx_test.mocks.dart'; diff --git a/test/cached_electrumx_test.mocks.dart b/test/cached_electrumx_test.mocks.dart index b00b89db4..5ae1523da 100644 --- a/test/cached_electrumx_test.mocks.dart +++ b/test/cached_electrumx_test.mocks.dart @@ -1,18 +1,19 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/cached_electrumx_test.dart. // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i6; -import 'dart:ui' as _i11; +import 'dart:async' as _i7; +import 'dart:ui' as _i12; import 'package:decimal/decimal.dart' as _i3; import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i6; import 'package:stackwallet/electrumx_rpc/electrumx_client.dart' as _i5; -import 'package:stackwallet/utilities/amount/amount_unit.dart' as _i10; -import 'package:stackwallet/utilities/enums/backup_frequency_type.dart' as _i9; -import 'package:stackwallet/utilities/enums/sync_type_enum.dart' as _i8; -import 'package:stackwallet/utilities/prefs.dart' as _i7; +import 'package:stackwallet/utilities/amount/amount_unit.dart' as _i11; +import 'package:stackwallet/utilities/enums/backup_frequency_type.dart' as _i10; +import 'package:stackwallet/utilities/enums/sync_type_enum.dart' as _i9; +import 'package:stackwallet/utilities/prefs.dart' as _i8; import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart' as _i2; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart' @@ -22,6 +23,8 @@ import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/cash_fusion_i // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -119,7 +122,10 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { @override String get host => (super.noSuchMethod( Invocation.getter(#host), - returnValue: '', + returnValue: _i6.dummyValue( + this, + Invocation.getter(#host), + ), ) as String); @override int get port => (super.noSuchMethod( @@ -132,16 +138,16 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { returnValue: false, ) as bool); @override - _i6.Future closeAdapter() => (super.noSuchMethod( + _i7.Future closeAdapter() => (super.noSuchMethod( Invocation.method( #closeAdapter, [], ), - returnValue: _i6.Future.value(), - returnValueForMissingStub: _i6.Future.value(), - ) as _i6.Future); + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value(), + ) as _i7.Future); @override - _i6.Future request({ + _i7.Future request({ required String? command, List? args = const [], String? requestID, @@ -160,10 +166,10 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { #requestTimeout: requestTimeout, }, ), - returnValue: _i6.Future.value(), - ) as _i6.Future); + returnValue: _i7.Future.value(), + ) as _i7.Future); @override - _i6.Future> batchRequest({ + _i7.Future> batchRequest({ required String? command, required List? args, Duration? requestTimeout = const Duration(seconds: 60), @@ -180,10 +186,10 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { #retries: retries, }, ), - returnValue: _i6.Future>.value([]), - ) as _i6.Future>); + returnValue: _i7.Future>.value([]), + ) as _i7.Future>); @override - _i6.Future ping({ + _i7.Future ping({ String? requestID, int? retryCount = 1, }) => @@ -196,10 +202,10 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { #retryCount: retryCount, }, ), - returnValue: _i6.Future.value(false), - ) as _i6.Future); + returnValue: _i7.Future.value(false), + ) as _i7.Future); @override - _i6.Future> getBlockHeadTip({String? requestID}) => + _i7.Future> getBlockHeadTip({String? requestID}) => (super.noSuchMethod( Invocation.method( #getBlockHeadTip, @@ -207,10 +213,10 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { {#requestID: requestID}, ), returnValue: - _i6.Future>.value({}), - ) as _i6.Future>); + _i7.Future>.value({}), + ) as _i7.Future>); @override - _i6.Future> getServerFeatures({String? requestID}) => + _i7.Future> getServerFeatures({String? requestID}) => (super.noSuchMethod( Invocation.method( #getServerFeatures, @@ -218,10 +224,10 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { {#requestID: requestID}, ), returnValue: - _i6.Future>.value({}), - ) as _i6.Future>); + _i7.Future>.value({}), + ) as _i7.Future>); @override - _i6.Future broadcastTransaction({ + _i7.Future broadcastTransaction({ required String? rawTx, String? requestID, }) => @@ -234,10 +240,20 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { #requestID: requestID, }, ), - returnValue: _i6.Future.value(''), - ) as _i6.Future); + returnValue: _i7.Future.value(_i6.dummyValue( + this, + Invocation.method( + #broadcastTransaction, + [], + { + #rawTx: rawTx, + #requestID: requestID, + }, + ), + )), + ) as _i7.Future); @override - _i6.Future> getBalance({ + _i7.Future> getBalance({ required String? scripthash, String? requestID, }) => @@ -251,10 +267,10 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { }, ), returnValue: - _i6.Future>.value({}), - ) as _i6.Future>); + _i7.Future>.value({}), + ) as _i7.Future>); @override - _i6.Future>> getHistory({ + _i7.Future>> getHistory({ required String? scripthash, String? requestID, }) => @@ -267,11 +283,11 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { #requestID: requestID, }, ), - returnValue: _i6.Future>>.value( + returnValue: _i7.Future>>.value( >[]), - ) as _i6.Future>>); + ) as _i7.Future>>); @override - _i6.Future>>> getBatchHistory( + _i7.Future>>> getBatchHistory( {required List? args}) => (super.noSuchMethod( Invocation.method( @@ -279,11 +295,11 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { [], {#args: args}, ), - returnValue: _i6.Future>>>.value( + returnValue: _i7.Future>>>.value( >>[]), - ) as _i6.Future>>>); + ) as _i7.Future>>>); @override - _i6.Future>> getUTXOs({ + _i7.Future>> getUTXOs({ required String? scripthash, String? requestID, }) => @@ -296,11 +312,11 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { #requestID: requestID, }, ), - returnValue: _i6.Future>>.value( + returnValue: _i7.Future>>.value( >[]), - ) as _i6.Future>>); + ) as _i7.Future>>); @override - _i6.Future>>> getBatchUTXOs( + _i7.Future>>> getBatchUTXOs( {required List? args}) => (super.noSuchMethod( Invocation.method( @@ -308,11 +324,11 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { [], {#args: args}, ), - returnValue: _i6.Future>>>.value( + returnValue: _i7.Future>>>.value( >>[]), - ) as _i6.Future>>>); + ) as _i7.Future>>>); @override - _i6.Future> getTransaction({ + _i7.Future> getTransaction({ required String? txHash, bool? verbose = true, String? requestID, @@ -328,10 +344,10 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { }, ), returnValue: - _i6.Future>.value({}), - ) as _i6.Future>); + _i7.Future>.value({}), + ) as _i7.Future>); @override - _i6.Future> getLelantusAnonymitySet({ + _i7.Future> getLelantusAnonymitySet({ String? groupId = r'1', String? blockhash = r'', String? requestID, @@ -347,10 +363,10 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { }, ), returnValue: - _i6.Future>.value({}), - ) as _i6.Future>); + _i7.Future>.value({}), + ) as _i7.Future>); @override - _i6.Future getLelantusMintData({ + _i7.Future getLelantusMintData({ dynamic mints, String? requestID, }) => @@ -363,10 +379,10 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { #requestID: requestID, }, ), - returnValue: _i6.Future.value(), - ) as _i6.Future); + returnValue: _i7.Future.value(), + ) as _i7.Future); @override - _i6.Future> getLelantusUsedCoinSerials({ + _i7.Future> getLelantusUsedCoinSerials({ String? requestID, required int? startNumber, }) => @@ -380,20 +396,20 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { }, ), returnValue: - _i6.Future>.value({}), - ) as _i6.Future>); + _i7.Future>.value({}), + ) as _i7.Future>); @override - _i6.Future getLelantusLatestCoinId({String? requestID}) => + _i7.Future getLelantusLatestCoinId({String? requestID}) => (super.noSuchMethod( Invocation.method( #getLelantusLatestCoinId, [], {#requestID: requestID}, ), - returnValue: _i6.Future.value(0), - ) as _i6.Future); + returnValue: _i7.Future.value(0), + ) as _i7.Future); @override - _i6.Future> getSparkAnonymitySet({ + _i7.Future> getSparkAnonymitySet({ String? coinGroupId = r'1', String? startBlockHash = r'', String? requestID, @@ -409,10 +425,10 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { }, ), returnValue: - _i6.Future>.value({}), - ) as _i6.Future>); + _i7.Future>.value({}), + ) as _i7.Future>); @override - _i6.Future> getSparkUsedCoinsTags({ + _i7.Future> getSparkUsedCoinsTags({ String? requestID, required int? startNumber, }) => @@ -425,10 +441,10 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { #startNumber: startNumber, }, ), - returnValue: _i6.Future>.value({}), - ) as _i6.Future>); + returnValue: _i7.Future>.value({}), + ) as _i7.Future>); @override - _i6.Future>> getSparkMintMetaData({ + _i7.Future>> getSparkMintMetaData({ String? requestID, required List? sparkCoinHashes, }) => @@ -441,21 +457,21 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { #sparkCoinHashes: sparkCoinHashes, }, ), - returnValue: _i6.Future>>.value( + returnValue: _i7.Future>>.value( >[]), - ) as _i6.Future>>); + ) as _i7.Future>>); @override - _i6.Future getSparkLatestCoinId({String? requestID}) => + _i7.Future getSparkLatestCoinId({String? requestID}) => (super.noSuchMethod( Invocation.method( #getSparkLatestCoinId, [], {#requestID: requestID}, ), - returnValue: _i6.Future.value(0), - ) as _i6.Future); + returnValue: _i7.Future.value(0), + ) as _i7.Future); @override - _i6.Future> getFeeRate({String? requestID}) => + _i7.Future> getFeeRate({String? requestID}) => (super.noSuchMethod( Invocation.method( #getFeeRate, @@ -463,10 +479,10 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { {#requestID: requestID}, ), returnValue: - _i6.Future>.value({}), - ) as _i6.Future>); + _i7.Future>.value({}), + ) as _i7.Future>); @override - _i6.Future<_i3.Decimal> estimateFee({ + _i7.Future<_i3.Decimal> estimateFee({ String? requestID, required int? blocks, }) => @@ -479,7 +495,7 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { #blocks: blocks, }, ), - returnValue: _i6.Future<_i3.Decimal>.value(_FakeDecimal_2( + returnValue: _i7.Future<_i3.Decimal>.value(_FakeDecimal_2( this, Invocation.method( #estimateFee, @@ -490,15 +506,15 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { }, ), )), - ) as _i6.Future<_i3.Decimal>); + ) as _i7.Future<_i3.Decimal>); @override - _i6.Future<_i3.Decimal> relayFee({String? requestID}) => (super.noSuchMethod( + _i7.Future<_i3.Decimal> relayFee({String? requestID}) => (super.noSuchMethod( Invocation.method( #relayFee, [], {#requestID: requestID}, ), - returnValue: _i6.Future<_i3.Decimal>.value(_FakeDecimal_2( + returnValue: _i7.Future<_i3.Decimal>.value(_FakeDecimal_2( this, Invocation.method( #relayFee, @@ -506,13 +522,13 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { {#requestID: requestID}, ), )), - ) as _i6.Future<_i3.Decimal>); + ) as _i7.Future<_i3.Decimal>); } /// A class which mocks [Prefs]. /// /// See the documentation for Mockito's code generation for more information. -class MockPrefs extends _i1.Mock implements _i7.Prefs { +class MockPrefs extends _i1.Mock implements _i8.Prefs { MockPrefs() { _i1.throwOnMissingStub(this); } @@ -568,12 +584,12 @@ class MockPrefs extends _i1.Mock implements _i7.Prefs { returnValueForMissingStub: null, ); @override - _i8.SyncingType get syncType => (super.noSuchMethod( + _i9.SyncingType get syncType => (super.noSuchMethod( Invocation.getter(#syncType), - returnValue: _i8.SyncingType.currentWalletOnly, - ) as _i8.SyncingType); + returnValue: _i9.SyncingType.currentWalletOnly, + ) as _i9.SyncingType); @override - set syncType(_i8.SyncingType? syncType) => super.noSuchMethod( + set syncType(_i9.SyncingType? syncType) => super.noSuchMethod( Invocation.setter( #syncType, syncType, @@ -609,7 +625,10 @@ class MockPrefs extends _i1.Mock implements _i7.Prefs { @override String get language => (super.noSuchMethod( Invocation.getter(#language), - returnValue: '', + returnValue: _i6.dummyValue( + this, + Invocation.getter(#language), + ), ) as String); @override set language(String? newLanguage) => super.noSuchMethod( @@ -622,7 +641,10 @@ class MockPrefs extends _i1.Mock implements _i7.Prefs { @override String get currency => (super.noSuchMethod( Invocation.getter(#currency), - returnValue: '', + returnValue: _i6.dummyValue( + this, + Invocation.getter(#currency), + ), ) as String); @override set currency(String? newCurrency) => super.noSuchMethod( @@ -732,12 +754,12 @@ class MockPrefs extends _i1.Mock implements _i7.Prefs { returnValueForMissingStub: null, ); @override - _i9.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod( + _i10.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod( Invocation.getter(#backupFrequencyType), - returnValue: _i9.BackupFrequencyType.everyTenMinutes, - ) as _i9.BackupFrequencyType); + returnValue: _i10.BackupFrequencyType.everyTenMinutes, + ) as _i10.BackupFrequencyType); @override - set backupFrequencyType(_i9.BackupFrequencyType? backupFrequencyType) => + set backupFrequencyType(_i10.BackupFrequencyType? backupFrequencyType) => super.noSuchMethod( Invocation.setter( #backupFrequencyType, @@ -831,7 +853,10 @@ class MockPrefs extends _i1.Mock implements _i7.Prefs { @override String get themeId => (super.noSuchMethod( Invocation.getter(#themeId), - returnValue: '', + returnValue: _i6.dummyValue( + this, + Invocation.getter(#themeId), + ), ) as String); @override set themeId(String? themeId) => super.noSuchMethod( @@ -844,7 +869,10 @@ class MockPrefs extends _i1.Mock implements _i7.Prefs { @override String get systemBrightnessLightThemeId => (super.noSuchMethod( Invocation.getter(#systemBrightnessLightThemeId), - returnValue: '', + returnValue: _i6.dummyValue( + this, + Invocation.getter(#systemBrightnessLightThemeId), + ), ) as String); @override set systemBrightnessLightThemeId(String? systemBrightnessLightThemeId) => @@ -858,7 +886,10 @@ class MockPrefs extends _i1.Mock implements _i7.Prefs { @override String get systemBrightnessDarkThemeId => (super.noSuchMethod( Invocation.getter(#systemBrightnessDarkThemeId), - returnValue: '', + returnValue: _i6.dummyValue( + this, + Invocation.getter(#systemBrightnessDarkThemeId), + ), ) as String); @override set systemBrightnessDarkThemeId(String? systemBrightnessDarkThemeId) => @@ -888,61 +919,61 @@ class MockPrefs extends _i1.Mock implements _i7.Prefs { returnValue: false, ) as bool); @override - _i6.Future init() => (super.noSuchMethod( + _i7.Future init() => (super.noSuchMethod( Invocation.method( #init, [], ), - returnValue: _i6.Future.value(), - returnValueForMissingStub: _i6.Future.value(), - ) as _i6.Future); + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value(), + ) as _i7.Future); @override - _i6.Future incrementCurrentNotificationIndex() => (super.noSuchMethod( + _i7.Future incrementCurrentNotificationIndex() => (super.noSuchMethod( Invocation.method( #incrementCurrentNotificationIndex, [], ), - returnValue: _i6.Future.value(), - returnValueForMissingStub: _i6.Future.value(), - ) as _i6.Future); + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value(), + ) as _i7.Future); @override - _i6.Future isExternalCallsSet() => (super.noSuchMethod( + _i7.Future isExternalCallsSet() => (super.noSuchMethod( Invocation.method( #isExternalCallsSet, [], ), - returnValue: _i6.Future.value(false), - ) as _i6.Future); + returnValue: _i7.Future.value(false), + ) as _i7.Future); @override - _i6.Future saveUserID(String? userId) => (super.noSuchMethod( + _i7.Future saveUserID(String? userId) => (super.noSuchMethod( Invocation.method( #saveUserID, [userId], ), - returnValue: _i6.Future.value(), - returnValueForMissingStub: _i6.Future.value(), - ) as _i6.Future); + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value(), + ) as _i7.Future); @override - _i6.Future saveSignupEpoch(int? signupEpoch) => (super.noSuchMethod( + _i7.Future saveSignupEpoch(int? signupEpoch) => (super.noSuchMethod( Invocation.method( #saveSignupEpoch, [signupEpoch], ), - returnValue: _i6.Future.value(), - returnValueForMissingStub: _i6.Future.value(), - ) as _i6.Future); + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value(), + ) as _i7.Future); @override - _i10.AmountUnit amountUnit(_i2.CryptoCurrency? coin) => (super.noSuchMethod( + _i11.AmountUnit amountUnit(_i2.CryptoCurrency? coin) => (super.noSuchMethod( Invocation.method( #amountUnit, [coin], ), - returnValue: _i10.AmountUnit.normal, - ) as _i10.AmountUnit); + returnValue: _i11.AmountUnit.normal, + ) as _i11.AmountUnit); @override void updateAmountUnit({ required _i2.CryptoCurrency? coin, - required _i10.AmountUnit? amountUnit, + required _i11.AmountUnit? amountUnit, }) => super.noSuchMethod( Invocation.method( @@ -1010,7 +1041,7 @@ class MockPrefs extends _i1.Mock implements _i7.Prefs { returnValueForMissingStub: null, ); @override - void addListener(_i11.VoidCallback? listener) => super.noSuchMethod( + void addListener(_i12.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #addListener, [listener], @@ -1018,7 +1049,7 @@ class MockPrefs extends _i1.Mock implements _i7.Prefs { returnValueForMissingStub: null, ); @override - void removeListener(_i11.VoidCallback? listener) => super.noSuchMethod( + void removeListener(_i12.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #removeListener, [listener], diff --git a/test/flutter_secure_storage_interface_test.mocks.dart b/test/flutter_secure_storage_interface_test.mocks.dart index e1541d3a2..8779d5147 100644 --- a/test/flutter_secure_storage_interface_test.mocks.dart +++ b/test/flutter_secure_storage_interface_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/flutter_secure_storage_interface_test.dart. // Do not manually edit this file. @@ -12,6 +12,8 @@ import 'package:mockito/mockito.dart' as _i1; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors diff --git a/test/global_events_test.dart b/test/global_events_test.dart index c7d68bdcc..fc65d41f9 100644 --- a/test/global_events_test.dart +++ b/test/global_events_test.dart @@ -4,7 +4,6 @@ import 'package:stackwallet/services/event_bus/events/global/refresh_percent_cha import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart'; import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/global_event_bus.dart'; -import 'package:stackwallet/wallets/crypto_currency/coins/bitcoin.dart'; import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart'; void main() { diff --git a/test/models/type_adapter_tests/lelantus_coin_adapter_test.dart b/test/models/type_adapter_tests/lelantus_coin_adapter_test.dart index 44c2a1a70..cae7ca876 100644 --- a/test/models/type_adapter_tests/lelantus_coin_adapter_test.dart +++ b/test/models/type_adapter_tests/lelantus_coin_adapter_test.dart @@ -10,11 +10,11 @@ import 'lelantus_coin_adapter_test.mocks.dart'; @GenerateMocks([BinaryReader, BinaryWriter]) void main() { test("read", () { - LelantusCoinAdapter adapter = LelantusCoinAdapter(); + final LelantusCoinAdapter adapter = LelantusCoinAdapter(); final reader = MockBinaryReader(); - List readByteResponses = [6, 0, 1, 2, 3, 4, 5]; - List readResponses = [ + final List readByteResponses = [6, 0, 1, 2, 3, 4, 5]; + final List readResponses = [ 1, 1000, "kjhxzcfg8u7ty23w8gbdsf87cfgsdf3", @@ -35,8 +35,8 @@ void main() { }); test("write", () { - LelantusCoinAdapter adapter = LelantusCoinAdapter(); - LelantusCoin obj = + final LelantusCoinAdapter adapter = LelantusCoinAdapter(); + final LelantusCoin obj = LelantusCoin(1, 100, "some public coin", "some txid", 1, true); final writer = MockBinaryWriter(); diff --git a/test/models/type_adapter_tests/lelantus_coin_adapter_test.mocks.dart b/test/models/type_adapter_tests/lelantus_coin_adapter_test.mocks.dart index 0ca7abc8d..13aeb65c7 100644 --- a/test/models/type_adapter_tests/lelantus_coin_adapter_test.mocks.dart +++ b/test/models/type_adapter_tests/lelantus_coin_adapter_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/models/type_adapter_tests/lelantus_coin_adapter_test.dart. // Do not manually edit this file. @@ -9,11 +9,14 @@ import 'dart:typed_data' as _i4; import 'package:hive/hive.dart' as _i3; import 'package:hive/src/object/hive_object.dart' as _i1; import 'package:mockito/mockito.dart' as _i2; +import 'package:mockito/src/dummies.dart' as _i6; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -143,7 +146,16 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { decoder, ], ), - returnValue: '', + returnValue: _i6.dummyValue( + this, + Invocation.method( + #readString, + [ + byteCount, + decoder, + ], + ), + ), ) as String); @override _i4.Uint8List readByteList([int? length]) => (super.noSuchMethod( diff --git a/test/models/type_adapter_tests/transactions_model_adapter_test.dart b/test/models/type_adapter_tests/transactions_model_adapter_test.dart index a7c9d0441..265cdc159 100644 --- a/test/models/type_adapter_tests/transactions_model_adapter_test.dart +++ b/test/models/type_adapter_tests/transactions_model_adapter_test.dart @@ -14,7 +14,7 @@ void main() { final adapter = TransactionDataAdapter(); final reader = MockBinaryReader(); - List readeByteResponses = [1, 0]; + final List readeByteResponses = [1, 0]; when(reader.readByte()).thenAnswer((_) => readeByteResponses.removeAt(0)); @@ -100,11 +100,11 @@ void main() { final adapter = TransactionChunkAdapter(); final reader = MockBinaryReader(); - List readByteResponses = [2, 0, 1]; + final List readByteResponses = [2, 0, 1]; when(reader.readByte()).thenAnswer((_) => readByteResponses.removeAt(0)); - List readResponses = [ + final List readResponses = [ 3426523234, [], ]; @@ -195,14 +195,14 @@ void main() { final adapter = TransactionAdapter(); final reader = MockBinaryReader(); - List readByteResponses = [20]; + final List readByteResponses = [20]; for (int i = 0; i < 20; i++) { readByteResponses.add(i); } when(reader.readByte()).thenAnswer((_) => readByteResponses.removeAt(0)); - List readResponses = [ + final List readResponses = [ "some txid", true, 872346534, @@ -425,14 +425,14 @@ void main() { final adapter = InputAdapter(); final reader = MockBinaryReader(); - List readByteResponses = [9]; + final List readByteResponses = [9]; for (int i = 0; i < 9; i++) { readByteResponses.add(i); } when(reader.readByte()).thenAnswer((_) => readByteResponses.removeAt(0)); - List readResponses = [ + final List readResponses = [ "some txid", 1, Output(scriptpubkeyAddress: "adr", value: 1), @@ -565,14 +565,14 @@ void main() { final adapter = OutputAdapter(); final reader = MockBinaryReader(); - List readByteResponses = [5]; + final List readByteResponses = [5]; for (int i = 0; i < 5; i++) { readByteResponses.add(i); } when(reader.readByte()).thenAnswer((_) => readByteResponses.removeAt(0)); - List readResponses = [ + final List readResponses = [ "some scriptpubkey", "some scriptpubkey asm", "some scriptpubkey type", diff --git a/test/models/type_adapter_tests/transactions_model_adapter_test.mocks.dart b/test/models/type_adapter_tests/transactions_model_adapter_test.mocks.dart index 3d5db62f2..53dd08255 100644 --- a/test/models/type_adapter_tests/transactions_model_adapter_test.mocks.dart +++ b/test/models/type_adapter_tests/transactions_model_adapter_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/models/type_adapter_tests/transactions_model_adapter_test.dart. // Do not manually edit this file. @@ -9,11 +9,14 @@ import 'dart:typed_data' as _i4; import 'package:hive/hive.dart' as _i3; import 'package:hive/src/object/hive_object.dart' as _i1; import 'package:mockito/mockito.dart' as _i2; +import 'package:mockito/src/dummies.dart' as _i6; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -143,7 +146,16 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { decoder, ], ), - returnValue: '', + returnValue: _i6.dummyValue( + this, + Invocation.method( + #readString, + [ + byteCount, + decoder, + ], + ), + ), ) as String); @override _i4.Uint8List readByteList([int? length]) => (super.noSuchMethod( diff --git a/test/models/type_adapter_tests/utxo_model_adapter_test.dart b/test/models/type_adapter_tests/utxo_model_adapter_test.dart index 9dfd5b0ba..5849c61bd 100644 --- a/test/models/type_adapter_tests/utxo_model_adapter_test.dart +++ b/test/models/type_adapter_tests/utxo_model_adapter_test.dart @@ -14,12 +14,12 @@ void main() { final adapter = UtxoDataAdapter(); final reader = MockBinaryReader(); - List readByteResponses = [5]; + final List readByteResponses = [5]; for (int i = 0; i < 5; i++) { readByteResponses.add(i); } - List readResponses = [ + final List readResponses = [ "100", 100000000, "10", @@ -135,12 +135,12 @@ void main() { final adapter = UtxoObjectAdapter(); final reader = MockBinaryReader(); - List readByteResponses = [8]; + final List readByteResponses = [8]; for (int i = 0; i < 8; i++) { readByteResponses.add(i); } - List readResponses = [ + final List readResponses = [ "100", 1, Status( @@ -286,12 +286,12 @@ void main() { final adapter = StatusAdapter(); final reader = MockBinaryReader(); - List readByteResponses = [5]; + final List readByteResponses = [5]; for (int i = 0; i < 5; i++) { readByteResponses.add(i); } - List readResponses = [ + final List readResponses = [ true, "some blockhash", 4587364, diff --git a/test/models/type_adapter_tests/utxo_model_adapter_test.mocks.dart b/test/models/type_adapter_tests/utxo_model_adapter_test.mocks.dart index f326e8cbc..f2750cf2a 100644 --- a/test/models/type_adapter_tests/utxo_model_adapter_test.mocks.dart +++ b/test/models/type_adapter_tests/utxo_model_adapter_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/models/type_adapter_tests/utxo_model_adapter_test.dart. // Do not manually edit this file. @@ -9,11 +9,14 @@ import 'dart:typed_data' as _i4; import 'package:hive/hive.dart' as _i3; import 'package:hive/src/object/hive_object.dart' as _i1; import 'package:mockito/mockito.dart' as _i2; +import 'package:mockito/src/dummies.dart' as _i6; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -143,7 +146,16 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { decoder, ], ), - returnValue: '', + returnValue: _i6.dummyValue( + this, + Invocation.method( + #readString, + [ + byteCount, + decoder, + ], + ), + ), ) as String); @override _i4.Uint8List readByteList([int? length]) => (super.noSuchMethod( diff --git a/test/notifications/notification_card_test.mocks.dart b/test/notifications/notification_card_test.mocks.dart index e325b3056..52f5ee6db 100644 --- a/test/notifications/notification_card_test.mocks.dart +++ b/test/notifications/notification_card_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/notifications/notification_card_test.dart. // Do not manually edit this file. @@ -16,6 +16,8 @@ import 'package:stackwallet/themes/theme_service.dart' as _i4; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors diff --git a/test/pages/send_view/send_view_test.mocks.dart b/test/pages/send_view/send_view_test.mocks.dart index bf71bbcf2..6a0bc127f 100644 --- a/test/pages/send_view/send_view_test.mocks.dart +++ b/test/pages/send_view/send_view_test.mocks.dart @@ -1,24 +1,25 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/pages/send_view/send_view_test.dart. // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i10; -import 'dart:typed_data' as _i18; +import 'dart:typed_data' as _i19; import 'dart:ui' as _i14; import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i16; import 'package:stackwallet/db/isar/main_db.dart' as _i3; -import 'package:stackwallet/models/isar/stack_theme.dart' as _i17; +import 'package:stackwallet/models/isar/stack_theme.dart' as _i18; import 'package:stackwallet/models/node_model.dart' as _i13; import 'package:stackwallet/networking/http.dart' as _i7; import 'package:stackwallet/services/locale_service.dart' as _i15; import 'package:stackwallet/services/node_service.dart' as _i2; import 'package:stackwallet/services/wallets.dart' as _i9; -import 'package:stackwallet/themes/theme_service.dart' as _i16; -import 'package:stackwallet/utilities/amount/amount_unit.dart' as _i21; -import 'package:stackwallet/utilities/enums/backup_frequency_type.dart' as _i20; -import 'package:stackwallet/utilities/enums/sync_type_enum.dart' as _i19; +import 'package:stackwallet/themes/theme_service.dart' as _i17; +import 'package:stackwallet/utilities/amount/amount_unit.dart' as _i22; +import 'package:stackwallet/utilities/enums/backup_frequency_type.dart' as _i21; +import 'package:stackwallet/utilities/enums/sync_type_enum.dart' as _i20; import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart' as _i6; import 'package:stackwallet/utilities/prefs.dart' as _i12; @@ -33,6 +34,8 @@ import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/cash_fusion_i // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -439,7 +442,10 @@ class MockLocaleService extends _i1.Mock implements _i15.LocaleService { @override String get locale => (super.noSuchMethod( Invocation.getter(#locale), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#locale), + ), ) as String); @override bool get hasListeners => (super.noSuchMethod( @@ -493,7 +499,7 @@ class MockLocaleService extends _i1.Mock implements _i15.LocaleService { /// A class which mocks [ThemeService]. /// /// See the documentation for Mockito's code generation for more information. -class MockThemeService extends _i1.Mock implements _i16.ThemeService { +class MockThemeService extends _i1.Mock implements _i17.ThemeService { MockThemeService() { _i1.throwOnMissingStub(this); } @@ -523,10 +529,10 @@ class MockThemeService extends _i1.Mock implements _i16.ThemeService { ), ) as _i3.MainDB); @override - List<_i17.StackTheme> get installedThemes => (super.noSuchMethod( + List<_i18.StackTheme> get installedThemes => (super.noSuchMethod( Invocation.getter(#installedThemes), - returnValue: <_i17.StackTheme>[], - ) as List<_i17.StackTheme>); + returnValue: <_i18.StackTheme>[], + ) as List<_i18.StackTheme>); @override void init(_i3.MainDB? db) => super.noSuchMethod( Invocation.method( @@ -536,7 +542,7 @@ class MockThemeService extends _i1.Mock implements _i16.ThemeService { returnValueForMissingStub: null, ); @override - _i10.Future install({required _i18.Uint8List? themeArchiveData}) => + _i10.Future install({required _i19.Uint8List? themeArchiveData}) => (super.noSuchMethod( Invocation.method( #install, @@ -576,33 +582,33 @@ class MockThemeService extends _i1.Mock implements _i16.ThemeService { returnValue: _i10.Future.value(false), ) as _i10.Future); @override - _i10.Future> fetchThemes() => + _i10.Future> fetchThemes() => (super.noSuchMethod( Invocation.method( #fetchThemes, [], ), - returnValue: _i10.Future>.value( - <_i16.StackThemeMetaData>[]), - ) as _i10.Future>); + returnValue: _i10.Future>.value( + <_i17.StackThemeMetaData>[]), + ) as _i10.Future>); @override - _i10.Future<_i18.Uint8List> fetchTheme( - {required _i16.StackThemeMetaData? themeMetaData}) => + _i10.Future<_i19.Uint8List> fetchTheme( + {required _i17.StackThemeMetaData? themeMetaData}) => (super.noSuchMethod( Invocation.method( #fetchTheme, [], {#themeMetaData: themeMetaData}, ), - returnValue: _i10.Future<_i18.Uint8List>.value(_i18.Uint8List(0)), - ) as _i10.Future<_i18.Uint8List>); + returnValue: _i10.Future<_i19.Uint8List>.value(_i19.Uint8List(0)), + ) as _i10.Future<_i19.Uint8List>); @override - _i17.StackTheme? getTheme({required String? themeId}) => + _i18.StackTheme? getTheme({required String? themeId}) => (super.noSuchMethod(Invocation.method( #getTheme, [], {#themeId: themeId}, - )) as _i17.StackTheme?); + )) as _i18.StackTheme?); } /// A class which mocks [Prefs]. @@ -664,12 +670,12 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { returnValueForMissingStub: null, ); @override - _i19.SyncingType get syncType => (super.noSuchMethod( + _i20.SyncingType get syncType => (super.noSuchMethod( Invocation.getter(#syncType), - returnValue: _i19.SyncingType.currentWalletOnly, - ) as _i19.SyncingType); + returnValue: _i20.SyncingType.currentWalletOnly, + ) as _i20.SyncingType); @override - set syncType(_i19.SyncingType? syncType) => super.noSuchMethod( + set syncType(_i20.SyncingType? syncType) => super.noSuchMethod( Invocation.setter( #syncType, syncType, @@ -705,7 +711,10 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { @override String get language => (super.noSuchMethod( Invocation.getter(#language), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#language), + ), ) as String); @override set language(String? newLanguage) => super.noSuchMethod( @@ -718,7 +727,10 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { @override String get currency => (super.noSuchMethod( Invocation.getter(#currency), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#currency), + ), ) as String); @override set currency(String? newCurrency) => super.noSuchMethod( @@ -828,12 +840,12 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { returnValueForMissingStub: null, ); @override - _i20.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod( + _i21.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod( Invocation.getter(#backupFrequencyType), - returnValue: _i20.BackupFrequencyType.everyTenMinutes, - ) as _i20.BackupFrequencyType); + returnValue: _i21.BackupFrequencyType.everyTenMinutes, + ) as _i21.BackupFrequencyType); @override - set backupFrequencyType(_i20.BackupFrequencyType? backupFrequencyType) => + set backupFrequencyType(_i21.BackupFrequencyType? backupFrequencyType) => super.noSuchMethod( Invocation.setter( #backupFrequencyType, @@ -927,7 +939,10 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { @override String get themeId => (super.noSuchMethod( Invocation.getter(#themeId), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#themeId), + ), ) as String); @override set themeId(String? themeId) => super.noSuchMethod( @@ -940,7 +955,10 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { @override String get systemBrightnessLightThemeId => (super.noSuchMethod( Invocation.getter(#systemBrightnessLightThemeId), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#systemBrightnessLightThemeId), + ), ) as String); @override set systemBrightnessLightThemeId(String? systemBrightnessLightThemeId) => @@ -954,7 +972,10 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { @override String get systemBrightnessDarkThemeId => (super.noSuchMethod( Invocation.getter(#systemBrightnessDarkThemeId), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#systemBrightnessDarkThemeId), + ), ) as String); @override set systemBrightnessDarkThemeId(String? systemBrightnessDarkThemeId) => @@ -1028,17 +1049,17 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); @override - _i21.AmountUnit amountUnit(_i4.CryptoCurrency? coin) => (super.noSuchMethod( + _i22.AmountUnit amountUnit(_i4.CryptoCurrency? coin) => (super.noSuchMethod( Invocation.method( #amountUnit, [coin], ), - returnValue: _i21.AmountUnit.normal, - ) as _i21.AmountUnit); + returnValue: _i22.AmountUnit.normal, + ) as _i22.AmountUnit); @override void updateAmountUnit({ required _i4.CryptoCurrency? coin, - required _i21.AmountUnit? amountUnit, + required _i22.AmountUnit? amountUnit, }) => super.noSuchMethod( Invocation.method( diff --git a/test/price_test.mocks.dart b/test/price_test.mocks.dart index 59b90851e..8ba8c6d26 100644 --- a/test/price_test.mocks.dart +++ b/test/price_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/price_test.dart. // Do not manually edit this file. @@ -14,6 +14,8 @@ import 'package:stackwallet/networking/http.dart' as _i2; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors diff --git a/test/sample_data/transaction_data_samples.dart b/test/sample_data/transaction_data_samples.dart index ed248a0ea..42962888b 100644 --- a/test/sample_data/transaction_data_samples.dart +++ b/test/sample_data/transaction_data_samples.dart @@ -2526,9 +2526,9 @@ final jsonTransactions = [ ]; Map get transactionDataMap { - Map result = {}; + final Map result = {}; for (final tx in jsonTransactions) { - String? bob = tx["txid"] as String?; + final String? bob = tx["txid"] as String?; result[bob] = tx; } return result; diff --git a/test/screen_tests/address_book_view/address_book_view_screen_test.dart b/test/screen_tests/address_book_view/address_book_view_screen_test.dart index dffa8f69d..ad7aff7c1 100644 --- a/test/screen_tests/address_book_view/address_book_view_screen_test.dart +++ b/test/screen_tests/address_book_view/address_book_view_screen_test.dart @@ -13,7 +13,7 @@ import 'package:stackwallet/services/address_book_service.dart'; // import 'address_book_view_screen_test.mocks.dart'; @GenerateMocks([], customMocks: [ - MockSpec(returnNullOnMissingStub: true), + MockSpec(), ]) void main() { // testWidgets("AddressBookView builds correctly", (tester) async { diff --git a/test/screen_tests/address_book_view/address_book_view_screen_test.mocks.dart b/test/screen_tests/address_book_view/address_book_view_screen_test.mocks.dart index b359426c3..b7a88864d 100644 --- a/test/screen_tests/address_book_view/address_book_view_screen_test.mocks.dart +++ b/test/screen_tests/address_book_view/address_book_view_screen_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/screen_tests/address_book_view/address_book_view_screen_test.dart. // Do not manually edit this file. @@ -14,6 +14,8 @@ import 'package:stackwallet/services/address_book_service.dart' as _i3; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -36,6 +38,10 @@ class _FakeContactEntry_0 extends _i1.SmartFake implements _i2.ContactEntry { /// See the documentation for Mockito's code generation for more information. class MockAddressBookService extends _i1.Mock implements _i3.AddressBookService { + MockAddressBookService() { + _i1.throwOnMissingStub(this); + } + @override List<_i2.ContactEntry> get contacts => (super.noSuchMethod( Invocation.getter(#contacts), diff --git a/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.dart b/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.dart index 5967b7f3f..437d941a0 100644 --- a/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.dart +++ b/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.dart @@ -21,7 +21,7 @@ import 'package:stackwallet/utilities/barcode_scanner_interface.dart'; @GenerateMocks([ BarcodeScannerWrapper ], customMocks: [ - MockSpec(returnNullOnMissingStub: true), + MockSpec(), ]) void main() { // testWidgets("AddAddressBookEntryView builds correctly", (tester) async { diff --git a/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.mocks.dart b/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.mocks.dart index 4926f74fb..63bbc41c5 100644 --- a/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.mocks.dart +++ b/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.dart. // Do not manually edit this file. @@ -16,6 +16,8 @@ import 'package:stackwallet/utilities/barcode_scanner_interface.dart' as _i4; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -77,6 +79,10 @@ class MockBarcodeScannerWrapper extends _i1.Mock /// See the documentation for Mockito's code generation for more information. class MockAddressBookService extends _i1.Mock implements _i6.AddressBookService { + MockAddressBookService() { + _i1.throwOnMissingStub(this); + } + @override List<_i3.ContactEntry> get contacts => (super.noSuchMethod( Invocation.getter(#contacts), diff --git a/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.dart b/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.dart index 40379c4f4..1d39a09ef 100644 --- a/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.dart +++ b/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.dart @@ -23,8 +23,8 @@ import 'package:stackwallet/services/locale_service.dart'; // import 'address_book_entry_details_view_screen_test.mocks.dart'; @GenerateMocks([], customMocks: [ - MockSpec(returnNullOnMissingStub: true), - MockSpec(returnNullOnMissingStub: true), + MockSpec(), + MockSpec(), ]) void main() { // testWidgets("AddressBookDetailsView builds correctly", (tester) async { diff --git a/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.mocks.dart b/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.mocks.dart index b16ab6149..97a359919 100644 --- a/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.mocks.dart +++ b/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.dart. // Do not manually edit this file. @@ -7,6 +7,7 @@ import 'dart:async' as _i4; import 'dart:ui' as _i5; import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i7; import 'package:stackwallet/models/isar/models/contact_entry.dart' as _i2; import 'package:stackwallet/services/address_book_service.dart' as _i3; import 'package:stackwallet/services/locale_service.dart' as _i6; @@ -15,6 +16,8 @@ import 'package:stackwallet/services/locale_service.dart' as _i6; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -37,6 +40,10 @@ class _FakeContactEntry_0 extends _i1.SmartFake implements _i2.ContactEntry { /// See the documentation for Mockito's code generation for more information. class MockAddressBookService extends _i1.Mock implements _i3.AddressBookService { + MockAddressBookService() { + _i1.throwOnMissingStub(this); + } + @override List<_i2.ContactEntry> get contacts => (super.noSuchMethod( Invocation.getter(#contacts), @@ -150,10 +157,17 @@ class MockAddressBookService extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockLocaleService extends _i1.Mock implements _i6.LocaleService { + MockLocaleService() { + _i1.throwOnMissingStub(this); + } + @override String get locale => (super.noSuchMethod( Invocation.getter(#locale), - returnValue: '', + returnValue: _i7.dummyValue( + this, + Invocation.getter(#locale), + ), ) as String); @override bool get hasListeners => (super.noSuchMethod( diff --git a/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.dart b/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.dart index db0e02703..bb93f1a2c 100644 --- a/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.dart +++ b/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.dart @@ -20,7 +20,7 @@ import 'package:stackwallet/services/address_book_service.dart'; // import 'edit_address_book_entry_view_screen_test.mocks.dart'; @GenerateMocks([], customMocks: [ - MockSpec(returnNullOnMissingStub: true), + MockSpec(), ]) void main() { // testWidgets("EditAddressBookEntryView builds correctly", (tester) async { diff --git a/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.mocks.dart b/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.mocks.dart index 1f3ee97fa..1f195d5c3 100644 --- a/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.mocks.dart +++ b/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.dart. // Do not manually edit this file. @@ -14,6 +14,8 @@ import 'package:stackwallet/services/address_book_service.dart' as _i3; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -36,6 +38,10 @@ class _FakeContactEntry_0 extends _i1.SmartFake implements _i2.ContactEntry { /// See the documentation for Mockito's code generation for more information. class MockAddressBookService extends _i1.Mock implements _i3.AddressBookService { + MockAddressBookService() { + _i1.throwOnMissingStub(this); + } + @override List<_i2.ContactEntry> get contacts => (super.noSuchMethod( Invocation.getter(#contacts), diff --git a/test/screen_tests/exchange/exchange_view_test.mocks.dart b/test/screen_tests/exchange/exchange_view_test.mocks.dart index 2691281c4..f00e95494 100644 --- a/test/screen_tests/exchange/exchange_view_test.mocks.dart +++ b/test/screen_tests/exchange/exchange_view_test.mocks.dart @@ -1,41 +1,42 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/screen_tests/exchange/exchange_view_test.dart. // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i8; -import 'dart:ui' as _i11; +import 'dart:async' as _i9; +import 'dart:ui' as _i12; -import 'package:decimal/decimal.dart' as _i17; +import 'package:decimal/decimal.dart' as _i18; import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i7; import 'package:stackwallet/models/exchange/change_now/cn_exchange_estimate.dart' - as _i20; -import 'package:stackwallet/models/exchange/change_now/exchange_transaction.dart' - as _i22; -import 'package:stackwallet/models/exchange/change_now/exchange_transaction_status.dart' - as _i23; -import 'package:stackwallet/models/exchange/response_objects/estimate.dart' - as _i19; -import 'package:stackwallet/models/exchange/response_objects/fixed_rate_market.dart' as _i21; +import 'package:stackwallet/models/exchange/change_now/exchange_transaction.dart' + as _i23; +import 'package:stackwallet/models/exchange/change_now/exchange_transaction_status.dart' + as _i24; +import 'package:stackwallet/models/exchange/response_objects/estimate.dart' + as _i20; +import 'package:stackwallet/models/exchange/response_objects/fixed_rate_market.dart' + as _i22; import 'package:stackwallet/models/exchange/response_objects/range.dart' - as _i18; + as _i19; import 'package:stackwallet/models/exchange/response_objects/trade.dart' - as _i13; -import 'package:stackwallet/models/isar/exchange_cache/currency.dart' as _i16; -import 'package:stackwallet/models/isar/exchange_cache/pair.dart' as _i24; + as _i14; +import 'package:stackwallet/models/isar/exchange_cache/currency.dart' as _i17; +import 'package:stackwallet/models/isar/exchange_cache/pair.dart' as _i25; import 'package:stackwallet/networking/http.dart' as _i3; import 'package:stackwallet/services/exchange/change_now/change_now_api.dart' - as _i15; + as _i16; import 'package:stackwallet/services/exchange/exchange_response.dart' as _i4; -import 'package:stackwallet/services/trade_notes_service.dart' as _i14; -import 'package:stackwallet/services/trade_service.dart' as _i12; -import 'package:stackwallet/utilities/amount/amount_unit.dart' as _i9; -import 'package:stackwallet/utilities/enums/backup_frequency_type.dart' as _i7; +import 'package:stackwallet/services/trade_notes_service.dart' as _i15; +import 'package:stackwallet/services/trade_service.dart' as _i13; +import 'package:stackwallet/utilities/amount/amount_unit.dart' as _i10; +import 'package:stackwallet/utilities/enums/backup_frequency_type.dart' as _i8; import 'package:stackwallet/utilities/enums/sync_type_enum.dart' as _i6; import 'package:stackwallet/utilities/prefs.dart' as _i5; import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart' - as _i10; + as _i11; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart' as _i2; @@ -43,6 +44,8 @@ import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/cash_fusion_i // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -181,7 +184,10 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { @override String get language => (super.noSuchMethod( Invocation.getter(#language), - returnValue: '', + returnValue: _i7.dummyValue( + this, + Invocation.getter(#language), + ), ) as String); @override set language(String? newLanguage) => super.noSuchMethod( @@ -194,7 +200,10 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { @override String get currency => (super.noSuchMethod( Invocation.getter(#currency), - returnValue: '', + returnValue: _i7.dummyValue( + this, + Invocation.getter(#currency), + ), ) as String); @override set currency(String? newCurrency) => super.noSuchMethod( @@ -304,12 +313,12 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { returnValueForMissingStub: null, ); @override - _i7.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod( + _i8.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod( Invocation.getter(#backupFrequencyType), - returnValue: _i7.BackupFrequencyType.everyTenMinutes, - ) as _i7.BackupFrequencyType); + returnValue: _i8.BackupFrequencyType.everyTenMinutes, + ) as _i8.BackupFrequencyType); @override - set backupFrequencyType(_i7.BackupFrequencyType? backupFrequencyType) => + set backupFrequencyType(_i8.BackupFrequencyType? backupFrequencyType) => super.noSuchMethod( Invocation.setter( #backupFrequencyType, @@ -403,7 +412,10 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { @override String get themeId => (super.noSuchMethod( Invocation.getter(#themeId), - returnValue: '', + returnValue: _i7.dummyValue( + this, + Invocation.getter(#themeId), + ), ) as String); @override set themeId(String? themeId) => super.noSuchMethod( @@ -416,7 +428,10 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { @override String get systemBrightnessLightThemeId => (super.noSuchMethod( Invocation.getter(#systemBrightnessLightThemeId), - returnValue: '', + returnValue: _i7.dummyValue( + this, + Invocation.getter(#systemBrightnessLightThemeId), + ), ) as String); @override set systemBrightnessLightThemeId(String? systemBrightnessLightThemeId) => @@ -430,7 +445,10 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { @override String get systemBrightnessDarkThemeId => (super.noSuchMethod( Invocation.getter(#systemBrightnessDarkThemeId), - returnValue: '', + returnValue: _i7.dummyValue( + this, + Invocation.getter(#systemBrightnessDarkThemeId), + ), ) as String); @override set systemBrightnessDarkThemeId(String? systemBrightnessDarkThemeId) => @@ -460,61 +478,61 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { returnValue: false, ) as bool); @override - _i8.Future init() => (super.noSuchMethod( + _i9.Future init() => (super.noSuchMethod( Invocation.method( #init, [], ), - returnValue: _i8.Future.value(), - returnValueForMissingStub: _i8.Future.value(), - ) as _i8.Future); + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); @override - _i8.Future incrementCurrentNotificationIndex() => (super.noSuchMethod( + _i9.Future incrementCurrentNotificationIndex() => (super.noSuchMethod( Invocation.method( #incrementCurrentNotificationIndex, [], ), - returnValue: _i8.Future.value(), - returnValueForMissingStub: _i8.Future.value(), - ) as _i8.Future); + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); @override - _i8.Future isExternalCallsSet() => (super.noSuchMethod( + _i9.Future isExternalCallsSet() => (super.noSuchMethod( Invocation.method( #isExternalCallsSet, [], ), - returnValue: _i8.Future.value(false), - ) as _i8.Future); + returnValue: _i9.Future.value(false), + ) as _i9.Future); @override - _i8.Future saveUserID(String? userId) => (super.noSuchMethod( + _i9.Future saveUserID(String? userId) => (super.noSuchMethod( Invocation.method( #saveUserID, [userId], ), - returnValue: _i8.Future.value(), - returnValueForMissingStub: _i8.Future.value(), - ) as _i8.Future); + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); @override - _i8.Future saveSignupEpoch(int? signupEpoch) => (super.noSuchMethod( + _i9.Future saveSignupEpoch(int? signupEpoch) => (super.noSuchMethod( Invocation.method( #saveSignupEpoch, [signupEpoch], ), - returnValue: _i8.Future.value(), - returnValueForMissingStub: _i8.Future.value(), - ) as _i8.Future); + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); @override - _i9.AmountUnit amountUnit(_i10.CryptoCurrency? coin) => (super.noSuchMethod( + _i10.AmountUnit amountUnit(_i11.CryptoCurrency? coin) => (super.noSuchMethod( Invocation.method( #amountUnit, [coin], ), - returnValue: _i9.AmountUnit.normal, - ) as _i9.AmountUnit); + returnValue: _i10.AmountUnit.normal, + ) as _i10.AmountUnit); @override void updateAmountUnit({ - required _i10.CryptoCurrency? coin, - required _i9.AmountUnit? amountUnit, + required _i11.CryptoCurrency? coin, + required _i10.AmountUnit? amountUnit, }) => super.noSuchMethod( Invocation.method( @@ -528,7 +546,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { returnValueForMissingStub: null, ); @override - int maxDecimals(_i10.CryptoCurrency? coin) => (super.noSuchMethod( + int maxDecimals(_i11.CryptoCurrency? coin) => (super.noSuchMethod( Invocation.method( #maxDecimals, [coin], @@ -537,7 +555,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ) as int); @override void updateMaxDecimals({ - required _i10.CryptoCurrency? coin, + required _i11.CryptoCurrency? coin, required int? maxDecimals, }) => super.noSuchMethod( @@ -552,7 +570,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { returnValueForMissingStub: null, ); @override - _i2.FusionInfo getFusionServerInfo(_i10.CryptoCurrency? coin) => + _i2.FusionInfo getFusionServerInfo(_i11.CryptoCurrency? coin) => (super.noSuchMethod( Invocation.method( #getFusionServerInfo, @@ -568,7 +586,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ) as _i2.FusionInfo); @override void setFusionServerInfo( - _i10.CryptoCurrency? coin, + _i11.CryptoCurrency? coin, _i2.FusionInfo? fusionServerInfo, ) => super.noSuchMethod( @@ -582,7 +600,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { returnValueForMissingStub: null, ); @override - void addListener(_i11.VoidCallback? listener) => super.noSuchMethod( + void addListener(_i12.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #addListener, [listener], @@ -590,7 +608,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { returnValueForMissingStub: null, ); @override - void removeListener(_i11.VoidCallback? listener) => super.noSuchMethod( + void removeListener(_i12.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #removeListener, [listener], @@ -618,29 +636,29 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { /// A class which mocks [TradesService]. /// /// See the documentation for Mockito's code generation for more information. -class MockTradesService extends _i1.Mock implements _i12.TradesService { +class MockTradesService extends _i1.Mock implements _i13.TradesService { MockTradesService() { _i1.throwOnMissingStub(this); } @override - List<_i13.Trade> get trades => (super.noSuchMethod( + List<_i14.Trade> get trades => (super.noSuchMethod( Invocation.getter(#trades), - returnValue: <_i13.Trade>[], - ) as List<_i13.Trade>); + returnValue: <_i14.Trade>[], + ) as List<_i14.Trade>); @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); @override - _i13.Trade? get(String? tradeId) => (super.noSuchMethod(Invocation.method( + _i14.Trade? get(String? tradeId) => (super.noSuchMethod(Invocation.method( #get, [tradeId], - )) as _i13.Trade?); + )) as _i14.Trade?); @override - _i8.Future add({ - required _i13.Trade? trade, + _i9.Future add({ + required _i14.Trade? trade, required bool? shouldNotifyListeners, }) => (super.noSuchMethod( @@ -652,12 +670,12 @@ class MockTradesService extends _i1.Mock implements _i12.TradesService { #shouldNotifyListeners: shouldNotifyListeners, }, ), - returnValue: _i8.Future.value(), - returnValueForMissingStub: _i8.Future.value(), - ) as _i8.Future); + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); @override - _i8.Future edit({ - required _i13.Trade? trade, + _i9.Future edit({ + required _i14.Trade? trade, required bool? shouldNotifyListeners, }) => (super.noSuchMethod( @@ -669,12 +687,12 @@ class MockTradesService extends _i1.Mock implements _i12.TradesService { #shouldNotifyListeners: shouldNotifyListeners, }, ), - returnValue: _i8.Future.value(), - returnValueForMissingStub: _i8.Future.value(), - ) as _i8.Future); + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); @override - _i8.Future delete({ - required _i13.Trade? trade, + _i9.Future delete({ + required _i14.Trade? trade, required bool? shouldNotifyListeners, }) => (super.noSuchMethod( @@ -686,11 +704,11 @@ class MockTradesService extends _i1.Mock implements _i12.TradesService { #shouldNotifyListeners: shouldNotifyListeners, }, ), - returnValue: _i8.Future.value(), - returnValueForMissingStub: _i8.Future.value(), - ) as _i8.Future); + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); @override - _i8.Future deleteByUuid({ + _i9.Future deleteByUuid({ required String? uuid, required bool? shouldNotifyListeners, }) => @@ -703,11 +721,11 @@ class MockTradesService extends _i1.Mock implements _i12.TradesService { #shouldNotifyListeners: shouldNotifyListeners, }, ), - returnValue: _i8.Future.value(), - returnValueForMissingStub: _i8.Future.value(), - ) as _i8.Future); + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); @override - void addListener(_i11.VoidCallback? listener) => super.noSuchMethod( + void addListener(_i12.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #addListener, [listener], @@ -715,7 +733,7 @@ class MockTradesService extends _i1.Mock implements _i12.TradesService { returnValueForMissingStub: null, ); @override - void removeListener(_i11.VoidCallback? listener) => super.noSuchMethod( + void removeListener(_i12.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #removeListener, [listener], @@ -743,7 +761,7 @@ class MockTradesService extends _i1.Mock implements _i12.TradesService { /// A class which mocks [TradeNotesService]. /// /// See the documentation for Mockito's code generation for more information. -class MockTradeNotesService extends _i1.Mock implements _i14.TradeNotesService { +class MockTradeNotesService extends _i1.Mock implements _i15.TradeNotesService { MockTradeNotesService() { _i1.throwOnMissingStub(this); } @@ -765,10 +783,17 @@ class MockTradeNotesService extends _i1.Mock implements _i14.TradeNotesService { [], {#tradeId: tradeId}, ), - returnValue: '', + returnValue: _i7.dummyValue( + this, + Invocation.method( + #getNote, + [], + {#tradeId: tradeId}, + ), + ), ) as String); @override - _i8.Future set({ + _i9.Future set({ required String? tradeId, required String? note, }) => @@ -781,21 +806,21 @@ class MockTradeNotesService extends _i1.Mock implements _i14.TradeNotesService { #note: note, }, ), - returnValue: _i8.Future.value(), - returnValueForMissingStub: _i8.Future.value(), - ) as _i8.Future); + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); @override - _i8.Future delete({required String? tradeId}) => (super.noSuchMethod( + _i9.Future delete({required String? tradeId}) => (super.noSuchMethod( Invocation.method( #delete, [], {#tradeId: tradeId}, ), - returnValue: _i8.Future.value(), - returnValueForMissingStub: _i8.Future.value(), - ) as _i8.Future); + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); @override - void addListener(_i11.VoidCallback? listener) => super.noSuchMethod( + void addListener(_i12.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #addListener, [listener], @@ -803,7 +828,7 @@ class MockTradeNotesService extends _i1.Mock implements _i14.TradeNotesService { returnValueForMissingStub: null, ); @override - void removeListener(_i11.VoidCallback? listener) => super.noSuchMethod( + void removeListener(_i12.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #removeListener, [listener], @@ -831,7 +856,7 @@ class MockTradeNotesService extends _i1.Mock implements _i14.TradeNotesService { /// A class which mocks [ChangeNowAPI]. /// /// See the documentation for Mockito's code generation for more information. -class MockChangeNowAPI extends _i1.Mock implements _i15.ChangeNowAPI { +class MockChangeNowAPI extends _i1.Mock implements _i16.ChangeNowAPI { MockChangeNowAPI() { _i1.throwOnMissingStub(this); } @@ -845,7 +870,7 @@ class MockChangeNowAPI extends _i1.Mock implements _i15.ChangeNowAPI { ), ) as _i3.HTTP); @override - _i8.Future<_i4.ExchangeResponse>> getAvailableCurrencies({ + _i9.Future<_i4.ExchangeResponse>> getAvailableCurrencies({ bool? fixedRate, bool? active, }) => @@ -859,8 +884,8 @@ class MockChangeNowAPI extends _i1.Mock implements _i15.ChangeNowAPI { }, ), returnValue: - _i8.Future<_i4.ExchangeResponse>>.value( - _FakeExchangeResponse_2>( + _i9.Future<_i4.ExchangeResponse>>.value( + _FakeExchangeResponse_2>( this, Invocation.method( #getAvailableCurrencies, @@ -871,26 +896,26 @@ class MockChangeNowAPI extends _i1.Mock implements _i15.ChangeNowAPI { }, ), )), - ) as _i8.Future<_i4.ExchangeResponse>>); + ) as _i9.Future<_i4.ExchangeResponse>>); @override - _i8.Future<_i4.ExchangeResponse>> getCurrenciesV2() => + _i9.Future<_i4.ExchangeResponse>> getCurrenciesV2() => (super.noSuchMethod( Invocation.method( #getCurrenciesV2, [], ), returnValue: - _i8.Future<_i4.ExchangeResponse>>.value( - _FakeExchangeResponse_2>( + _i9.Future<_i4.ExchangeResponse>>.value( + _FakeExchangeResponse_2>( this, Invocation.method( #getCurrenciesV2, [], ), )), - ) as _i8.Future<_i4.ExchangeResponse>>); + ) as _i9.Future<_i4.ExchangeResponse>>); @override - _i8.Future<_i4.ExchangeResponse>> getPairedCurrencies({ + _i9.Future<_i4.ExchangeResponse>> getPairedCurrencies({ required String? ticker, bool? fixedRate, }) => @@ -904,8 +929,8 @@ class MockChangeNowAPI extends _i1.Mock implements _i15.ChangeNowAPI { }, ), returnValue: - _i8.Future<_i4.ExchangeResponse>>.value( - _FakeExchangeResponse_2>( + _i9.Future<_i4.ExchangeResponse>>.value( + _FakeExchangeResponse_2>( this, Invocation.method( #getPairedCurrencies, @@ -916,9 +941,9 @@ class MockChangeNowAPI extends _i1.Mock implements _i15.ChangeNowAPI { }, ), )), - ) as _i8.Future<_i4.ExchangeResponse>>); + ) as _i9.Future<_i4.ExchangeResponse>>); @override - _i8.Future<_i4.ExchangeResponse<_i17.Decimal>> getMinimalExchangeAmount({ + _i9.Future<_i4.ExchangeResponse<_i18.Decimal>> getMinimalExchangeAmount({ required String? fromTicker, required String? toTicker, String? apiKey, @@ -933,8 +958,8 @@ class MockChangeNowAPI extends _i1.Mock implements _i15.ChangeNowAPI { #apiKey: apiKey, }, ), - returnValue: _i8.Future<_i4.ExchangeResponse<_i17.Decimal>>.value( - _FakeExchangeResponse_2<_i17.Decimal>( + returnValue: _i9.Future<_i4.ExchangeResponse<_i18.Decimal>>.value( + _FakeExchangeResponse_2<_i18.Decimal>( this, Invocation.method( #getMinimalExchangeAmount, @@ -946,9 +971,9 @@ class MockChangeNowAPI extends _i1.Mock implements _i15.ChangeNowAPI { }, ), )), - ) as _i8.Future<_i4.ExchangeResponse<_i17.Decimal>>); + ) as _i9.Future<_i4.ExchangeResponse<_i18.Decimal>>); @override - _i8.Future<_i4.ExchangeResponse<_i18.Range>> getRange({ + _i9.Future<_i4.ExchangeResponse<_i19.Range>> getRange({ required String? fromTicker, required String? toTicker, required bool? isFixedRate, @@ -965,8 +990,8 @@ class MockChangeNowAPI extends _i1.Mock implements _i15.ChangeNowAPI { #apiKey: apiKey, }, ), - returnValue: _i8.Future<_i4.ExchangeResponse<_i18.Range>>.value( - _FakeExchangeResponse_2<_i18.Range>( + returnValue: _i9.Future<_i4.ExchangeResponse<_i19.Range>>.value( + _FakeExchangeResponse_2<_i19.Range>( this, Invocation.method( #getRange, @@ -979,12 +1004,12 @@ class MockChangeNowAPI extends _i1.Mock implements _i15.ChangeNowAPI { }, ), )), - ) as _i8.Future<_i4.ExchangeResponse<_i18.Range>>); + ) as _i9.Future<_i4.ExchangeResponse<_i19.Range>>); @override - _i8.Future<_i4.ExchangeResponse<_i19.Estimate>> getEstimatedExchangeAmount({ + _i9.Future<_i4.ExchangeResponse<_i20.Estimate>> getEstimatedExchangeAmount({ required String? fromTicker, required String? toTicker, - required _i17.Decimal? fromAmount, + required _i18.Decimal? fromAmount, String? apiKey, }) => (super.noSuchMethod( @@ -998,8 +1023,8 @@ class MockChangeNowAPI extends _i1.Mock implements _i15.ChangeNowAPI { #apiKey: apiKey, }, ), - returnValue: _i8.Future<_i4.ExchangeResponse<_i19.Estimate>>.value( - _FakeExchangeResponse_2<_i19.Estimate>( + returnValue: _i9.Future<_i4.ExchangeResponse<_i20.Estimate>>.value( + _FakeExchangeResponse_2<_i20.Estimate>( this, Invocation.method( #getEstimatedExchangeAmount, @@ -1012,13 +1037,13 @@ class MockChangeNowAPI extends _i1.Mock implements _i15.ChangeNowAPI { }, ), )), - ) as _i8.Future<_i4.ExchangeResponse<_i19.Estimate>>); + ) as _i9.Future<_i4.ExchangeResponse<_i20.Estimate>>); @override - _i8.Future<_i4.ExchangeResponse<_i19.Estimate>> + _i9.Future<_i4.ExchangeResponse<_i20.Estimate>> getEstimatedExchangeAmountFixedRate({ required String? fromTicker, required String? toTicker, - required _i17.Decimal? fromAmount, + required _i18.Decimal? fromAmount, required bool? reversed, bool? useRateId = true, String? apiKey, @@ -1036,8 +1061,8 @@ class MockChangeNowAPI extends _i1.Mock implements _i15.ChangeNowAPI { #apiKey: apiKey, }, ), - returnValue: _i8.Future<_i4.ExchangeResponse<_i19.Estimate>>.value( - _FakeExchangeResponse_2<_i19.Estimate>( + returnValue: _i9.Future<_i4.ExchangeResponse<_i20.Estimate>>.value( + _FakeExchangeResponse_2<_i20.Estimate>( this, Invocation.method( #getEstimatedExchangeAmountFixedRate, @@ -1052,17 +1077,17 @@ class MockChangeNowAPI extends _i1.Mock implements _i15.ChangeNowAPI { }, ), )), - ) as _i8.Future<_i4.ExchangeResponse<_i19.Estimate>>); + ) as _i9.Future<_i4.ExchangeResponse<_i20.Estimate>>); @override - _i8.Future<_i4.ExchangeResponse<_i20.CNExchangeEstimate>> + _i9.Future<_i4.ExchangeResponse<_i21.CNExchangeEstimate>> getEstimatedExchangeAmountV2({ required String? fromTicker, required String? toTicker, - required _i20.CNEstimateType? fromOrTo, - required _i17.Decimal? amount, + required _i21.CNEstimateType? fromOrTo, + required _i18.Decimal? amount, String? fromNetwork, String? toNetwork, - _i20.CNFlowType? flow = _i20.CNFlowType.standard, + _i21.CNFlowType? flow = _i21.CNFlowType.standard, String? apiKey, }) => (super.noSuchMethod( @@ -1081,8 +1106,8 @@ class MockChangeNowAPI extends _i1.Mock implements _i15.ChangeNowAPI { }, ), returnValue: - _i8.Future<_i4.ExchangeResponse<_i20.CNExchangeEstimate>>.value( - _FakeExchangeResponse_2<_i20.CNExchangeEstimate>( + _i9.Future<_i4.ExchangeResponse<_i21.CNExchangeEstimate>>.value( + _FakeExchangeResponse_2<_i21.CNExchangeEstimate>( this, Invocation.method( #getEstimatedExchangeAmountV2, @@ -1099,18 +1124,18 @@ class MockChangeNowAPI extends _i1.Mock implements _i15.ChangeNowAPI { }, ), )), - ) as _i8.Future<_i4.ExchangeResponse<_i20.CNExchangeEstimate>>); + ) as _i9.Future<_i4.ExchangeResponse<_i21.CNExchangeEstimate>>); @override - _i8.Future<_i4.ExchangeResponse>> + _i9.Future<_i4.ExchangeResponse>> getAvailableFixedRateMarkets({String? apiKey}) => (super.noSuchMethod( Invocation.method( #getAvailableFixedRateMarkets, [], {#apiKey: apiKey}, ), - returnValue: _i8 - .Future<_i4.ExchangeResponse>>.value( - _FakeExchangeResponse_2>( + returnValue: _i9 + .Future<_i4.ExchangeResponse>>.value( + _FakeExchangeResponse_2>( this, Invocation.method( #getAvailableFixedRateMarkets, @@ -1118,14 +1143,14 @@ class MockChangeNowAPI extends _i1.Mock implements _i15.ChangeNowAPI { {#apiKey: apiKey}, ), )), - ) as _i8.Future<_i4.ExchangeResponse>>); + ) as _i9.Future<_i4.ExchangeResponse>>); @override - _i8.Future<_i4.ExchangeResponse<_i22.ExchangeTransaction>> + _i9.Future<_i4.ExchangeResponse<_i23.ExchangeTransaction>> createStandardExchangeTransaction({ required String? fromTicker, required String? toTicker, required String? receivingAddress, - required _i17.Decimal? amount, + required _i18.Decimal? amount, String? extraId = r'', String? userId = r'', String? contactEmail = r'', @@ -1150,9 +1175,9 @@ class MockChangeNowAPI extends _i1.Mock implements _i15.ChangeNowAPI { #apiKey: apiKey, }, ), - returnValue: _i8 - .Future<_i4.ExchangeResponse<_i22.ExchangeTransaction>>.value( - _FakeExchangeResponse_2<_i22.ExchangeTransaction>( + returnValue: _i9 + .Future<_i4.ExchangeResponse<_i23.ExchangeTransaction>>.value( + _FakeExchangeResponse_2<_i23.ExchangeTransaction>( this, Invocation.method( #createStandardExchangeTransaction, @@ -1171,14 +1196,14 @@ class MockChangeNowAPI extends _i1.Mock implements _i15.ChangeNowAPI { }, ), )), - ) as _i8.Future<_i4.ExchangeResponse<_i22.ExchangeTransaction>>); + ) as _i9.Future<_i4.ExchangeResponse<_i23.ExchangeTransaction>>); @override - _i8.Future<_i4.ExchangeResponse<_i22.ExchangeTransaction>> + _i9.Future<_i4.ExchangeResponse<_i23.ExchangeTransaction>> createFixedRateExchangeTransaction({ required String? fromTicker, required String? toTicker, required String? receivingAddress, - required _i17.Decimal? amount, + required _i18.Decimal? amount, required String? rateId, required bool? reversed, String? extraId = r'', @@ -1207,9 +1232,9 @@ class MockChangeNowAPI extends _i1.Mock implements _i15.ChangeNowAPI { #apiKey: apiKey, }, ), - returnValue: _i8 - .Future<_i4.ExchangeResponse<_i22.ExchangeTransaction>>.value( - _FakeExchangeResponse_2<_i22.ExchangeTransaction>( + returnValue: _i9 + .Future<_i4.ExchangeResponse<_i23.ExchangeTransaction>>.value( + _FakeExchangeResponse_2<_i23.ExchangeTransaction>( this, Invocation.method( #createFixedRateExchangeTransaction, @@ -1230,11 +1255,11 @@ class MockChangeNowAPI extends _i1.Mock implements _i15.ChangeNowAPI { }, ), )), - ) as _i8.Future<_i4.ExchangeResponse<_i22.ExchangeTransaction>>); + ) as _i9.Future<_i4.ExchangeResponse<_i23.ExchangeTransaction>>); @override - _i8.Future< + _i9.Future< _i4 - .ExchangeResponse<_i23.ExchangeTransactionStatus>> getTransactionStatus({ + .ExchangeResponse<_i24.ExchangeTransactionStatus>> getTransactionStatus({ required String? id, String? apiKey, }) => @@ -1247,9 +1272,9 @@ class MockChangeNowAPI extends _i1.Mock implements _i15.ChangeNowAPI { #apiKey: apiKey, }, ), - returnValue: _i8 - .Future<_i4.ExchangeResponse<_i23.ExchangeTransactionStatus>>.value( - _FakeExchangeResponse_2<_i23.ExchangeTransactionStatus>( + returnValue: _i9 + .Future<_i4.ExchangeResponse<_i24.ExchangeTransactionStatus>>.value( + _FakeExchangeResponse_2<_i24.ExchangeTransactionStatus>( this, Invocation.method( #getTransactionStatus, @@ -1260,9 +1285,9 @@ class MockChangeNowAPI extends _i1.Mock implements _i15.ChangeNowAPI { }, ), )), - ) as _i8.Future<_i4.ExchangeResponse<_i23.ExchangeTransactionStatus>>); + ) as _i9.Future<_i4.ExchangeResponse<_i24.ExchangeTransactionStatus>>); @override - _i8.Future<_i4.ExchangeResponse>> + _i9.Future<_i4.ExchangeResponse>> getAvailableFloatingRatePairs({bool? includePartners = false}) => (super.noSuchMethod( Invocation.method( @@ -1271,8 +1296,8 @@ class MockChangeNowAPI extends _i1.Mock implements _i15.ChangeNowAPI { {#includePartners: includePartners}, ), returnValue: - _i8.Future<_i4.ExchangeResponse>>.value( - _FakeExchangeResponse_2>( + _i9.Future<_i4.ExchangeResponse>>.value( + _FakeExchangeResponse_2>( this, Invocation.method( #getAvailableFloatingRatePairs, @@ -1280,5 +1305,5 @@ class MockChangeNowAPI extends _i1.Mock implements _i15.ChangeNowAPI { {#includePartners: includePartners}, ), )), - ) as _i8.Future<_i4.ExchangeResponse>>); + ) as _i9.Future<_i4.ExchangeResponse>>); } diff --git a/test/screen_tests/lockscreen_view_screen_test.dart b/test/screen_tests/lockscreen_view_screen_test.dart index c52838459..8019ad22d 100644 --- a/test/screen_tests/lockscreen_view_screen_test.dart +++ b/test/screen_tests/lockscreen_view_screen_test.dart @@ -5,10 +5,13 @@ import 'package:mockito/annotations.dart'; import 'package:stackwallet/services/node_service.dart'; import 'package:stackwallet/services/wallets_service.dart'; -@GenerateMocks([], customMocks: [ - MockSpec(returnNullOnMissingStub: true), - MockSpec(returnNullOnMissingStub: true), -]) +@GenerateMocks( + [], + customMocks: [ + MockSpec(), + MockSpec(), + ], +) void main() { testWidgets("LockscreenView builds correctly", (tester) async { // final navigator = mockingjay.MockNavigator(); diff --git a/test/screen_tests/lockscreen_view_screen_test.mocks.dart b/test/screen_tests/lockscreen_view_screen_test.mocks.dart index a76f5c498..2a0844351 100644 --- a/test/screen_tests/lockscreen_view_screen_test.mocks.dart +++ b/test/screen_tests/lockscreen_view_screen_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/screen_tests/lockscreen_view_screen_test.dart. // Do not manually edit this file. @@ -19,6 +19,8 @@ import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart' // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -41,6 +43,10 @@ class _FakeSecureStorageInterface_0 extends _i1.SmartFake /// /// See the documentation for Mockito's code generation for more information. class MockWalletsService extends _i1.Mock implements _i3.WalletsService { + MockWalletsService() { + _i1.throwOnMissingStub(this); + } + @override _i4.Future> get walletNames => (super.noSuchMethod( @@ -91,6 +97,10 @@ class MockWalletsService extends _i1.Mock implements _i3.WalletsService { /// /// See the documentation for Mockito's code generation for more information. class MockNodeService extends _i1.Mock implements _i6.NodeService { + MockNodeService() { + _i1.throwOnMissingStub(this); + } + @override _i2.SecureStorageInterface get secureStorageInterface => (super.noSuchMethod( Invocation.getter(#secureStorageInterface), diff --git a/test/screen_tests/main_view_tests/main_view_screen_testA_test.dart b/test/screen_tests/main_view_tests/main_view_screen_testA_test.dart index f38c3735d..2ae8ea4dd 100644 --- a/test/screen_tests/main_view_tests/main_view_screen_testA_test.dart +++ b/test/screen_tests/main_view_tests/main_view_screen_testA_test.dart @@ -14,8 +14,8 @@ import 'package:stackwallet/services/wallets_service.dart'; // import 'main_view_screen_testA_test.mocks.dart'; @GenerateMocks([], customMocks: [ - MockSpec(returnNullOnMissingStub: true), - MockSpec(returnNullOnMissingStub: true), + MockSpec(), + MockSpec(), ]) void main() { // testWidgets("tap receive", (tester) async { diff --git a/test/screen_tests/main_view_tests/main_view_screen_testA_test.mocks.dart b/test/screen_tests/main_view_tests/main_view_screen_testA_test.mocks.dart index e9b29b616..9860ea5e7 100644 --- a/test/screen_tests/main_view_tests/main_view_screen_testA_test.mocks.dart +++ b/test/screen_tests/main_view_tests/main_view_screen_testA_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/screen_tests/main_view_tests/main_view_screen_testA_test.dart. // Do not manually edit this file. @@ -7,6 +7,7 @@ import 'dart:async' as _i3; import 'dart:ui' as _i4; import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i6; import 'package:stackwallet/services/locale_service.dart' as _i5; import 'package:stackwallet/services/wallets_service.dart' as _i2; @@ -14,6 +15,8 @@ import 'package:stackwallet/services/wallets_service.dart' as _i2; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -25,6 +28,10 @@ import 'package:stackwallet/services/wallets_service.dart' as _i2; /// /// See the documentation for Mockito's code generation for more information. class MockWalletsService extends _i1.Mock implements _i2.WalletsService { + MockWalletsService() { + _i1.throwOnMissingStub(this); + } + @override _i3.Future> get walletNames => (super.noSuchMethod( @@ -75,10 +82,17 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { /// /// See the documentation for Mockito's code generation for more information. class MockLocaleService extends _i1.Mock implements _i5.LocaleService { + MockLocaleService() { + _i1.throwOnMissingStub(this); + } + @override String get locale => (super.noSuchMethod( Invocation.getter(#locale), - returnValue: '', + returnValue: _i6.dummyValue( + this, + Invocation.getter(#locale), + ), ) as String); @override bool get hasListeners => (super.noSuchMethod( diff --git a/test/screen_tests/main_view_tests/main_view_screen_testB_test.dart b/test/screen_tests/main_view_tests/main_view_screen_testB_test.dart index a14410d2d..59f049955 100644 --- a/test/screen_tests/main_view_tests/main_view_screen_testB_test.dart +++ b/test/screen_tests/main_view_tests/main_view_screen_testB_test.dart @@ -16,8 +16,8 @@ import 'package:stackwallet/services/wallets_service.dart'; // import 'main_view_screen_testB_test.mocks.dart'; @GenerateMocks([], customMocks: [ - MockSpec(returnNullOnMissingStub: true), - MockSpec(returnNullOnMissingStub: true), + MockSpec(), + MockSpec(), ]) void main() { // testWidgets("tap refresh", (tester) async { diff --git a/test/screen_tests/main_view_tests/main_view_screen_testB_test.mocks.dart b/test/screen_tests/main_view_tests/main_view_screen_testB_test.mocks.dart index 75731831b..2cc8ad83e 100644 --- a/test/screen_tests/main_view_tests/main_view_screen_testB_test.mocks.dart +++ b/test/screen_tests/main_view_tests/main_view_screen_testB_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/screen_tests/main_view_tests/main_view_screen_testB_test.dart. // Do not manually edit this file. @@ -7,6 +7,7 @@ import 'dart:async' as _i3; import 'dart:ui' as _i4; import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i6; import 'package:stackwallet/services/locale_service.dart' as _i5; import 'package:stackwallet/services/wallets_service.dart' as _i2; @@ -14,6 +15,8 @@ import 'package:stackwallet/services/wallets_service.dart' as _i2; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -25,6 +28,10 @@ import 'package:stackwallet/services/wallets_service.dart' as _i2; /// /// See the documentation for Mockito's code generation for more information. class MockWalletsService extends _i1.Mock implements _i2.WalletsService { + MockWalletsService() { + _i1.throwOnMissingStub(this); + } + @override _i3.Future> get walletNames => (super.noSuchMethod( @@ -75,10 +82,17 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { /// /// See the documentation for Mockito's code generation for more information. class MockLocaleService extends _i1.Mock implements _i5.LocaleService { + MockLocaleService() { + _i1.throwOnMissingStub(this); + } + @override String get locale => (super.noSuchMethod( Invocation.getter(#locale), - returnValue: '', + returnValue: _i6.dummyValue( + this, + Invocation.getter(#locale), + ), ) as String); @override bool get hasListeners => (super.noSuchMethod( diff --git a/test/screen_tests/main_view_tests/main_view_screen_testC_test.dart b/test/screen_tests/main_view_tests/main_view_screen_testC_test.dart index 9e10391e8..191974b1c 100644 --- a/test/screen_tests/main_view_tests/main_view_screen_testC_test.dart +++ b/test/screen_tests/main_view_tests/main_view_screen_testC_test.dart @@ -14,8 +14,8 @@ import 'package:stackwallet/services/wallets_service.dart'; // import 'main_view_screen_testC_test.mocks.dart'; @GenerateMocks([], customMocks: [ - MockSpec(returnNullOnMissingStub: true), - MockSpec(returnNullOnMissingStub: true), + MockSpec(), + MockSpec(), ]) void main() { // testWidgets("tap send", (tester) async { diff --git a/test/screen_tests/main_view_tests/main_view_screen_testC_test.mocks.dart b/test/screen_tests/main_view_tests/main_view_screen_testC_test.mocks.dart index 01b3907bc..2da39262c 100644 --- a/test/screen_tests/main_view_tests/main_view_screen_testC_test.mocks.dart +++ b/test/screen_tests/main_view_tests/main_view_screen_testC_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/screen_tests/main_view_tests/main_view_screen_testC_test.dart. // Do not manually edit this file. @@ -7,6 +7,7 @@ import 'dart:async' as _i3; import 'dart:ui' as _i4; import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i6; import 'package:stackwallet/services/locale_service.dart' as _i5; import 'package:stackwallet/services/wallets_service.dart' as _i2; @@ -14,6 +15,8 @@ import 'package:stackwallet/services/wallets_service.dart' as _i2; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -25,6 +28,10 @@ import 'package:stackwallet/services/wallets_service.dart' as _i2; /// /// See the documentation for Mockito's code generation for more information. class MockWalletsService extends _i1.Mock implements _i2.WalletsService { + MockWalletsService() { + _i1.throwOnMissingStub(this); + } + @override _i3.Future> get walletNames => (super.noSuchMethod( @@ -75,10 +82,17 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { /// /// See the documentation for Mockito's code generation for more information. class MockLocaleService extends _i1.Mock implements _i5.LocaleService { + MockLocaleService() { + _i1.throwOnMissingStub(this); + } + @override String get locale => (super.noSuchMethod( Invocation.getter(#locale), - returnValue: '', + returnValue: _i6.dummyValue( + this, + Invocation.getter(#locale), + ), ) as String); @override bool get hasListeners => (super.noSuchMethod( diff --git a/test/screen_tests/onboarding/backup_key_warning_view_screen_test.dart b/test/screen_tests/onboarding/backup_key_warning_view_screen_test.dart index ec6e105ba..023d6758a 100644 --- a/test/screen_tests/onboarding/backup_key_warning_view_screen_test.dart +++ b/test/screen_tests/onboarding/backup_key_warning_view_screen_test.dart @@ -12,7 +12,7 @@ import 'package:stackwallet/services/wallets_service.dart'; // import 'backup_key_warning_view_screen_test.mocks.dart'; @GenerateMocks([], customMocks: [ - MockSpec(returnNullOnMissingStub: true), + MockSpec(), ]) void main() { // testWidgets("BackupKeyWarningView builds correctly", (tester) async { diff --git a/test/screen_tests/onboarding/backup_key_warning_view_screen_test.mocks.dart b/test/screen_tests/onboarding/backup_key_warning_view_screen_test.mocks.dart index fe6e52860..7544b767c 100644 --- a/test/screen_tests/onboarding/backup_key_warning_view_screen_test.mocks.dart +++ b/test/screen_tests/onboarding/backup_key_warning_view_screen_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/screen_tests/onboarding/backup_key_warning_view_screen_test.dart. // Do not manually edit this file. @@ -13,6 +13,8 @@ import 'package:stackwallet/services/wallets_service.dart' as _i2; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -24,6 +26,10 @@ import 'package:stackwallet/services/wallets_service.dart' as _i2; /// /// See the documentation for Mockito's code generation for more information. class MockWalletsService extends _i1.Mock implements _i2.WalletsService { + MockWalletsService() { + _i1.throwOnMissingStub(this); + } + @override _i3.Future> get walletNames => (super.noSuchMethod( diff --git a/test/screen_tests/onboarding/create_pin_view_screen_test.dart b/test/screen_tests/onboarding/create_pin_view_screen_test.dart index d7b0ce28f..fcd0aa2c5 100644 --- a/test/screen_tests/onboarding/create_pin_view_screen_test.dart +++ b/test/screen_tests/onboarding/create_pin_view_screen_test.dart @@ -18,8 +18,8 @@ import 'package:stackwallet/services/wallets_service.dart'; // import 'create_pin_view_screen_test.mocks.dart'; @GenerateMocks([], customMocks: [ - MockSpec(returnNullOnMissingStub: true), - MockSpec(returnNullOnMissingStub: true), + MockSpec(), + MockSpec(), ]) void main() { // testWidgets("CreatePinView builds correctly", (tester) async { diff --git a/test/screen_tests/onboarding/create_pin_view_screen_test.mocks.dart b/test/screen_tests/onboarding/create_pin_view_screen_test.mocks.dart index 3f7fd0767..5aed33644 100644 --- a/test/screen_tests/onboarding/create_pin_view_screen_test.mocks.dart +++ b/test/screen_tests/onboarding/create_pin_view_screen_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/screen_tests/onboarding/create_pin_view_screen_test.dart. // Do not manually edit this file. @@ -19,6 +19,8 @@ import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart' // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -41,6 +43,10 @@ class _FakeSecureStorageInterface_0 extends _i1.SmartFake /// /// See the documentation for Mockito's code generation for more information. class MockWalletsService extends _i1.Mock implements _i3.WalletsService { + MockWalletsService() { + _i1.throwOnMissingStub(this); + } + @override _i4.Future> get walletNames => (super.noSuchMethod( @@ -91,6 +97,10 @@ class MockWalletsService extends _i1.Mock implements _i3.WalletsService { /// /// See the documentation for Mockito's code generation for more information. class MockNodeService extends _i1.Mock implements _i6.NodeService { + MockNodeService() { + _i1.throwOnMissingStub(this); + } + @override _i2.SecureStorageInterface get secureStorageInterface => (super.noSuchMethod( Invocation.getter(#secureStorageInterface), diff --git a/test/screen_tests/onboarding/name_your_wallet_view_screen_test.dart b/test/screen_tests/onboarding/name_your_wallet_view_screen_test.dart index ee75e1260..44dbe8baf 100644 --- a/test/screen_tests/onboarding/name_your_wallet_view_screen_test.dart +++ b/test/screen_tests/onboarding/name_your_wallet_view_screen_test.dart @@ -12,7 +12,7 @@ import 'package:stackwallet/services/wallets_service.dart'; // import 'name_your_wallet_view_screen_test.mocks.dart'; @GenerateMocks([], customMocks: [ - MockSpec(returnNullOnMissingStub: true), + MockSpec(), ]) void main() { // testWidgets("NameYourWalletView builds correctly with testnet option", diff --git a/test/screen_tests/onboarding/name_your_wallet_view_screen_test.mocks.dart b/test/screen_tests/onboarding/name_your_wallet_view_screen_test.mocks.dart index 35f9c5505..ea7cadff1 100644 --- a/test/screen_tests/onboarding/name_your_wallet_view_screen_test.mocks.dart +++ b/test/screen_tests/onboarding/name_your_wallet_view_screen_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/screen_tests/onboarding/name_your_wallet_view_screen_test.dart. // Do not manually edit this file. @@ -13,6 +13,8 @@ import 'package:stackwallet/services/wallets_service.dart' as _i2; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -24,6 +26,10 @@ import 'package:stackwallet/services/wallets_service.dart' as _i2; /// /// See the documentation for Mockito's code generation for more information. class MockWalletsService extends _i1.Mock implements _i2.WalletsService { + MockWalletsService() { + _i1.throwOnMissingStub(this); + } + @override _i3.Future> get walletNames => (super.noSuchMethod( diff --git a/test/screen_tests/onboarding/restore_wallet_view_screen_test.dart b/test/screen_tests/onboarding/restore_wallet_view_screen_test.dart index f0f00fb85..a38774977 100644 --- a/test/screen_tests/onboarding/restore_wallet_view_screen_test.dart +++ b/test/screen_tests/onboarding/restore_wallet_view_screen_test.dart @@ -24,8 +24,8 @@ import 'package:stackwallet/utilities/barcode_scanner_interface.dart'; @GenerateMocks([ BarcodeScannerWrapper ], customMocks: [ - MockSpec(returnNullOnMissingStub: true), - MockSpec(returnNullOnMissingStub: true), + MockSpec(), + MockSpec(), ]) void main() { // testWidgets("RestoreWalletView builds correctly", (tester) async { diff --git a/test/screen_tests/onboarding/restore_wallet_view_screen_test.mocks.dart b/test/screen_tests/onboarding/restore_wallet_view_screen_test.mocks.dart index 58c717a5b..7ce8c0b95 100644 --- a/test/screen_tests/onboarding/restore_wallet_view_screen_test.mocks.dart +++ b/test/screen_tests/onboarding/restore_wallet_view_screen_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/screen_tests/onboarding/restore_wallet_view_screen_test.dart. // Do not manually edit this file. @@ -21,6 +21,8 @@ import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart' // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -82,6 +84,10 @@ class MockBarcodeScannerWrapper extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWalletsService extends _i1.Mock implements _i6.WalletsService { + MockWalletsService() { + _i1.throwOnMissingStub(this); + } + @override _i5.Future> get walletNames => (super.noSuchMethod( @@ -132,6 +138,10 @@ class MockWalletsService extends _i1.Mock implements _i6.WalletsService { /// /// See the documentation for Mockito's code generation for more information. class MockNodeService extends _i1.Mock implements _i8.NodeService { + MockNodeService() { + _i1.throwOnMissingStub(this); + } + @override _i3.SecureStorageInterface get secureStorageInterface => (super.noSuchMethod( Invocation.getter(#secureStorageInterface), diff --git a/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/add_custom_node_view_screen_test.dart b/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/add_custom_node_view_screen_test.dart index 5db5f662e..524971fcb 100644 --- a/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/add_custom_node_view_screen_test.dart +++ b/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/add_custom_node_view_screen_test.dart @@ -15,7 +15,7 @@ import 'package:stackwallet/services/node_service.dart'; // import 'add_custom_node_view_screen_test.mocks.dart'; // @GenerateMocks([], customMocks: [ - MockSpec(returnNullOnMissingStub: true), + MockSpec(), ]) void main() { // testWidgets("AddCustomNodeView builds correctly", (tester) async { diff --git a/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/add_custom_node_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/add_custom_node_view_screen_test.mocks.dart index ab3c043d3..6216d4e86 100644 --- a/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/add_custom_node_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/add_custom_node_view_screen_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/add_custom_node_view_screen_test.dart. // Do not manually edit this file. @@ -18,6 +18,8 @@ import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart' // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -40,6 +42,10 @@ class _FakeSecureStorageInterface_0 extends _i1.SmartFake /// /// See the documentation for Mockito's code generation for more information. class MockNodeService extends _i1.Mock implements _i3.NodeService { + MockNodeService() { + _i1.throwOnMissingStub(this); + } + @override _i2.SecureStorageInterface get secureStorageInterface => (super.noSuchMethod( Invocation.getter(#secureStorageInterface), diff --git a/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/node_details_view_screen_test.dart b/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/node_details_view_screen_test.dart index 02309a56d..6637999f8 100644 --- a/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/node_details_view_screen_test.dart +++ b/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/node_details_view_screen_test.dart @@ -17,7 +17,7 @@ import 'package:stackwallet/services/node_service.dart'; // import 'node_details_view_screen_test.mocks.dart'; @GenerateMocks([], customMocks: [ - MockSpec(returnNullOnMissingStub: true), + MockSpec(), ]) void main() { // testWidgets("NodeDetailsView non-editing builds correctly", (tester) async { diff --git a/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/node_details_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/node_details_view_screen_test.mocks.dart index f203b112b..f1948fa9a 100644 --- a/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/node_details_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/node_details_view_screen_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/node_details_view_screen_test.dart. // Do not manually edit this file. @@ -18,6 +18,8 @@ import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart' // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -40,6 +42,10 @@ class _FakeSecureStorageInterface_0 extends _i1.SmartFake /// /// See the documentation for Mockito's code generation for more information. class MockNodeService extends _i1.Mock implements _i3.NodeService { + MockNodeService() { + _i1.throwOnMissingStub(this); + } + @override _i2.SecureStorageInterface get secureStorageInterface => (super.noSuchMethod( Invocation.getter(#secureStorageInterface), diff --git a/test/screen_tests/settings_view/settings_subviews/network_settings_view_screen_test.dart b/test/screen_tests/settings_view/settings_subviews/network_settings_view_screen_test.dart index a466a5dfc..67d5398ab 100644 --- a/test/screen_tests/settings_view/settings_subviews/network_settings_view_screen_test.dart +++ b/test/screen_tests/settings_view/settings_subviews/network_settings_view_screen_test.dart @@ -13,7 +13,7 @@ import 'package:stackwallet/services/node_service.dart'; // import 'network_settings_view_screen_test.mocks.dart'; @GenerateMocks([], customMocks: [ - MockSpec(returnNullOnMissingStub: true), + MockSpec(), ]) void main() { // testWidgets("NetworkSettingsView builds correctly", (tester) async { diff --git a/test/screen_tests/settings_view/settings_subviews/network_settings_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/network_settings_view_screen_test.mocks.dart index 217f88d9f..4adf647f0 100644 --- a/test/screen_tests/settings_view/settings_subviews/network_settings_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/network_settings_view_screen_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/screen_tests/settings_view/settings_subviews/network_settings_view_screen_test.dart. // Do not manually edit this file. @@ -18,6 +18,8 @@ import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart' // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -40,6 +42,10 @@ class _FakeSecureStorageInterface_0 extends _i1.SmartFake /// /// See the documentation for Mockito's code generation for more information. class MockNodeService extends _i1.Mock implements _i3.NodeService { + MockNodeService() { + _i1.throwOnMissingStub(this); + } + @override _i2.SecureStorageInterface get secureStorageInterface => (super.noSuchMethod( Invocation.getter(#secureStorageInterface), diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/change_pin_view_screen_test.dart b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/change_pin_view_screen_test.dart index 8cf9cc7bb..3c7332e9a 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/change_pin_view_screen_test.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/change_pin_view_screen_test.dart @@ -15,7 +15,7 @@ import 'package:stackwallet/services/wallets_service.dart'; // import 'change_pin_view_screen_test.mocks.dart'; @GenerateMocks([], customMocks: [ - MockSpec(returnNullOnMissingStub: true), + MockSpec(), ]) void main() { // testWidgets("ChangePinView builds correctly", (tester) async { diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/change_pin_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/change_pin_view_screen_test.mocks.dart index 764ed5967..b9014e5fe 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/change_pin_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/change_pin_view_screen_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/change_pin_view_screen_test.dart. // Do not manually edit this file. @@ -13,6 +13,8 @@ import 'package:stackwallet/services/wallets_service.dart' as _i2; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -24,6 +26,10 @@ import 'package:stackwallet/services/wallets_service.dart' as _i2; /// /// See the documentation for Mockito's code generation for more information. class MockWalletsService extends _i1.Mock implements _i2.WalletsService { + MockWalletsService() { + _i1.throwOnMissingStub(this); + } + @override _i3.Future> get walletNames => (super.noSuchMethod( diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rename_wallet_view_screen_test.dart b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rename_wallet_view_screen_test.dart index 59a45306a..5216180ac 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rename_wallet_view_screen_test.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rename_wallet_view_screen_test.dart @@ -14,7 +14,7 @@ import 'package:stackwallet/services/wallets_service.dart'; // import 'rename_wallet_view_screen_test.mocks.dart'; @GenerateMocks([], customMocks: [ - MockSpec(returnNullOnMissingStub: true), + MockSpec(), ]) void main() { // testWidgets("RenameWalletView builds correctly", (tester) async { diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rename_wallet_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rename_wallet_view_screen_test.mocks.dart index 7db7d3109..e35762cce 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rename_wallet_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rename_wallet_view_screen_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rename_wallet_view_screen_test.dart. // Do not manually edit this file. @@ -13,6 +13,8 @@ import 'package:stackwallet/services/wallets_service.dart' as _i2; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -24,6 +26,10 @@ import 'package:stackwallet/services/wallets_service.dart' as _i2; /// /// See the documentation for Mockito's code generation for more information. class MockWalletsService extends _i1.Mock implements _i2.WalletsService { + MockWalletsService() { + _i1.throwOnMissingStub(this); + } + @override _i3.Future> get walletNames => (super.noSuchMethod( diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/wallet_delete_mnemonic_view_screen_test.dart b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/wallet_delete_mnemonic_view_screen_test.dart index 86a674aec..802e8a522 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/wallet_delete_mnemonic_view_screen_test.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/wallet_delete_mnemonic_view_screen_test.dart @@ -20,7 +20,7 @@ import 'package:stackwallet/services/wallets_service.dart'; // import 'wallet_delete_mnemonic_view_screen_test.mocks.dart'; @GenerateMocks([], customMocks: [ - MockSpec(returnNullOnMissingStub: true), + MockSpec(), ]) void main() { // testWidgets("WalletDeleteMnemonicView builds correctly", (tester) async { diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/wallet_delete_mnemonic_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/wallet_delete_mnemonic_view_screen_test.mocks.dart index 42b82db3b..7855c17d8 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/wallet_delete_mnemonic_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/wallet_delete_mnemonic_view_screen_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/wallet_delete_mnemonic_view_screen_test.dart. // Do not manually edit this file. @@ -13,6 +13,8 @@ import 'package:stackwallet/services/wallets_service.dart' as _i2; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -24,6 +26,10 @@ import 'package:stackwallet/services/wallets_service.dart' as _i2; /// /// See the documentation for Mockito's code generation for more information. class MockWalletsService extends _i1.Mock implements _i2.WalletsService { + MockWalletsService() { + _i1.throwOnMissingStub(this); + } + @override _i3.Future> get walletNames => (super.noSuchMethod( diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.dart b/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.dart index 7f6c150bc..c23cc32e0 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.dart @@ -24,7 +24,7 @@ import 'package:stackwallet/utilities/biometrics.dart'; LocalAuthentication, Biometrics, ], customMocks: [ - MockSpec(returnNullOnMissingStub: true), + MockSpec(), ]) void main() { // testWidgets("WalletSettingsView builds correctly", (tester) async { diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart index 63c207931..446894293 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart @@ -1,18 +1,19 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.dart. // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i4; -import 'dart:ui' as _i10; +import 'dart:ui' as _i11; -import 'package:local_auth/auth_strings.dart' as _i7; -import 'package:local_auth/local_auth.dart' as _i6; +import 'package:local_auth/auth_strings.dart' as _i8; +import 'package:local_auth/local_auth.dart' as _i7; import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i6; import 'package:stackwallet/electrumx_rpc/cached_electrumx_client.dart' as _i3; import 'package:stackwallet/electrumx_rpc/electrumx_client.dart' as _i2; -import 'package:stackwallet/services/wallets_service.dart' as _i9; -import 'package:stackwallet/utilities/biometrics.dart' as _i8; +import 'package:stackwallet/services/wallets_service.dart' as _i10; +import 'package:stackwallet/utilities/biometrics.dart' as _i9; import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart' as _i5; @@ -20,6 +21,8 @@ import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart' // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -101,7 +104,13 @@ class MockCachedElectrumXClient extends _i1.Mock #base64ToHex, [source], ), - returnValue: '', + returnValue: _i6.dummyValue( + this, + Invocation.method( + #base64ToHex, + [source], + ), + ), ) as String); @override String base64ToReverseHex(String? source) => (super.noSuchMethod( @@ -109,7 +118,13 @@ class MockCachedElectrumXClient extends _i1.Mock #base64ToReverseHex, [source], ), - returnValue: '', + returnValue: _i6.dummyValue( + this, + Invocation.method( + #base64ToReverseHex, + [source], + ), + ), ) as String); @override _i4.Future> getTransaction({ @@ -175,7 +190,7 @@ class MockCachedElectrumXClient extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockLocalAuthentication extends _i1.Mock - implements _i6.LocalAuthentication { + implements _i7.LocalAuthentication { MockLocalAuthentication() { _i1.throwOnMissingStub(this); } @@ -190,9 +205,9 @@ class MockLocalAuthentication extends _i1.Mock required String? localizedReason, bool? useErrorDialogs = true, bool? stickyAuth = false, - _i7.AndroidAuthMessages? androidAuthStrings = - const _i7.AndroidAuthMessages(), - _i7.IOSAuthMessages? iOSAuthStrings = const _i7.IOSAuthMessages(), + _i8.AndroidAuthMessages? androidAuthStrings = + const _i8.AndroidAuthMessages(), + _i8.IOSAuthMessages? iOSAuthStrings = const _i8.IOSAuthMessages(), bool? sensitiveTransaction = true, }) => (super.noSuchMethod( @@ -215,9 +230,9 @@ class MockLocalAuthentication extends _i1.Mock required String? localizedReason, bool? useErrorDialogs = true, bool? stickyAuth = false, - _i7.AndroidAuthMessages? androidAuthStrings = - const _i7.AndroidAuthMessages(), - _i7.IOSAuthMessages? iOSAuthStrings = const _i7.IOSAuthMessages(), + _i8.AndroidAuthMessages? androidAuthStrings = + const _i8.AndroidAuthMessages(), + _i8.IOSAuthMessages? iOSAuthStrings = const _i8.IOSAuthMessages(), bool? sensitiveTransaction = true, bool? biometricOnly = false, }) => @@ -254,21 +269,21 @@ class MockLocalAuthentication extends _i1.Mock returnValue: _i4.Future.value(false), ) as _i4.Future); @override - _i4.Future> getAvailableBiometrics() => + _i4.Future> getAvailableBiometrics() => (super.noSuchMethod( Invocation.method( #getAvailableBiometrics, [], ), returnValue: - _i4.Future>.value(<_i6.BiometricType>[]), - ) as _i4.Future>); + _i4.Future>.value(<_i7.BiometricType>[]), + ) as _i4.Future>); } /// A class which mocks [Biometrics]. /// /// See the documentation for Mockito's code generation for more information. -class MockBiometrics extends _i1.Mock implements _i8.Biometrics { +class MockBiometrics extends _i1.Mock implements _i9.Biometrics { MockBiometrics() { _i1.throwOnMissingStub(this); } @@ -296,21 +311,25 @@ class MockBiometrics extends _i1.Mock implements _i8.Biometrics { /// A class which mocks [WalletsService]. /// /// See the documentation for Mockito's code generation for more information. -class MockWalletsService extends _i1.Mock implements _i9.WalletsService { +class MockWalletsService extends _i1.Mock implements _i10.WalletsService { + MockWalletsService() { + _i1.throwOnMissingStub(this); + } + @override - _i4.Future> get walletNames => + _i4.Future> get walletNames => (super.noSuchMethod( Invocation.getter(#walletNames), - returnValue: _i4.Future>.value( - {}), - ) as _i4.Future>); + returnValue: _i4.Future>.value( + {}), + ) as _i4.Future>); @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); @override - void addListener(_i10.VoidCallback? listener) => super.noSuchMethod( + void addListener(_i11.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #addListener, [listener], @@ -318,7 +337,7 @@ class MockWalletsService extends _i1.Mock implements _i9.WalletsService { returnValueForMissingStub: null, ); @override - void removeListener(_i10.VoidCallback? listener) => super.noSuchMethod( + void removeListener(_i11.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #removeListener, [listener], diff --git a/test/screen_tests/settings_view/settings_view_screen_test.dart b/test/screen_tests/settings_view/settings_view_screen_test.dart index 10e05b8f9..5cfe925b3 100644 --- a/test/screen_tests/settings_view/settings_view_screen_test.dart +++ b/test/screen_tests/settings_view/settings_view_screen_test.dart @@ -16,7 +16,7 @@ import 'package:stackwallet/services/wallets_service.dart'; // import 'settings_view_screen_test.mocks.dart'; @GenerateMocks([], customMocks: [ - MockSpec(returnNullOnMissingStub: true), + MockSpec(), ]) void main() { // testWidgets("SettingsView builds correctly", (tester) async { diff --git a/test/screen_tests/settings_view/settings_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_view_screen_test.mocks.dart index a4edcd6ab..21c907eb7 100644 --- a/test/screen_tests/settings_view/settings_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_view_screen_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/screen_tests/settings_view/settings_view_screen_test.dart. // Do not manually edit this file. @@ -13,6 +13,8 @@ import 'package:stackwallet/services/wallets_service.dart' as _i2; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -24,6 +26,10 @@ import 'package:stackwallet/services/wallets_service.dart' as _i2; /// /// See the documentation for Mockito's code generation for more information. class MockWalletsService extends _i1.Mock implements _i2.WalletsService { + MockWalletsService() { + _i1.throwOnMissingStub(this); + } + @override _i3.Future> get walletNames => (super.noSuchMethod( diff --git a/test/screen_tests/transaction_subviews/transaction_details_view_screen_test.dart b/test/screen_tests/transaction_subviews/transaction_details_view_screen_test.dart index 41bb64552..868698391 100644 --- a/test/screen_tests/transaction_subviews/transaction_details_view_screen_test.dart +++ b/test/screen_tests/transaction_subviews/transaction_details_view_screen_test.dart @@ -15,8 +15,8 @@ import 'package:stackwallet/services/locale_service.dart'; // import 'transaction_details_view_screen_test.mocks.dart'; @GenerateMocks([], customMocks: [ - MockSpec(returnNullOnMissingStub: true), - MockSpec(returnNullOnMissingStub: true), + MockSpec(), + MockSpec(), ]) void main() { // final transactionA = Transaction( diff --git a/test/screen_tests/transaction_subviews/transaction_details_view_screen_test.mocks.dart b/test/screen_tests/transaction_subviews/transaction_details_view_screen_test.mocks.dart index 7d62e5a47..7c8355a96 100644 --- a/test/screen_tests/transaction_subviews/transaction_details_view_screen_test.mocks.dart +++ b/test/screen_tests/transaction_subviews/transaction_details_view_screen_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/screen_tests/transaction_subviews/transaction_details_view_screen_test.dart. // Do not manually edit this file. @@ -7,6 +7,7 @@ import 'dart:async' as _i4; import 'dart:ui' as _i5; import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i7; import 'package:stackwallet/models/isar/models/contact_entry.dart' as _i2; import 'package:stackwallet/services/address_book_service.dart' as _i3; import 'package:stackwallet/services/locale_service.dart' as _i6; @@ -15,6 +16,8 @@ import 'package:stackwallet/services/locale_service.dart' as _i6; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -37,6 +40,10 @@ class _FakeContactEntry_0 extends _i1.SmartFake implements _i2.ContactEntry { /// See the documentation for Mockito's code generation for more information. class MockAddressBookService extends _i1.Mock implements _i3.AddressBookService { + MockAddressBookService() { + _i1.throwOnMissingStub(this); + } + @override List<_i2.ContactEntry> get contacts => (super.noSuchMethod( Invocation.getter(#contacts), @@ -150,10 +157,17 @@ class MockAddressBookService extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockLocaleService extends _i1.Mock implements _i6.LocaleService { + MockLocaleService() { + _i1.throwOnMissingStub(this); + } + @override String get locale => (super.noSuchMethod( Invocation.getter(#locale), - returnValue: '', + returnValue: _i7.dummyValue( + this, + Invocation.getter(#locale), + ), ) as String); @override bool get hasListeners => (super.noSuchMethod( diff --git a/test/screen_tests/transaction_subviews/transaction_search_results_view_screen_test.dart b/test/screen_tests/transaction_subviews/transaction_search_results_view_screen_test.dart index d3d676211..8a50045a6 100644 --- a/test/screen_tests/transaction_subviews/transaction_search_results_view_screen_test.dart +++ b/test/screen_tests/transaction_subviews/transaction_search_results_view_screen_test.dart @@ -16,7 +16,7 @@ import 'package:stackwallet/services/locale_service.dart'; // import 'transaction_search_results_view_screen_test.mocks.dart'; @GenerateMocks([], customMocks: [ - MockSpec(returnNullOnMissingStub: true), + MockSpec(), ]) void main() { // testWidgets( diff --git a/test/screen_tests/transaction_subviews/transaction_search_results_view_screen_test.mocks.dart b/test/screen_tests/transaction_subviews/transaction_search_results_view_screen_test.mocks.dart index 46f63781d..c4aad0a14 100644 --- a/test/screen_tests/transaction_subviews/transaction_search_results_view_screen_test.mocks.dart +++ b/test/screen_tests/transaction_subviews/transaction_search_results_view_screen_test.mocks.dart @@ -1,18 +1,21 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/screen_tests/transaction_subviews/transaction_search_results_view_screen_test.dart. // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i3; -import 'dart:ui' as _i4; +import 'dart:async' as _i4; +import 'dart:ui' as _i5; import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i3; import 'package:stackwallet/services/locale_service.dart' as _i2; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -24,10 +27,17 @@ import 'package:stackwallet/services/locale_service.dart' as _i2; /// /// See the documentation for Mockito's code generation for more information. class MockLocaleService extends _i1.Mock implements _i2.LocaleService { + MockLocaleService() { + _i1.throwOnMissingStub(this); + } + @override String get locale => (super.noSuchMethod( Invocation.getter(#locale), - returnValue: '', + returnValue: _i3.dummyValue( + this, + Invocation.getter(#locale), + ), ) as String); @override bool get hasListeners => (super.noSuchMethod( @@ -35,17 +45,17 @@ class MockLocaleService extends _i1.Mock implements _i2.LocaleService { returnValue: false, ) as bool); @override - _i3.Future loadLocale({bool? notify = true}) => (super.noSuchMethod( + _i4.Future loadLocale({bool? notify = true}) => (super.noSuchMethod( Invocation.method( #loadLocale, [], {#notify: notify}, ), - returnValue: _i3.Future.value(), - returnValueForMissingStub: _i3.Future.value(), - ) as _i3.Future); + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); @override - void addListener(_i4.VoidCallback? listener) => super.noSuchMethod( + void addListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #addListener, [listener], @@ -53,7 +63,7 @@ class MockLocaleService extends _i1.Mock implements _i2.LocaleService { returnValueForMissingStub: null, ); @override - void removeListener(_i4.VoidCallback? listener) => super.noSuchMethod( + void removeListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #removeListener, [listener], diff --git a/test/screen_tests/transaction_subviews/transaction_search_view_screen_test.dart b/test/screen_tests/transaction_subviews/transaction_search_view_screen_test.dart index fb4280932..080a1ffbd 100644 --- a/test/screen_tests/transaction_subviews/transaction_search_view_screen_test.dart +++ b/test/screen_tests/transaction_subviews/transaction_search_view_screen_test.dart @@ -14,7 +14,7 @@ import 'package:stackwallet/services/address_book_service.dart'; // import 'transaction_search_view_screen_test.mocks.dart'; @GenerateMocks([], customMocks: [ - MockSpec(returnNullOnMissingStub: true), + MockSpec(), ]) void main() { // testWidgets("TransactionSearchView builds correctly", (tester) async { diff --git a/test/screen_tests/transaction_subviews/transaction_search_view_screen_test.mocks.dart b/test/screen_tests/transaction_subviews/transaction_search_view_screen_test.mocks.dart index 652e170f0..6fd708af7 100644 --- a/test/screen_tests/transaction_subviews/transaction_search_view_screen_test.mocks.dart +++ b/test/screen_tests/transaction_subviews/transaction_search_view_screen_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/screen_tests/transaction_subviews/transaction_search_view_screen_test.dart. // Do not manually edit this file. @@ -14,6 +14,8 @@ import 'package:stackwallet/services/address_book_service.dart' as _i3; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -36,6 +38,10 @@ class _FakeContactEntry_0 extends _i1.SmartFake implements _i2.ContactEntry { /// See the documentation for Mockito's code generation for more information. class MockAddressBookService extends _i1.Mock implements _i3.AddressBookService { + MockAddressBookService() { + _i1.throwOnMissingStub(this); + } + @override List<_i2.ContactEntry> get contacts => (super.noSuchMethod( Invocation.getter(#contacts), diff --git a/test/screen_tests/wallet_view/send_view_screen_test.mocks.dart b/test/screen_tests/wallet_view/send_view_screen_test.mocks.dart index 666ce4e98..a7a3ad695 100644 --- a/test/screen_tests/wallet_view/send_view_screen_test.mocks.dart +++ b/test/screen_tests/wallet_view/send_view_screen_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/screen_tests/wallet_view/send_view_screen_test.dart. // Do not manually edit this file. @@ -13,6 +13,8 @@ import 'package:stackwallet/utilities/barcode_scanner_interface.dart' as _i3; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors diff --git a/test/screen_tests/wallet_view/wallet_view_screen_test.dart b/test/screen_tests/wallet_view/wallet_view_screen_test.dart index 6c6815d4c..02ed59580 100644 --- a/test/screen_tests/wallet_view/wallet_view_screen_test.dart +++ b/test/screen_tests/wallet_view/wallet_view_screen_test.dart @@ -22,7 +22,7 @@ import 'package:stackwallet/services/locale_service.dart'; // import 'wallet_view_screen_test.mocks.dart'; @GenerateMocks([], customMocks: [ - MockSpec(returnNullOnMissingStub: true), + MockSpec(), ]) void main() { // testWidgets("WalletView builds correctly with no transactions", diff --git a/test/screen_tests/wallet_view/wallet_view_screen_test.mocks.dart b/test/screen_tests/wallet_view/wallet_view_screen_test.mocks.dart index 41319572b..b85201783 100644 --- a/test/screen_tests/wallet_view/wallet_view_screen_test.mocks.dart +++ b/test/screen_tests/wallet_view/wallet_view_screen_test.mocks.dart @@ -1,18 +1,21 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/screen_tests/wallet_view/wallet_view_screen_test.dart. // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i3; -import 'dart:ui' as _i4; +import 'dart:async' as _i4; +import 'dart:ui' as _i5; import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i3; import 'package:stackwallet/services/locale_service.dart' as _i2; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -24,10 +27,17 @@ import 'package:stackwallet/services/locale_service.dart' as _i2; /// /// See the documentation for Mockito's code generation for more information. class MockLocaleService extends _i1.Mock implements _i2.LocaleService { + MockLocaleService() { + _i1.throwOnMissingStub(this); + } + @override String get locale => (super.noSuchMethod( Invocation.getter(#locale), - returnValue: '', + returnValue: _i3.dummyValue( + this, + Invocation.getter(#locale), + ), ) as String); @override bool get hasListeners => (super.noSuchMethod( @@ -35,17 +45,17 @@ class MockLocaleService extends _i1.Mock implements _i2.LocaleService { returnValue: false, ) as bool); @override - _i3.Future loadLocale({bool? notify = true}) => (super.noSuchMethod( + _i4.Future loadLocale({bool? notify = true}) => (super.noSuchMethod( Invocation.method( #loadLocale, [], {#notify: notify}, ), - returnValue: _i3.Future.value(), - returnValueForMissingStub: _i3.Future.value(), - ) as _i3.Future); + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); @override - void addListener(_i4.VoidCallback? listener) => super.noSuchMethod( + void addListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #addListener, [listener], @@ -53,7 +63,7 @@ class MockLocaleService extends _i1.Mock implements _i2.LocaleService { returnValueForMissingStub: null, ); @override - void removeListener(_i4.VoidCallback? listener) => super.noSuchMethod( + void removeListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #removeListener, [listener], diff --git a/test/services/change_now/change_now_test.mocks.dart b/test/services/change_now/change_now_test.mocks.dart index f0b375d72..1ee496d32 100644 --- a/test/services/change_now/change_now_test.mocks.dart +++ b/test/services/change_now/change_now_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/services/change_now/change_now_test.dart. // Do not manually edit this file. @@ -14,6 +14,8 @@ import 'package:stackwallet/networking/http.dart' as _i2; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors diff --git a/test/services/coins/bitcoin/bitcoin_wallet_test.mocks.dart b/test/services/coins/bitcoin/bitcoin_wallet_test.mocks.dart index efcb9f626..776e6874b 100644 --- a/test/services/coins/bitcoin/bitcoin_wallet_test.mocks.dart +++ b/test/services/coins/bitcoin/bitcoin_wallet_test.mocks.dart @@ -1,16 +1,17 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/services/coins/bitcoin/bitcoin_wallet_test.dart. // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i5; +import 'dart:async' as _i6; import 'package:decimal/decimal.dart' as _i3; import 'package:mockito/mockito.dart' as _i1; -import 'package:stackwallet/electrumx_rpc/cached_electrumx_client.dart' as _i6; +import 'package:mockito/src/dummies.dart' as _i5; +import 'package:stackwallet/electrumx_rpc/cached_electrumx_client.dart' as _i7; import 'package:stackwallet/electrumx_rpc/electrumx_client.dart' as _i4; import 'package:stackwallet/services/transaction_notification_tracker.dart' - as _i7; + as _i8; import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart' as _i2; @@ -18,6 +19,8 @@ import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart' // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -116,7 +119,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { @override String get host => (super.noSuchMethod( Invocation.getter(#host), - returnValue: '', + returnValue: _i5.dummyValue( + this, + Invocation.getter(#host), + ), ) as String); @override int get port => (super.noSuchMethod( @@ -129,16 +135,16 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: false, ) as bool); @override - _i5.Future closeAdapter() => (super.noSuchMethod( + _i6.Future closeAdapter() => (super.noSuchMethod( Invocation.method( #closeAdapter, [], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); @override - _i5.Future request({ + _i6.Future request({ required String? command, List? args = const [], String? requestID, @@ -157,10 +163,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #requestTimeout: requestTimeout, }, ), - returnValue: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + ) as _i6.Future); @override - _i5.Future> batchRequest({ + _i6.Future> batchRequest({ required String? command, required List? args, Duration? requestTimeout = const Duration(seconds: 60), @@ -177,10 +183,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #retries: retries, }, ), - returnValue: _i5.Future>.value([]), - ) as _i5.Future>); + returnValue: _i6.Future>.value([]), + ) as _i6.Future>); @override - _i5.Future ping({ + _i6.Future ping({ String? requestID, int? retryCount = 1, }) => @@ -193,10 +199,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #retryCount: retryCount, }, ), - returnValue: _i5.Future.value(false), - ) as _i5.Future); + returnValue: _i6.Future.value(false), + ) as _i6.Future); @override - _i5.Future> getBlockHeadTip({String? requestID}) => + _i6.Future> getBlockHeadTip({String? requestID}) => (super.noSuchMethod( Invocation.method( #getBlockHeadTip, @@ -204,10 +210,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { {#requestID: requestID}, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future> getServerFeatures({String? requestID}) => + _i6.Future> getServerFeatures({String? requestID}) => (super.noSuchMethod( Invocation.method( #getServerFeatures, @@ -215,10 +221,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { {#requestID: requestID}, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future broadcastTransaction({ + _i6.Future broadcastTransaction({ required String? rawTx, String? requestID, }) => @@ -231,10 +237,20 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #requestID: requestID, }, ), - returnValue: _i5.Future.value(''), - ) as _i5.Future); + returnValue: _i6.Future.value(_i5.dummyValue( + this, + Invocation.method( + #broadcastTransaction, + [], + { + #rawTx: rawTx, + #requestID: requestID, + }, + ), + )), + ) as _i6.Future); @override - _i5.Future> getBalance({ + _i6.Future> getBalance({ required String? scripthash, String? requestID, }) => @@ -248,10 +264,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future>> getHistory({ + _i6.Future>> getHistory({ required String? scripthash, String? requestID, }) => @@ -264,11 +280,11 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #requestID: requestID, }, ), - returnValue: _i5.Future>>.value( + returnValue: _i6.Future>>.value( >[]), - ) as _i5.Future>>); + ) as _i6.Future>>); @override - _i5.Future>>> getBatchHistory( + _i6.Future>>> getBatchHistory( {required List? args}) => (super.noSuchMethod( Invocation.method( @@ -276,11 +292,11 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { [], {#args: args}, ), - returnValue: _i5.Future>>>.value( + returnValue: _i6.Future>>>.value( >>[]), - ) as _i5.Future>>>); + ) as _i6.Future>>>); @override - _i5.Future>> getUTXOs({ + _i6.Future>> getUTXOs({ required String? scripthash, String? requestID, }) => @@ -293,11 +309,11 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #requestID: requestID, }, ), - returnValue: _i5.Future>>.value( + returnValue: _i6.Future>>.value( >[]), - ) as _i5.Future>>); + ) as _i6.Future>>); @override - _i5.Future>>> getBatchUTXOs( + _i6.Future>>> getBatchUTXOs( {required List? args}) => (super.noSuchMethod( Invocation.method( @@ -305,11 +321,11 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { [], {#args: args}, ), - returnValue: _i5.Future>>>.value( + returnValue: _i6.Future>>>.value( >>[]), - ) as _i5.Future>>>); + ) as _i6.Future>>>); @override - _i5.Future> getTransaction({ + _i6.Future> getTransaction({ required String? txHash, bool? verbose = true, String? requestID, @@ -325,10 +341,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future> getLelantusAnonymitySet({ + _i6.Future> getLelantusAnonymitySet({ String? groupId = r'1', String? blockhash = r'', String? requestID, @@ -344,10 +360,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future getLelantusMintData({ + _i6.Future getLelantusMintData({ dynamic mints, String? requestID, }) => @@ -360,10 +376,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #requestID: requestID, }, ), - returnValue: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + ) as _i6.Future); @override - _i5.Future> getLelantusUsedCoinSerials({ + _i6.Future> getLelantusUsedCoinSerials({ String? requestID, required int? startNumber, }) => @@ -377,20 +393,20 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future getLelantusLatestCoinId({String? requestID}) => + _i6.Future getLelantusLatestCoinId({String? requestID}) => (super.noSuchMethod( Invocation.method( #getLelantusLatestCoinId, [], {#requestID: requestID}, ), - returnValue: _i5.Future.value(0), - ) as _i5.Future); + returnValue: _i6.Future.value(0), + ) as _i6.Future); @override - _i5.Future> getSparkAnonymitySet({ + _i6.Future> getSparkAnonymitySet({ String? coinGroupId = r'1', String? startBlockHash = r'', String? requestID, @@ -406,10 +422,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future> getSparkUsedCoinsTags({ + _i6.Future> getSparkUsedCoinsTags({ String? requestID, required int? startNumber, }) => @@ -422,10 +438,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #startNumber: startNumber, }, ), - returnValue: _i5.Future>.value({}), - ) as _i5.Future>); + returnValue: _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future>> getSparkMintMetaData({ + _i6.Future>> getSparkMintMetaData({ String? requestID, required List? sparkCoinHashes, }) => @@ -438,21 +454,21 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #sparkCoinHashes: sparkCoinHashes, }, ), - returnValue: _i5.Future>>.value( + returnValue: _i6.Future>>.value( >[]), - ) as _i5.Future>>); + ) as _i6.Future>>); @override - _i5.Future getSparkLatestCoinId({String? requestID}) => + _i6.Future getSparkLatestCoinId({String? requestID}) => (super.noSuchMethod( Invocation.method( #getSparkLatestCoinId, [], {#requestID: requestID}, ), - returnValue: _i5.Future.value(0), - ) as _i5.Future); + returnValue: _i6.Future.value(0), + ) as _i6.Future); @override - _i5.Future> getFeeRate({String? requestID}) => + _i6.Future> getFeeRate({String? requestID}) => (super.noSuchMethod( Invocation.method( #getFeeRate, @@ -460,10 +476,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { {#requestID: requestID}, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future<_i3.Decimal> estimateFee({ + _i6.Future<_i3.Decimal> estimateFee({ String? requestID, required int? blocks, }) => @@ -476,7 +492,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #blocks: blocks, }, ), - returnValue: _i5.Future<_i3.Decimal>.value(_FakeDecimal_2( + returnValue: _i6.Future<_i3.Decimal>.value(_FakeDecimal_2( this, Invocation.method( #estimateFee, @@ -487,15 +503,15 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), )), - ) as _i5.Future<_i3.Decimal>); + ) as _i6.Future<_i3.Decimal>); @override - _i5.Future<_i3.Decimal> relayFee({String? requestID}) => (super.noSuchMethod( + _i6.Future<_i3.Decimal> relayFee({String? requestID}) => (super.noSuchMethod( Invocation.method( #relayFee, [], {#requestID: requestID}, ), - returnValue: _i5.Future<_i3.Decimal>.value(_FakeDecimal_2( + returnValue: _i6.Future<_i3.Decimal>.value(_FakeDecimal_2( this, Invocation.method( #relayFee, @@ -503,14 +519,14 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { {#requestID: requestID}, ), )), - ) as _i5.Future<_i3.Decimal>); + ) as _i6.Future<_i3.Decimal>); } /// A class which mocks [CachedElectrumXClient]. /// /// See the documentation for Mockito's code generation for more information. class MockCachedElectrumXClient extends _i1.Mock - implements _i6.CachedElectrumXClient { + implements _i7.CachedElectrumXClient { MockCachedElectrumXClient() { _i1.throwOnMissingStub(this); } @@ -524,7 +540,7 @@ class MockCachedElectrumXClient extends _i1.Mock ), ) as _i4.ElectrumXClient); @override - _i5.Future> getAnonymitySet({ + _i6.Future> getAnonymitySet({ required String? groupId, String? blockhash = r'', required _i2.CryptoCurrency? cryptoCurrency, @@ -540,10 +556,10 @@ class MockCachedElectrumXClient extends _i1.Mock }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future> getSparkAnonymitySet({ + _i6.Future> getSparkAnonymitySet({ required String? groupId, String? blockhash = r'', required _i2.CryptoCurrency? cryptoCurrency, @@ -561,15 +577,21 @@ class MockCachedElectrumXClient extends _i1.Mock }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override String base64ToHex(String? source) => (super.noSuchMethod( Invocation.method( #base64ToHex, [source], ), - returnValue: '', + returnValue: _i5.dummyValue( + this, + Invocation.method( + #base64ToHex, + [source], + ), + ), ) as String); @override String base64ToReverseHex(String? source) => (super.noSuchMethod( @@ -577,10 +599,16 @@ class MockCachedElectrumXClient extends _i1.Mock #base64ToReverseHex, [source], ), - returnValue: '', + returnValue: _i5.dummyValue( + this, + Invocation.method( + #base64ToReverseHex, + [source], + ), + ), ) as String); @override - _i5.Future> getTransaction({ + _i6.Future> getTransaction({ required String? txHash, required _i2.CryptoCurrency? cryptoCurrency, bool? verbose = true, @@ -596,10 +624,10 @@ class MockCachedElectrumXClient extends _i1.Mock }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future> getUsedCoinSerials({ + _i6.Future> getUsedCoinSerials({ required _i2.CryptoCurrency? cryptoCurrency, int? startNumber = 0, }) => @@ -612,10 +640,10 @@ class MockCachedElectrumXClient extends _i1.Mock #startNumber: startNumber, }, ), - returnValue: _i5.Future>.value([]), - ) as _i5.Future>); + returnValue: _i6.Future>.value([]), + ) as _i6.Future>); @override - _i5.Future> getSparkUsedCoinsTags( + _i6.Future> getSparkUsedCoinsTags( {required _i2.CryptoCurrency? cryptoCurrency}) => (super.noSuchMethod( Invocation.method( @@ -623,10 +651,10 @@ class MockCachedElectrumXClient extends _i1.Mock [], {#cryptoCurrency: cryptoCurrency}, ), - returnValue: _i5.Future>.value({}), - ) as _i5.Future>); + returnValue: _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future clearSharedTransactionCache( + _i6.Future clearSharedTransactionCache( {required _i2.CryptoCurrency? cryptoCurrency}) => (super.noSuchMethod( Invocation.method( @@ -634,16 +662,16 @@ class MockCachedElectrumXClient extends _i1.Mock [], {#cryptoCurrency: cryptoCurrency}, ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); } /// A class which mocks [TransactionNotificationTracker]. /// /// See the documentation for Mockito's code generation for more information. class MockTransactionNotificationTracker extends _i1.Mock - implements _i7.TransactionNotificationTracker { + implements _i8.TransactionNotificationTracker { MockTransactionNotificationTracker() { _i1.throwOnMissingStub(this); } @@ -651,7 +679,10 @@ class MockTransactionNotificationTracker extends _i1.Mock @override String get walletId => (super.noSuchMethod( Invocation.getter(#walletId), - returnValue: '', + returnValue: _i5.dummyValue( + this, + Invocation.getter(#walletId), + ), ) as String); @override List get pendings => (super.noSuchMethod( @@ -672,14 +703,14 @@ class MockTransactionNotificationTracker extends _i1.Mock returnValue: false, ) as bool); @override - _i5.Future addNotifiedPending(String? txid) => (super.noSuchMethod( + _i6.Future addNotifiedPending(String? txid) => (super.noSuchMethod( Invocation.method( #addNotifiedPending, [txid], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); @override bool wasNotifiedConfirmed(String? txid) => (super.noSuchMethod( Invocation.method( @@ -689,21 +720,21 @@ class MockTransactionNotificationTracker extends _i1.Mock returnValue: false, ) as bool); @override - _i5.Future addNotifiedConfirmed(String? txid) => (super.noSuchMethod( + _i6.Future addNotifiedConfirmed(String? txid) => (super.noSuchMethod( Invocation.method( #addNotifiedConfirmed, [txid], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); @override - _i5.Future deleteTransaction(String? txid) => (super.noSuchMethod( + _i6.Future deleteTransaction(String? txid) => (super.noSuchMethod( Invocation.method( #deleteTransaction, [txid], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); } diff --git a/test/services/coins/bitcoincash/bitcoincash_wallet_test.mocks.dart b/test/services/coins/bitcoincash/bitcoincash_wallet_test.mocks.dart index 7cfef5ab4..a2238f324 100644 --- a/test/services/coins/bitcoincash/bitcoincash_wallet_test.mocks.dart +++ b/test/services/coins/bitcoincash/bitcoincash_wallet_test.mocks.dart @@ -1,16 +1,17 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/services/coins/bitcoincash/bitcoincash_wallet_test.dart. // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i5; +import 'dart:async' as _i6; import 'package:decimal/decimal.dart' as _i3; import 'package:mockito/mockito.dart' as _i1; -import 'package:stackwallet/electrumx_rpc/cached_electrumx_client.dart' as _i6; +import 'package:mockito/src/dummies.dart' as _i5; +import 'package:stackwallet/electrumx_rpc/cached_electrumx_client.dart' as _i7; import 'package:stackwallet/electrumx_rpc/electrumx_client.dart' as _i4; import 'package:stackwallet/services/transaction_notification_tracker.dart' - as _i7; + as _i8; import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart' as _i2; @@ -18,6 +19,8 @@ import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart' // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -116,7 +119,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { @override String get host => (super.noSuchMethod( Invocation.getter(#host), - returnValue: '', + returnValue: _i5.dummyValue( + this, + Invocation.getter(#host), + ), ) as String); @override int get port => (super.noSuchMethod( @@ -129,16 +135,16 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: false, ) as bool); @override - _i5.Future closeAdapter() => (super.noSuchMethod( + _i6.Future closeAdapter() => (super.noSuchMethod( Invocation.method( #closeAdapter, [], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); @override - _i5.Future request({ + _i6.Future request({ required String? command, List? args = const [], String? requestID, @@ -157,10 +163,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #requestTimeout: requestTimeout, }, ), - returnValue: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + ) as _i6.Future); @override - _i5.Future> batchRequest({ + _i6.Future> batchRequest({ required String? command, required List? args, Duration? requestTimeout = const Duration(seconds: 60), @@ -177,10 +183,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #retries: retries, }, ), - returnValue: _i5.Future>.value([]), - ) as _i5.Future>); + returnValue: _i6.Future>.value([]), + ) as _i6.Future>); @override - _i5.Future ping({ + _i6.Future ping({ String? requestID, int? retryCount = 1, }) => @@ -193,10 +199,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #retryCount: retryCount, }, ), - returnValue: _i5.Future.value(false), - ) as _i5.Future); + returnValue: _i6.Future.value(false), + ) as _i6.Future); @override - _i5.Future> getBlockHeadTip({String? requestID}) => + _i6.Future> getBlockHeadTip({String? requestID}) => (super.noSuchMethod( Invocation.method( #getBlockHeadTip, @@ -204,10 +210,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { {#requestID: requestID}, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future> getServerFeatures({String? requestID}) => + _i6.Future> getServerFeatures({String? requestID}) => (super.noSuchMethod( Invocation.method( #getServerFeatures, @@ -215,10 +221,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { {#requestID: requestID}, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future broadcastTransaction({ + _i6.Future broadcastTransaction({ required String? rawTx, String? requestID, }) => @@ -231,10 +237,20 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #requestID: requestID, }, ), - returnValue: _i5.Future.value(''), - ) as _i5.Future); + returnValue: _i6.Future.value(_i5.dummyValue( + this, + Invocation.method( + #broadcastTransaction, + [], + { + #rawTx: rawTx, + #requestID: requestID, + }, + ), + )), + ) as _i6.Future); @override - _i5.Future> getBalance({ + _i6.Future> getBalance({ required String? scripthash, String? requestID, }) => @@ -248,10 +264,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future>> getHistory({ + _i6.Future>> getHistory({ required String? scripthash, String? requestID, }) => @@ -264,11 +280,11 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #requestID: requestID, }, ), - returnValue: _i5.Future>>.value( + returnValue: _i6.Future>>.value( >[]), - ) as _i5.Future>>); + ) as _i6.Future>>); @override - _i5.Future>>> getBatchHistory( + _i6.Future>>> getBatchHistory( {required List? args}) => (super.noSuchMethod( Invocation.method( @@ -276,11 +292,11 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { [], {#args: args}, ), - returnValue: _i5.Future>>>.value( + returnValue: _i6.Future>>>.value( >>[]), - ) as _i5.Future>>>); + ) as _i6.Future>>>); @override - _i5.Future>> getUTXOs({ + _i6.Future>> getUTXOs({ required String? scripthash, String? requestID, }) => @@ -293,11 +309,11 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #requestID: requestID, }, ), - returnValue: _i5.Future>>.value( + returnValue: _i6.Future>>.value( >[]), - ) as _i5.Future>>); + ) as _i6.Future>>); @override - _i5.Future>>> getBatchUTXOs( + _i6.Future>>> getBatchUTXOs( {required List? args}) => (super.noSuchMethod( Invocation.method( @@ -305,11 +321,11 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { [], {#args: args}, ), - returnValue: _i5.Future>>>.value( + returnValue: _i6.Future>>>.value( >>[]), - ) as _i5.Future>>>); + ) as _i6.Future>>>); @override - _i5.Future> getTransaction({ + _i6.Future> getTransaction({ required String? txHash, bool? verbose = true, String? requestID, @@ -325,10 +341,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future> getLelantusAnonymitySet({ + _i6.Future> getLelantusAnonymitySet({ String? groupId = r'1', String? blockhash = r'', String? requestID, @@ -344,10 +360,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future getLelantusMintData({ + _i6.Future getLelantusMintData({ dynamic mints, String? requestID, }) => @@ -360,10 +376,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #requestID: requestID, }, ), - returnValue: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + ) as _i6.Future); @override - _i5.Future> getLelantusUsedCoinSerials({ + _i6.Future> getLelantusUsedCoinSerials({ String? requestID, required int? startNumber, }) => @@ -377,20 +393,20 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future getLelantusLatestCoinId({String? requestID}) => + _i6.Future getLelantusLatestCoinId({String? requestID}) => (super.noSuchMethod( Invocation.method( #getLelantusLatestCoinId, [], {#requestID: requestID}, ), - returnValue: _i5.Future.value(0), - ) as _i5.Future); + returnValue: _i6.Future.value(0), + ) as _i6.Future); @override - _i5.Future> getSparkAnonymitySet({ + _i6.Future> getSparkAnonymitySet({ String? coinGroupId = r'1', String? startBlockHash = r'', String? requestID, @@ -406,10 +422,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future> getSparkUsedCoinsTags({ + _i6.Future> getSparkUsedCoinsTags({ String? requestID, required int? startNumber, }) => @@ -422,10 +438,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #startNumber: startNumber, }, ), - returnValue: _i5.Future>.value({}), - ) as _i5.Future>); + returnValue: _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future>> getSparkMintMetaData({ + _i6.Future>> getSparkMintMetaData({ String? requestID, required List? sparkCoinHashes, }) => @@ -438,21 +454,21 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #sparkCoinHashes: sparkCoinHashes, }, ), - returnValue: _i5.Future>>.value( + returnValue: _i6.Future>>.value( >[]), - ) as _i5.Future>>); + ) as _i6.Future>>); @override - _i5.Future getSparkLatestCoinId({String? requestID}) => + _i6.Future getSparkLatestCoinId({String? requestID}) => (super.noSuchMethod( Invocation.method( #getSparkLatestCoinId, [], {#requestID: requestID}, ), - returnValue: _i5.Future.value(0), - ) as _i5.Future); + returnValue: _i6.Future.value(0), + ) as _i6.Future); @override - _i5.Future> getFeeRate({String? requestID}) => + _i6.Future> getFeeRate({String? requestID}) => (super.noSuchMethod( Invocation.method( #getFeeRate, @@ -460,10 +476,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { {#requestID: requestID}, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future<_i3.Decimal> estimateFee({ + _i6.Future<_i3.Decimal> estimateFee({ String? requestID, required int? blocks, }) => @@ -476,7 +492,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #blocks: blocks, }, ), - returnValue: _i5.Future<_i3.Decimal>.value(_FakeDecimal_2( + returnValue: _i6.Future<_i3.Decimal>.value(_FakeDecimal_2( this, Invocation.method( #estimateFee, @@ -487,15 +503,15 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), )), - ) as _i5.Future<_i3.Decimal>); + ) as _i6.Future<_i3.Decimal>); @override - _i5.Future<_i3.Decimal> relayFee({String? requestID}) => (super.noSuchMethod( + _i6.Future<_i3.Decimal> relayFee({String? requestID}) => (super.noSuchMethod( Invocation.method( #relayFee, [], {#requestID: requestID}, ), - returnValue: _i5.Future<_i3.Decimal>.value(_FakeDecimal_2( + returnValue: _i6.Future<_i3.Decimal>.value(_FakeDecimal_2( this, Invocation.method( #relayFee, @@ -503,14 +519,14 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { {#requestID: requestID}, ), )), - ) as _i5.Future<_i3.Decimal>); + ) as _i6.Future<_i3.Decimal>); } /// A class which mocks [CachedElectrumXClient]. /// /// See the documentation for Mockito's code generation for more information. class MockCachedElectrumXClient extends _i1.Mock - implements _i6.CachedElectrumXClient { + implements _i7.CachedElectrumXClient { MockCachedElectrumXClient() { _i1.throwOnMissingStub(this); } @@ -524,7 +540,7 @@ class MockCachedElectrumXClient extends _i1.Mock ), ) as _i4.ElectrumXClient); @override - _i5.Future> getAnonymitySet({ + _i6.Future> getAnonymitySet({ required String? groupId, String? blockhash = r'', required _i2.CryptoCurrency? cryptoCurrency, @@ -540,10 +556,10 @@ class MockCachedElectrumXClient extends _i1.Mock }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future> getSparkAnonymitySet({ + _i6.Future> getSparkAnonymitySet({ required String? groupId, String? blockhash = r'', required _i2.CryptoCurrency? cryptoCurrency, @@ -561,15 +577,21 @@ class MockCachedElectrumXClient extends _i1.Mock }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override String base64ToHex(String? source) => (super.noSuchMethod( Invocation.method( #base64ToHex, [source], ), - returnValue: '', + returnValue: _i5.dummyValue( + this, + Invocation.method( + #base64ToHex, + [source], + ), + ), ) as String); @override String base64ToReverseHex(String? source) => (super.noSuchMethod( @@ -577,10 +599,16 @@ class MockCachedElectrumXClient extends _i1.Mock #base64ToReverseHex, [source], ), - returnValue: '', + returnValue: _i5.dummyValue( + this, + Invocation.method( + #base64ToReverseHex, + [source], + ), + ), ) as String); @override - _i5.Future> getTransaction({ + _i6.Future> getTransaction({ required String? txHash, required _i2.CryptoCurrency? cryptoCurrency, bool? verbose = true, @@ -596,10 +624,10 @@ class MockCachedElectrumXClient extends _i1.Mock }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future> getUsedCoinSerials({ + _i6.Future> getUsedCoinSerials({ required _i2.CryptoCurrency? cryptoCurrency, int? startNumber = 0, }) => @@ -612,10 +640,10 @@ class MockCachedElectrumXClient extends _i1.Mock #startNumber: startNumber, }, ), - returnValue: _i5.Future>.value([]), - ) as _i5.Future>); + returnValue: _i6.Future>.value([]), + ) as _i6.Future>); @override - _i5.Future> getSparkUsedCoinsTags( + _i6.Future> getSparkUsedCoinsTags( {required _i2.CryptoCurrency? cryptoCurrency}) => (super.noSuchMethod( Invocation.method( @@ -623,10 +651,10 @@ class MockCachedElectrumXClient extends _i1.Mock [], {#cryptoCurrency: cryptoCurrency}, ), - returnValue: _i5.Future>.value({}), - ) as _i5.Future>); + returnValue: _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future clearSharedTransactionCache( + _i6.Future clearSharedTransactionCache( {required _i2.CryptoCurrency? cryptoCurrency}) => (super.noSuchMethod( Invocation.method( @@ -634,16 +662,16 @@ class MockCachedElectrumXClient extends _i1.Mock [], {#cryptoCurrency: cryptoCurrency}, ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); } /// A class which mocks [TransactionNotificationTracker]. /// /// See the documentation for Mockito's code generation for more information. class MockTransactionNotificationTracker extends _i1.Mock - implements _i7.TransactionNotificationTracker { + implements _i8.TransactionNotificationTracker { MockTransactionNotificationTracker() { _i1.throwOnMissingStub(this); } @@ -651,7 +679,10 @@ class MockTransactionNotificationTracker extends _i1.Mock @override String get walletId => (super.noSuchMethod( Invocation.getter(#walletId), - returnValue: '', + returnValue: _i5.dummyValue( + this, + Invocation.getter(#walletId), + ), ) as String); @override List get pendings => (super.noSuchMethod( @@ -672,14 +703,14 @@ class MockTransactionNotificationTracker extends _i1.Mock returnValue: false, ) as bool); @override - _i5.Future addNotifiedPending(String? txid) => (super.noSuchMethod( + _i6.Future addNotifiedPending(String? txid) => (super.noSuchMethod( Invocation.method( #addNotifiedPending, [txid], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); @override bool wasNotifiedConfirmed(String? txid) => (super.noSuchMethod( Invocation.method( @@ -689,21 +720,21 @@ class MockTransactionNotificationTracker extends _i1.Mock returnValue: false, ) as bool); @override - _i5.Future addNotifiedConfirmed(String? txid) => (super.noSuchMethod( + _i6.Future addNotifiedConfirmed(String? txid) => (super.noSuchMethod( Invocation.method( #addNotifiedConfirmed, [txid], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); @override - _i5.Future deleteTransaction(String? txid) => (super.noSuchMethod( + _i6.Future deleteTransaction(String? txid) => (super.noSuchMethod( Invocation.method( #deleteTransaction, [txid], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); } diff --git a/test/services/coins/bitcoincash/cashtokens_test.dart b/test/services/coins/bitcoincash/cashtokens_test.dart index be8a8b24b..d5e8b390b 100644 --- a/test/services/coins/bitcoincash/cashtokens_test.dart +++ b/test/services/coins/bitcoincash/cashtokens_test.dart @@ -10,17 +10,17 @@ void main() { // Replace "var1" with a hex string containing an output (script pub key) test("testUnwrapSPK", () { // Example Hex format string - String var1 = "76a91463456150b05a67084d795fbce22c8fbbca37697288ac"; + final String var1 = "76a91463456150b05a67084d795fbce22c8fbbca37697288ac"; // Convert the Hex string to Uint8List - Uint8List wrapped_spk = Uint8List.fromList(HEX.decode(var1)); + final Uint8List wrapped_spk = Uint8List.fromList(HEX.decode(var1)); // Call unwrap_spk - ParsedOutput parsedOutput = unwrap_spk(wrapped_spk); + final ParsedOutput parsedOutput = unwrap_spk(wrapped_spk); print("Parsed Output: $parsedOutput"); // Access token_data inside parsedOutput - TokenOutputData? tokenData = parsedOutput.token_data; + final TokenOutputData? tokenData = parsedOutput.token_data; // Check if tokenData is null if (tokenData != null) { diff --git a/test/services/coins/dogecoin/dogecoin_wallet_test.mocks.dart b/test/services/coins/dogecoin/dogecoin_wallet_test.mocks.dart index eb9afea89..f80e8a840 100644 --- a/test/services/coins/dogecoin/dogecoin_wallet_test.mocks.dart +++ b/test/services/coins/dogecoin/dogecoin_wallet_test.mocks.dart @@ -1,16 +1,17 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/services/coins/dogecoin/dogecoin_wallet_test.dart. // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i5; +import 'dart:async' as _i6; import 'package:decimal/decimal.dart' as _i3; import 'package:mockito/mockito.dart' as _i1; -import 'package:stackwallet/electrumx_rpc/cached_electrumx_client.dart' as _i6; +import 'package:mockito/src/dummies.dart' as _i5; +import 'package:stackwallet/electrumx_rpc/cached_electrumx_client.dart' as _i7; import 'package:stackwallet/electrumx_rpc/electrumx_client.dart' as _i4; import 'package:stackwallet/services/transaction_notification_tracker.dart' - as _i7; + as _i8; import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart' as _i2; @@ -18,6 +19,8 @@ import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart' // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -116,7 +119,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { @override String get host => (super.noSuchMethod( Invocation.getter(#host), - returnValue: '', + returnValue: _i5.dummyValue( + this, + Invocation.getter(#host), + ), ) as String); @override int get port => (super.noSuchMethod( @@ -129,16 +135,16 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: false, ) as bool); @override - _i5.Future closeAdapter() => (super.noSuchMethod( + _i6.Future closeAdapter() => (super.noSuchMethod( Invocation.method( #closeAdapter, [], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); @override - _i5.Future request({ + _i6.Future request({ required String? command, List? args = const [], String? requestID, @@ -157,10 +163,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #requestTimeout: requestTimeout, }, ), - returnValue: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + ) as _i6.Future); @override - _i5.Future> batchRequest({ + _i6.Future> batchRequest({ required String? command, required List? args, Duration? requestTimeout = const Duration(seconds: 60), @@ -177,10 +183,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #retries: retries, }, ), - returnValue: _i5.Future>.value([]), - ) as _i5.Future>); + returnValue: _i6.Future>.value([]), + ) as _i6.Future>); @override - _i5.Future ping({ + _i6.Future ping({ String? requestID, int? retryCount = 1, }) => @@ -193,10 +199,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #retryCount: retryCount, }, ), - returnValue: _i5.Future.value(false), - ) as _i5.Future); + returnValue: _i6.Future.value(false), + ) as _i6.Future); @override - _i5.Future> getBlockHeadTip({String? requestID}) => + _i6.Future> getBlockHeadTip({String? requestID}) => (super.noSuchMethod( Invocation.method( #getBlockHeadTip, @@ -204,10 +210,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { {#requestID: requestID}, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future> getServerFeatures({String? requestID}) => + _i6.Future> getServerFeatures({String? requestID}) => (super.noSuchMethod( Invocation.method( #getServerFeatures, @@ -215,10 +221,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { {#requestID: requestID}, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future broadcastTransaction({ + _i6.Future broadcastTransaction({ required String? rawTx, String? requestID, }) => @@ -231,10 +237,20 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #requestID: requestID, }, ), - returnValue: _i5.Future.value(''), - ) as _i5.Future); + returnValue: _i6.Future.value(_i5.dummyValue( + this, + Invocation.method( + #broadcastTransaction, + [], + { + #rawTx: rawTx, + #requestID: requestID, + }, + ), + )), + ) as _i6.Future); @override - _i5.Future> getBalance({ + _i6.Future> getBalance({ required String? scripthash, String? requestID, }) => @@ -248,10 +264,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future>> getHistory({ + _i6.Future>> getHistory({ required String? scripthash, String? requestID, }) => @@ -264,11 +280,11 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #requestID: requestID, }, ), - returnValue: _i5.Future>>.value( + returnValue: _i6.Future>>.value( >[]), - ) as _i5.Future>>); + ) as _i6.Future>>); @override - _i5.Future>>> getBatchHistory( + _i6.Future>>> getBatchHistory( {required List? args}) => (super.noSuchMethod( Invocation.method( @@ -276,11 +292,11 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { [], {#args: args}, ), - returnValue: _i5.Future>>>.value( + returnValue: _i6.Future>>>.value( >>[]), - ) as _i5.Future>>>); + ) as _i6.Future>>>); @override - _i5.Future>> getUTXOs({ + _i6.Future>> getUTXOs({ required String? scripthash, String? requestID, }) => @@ -293,11 +309,11 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #requestID: requestID, }, ), - returnValue: _i5.Future>>.value( + returnValue: _i6.Future>>.value( >[]), - ) as _i5.Future>>); + ) as _i6.Future>>); @override - _i5.Future>>> getBatchUTXOs( + _i6.Future>>> getBatchUTXOs( {required List? args}) => (super.noSuchMethod( Invocation.method( @@ -305,11 +321,11 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { [], {#args: args}, ), - returnValue: _i5.Future>>>.value( + returnValue: _i6.Future>>>.value( >>[]), - ) as _i5.Future>>>); + ) as _i6.Future>>>); @override - _i5.Future> getTransaction({ + _i6.Future> getTransaction({ required String? txHash, bool? verbose = true, String? requestID, @@ -325,10 +341,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future> getLelantusAnonymitySet({ + _i6.Future> getLelantusAnonymitySet({ String? groupId = r'1', String? blockhash = r'', String? requestID, @@ -344,10 +360,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future getLelantusMintData({ + _i6.Future getLelantusMintData({ dynamic mints, String? requestID, }) => @@ -360,10 +376,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #requestID: requestID, }, ), - returnValue: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + ) as _i6.Future); @override - _i5.Future> getLelantusUsedCoinSerials({ + _i6.Future> getLelantusUsedCoinSerials({ String? requestID, required int? startNumber, }) => @@ -377,20 +393,20 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future getLelantusLatestCoinId({String? requestID}) => + _i6.Future getLelantusLatestCoinId({String? requestID}) => (super.noSuchMethod( Invocation.method( #getLelantusLatestCoinId, [], {#requestID: requestID}, ), - returnValue: _i5.Future.value(0), - ) as _i5.Future); + returnValue: _i6.Future.value(0), + ) as _i6.Future); @override - _i5.Future> getSparkAnonymitySet({ + _i6.Future> getSparkAnonymitySet({ String? coinGroupId = r'1', String? startBlockHash = r'', String? requestID, @@ -406,10 +422,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future> getSparkUsedCoinsTags({ + _i6.Future> getSparkUsedCoinsTags({ String? requestID, required int? startNumber, }) => @@ -422,10 +438,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #startNumber: startNumber, }, ), - returnValue: _i5.Future>.value({}), - ) as _i5.Future>); + returnValue: _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future>> getSparkMintMetaData({ + _i6.Future>> getSparkMintMetaData({ String? requestID, required List? sparkCoinHashes, }) => @@ -438,21 +454,21 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #sparkCoinHashes: sparkCoinHashes, }, ), - returnValue: _i5.Future>>.value( + returnValue: _i6.Future>>.value( >[]), - ) as _i5.Future>>); + ) as _i6.Future>>); @override - _i5.Future getSparkLatestCoinId({String? requestID}) => + _i6.Future getSparkLatestCoinId({String? requestID}) => (super.noSuchMethod( Invocation.method( #getSparkLatestCoinId, [], {#requestID: requestID}, ), - returnValue: _i5.Future.value(0), - ) as _i5.Future); + returnValue: _i6.Future.value(0), + ) as _i6.Future); @override - _i5.Future> getFeeRate({String? requestID}) => + _i6.Future> getFeeRate({String? requestID}) => (super.noSuchMethod( Invocation.method( #getFeeRate, @@ -460,10 +476,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { {#requestID: requestID}, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future<_i3.Decimal> estimateFee({ + _i6.Future<_i3.Decimal> estimateFee({ String? requestID, required int? blocks, }) => @@ -476,7 +492,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #blocks: blocks, }, ), - returnValue: _i5.Future<_i3.Decimal>.value(_FakeDecimal_2( + returnValue: _i6.Future<_i3.Decimal>.value(_FakeDecimal_2( this, Invocation.method( #estimateFee, @@ -487,15 +503,15 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), )), - ) as _i5.Future<_i3.Decimal>); + ) as _i6.Future<_i3.Decimal>); @override - _i5.Future<_i3.Decimal> relayFee({String? requestID}) => (super.noSuchMethod( + _i6.Future<_i3.Decimal> relayFee({String? requestID}) => (super.noSuchMethod( Invocation.method( #relayFee, [], {#requestID: requestID}, ), - returnValue: _i5.Future<_i3.Decimal>.value(_FakeDecimal_2( + returnValue: _i6.Future<_i3.Decimal>.value(_FakeDecimal_2( this, Invocation.method( #relayFee, @@ -503,14 +519,14 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { {#requestID: requestID}, ), )), - ) as _i5.Future<_i3.Decimal>); + ) as _i6.Future<_i3.Decimal>); } /// A class which mocks [CachedElectrumXClient]. /// /// See the documentation for Mockito's code generation for more information. class MockCachedElectrumXClient extends _i1.Mock - implements _i6.CachedElectrumXClient { + implements _i7.CachedElectrumXClient { MockCachedElectrumXClient() { _i1.throwOnMissingStub(this); } @@ -524,7 +540,7 @@ class MockCachedElectrumXClient extends _i1.Mock ), ) as _i4.ElectrumXClient); @override - _i5.Future> getAnonymitySet({ + _i6.Future> getAnonymitySet({ required String? groupId, String? blockhash = r'', required _i2.CryptoCurrency? cryptoCurrency, @@ -540,10 +556,10 @@ class MockCachedElectrumXClient extends _i1.Mock }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future> getSparkAnonymitySet({ + _i6.Future> getSparkAnonymitySet({ required String? groupId, String? blockhash = r'', required _i2.CryptoCurrency? cryptoCurrency, @@ -561,15 +577,21 @@ class MockCachedElectrumXClient extends _i1.Mock }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override String base64ToHex(String? source) => (super.noSuchMethod( Invocation.method( #base64ToHex, [source], ), - returnValue: '', + returnValue: _i5.dummyValue( + this, + Invocation.method( + #base64ToHex, + [source], + ), + ), ) as String); @override String base64ToReverseHex(String? source) => (super.noSuchMethod( @@ -577,10 +599,16 @@ class MockCachedElectrumXClient extends _i1.Mock #base64ToReverseHex, [source], ), - returnValue: '', + returnValue: _i5.dummyValue( + this, + Invocation.method( + #base64ToReverseHex, + [source], + ), + ), ) as String); @override - _i5.Future> getTransaction({ + _i6.Future> getTransaction({ required String? txHash, required _i2.CryptoCurrency? cryptoCurrency, bool? verbose = true, @@ -596,10 +624,10 @@ class MockCachedElectrumXClient extends _i1.Mock }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future> getUsedCoinSerials({ + _i6.Future> getUsedCoinSerials({ required _i2.CryptoCurrency? cryptoCurrency, int? startNumber = 0, }) => @@ -612,10 +640,10 @@ class MockCachedElectrumXClient extends _i1.Mock #startNumber: startNumber, }, ), - returnValue: _i5.Future>.value([]), - ) as _i5.Future>); + returnValue: _i6.Future>.value([]), + ) as _i6.Future>); @override - _i5.Future> getSparkUsedCoinsTags( + _i6.Future> getSparkUsedCoinsTags( {required _i2.CryptoCurrency? cryptoCurrency}) => (super.noSuchMethod( Invocation.method( @@ -623,10 +651,10 @@ class MockCachedElectrumXClient extends _i1.Mock [], {#cryptoCurrency: cryptoCurrency}, ), - returnValue: _i5.Future>.value({}), - ) as _i5.Future>); + returnValue: _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future clearSharedTransactionCache( + _i6.Future clearSharedTransactionCache( {required _i2.CryptoCurrency? cryptoCurrency}) => (super.noSuchMethod( Invocation.method( @@ -634,16 +662,16 @@ class MockCachedElectrumXClient extends _i1.Mock [], {#cryptoCurrency: cryptoCurrency}, ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); } /// A class which mocks [TransactionNotificationTracker]. /// /// See the documentation for Mockito's code generation for more information. class MockTransactionNotificationTracker extends _i1.Mock - implements _i7.TransactionNotificationTracker { + implements _i8.TransactionNotificationTracker { MockTransactionNotificationTracker() { _i1.throwOnMissingStub(this); } @@ -651,7 +679,10 @@ class MockTransactionNotificationTracker extends _i1.Mock @override String get walletId => (super.noSuchMethod( Invocation.getter(#walletId), - returnValue: '', + returnValue: _i5.dummyValue( + this, + Invocation.getter(#walletId), + ), ) as String); @override List get pendings => (super.noSuchMethod( @@ -672,14 +703,14 @@ class MockTransactionNotificationTracker extends _i1.Mock returnValue: false, ) as bool); @override - _i5.Future addNotifiedPending(String? txid) => (super.noSuchMethod( + _i6.Future addNotifiedPending(String? txid) => (super.noSuchMethod( Invocation.method( #addNotifiedPending, [txid], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); @override bool wasNotifiedConfirmed(String? txid) => (super.noSuchMethod( Invocation.method( @@ -689,21 +720,21 @@ class MockTransactionNotificationTracker extends _i1.Mock returnValue: false, ) as bool); @override - _i5.Future addNotifiedConfirmed(String? txid) => (super.noSuchMethod( + _i6.Future addNotifiedConfirmed(String? txid) => (super.noSuchMethod( Invocation.method( #addNotifiedConfirmed, [txid], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); @override - _i5.Future deleteTransaction(String? txid) => (super.noSuchMethod( + _i6.Future deleteTransaction(String? txid) => (super.noSuchMethod( Invocation.method( #deleteTransaction, [txid], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); } diff --git a/test/services/coins/firo/sample_data/transaction_data_samples.dart b/test/services/coins/firo/sample_data/transaction_data_samples.dart index 07108ece0..6e22d9b2f 100644 --- a/test/services/coins/firo/sample_data/transaction_data_samples.dart +++ b/test/services/coins/firo/sample_data/transaction_data_samples.dart @@ -2506,7 +2506,7 @@ final jsonTransactions = [ ]; Map get transactionDataMap { - Map result = {}; + final Map result = {}; for (final tx in jsonTransactions) { result[tx["txid"] as String] = Transaction.fromJson(tx); } diff --git a/test/services/coins/namecoin/namecoin_wallet_test.mocks.dart b/test/services/coins/namecoin/namecoin_wallet_test.mocks.dart index a8a5fb3c2..9f13d167b 100644 --- a/test/services/coins/namecoin/namecoin_wallet_test.mocks.dart +++ b/test/services/coins/namecoin/namecoin_wallet_test.mocks.dart @@ -1,16 +1,17 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/services/coins/namecoin/namecoin_wallet_test.dart. // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i5; +import 'dart:async' as _i6; import 'package:decimal/decimal.dart' as _i3; import 'package:mockito/mockito.dart' as _i1; -import 'package:stackwallet/electrumx_rpc/cached_electrumx_client.dart' as _i6; +import 'package:mockito/src/dummies.dart' as _i5; +import 'package:stackwallet/electrumx_rpc/cached_electrumx_client.dart' as _i7; import 'package:stackwallet/electrumx_rpc/electrumx_client.dart' as _i4; import 'package:stackwallet/services/transaction_notification_tracker.dart' - as _i7; + as _i8; import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart' as _i2; @@ -18,6 +19,8 @@ import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart' // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -116,7 +119,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { @override String get host => (super.noSuchMethod( Invocation.getter(#host), - returnValue: '', + returnValue: _i5.dummyValue( + this, + Invocation.getter(#host), + ), ) as String); @override int get port => (super.noSuchMethod( @@ -129,16 +135,16 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: false, ) as bool); @override - _i5.Future closeAdapter() => (super.noSuchMethod( + _i6.Future closeAdapter() => (super.noSuchMethod( Invocation.method( #closeAdapter, [], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); @override - _i5.Future request({ + _i6.Future request({ required String? command, List? args = const [], String? requestID, @@ -157,10 +163,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #requestTimeout: requestTimeout, }, ), - returnValue: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + ) as _i6.Future); @override - _i5.Future> batchRequest({ + _i6.Future> batchRequest({ required String? command, required List? args, Duration? requestTimeout = const Duration(seconds: 60), @@ -177,10 +183,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #retries: retries, }, ), - returnValue: _i5.Future>.value([]), - ) as _i5.Future>); + returnValue: _i6.Future>.value([]), + ) as _i6.Future>); @override - _i5.Future ping({ + _i6.Future ping({ String? requestID, int? retryCount = 1, }) => @@ -193,10 +199,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #retryCount: retryCount, }, ), - returnValue: _i5.Future.value(false), - ) as _i5.Future); + returnValue: _i6.Future.value(false), + ) as _i6.Future); @override - _i5.Future> getBlockHeadTip({String? requestID}) => + _i6.Future> getBlockHeadTip({String? requestID}) => (super.noSuchMethod( Invocation.method( #getBlockHeadTip, @@ -204,10 +210,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { {#requestID: requestID}, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future> getServerFeatures({String? requestID}) => + _i6.Future> getServerFeatures({String? requestID}) => (super.noSuchMethod( Invocation.method( #getServerFeatures, @@ -215,10 +221,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { {#requestID: requestID}, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future broadcastTransaction({ + _i6.Future broadcastTransaction({ required String? rawTx, String? requestID, }) => @@ -231,10 +237,20 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #requestID: requestID, }, ), - returnValue: _i5.Future.value(''), - ) as _i5.Future); + returnValue: _i6.Future.value(_i5.dummyValue( + this, + Invocation.method( + #broadcastTransaction, + [], + { + #rawTx: rawTx, + #requestID: requestID, + }, + ), + )), + ) as _i6.Future); @override - _i5.Future> getBalance({ + _i6.Future> getBalance({ required String? scripthash, String? requestID, }) => @@ -248,10 +264,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future>> getHistory({ + _i6.Future>> getHistory({ required String? scripthash, String? requestID, }) => @@ -264,11 +280,11 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #requestID: requestID, }, ), - returnValue: _i5.Future>>.value( + returnValue: _i6.Future>>.value( >[]), - ) as _i5.Future>>); + ) as _i6.Future>>); @override - _i5.Future>>> getBatchHistory( + _i6.Future>>> getBatchHistory( {required List? args}) => (super.noSuchMethod( Invocation.method( @@ -276,11 +292,11 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { [], {#args: args}, ), - returnValue: _i5.Future>>>.value( + returnValue: _i6.Future>>>.value( >>[]), - ) as _i5.Future>>>); + ) as _i6.Future>>>); @override - _i5.Future>> getUTXOs({ + _i6.Future>> getUTXOs({ required String? scripthash, String? requestID, }) => @@ -293,11 +309,11 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #requestID: requestID, }, ), - returnValue: _i5.Future>>.value( + returnValue: _i6.Future>>.value( >[]), - ) as _i5.Future>>); + ) as _i6.Future>>); @override - _i5.Future>>> getBatchUTXOs( + _i6.Future>>> getBatchUTXOs( {required List? args}) => (super.noSuchMethod( Invocation.method( @@ -305,11 +321,11 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { [], {#args: args}, ), - returnValue: _i5.Future>>>.value( + returnValue: _i6.Future>>>.value( >>[]), - ) as _i5.Future>>>); + ) as _i6.Future>>>); @override - _i5.Future> getTransaction({ + _i6.Future> getTransaction({ required String? txHash, bool? verbose = true, String? requestID, @@ -325,10 +341,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future> getLelantusAnonymitySet({ + _i6.Future> getLelantusAnonymitySet({ String? groupId = r'1', String? blockhash = r'', String? requestID, @@ -344,10 +360,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future getLelantusMintData({ + _i6.Future getLelantusMintData({ dynamic mints, String? requestID, }) => @@ -360,10 +376,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #requestID: requestID, }, ), - returnValue: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + ) as _i6.Future); @override - _i5.Future> getLelantusUsedCoinSerials({ + _i6.Future> getLelantusUsedCoinSerials({ String? requestID, required int? startNumber, }) => @@ -377,20 +393,20 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future getLelantusLatestCoinId({String? requestID}) => + _i6.Future getLelantusLatestCoinId({String? requestID}) => (super.noSuchMethod( Invocation.method( #getLelantusLatestCoinId, [], {#requestID: requestID}, ), - returnValue: _i5.Future.value(0), - ) as _i5.Future); + returnValue: _i6.Future.value(0), + ) as _i6.Future); @override - _i5.Future> getSparkAnonymitySet({ + _i6.Future> getSparkAnonymitySet({ String? coinGroupId = r'1', String? startBlockHash = r'', String? requestID, @@ -406,10 +422,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future> getSparkUsedCoinsTags({ + _i6.Future> getSparkUsedCoinsTags({ String? requestID, required int? startNumber, }) => @@ -422,10 +438,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #startNumber: startNumber, }, ), - returnValue: _i5.Future>.value({}), - ) as _i5.Future>); + returnValue: _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future>> getSparkMintMetaData({ + _i6.Future>> getSparkMintMetaData({ String? requestID, required List? sparkCoinHashes, }) => @@ -438,21 +454,21 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #sparkCoinHashes: sparkCoinHashes, }, ), - returnValue: _i5.Future>>.value( + returnValue: _i6.Future>>.value( >[]), - ) as _i5.Future>>); + ) as _i6.Future>>); @override - _i5.Future getSparkLatestCoinId({String? requestID}) => + _i6.Future getSparkLatestCoinId({String? requestID}) => (super.noSuchMethod( Invocation.method( #getSparkLatestCoinId, [], {#requestID: requestID}, ), - returnValue: _i5.Future.value(0), - ) as _i5.Future); + returnValue: _i6.Future.value(0), + ) as _i6.Future); @override - _i5.Future> getFeeRate({String? requestID}) => + _i6.Future> getFeeRate({String? requestID}) => (super.noSuchMethod( Invocation.method( #getFeeRate, @@ -460,10 +476,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { {#requestID: requestID}, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future<_i3.Decimal> estimateFee({ + _i6.Future<_i3.Decimal> estimateFee({ String? requestID, required int? blocks, }) => @@ -476,7 +492,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #blocks: blocks, }, ), - returnValue: _i5.Future<_i3.Decimal>.value(_FakeDecimal_2( + returnValue: _i6.Future<_i3.Decimal>.value(_FakeDecimal_2( this, Invocation.method( #estimateFee, @@ -487,15 +503,15 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), )), - ) as _i5.Future<_i3.Decimal>); + ) as _i6.Future<_i3.Decimal>); @override - _i5.Future<_i3.Decimal> relayFee({String? requestID}) => (super.noSuchMethod( + _i6.Future<_i3.Decimal> relayFee({String? requestID}) => (super.noSuchMethod( Invocation.method( #relayFee, [], {#requestID: requestID}, ), - returnValue: _i5.Future<_i3.Decimal>.value(_FakeDecimal_2( + returnValue: _i6.Future<_i3.Decimal>.value(_FakeDecimal_2( this, Invocation.method( #relayFee, @@ -503,14 +519,14 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { {#requestID: requestID}, ), )), - ) as _i5.Future<_i3.Decimal>); + ) as _i6.Future<_i3.Decimal>); } /// A class which mocks [CachedElectrumXClient]. /// /// See the documentation for Mockito's code generation for more information. class MockCachedElectrumXClient extends _i1.Mock - implements _i6.CachedElectrumXClient { + implements _i7.CachedElectrumXClient { MockCachedElectrumXClient() { _i1.throwOnMissingStub(this); } @@ -524,7 +540,7 @@ class MockCachedElectrumXClient extends _i1.Mock ), ) as _i4.ElectrumXClient); @override - _i5.Future> getAnonymitySet({ + _i6.Future> getAnonymitySet({ required String? groupId, String? blockhash = r'', required _i2.CryptoCurrency? cryptoCurrency, @@ -540,10 +556,10 @@ class MockCachedElectrumXClient extends _i1.Mock }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future> getSparkAnonymitySet({ + _i6.Future> getSparkAnonymitySet({ required String? groupId, String? blockhash = r'', required _i2.CryptoCurrency? cryptoCurrency, @@ -561,15 +577,21 @@ class MockCachedElectrumXClient extends _i1.Mock }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override String base64ToHex(String? source) => (super.noSuchMethod( Invocation.method( #base64ToHex, [source], ), - returnValue: '', + returnValue: _i5.dummyValue( + this, + Invocation.method( + #base64ToHex, + [source], + ), + ), ) as String); @override String base64ToReverseHex(String? source) => (super.noSuchMethod( @@ -577,10 +599,16 @@ class MockCachedElectrumXClient extends _i1.Mock #base64ToReverseHex, [source], ), - returnValue: '', + returnValue: _i5.dummyValue( + this, + Invocation.method( + #base64ToReverseHex, + [source], + ), + ), ) as String); @override - _i5.Future> getTransaction({ + _i6.Future> getTransaction({ required String? txHash, required _i2.CryptoCurrency? cryptoCurrency, bool? verbose = true, @@ -596,10 +624,10 @@ class MockCachedElectrumXClient extends _i1.Mock }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future> getUsedCoinSerials({ + _i6.Future> getUsedCoinSerials({ required _i2.CryptoCurrency? cryptoCurrency, int? startNumber = 0, }) => @@ -612,10 +640,10 @@ class MockCachedElectrumXClient extends _i1.Mock #startNumber: startNumber, }, ), - returnValue: _i5.Future>.value([]), - ) as _i5.Future>); + returnValue: _i6.Future>.value([]), + ) as _i6.Future>); @override - _i5.Future> getSparkUsedCoinsTags( + _i6.Future> getSparkUsedCoinsTags( {required _i2.CryptoCurrency? cryptoCurrency}) => (super.noSuchMethod( Invocation.method( @@ -623,10 +651,10 @@ class MockCachedElectrumXClient extends _i1.Mock [], {#cryptoCurrency: cryptoCurrency}, ), - returnValue: _i5.Future>.value({}), - ) as _i5.Future>); + returnValue: _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future clearSharedTransactionCache( + _i6.Future clearSharedTransactionCache( {required _i2.CryptoCurrency? cryptoCurrency}) => (super.noSuchMethod( Invocation.method( @@ -634,16 +662,16 @@ class MockCachedElectrumXClient extends _i1.Mock [], {#cryptoCurrency: cryptoCurrency}, ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); } /// A class which mocks [TransactionNotificationTracker]. /// /// See the documentation for Mockito's code generation for more information. class MockTransactionNotificationTracker extends _i1.Mock - implements _i7.TransactionNotificationTracker { + implements _i8.TransactionNotificationTracker { MockTransactionNotificationTracker() { _i1.throwOnMissingStub(this); } @@ -651,7 +679,10 @@ class MockTransactionNotificationTracker extends _i1.Mock @override String get walletId => (super.noSuchMethod( Invocation.getter(#walletId), - returnValue: '', + returnValue: _i5.dummyValue( + this, + Invocation.getter(#walletId), + ), ) as String); @override List get pendings => (super.noSuchMethod( @@ -672,14 +703,14 @@ class MockTransactionNotificationTracker extends _i1.Mock returnValue: false, ) as bool); @override - _i5.Future addNotifiedPending(String? txid) => (super.noSuchMethod( + _i6.Future addNotifiedPending(String? txid) => (super.noSuchMethod( Invocation.method( #addNotifiedPending, [txid], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); @override bool wasNotifiedConfirmed(String? txid) => (super.noSuchMethod( Invocation.method( @@ -689,21 +720,21 @@ class MockTransactionNotificationTracker extends _i1.Mock returnValue: false, ) as bool); @override - _i5.Future addNotifiedConfirmed(String? txid) => (super.noSuchMethod( + _i6.Future addNotifiedConfirmed(String? txid) => (super.noSuchMethod( Invocation.method( #addNotifiedConfirmed, [txid], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); @override - _i5.Future deleteTransaction(String? txid) => (super.noSuchMethod( + _i6.Future deleteTransaction(String? txid) => (super.noSuchMethod( Invocation.method( #deleteTransaction, [txid], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); } diff --git a/test/services/coins/particl/particl_wallet_test.mocks.dart b/test/services/coins/particl/particl_wallet_test.mocks.dart index aac19401c..5c6f92792 100644 --- a/test/services/coins/particl/particl_wallet_test.mocks.dart +++ b/test/services/coins/particl/particl_wallet_test.mocks.dart @@ -1,16 +1,17 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/services/coins/particl/particl_wallet_test.dart. // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i5; +import 'dart:async' as _i6; import 'package:decimal/decimal.dart' as _i3; import 'package:mockito/mockito.dart' as _i1; -import 'package:stackwallet/electrumx_rpc/cached_electrumx_client.dart' as _i6; +import 'package:mockito/src/dummies.dart' as _i5; +import 'package:stackwallet/electrumx_rpc/cached_electrumx_client.dart' as _i7; import 'package:stackwallet/electrumx_rpc/electrumx_client.dart' as _i4; import 'package:stackwallet/services/transaction_notification_tracker.dart' - as _i7; + as _i8; import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart' as _i2; @@ -18,6 +19,8 @@ import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart' // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -116,7 +119,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { @override String get host => (super.noSuchMethod( Invocation.getter(#host), - returnValue: '', + returnValue: _i5.dummyValue( + this, + Invocation.getter(#host), + ), ) as String); @override int get port => (super.noSuchMethod( @@ -129,16 +135,16 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: false, ) as bool); @override - _i5.Future closeAdapter() => (super.noSuchMethod( + _i6.Future closeAdapter() => (super.noSuchMethod( Invocation.method( #closeAdapter, [], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); @override - _i5.Future request({ + _i6.Future request({ required String? command, List? args = const [], String? requestID, @@ -157,10 +163,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #requestTimeout: requestTimeout, }, ), - returnValue: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + ) as _i6.Future); @override - _i5.Future> batchRequest({ + _i6.Future> batchRequest({ required String? command, required List? args, Duration? requestTimeout = const Duration(seconds: 60), @@ -177,10 +183,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #retries: retries, }, ), - returnValue: _i5.Future>.value([]), - ) as _i5.Future>); + returnValue: _i6.Future>.value([]), + ) as _i6.Future>); @override - _i5.Future ping({ + _i6.Future ping({ String? requestID, int? retryCount = 1, }) => @@ -193,10 +199,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #retryCount: retryCount, }, ), - returnValue: _i5.Future.value(false), - ) as _i5.Future); + returnValue: _i6.Future.value(false), + ) as _i6.Future); @override - _i5.Future> getBlockHeadTip({String? requestID}) => + _i6.Future> getBlockHeadTip({String? requestID}) => (super.noSuchMethod( Invocation.method( #getBlockHeadTip, @@ -204,10 +210,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { {#requestID: requestID}, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future> getServerFeatures({String? requestID}) => + _i6.Future> getServerFeatures({String? requestID}) => (super.noSuchMethod( Invocation.method( #getServerFeatures, @@ -215,10 +221,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { {#requestID: requestID}, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future broadcastTransaction({ + _i6.Future broadcastTransaction({ required String? rawTx, String? requestID, }) => @@ -231,10 +237,20 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #requestID: requestID, }, ), - returnValue: _i5.Future.value(''), - ) as _i5.Future); + returnValue: _i6.Future.value(_i5.dummyValue( + this, + Invocation.method( + #broadcastTransaction, + [], + { + #rawTx: rawTx, + #requestID: requestID, + }, + ), + )), + ) as _i6.Future); @override - _i5.Future> getBalance({ + _i6.Future> getBalance({ required String? scripthash, String? requestID, }) => @@ -248,10 +264,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future>> getHistory({ + _i6.Future>> getHistory({ required String? scripthash, String? requestID, }) => @@ -264,11 +280,11 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #requestID: requestID, }, ), - returnValue: _i5.Future>>.value( + returnValue: _i6.Future>>.value( >[]), - ) as _i5.Future>>); + ) as _i6.Future>>); @override - _i5.Future>>> getBatchHistory( + _i6.Future>>> getBatchHistory( {required List? args}) => (super.noSuchMethod( Invocation.method( @@ -276,11 +292,11 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { [], {#args: args}, ), - returnValue: _i5.Future>>>.value( + returnValue: _i6.Future>>>.value( >>[]), - ) as _i5.Future>>>); + ) as _i6.Future>>>); @override - _i5.Future>> getUTXOs({ + _i6.Future>> getUTXOs({ required String? scripthash, String? requestID, }) => @@ -293,11 +309,11 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #requestID: requestID, }, ), - returnValue: _i5.Future>>.value( + returnValue: _i6.Future>>.value( >[]), - ) as _i5.Future>>); + ) as _i6.Future>>); @override - _i5.Future>>> getBatchUTXOs( + _i6.Future>>> getBatchUTXOs( {required List? args}) => (super.noSuchMethod( Invocation.method( @@ -305,11 +321,11 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { [], {#args: args}, ), - returnValue: _i5.Future>>>.value( + returnValue: _i6.Future>>>.value( >>[]), - ) as _i5.Future>>>); + ) as _i6.Future>>>); @override - _i5.Future> getTransaction({ + _i6.Future> getTransaction({ required String? txHash, bool? verbose = true, String? requestID, @@ -325,10 +341,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future> getLelantusAnonymitySet({ + _i6.Future> getLelantusAnonymitySet({ String? groupId = r'1', String? blockhash = r'', String? requestID, @@ -344,10 +360,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future getLelantusMintData({ + _i6.Future getLelantusMintData({ dynamic mints, String? requestID, }) => @@ -360,10 +376,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #requestID: requestID, }, ), - returnValue: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + ) as _i6.Future); @override - _i5.Future> getLelantusUsedCoinSerials({ + _i6.Future> getLelantusUsedCoinSerials({ String? requestID, required int? startNumber, }) => @@ -377,20 +393,20 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future getLelantusLatestCoinId({String? requestID}) => + _i6.Future getLelantusLatestCoinId({String? requestID}) => (super.noSuchMethod( Invocation.method( #getLelantusLatestCoinId, [], {#requestID: requestID}, ), - returnValue: _i5.Future.value(0), - ) as _i5.Future); + returnValue: _i6.Future.value(0), + ) as _i6.Future); @override - _i5.Future> getSparkAnonymitySet({ + _i6.Future> getSparkAnonymitySet({ String? coinGroupId = r'1', String? startBlockHash = r'', String? requestID, @@ -406,10 +422,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future> getSparkUsedCoinsTags({ + _i6.Future> getSparkUsedCoinsTags({ String? requestID, required int? startNumber, }) => @@ -422,10 +438,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #startNumber: startNumber, }, ), - returnValue: _i5.Future>.value({}), - ) as _i5.Future>); + returnValue: _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future>> getSparkMintMetaData({ + _i6.Future>> getSparkMintMetaData({ String? requestID, required List? sparkCoinHashes, }) => @@ -438,21 +454,21 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #sparkCoinHashes: sparkCoinHashes, }, ), - returnValue: _i5.Future>>.value( + returnValue: _i6.Future>>.value( >[]), - ) as _i5.Future>>); + ) as _i6.Future>>); @override - _i5.Future getSparkLatestCoinId({String? requestID}) => + _i6.Future getSparkLatestCoinId({String? requestID}) => (super.noSuchMethod( Invocation.method( #getSparkLatestCoinId, [], {#requestID: requestID}, ), - returnValue: _i5.Future.value(0), - ) as _i5.Future); + returnValue: _i6.Future.value(0), + ) as _i6.Future); @override - _i5.Future> getFeeRate({String? requestID}) => + _i6.Future> getFeeRate({String? requestID}) => (super.noSuchMethod( Invocation.method( #getFeeRate, @@ -460,10 +476,10 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { {#requestID: requestID}, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future<_i3.Decimal> estimateFee({ + _i6.Future<_i3.Decimal> estimateFee({ String? requestID, required int? blocks, }) => @@ -476,7 +492,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #blocks: blocks, }, ), - returnValue: _i5.Future<_i3.Decimal>.value(_FakeDecimal_2( + returnValue: _i6.Future<_i3.Decimal>.value(_FakeDecimal_2( this, Invocation.method( #estimateFee, @@ -487,15 +503,15 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { }, ), )), - ) as _i5.Future<_i3.Decimal>); + ) as _i6.Future<_i3.Decimal>); @override - _i5.Future<_i3.Decimal> relayFee({String? requestID}) => (super.noSuchMethod( + _i6.Future<_i3.Decimal> relayFee({String? requestID}) => (super.noSuchMethod( Invocation.method( #relayFee, [], {#requestID: requestID}, ), - returnValue: _i5.Future<_i3.Decimal>.value(_FakeDecimal_2( + returnValue: _i6.Future<_i3.Decimal>.value(_FakeDecimal_2( this, Invocation.method( #relayFee, @@ -503,14 +519,14 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { {#requestID: requestID}, ), )), - ) as _i5.Future<_i3.Decimal>); + ) as _i6.Future<_i3.Decimal>); } /// A class which mocks [CachedElectrumXClient]. /// /// See the documentation for Mockito's code generation for more information. class MockCachedElectrumXClient extends _i1.Mock - implements _i6.CachedElectrumXClient { + implements _i7.CachedElectrumXClient { MockCachedElectrumXClient() { _i1.throwOnMissingStub(this); } @@ -524,7 +540,7 @@ class MockCachedElectrumXClient extends _i1.Mock ), ) as _i4.ElectrumXClient); @override - _i5.Future> getAnonymitySet({ + _i6.Future> getAnonymitySet({ required String? groupId, String? blockhash = r'', required _i2.CryptoCurrency? cryptoCurrency, @@ -540,10 +556,10 @@ class MockCachedElectrumXClient extends _i1.Mock }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future> getSparkAnonymitySet({ + _i6.Future> getSparkAnonymitySet({ required String? groupId, String? blockhash = r'', required _i2.CryptoCurrency? cryptoCurrency, @@ -561,15 +577,21 @@ class MockCachedElectrumXClient extends _i1.Mock }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override String base64ToHex(String? source) => (super.noSuchMethod( Invocation.method( #base64ToHex, [source], ), - returnValue: '', + returnValue: _i5.dummyValue( + this, + Invocation.method( + #base64ToHex, + [source], + ), + ), ) as String); @override String base64ToReverseHex(String? source) => (super.noSuchMethod( @@ -577,10 +599,16 @@ class MockCachedElectrumXClient extends _i1.Mock #base64ToReverseHex, [source], ), - returnValue: '', + returnValue: _i5.dummyValue( + this, + Invocation.method( + #base64ToReverseHex, + [source], + ), + ), ) as String); @override - _i5.Future> getTransaction({ + _i6.Future> getTransaction({ required String? txHash, required _i2.CryptoCurrency? cryptoCurrency, bool? verbose = true, @@ -596,10 +624,10 @@ class MockCachedElectrumXClient extends _i1.Mock }, ), returnValue: - _i5.Future>.value({}), - ) as _i5.Future>); + _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future> getUsedCoinSerials({ + _i6.Future> getUsedCoinSerials({ required _i2.CryptoCurrency? cryptoCurrency, int? startNumber = 0, }) => @@ -612,10 +640,10 @@ class MockCachedElectrumXClient extends _i1.Mock #startNumber: startNumber, }, ), - returnValue: _i5.Future>.value([]), - ) as _i5.Future>); + returnValue: _i6.Future>.value([]), + ) as _i6.Future>); @override - _i5.Future> getSparkUsedCoinsTags( + _i6.Future> getSparkUsedCoinsTags( {required _i2.CryptoCurrency? cryptoCurrency}) => (super.noSuchMethod( Invocation.method( @@ -623,10 +651,10 @@ class MockCachedElectrumXClient extends _i1.Mock [], {#cryptoCurrency: cryptoCurrency}, ), - returnValue: _i5.Future>.value({}), - ) as _i5.Future>); + returnValue: _i6.Future>.value({}), + ) as _i6.Future>); @override - _i5.Future clearSharedTransactionCache( + _i6.Future clearSharedTransactionCache( {required _i2.CryptoCurrency? cryptoCurrency}) => (super.noSuchMethod( Invocation.method( @@ -634,16 +662,16 @@ class MockCachedElectrumXClient extends _i1.Mock [], {#cryptoCurrency: cryptoCurrency}, ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); } /// A class which mocks [TransactionNotificationTracker]. /// /// See the documentation for Mockito's code generation for more information. class MockTransactionNotificationTracker extends _i1.Mock - implements _i7.TransactionNotificationTracker { + implements _i8.TransactionNotificationTracker { MockTransactionNotificationTracker() { _i1.throwOnMissingStub(this); } @@ -651,7 +679,10 @@ class MockTransactionNotificationTracker extends _i1.Mock @override String get walletId => (super.noSuchMethod( Invocation.getter(#walletId), - returnValue: '', + returnValue: _i5.dummyValue( + this, + Invocation.getter(#walletId), + ), ) as String); @override List get pendings => (super.noSuchMethod( @@ -672,14 +703,14 @@ class MockTransactionNotificationTracker extends _i1.Mock returnValue: false, ) as bool); @override - _i5.Future addNotifiedPending(String? txid) => (super.noSuchMethod( + _i6.Future addNotifiedPending(String? txid) => (super.noSuchMethod( Invocation.method( #addNotifiedPending, [txid], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); @override bool wasNotifiedConfirmed(String? txid) => (super.noSuchMethod( Invocation.method( @@ -689,21 +720,21 @@ class MockTransactionNotificationTracker extends _i1.Mock returnValue: false, ) as bool); @override - _i5.Future addNotifiedConfirmed(String? txid) => (super.noSuchMethod( + _i6.Future addNotifiedConfirmed(String? txid) => (super.noSuchMethod( Invocation.method( #addNotifiedConfirmed, [txid], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); @override - _i5.Future deleteTransaction(String? txid) => (super.noSuchMethod( + _i6.Future deleteTransaction(String? txid) => (super.noSuchMethod( Invocation.method( #deleteTransaction, [txid], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); } diff --git a/test/utilities/amount/amount_unit_test.dart b/test/utilities/amount/amount_unit_test.dart index e4ecb33dd..96a708702 100644 --- a/test/utilities/amount/amount_unit_test.dart +++ b/test/utilities/amount/amount_unit_test.dart @@ -2,8 +2,6 @@ import 'package:decimal/decimal.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; import 'package:stackwallet/utilities/amount/amount_unit.dart'; -import 'package:stackwallet/wallets/crypto_currency/coins/bitcoin.dart'; -import 'package:stackwallet/wallets/crypto_currency/coins/ethereum.dart'; import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart'; void main() { diff --git a/test/widget_tests/address_book_card_test.dart b/test/widget_tests/address_book_card_test.dart index 674a259af..36199554f 100644 --- a/test/widget_tests/address_book_card_test.dart +++ b/test/widget_tests/address_book_card_test.dart @@ -12,7 +12,6 @@ import 'package:stackwallet/providers/global/address_book_service_provider.dart' import 'package:stackwallet/services/address_book_service.dart'; import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/utilities/util.dart'; -import 'package:stackwallet/wallets/crypto_currency/coins/bitcoincash.dart'; import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart'; import 'package:stackwallet/widgets/address_book_card.dart'; diff --git a/test/widget_tests/address_book_card_test.mocks.dart b/test/widget_tests/address_book_card_test.mocks.dart index b24ff6ed7..cfb056577 100644 --- a/test/widget_tests/address_book_card_test.mocks.dart +++ b/test/widget_tests/address_book_card_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/widget_tests/address_book_card_test.dart. // Do not manually edit this file. @@ -14,6 +14,8 @@ import 'package:stackwallet/services/address_book_service.dart' as _i3; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors diff --git a/test/widget_tests/custom_buttons/favorite_toggle_test.mocks.dart b/test/widget_tests/custom_buttons/favorite_toggle_test.mocks.dart index 20727e854..9463dccb8 100644 --- a/test/widget_tests/custom_buttons/favorite_toggle_test.mocks.dart +++ b/test/widget_tests/custom_buttons/favorite_toggle_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/widget_tests/custom_buttons/favorite_toggle_test.dart. // Do not manually edit this file. @@ -16,6 +16,8 @@ import 'package:stackwallet/themes/theme_service.dart' as _i4; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors diff --git a/test/widget_tests/custom_loading_overlay_test.mocks.dart b/test/widget_tests/custom_loading_overlay_test.mocks.dart index 142052fa2..6848da7ee 100644 --- a/test/widget_tests/custom_loading_overlay_test.mocks.dart +++ b/test/widget_tests/custom_loading_overlay_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/widget_tests/custom_loading_overlay_test.dart. // Do not manually edit this file. @@ -16,6 +16,8 @@ import 'package:stackwallet/themes/theme_service.dart' as _i4; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors diff --git a/test/widget_tests/custom_pin_put_test.dart b/test/widget_tests/custom_pin_put_test.dart index 1b3c1eee3..473731639 100644 --- a/test/widget_tests/custom_pin_put_test.dart +++ b/test/widget_tests/custom_pin_put_test.dart @@ -32,7 +32,7 @@ class PinWidgetState extends State { @override Widget build(BuildContext context) { - bool submittedPinMatches = false; + final bool submittedPinMatches = false; return CustomPinPut( fieldsCount: pinCount, diff --git a/test/widget_tests/desktop/desktop_scaffold_test.mocks.dart b/test/widget_tests/desktop/desktop_scaffold_test.mocks.dart index 8507ace66..046da5ebf 100644 --- a/test/widget_tests/desktop/desktop_scaffold_test.mocks.dart +++ b/test/widget_tests/desktop/desktop_scaffold_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/widget_tests/desktop/desktop_scaffold_test.dart. // Do not manually edit this file. @@ -16,6 +16,8 @@ import 'package:stackwallet/themes/theme_service.dart' as _i4; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors diff --git a/test/widget_tests/managed_favorite_test.dart b/test/widget_tests/managed_favorite_test.dart index 0d60b87c9..5298f4498 100644 --- a/test/widget_tests/managed_favorite_test.dart +++ b/test/widget_tests/managed_favorite_test.dart @@ -14,14 +14,17 @@ Amount _a(int i) => Amount.fromDecimal( fractionDigits: 8, ); -@GenerateMocks([ - Wallets, - ThemeService, - Prefs, - LocaleService -], customMocks: [ - MockSpec(returnNullOnMissingStub: true), -]) +@GenerateMocks( + [ + Wallets, + ThemeService, + Prefs, + LocaleService, + ], + customMocks: [ + MockSpec(), + ], +) void main() { // testWidgets("Test wallet info row displays correctly", (widgetTester) async { // final wallets = MockWallets(); diff --git a/test/widget_tests/managed_favorite_test.mocks.dart b/test/widget_tests/managed_favorite_test.mocks.dart index ce01550b6..dad98a259 100644 --- a/test/widget_tests/managed_favorite_test.mocks.dart +++ b/test/widget_tests/managed_favorite_test.mocks.dart @@ -1,23 +1,24 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/widget_tests/managed_favorite_test.dart. // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i10; import 'dart:typed_data' as _i15; -import 'dart:ui' as _i19; +import 'dart:ui' as _i20; import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i17; import 'package:stackwallet/db/isar/main_db.dart' as _i3; import 'package:stackwallet/models/isar/stack_theme.dart' as _i14; -import 'package:stackwallet/models/node_model.dart' as _i21; +import 'package:stackwallet/models/node_model.dart' as _i22; import 'package:stackwallet/networking/http.dart' as _i6; -import 'package:stackwallet/services/locale_service.dart' as _i20; +import 'package:stackwallet/services/locale_service.dart' as _i21; import 'package:stackwallet/services/node_service.dart' as _i2; import 'package:stackwallet/services/wallets.dart' as _i9; import 'package:stackwallet/themes/theme_service.dart' as _i13; -import 'package:stackwallet/utilities/amount/amount_unit.dart' as _i18; -import 'package:stackwallet/utilities/enums/backup_frequency_type.dart' as _i17; +import 'package:stackwallet/utilities/amount/amount_unit.dart' as _i19; +import 'package:stackwallet/utilities/enums/backup_frequency_type.dart' as _i18; import 'package:stackwallet/utilities/enums/sync_type_enum.dart' as _i16; import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart' as _i8; @@ -33,6 +34,8 @@ import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/cash_fusion_i // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -437,7 +440,10 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { @override String get language => (super.noSuchMethod( Invocation.getter(#language), - returnValue: '', + returnValue: _i17.dummyValue( + this, + Invocation.getter(#language), + ), ) as String); @override set language(String? newLanguage) => super.noSuchMethod( @@ -450,7 +456,10 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { @override String get currency => (super.noSuchMethod( Invocation.getter(#currency), - returnValue: '', + returnValue: _i17.dummyValue( + this, + Invocation.getter(#currency), + ), ) as String); @override set currency(String? newCurrency) => super.noSuchMethod( @@ -560,12 +569,12 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { returnValueForMissingStub: null, ); @override - _i17.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod( + _i18.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod( Invocation.getter(#backupFrequencyType), - returnValue: _i17.BackupFrequencyType.everyTenMinutes, - ) as _i17.BackupFrequencyType); + returnValue: _i18.BackupFrequencyType.everyTenMinutes, + ) as _i18.BackupFrequencyType); @override - set backupFrequencyType(_i17.BackupFrequencyType? backupFrequencyType) => + set backupFrequencyType(_i18.BackupFrequencyType? backupFrequencyType) => super.noSuchMethod( Invocation.setter( #backupFrequencyType, @@ -659,7 +668,10 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { @override String get themeId => (super.noSuchMethod( Invocation.getter(#themeId), - returnValue: '', + returnValue: _i17.dummyValue( + this, + Invocation.getter(#themeId), + ), ) as String); @override set themeId(String? themeId) => super.noSuchMethod( @@ -672,7 +684,10 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { @override String get systemBrightnessLightThemeId => (super.noSuchMethod( Invocation.getter(#systemBrightnessLightThemeId), - returnValue: '', + returnValue: _i17.dummyValue( + this, + Invocation.getter(#systemBrightnessLightThemeId), + ), ) as String); @override set systemBrightnessLightThemeId(String? systemBrightnessLightThemeId) => @@ -686,7 +701,10 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { @override String get systemBrightnessDarkThemeId => (super.noSuchMethod( Invocation.getter(#systemBrightnessDarkThemeId), - returnValue: '', + returnValue: _i17.dummyValue( + this, + Invocation.getter(#systemBrightnessDarkThemeId), + ), ) as String); @override set systemBrightnessDarkThemeId(String? systemBrightnessDarkThemeId) => @@ -760,17 +778,17 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); @override - _i18.AmountUnit amountUnit(_i4.CryptoCurrency? coin) => (super.noSuchMethod( + _i19.AmountUnit amountUnit(_i4.CryptoCurrency? coin) => (super.noSuchMethod( Invocation.method( #amountUnit, [coin], ), - returnValue: _i18.AmountUnit.normal, - ) as _i18.AmountUnit); + returnValue: _i19.AmountUnit.normal, + ) as _i19.AmountUnit); @override void updateAmountUnit({ required _i4.CryptoCurrency? coin, - required _i18.AmountUnit? amountUnit, + required _i19.AmountUnit? amountUnit, }) => super.noSuchMethod( Invocation.method( @@ -838,7 +856,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { returnValueForMissingStub: null, ); @override - void addListener(_i19.VoidCallback? listener) => super.noSuchMethod( + void addListener(_i20.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #addListener, [listener], @@ -846,7 +864,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { returnValueForMissingStub: null, ); @override - void removeListener(_i19.VoidCallback? listener) => super.noSuchMethod( + void removeListener(_i20.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #removeListener, [listener], @@ -874,7 +892,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { /// A class which mocks [LocaleService]. /// /// See the documentation for Mockito's code generation for more information. -class MockLocaleService extends _i1.Mock implements _i20.LocaleService { +class MockLocaleService extends _i1.Mock implements _i21.LocaleService { MockLocaleService() { _i1.throwOnMissingStub(this); } @@ -882,7 +900,10 @@ class MockLocaleService extends _i1.Mock implements _i20.LocaleService { @override String get locale => (super.noSuchMethod( Invocation.getter(#locale), - returnValue: '', + returnValue: _i17.dummyValue( + this, + Invocation.getter(#locale), + ), ) as String); @override bool get hasListeners => (super.noSuchMethod( @@ -900,7 +921,7 @@ class MockLocaleService extends _i1.Mock implements _i20.LocaleService { returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); @override - void addListener(_i19.VoidCallback? listener) => super.noSuchMethod( + void addListener(_i20.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #addListener, [listener], @@ -908,7 +929,7 @@ class MockLocaleService extends _i1.Mock implements _i20.LocaleService { returnValueForMissingStub: null, ); @override - void removeListener(_i19.VoidCallback? listener) => super.noSuchMethod( + void removeListener(_i20.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #removeListener, [listener], @@ -937,6 +958,10 @@ class MockLocaleService extends _i1.Mock implements _i20.LocaleService { /// /// See the documentation for Mockito's code generation for more information. class MockNodeService extends _i1.Mock implements _i2.NodeService { + MockNodeService() { + _i1.throwOnMissingStub(this); + } + @override _i8.SecureStorageInterface get secureStorageInterface => (super.noSuchMethod( Invocation.getter(#secureStorageInterface), @@ -946,15 +971,15 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), ) as _i8.SecureStorageInterface); @override - List<_i21.NodeModel> get primaryNodes => (super.noSuchMethod( + List<_i22.NodeModel> get primaryNodes => (super.noSuchMethod( Invocation.getter(#primaryNodes), - returnValue: <_i21.NodeModel>[], - ) as List<_i21.NodeModel>); + returnValue: <_i22.NodeModel>[], + ) as List<_i22.NodeModel>); @override - List<_i21.NodeModel> get nodes => (super.noSuchMethod( + List<_i22.NodeModel> get nodes => (super.noSuchMethod( Invocation.getter(#nodes), - returnValue: <_i21.NodeModel>[], - ) as List<_i21.NodeModel>); + returnValue: <_i22.NodeModel>[], + ) as List<_i22.NodeModel>); @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), @@ -972,7 +997,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { @override _i10.Future setPrimaryNodeFor({ required _i4.CryptoCurrency? coin, - required _i21.NodeModel? node, + required _i22.NodeModel? node, bool? shouldNotifyListeners = false, }) => (super.noSuchMethod( @@ -989,30 +1014,30 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); @override - _i21.NodeModel? getPrimaryNodeFor({required _i4.CryptoCurrency? currency}) => + _i22.NodeModel? getPrimaryNodeFor({required _i4.CryptoCurrency? currency}) => (super.noSuchMethod(Invocation.method( #getPrimaryNodeFor, [], {#currency: currency}, - )) as _i21.NodeModel?); + )) as _i22.NodeModel?); @override - List<_i21.NodeModel> getNodesFor(_i4.CryptoCurrency? coin) => + List<_i22.NodeModel> getNodesFor(_i4.CryptoCurrency? coin) => (super.noSuchMethod( Invocation.method( #getNodesFor, [coin], ), - returnValue: <_i21.NodeModel>[], - ) as List<_i21.NodeModel>); + returnValue: <_i22.NodeModel>[], + ) as List<_i22.NodeModel>); @override - _i21.NodeModel? getNodeById({required String? id}) => + _i22.NodeModel? getNodeById({required String? id}) => (super.noSuchMethod(Invocation.method( #getNodeById, [], {#id: id}, - )) as _i21.NodeModel?); + )) as _i22.NodeModel?); @override - List<_i21.NodeModel> failoverNodesFor( + List<_i22.NodeModel> failoverNodesFor( {required _i4.CryptoCurrency? currency}) => (super.noSuchMethod( Invocation.method( @@ -1020,11 +1045,11 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { [], {#currency: currency}, ), - returnValue: <_i21.NodeModel>[], - ) as List<_i21.NodeModel>); + returnValue: <_i22.NodeModel>[], + ) as List<_i22.NodeModel>); @override _i10.Future add( - _i21.NodeModel? node, + _i22.NodeModel? node, String? password, bool? shouldNotifyListeners, ) => @@ -1076,7 +1101,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ) as _i10.Future); @override _i10.Future edit( - _i21.NodeModel? editedNode, + _i22.NodeModel? editedNode, String? password, bool? shouldNotifyListeners, ) => @@ -1102,7 +1127,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); @override - void addListener(_i19.VoidCallback? listener) => super.noSuchMethod( + void addListener(_i20.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #addListener, [listener], @@ -1110,7 +1135,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValueForMissingStub: null, ); @override - void removeListener(_i19.VoidCallback? listener) => super.noSuchMethod( + void removeListener(_i20.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #removeListener, [listener], diff --git a/test/widget_tests/node_card_test.dart b/test/widget_tests/node_card_test.dart index f98814b58..45c0dbe12 100644 --- a/test/widget_tests/node_card_test.dart +++ b/test/widget_tests/node_card_test.dart @@ -10,7 +10,6 @@ import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/services/node_service.dart'; import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/utilities/util.dart'; -import 'package:stackwallet/wallets/crypto_currency/coins/bitcoin.dart'; import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart'; import 'package:stackwallet/widgets/node_card.dart'; import 'package:stackwallet/widgets/node_options_sheet.dart'; diff --git a/test/widget_tests/node_card_test.mocks.dart b/test/widget_tests/node_card_test.mocks.dart index ec802d4e8..c34b5b77f 100644 --- a/test/widget_tests/node_card_test.mocks.dart +++ b/test/widget_tests/node_card_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/widget_tests/node_card_test.dart. // Do not manually edit this file. @@ -18,6 +18,8 @@ import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart' // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors diff --git a/test/widget_tests/node_options_sheet_test.dart b/test/widget_tests/node_options_sheet_test.dart index 94121acf2..c289b29b5 100644 --- a/test/widget_tests/node_options_sheet_test.dart +++ b/test/widget_tests/node_options_sheet_test.dart @@ -12,7 +12,6 @@ import 'package:stackwallet/services/tor_service.dart'; import 'package:stackwallet/services/wallets.dart'; import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/utilities/prefs.dart'; -import 'package:stackwallet/wallets/crypto_currency/coins/bitcoin.dart'; import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart'; import 'package:stackwallet/widgets/node_options_sheet.dart'; @@ -166,7 +165,7 @@ void main() { await tester.tap(find.text("Details")); await tester.pumpAndSettle(); - var currentRoute = navigatorKey.currentState?.overlay?.context; + final currentRoute = navigatorKey.currentState?.overlay?.context; expect(currentRoute, isNotNull); }); diff --git a/test/widget_tests/node_options_sheet_test.mocks.dart b/test/widget_tests/node_options_sheet_test.mocks.dart index ff15b6330..59450b63e 100644 --- a/test/widget_tests/node_options_sheet_test.mocks.dart +++ b/test/widget_tests/node_options_sheet_test.mocks.dart @@ -1,22 +1,23 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/widget_tests/node_options_sheet_test.dart. // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i10; import 'dart:io' as _i8; -import 'dart:ui' as _i16; +import 'dart:ui' as _i17; import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i14; import 'package:stackwallet/db/isar/main_db.dart' as _i3; -import 'package:stackwallet/models/node_model.dart' as _i17; +import 'package:stackwallet/models/node_model.dart' as _i18; import 'package:stackwallet/services/event_bus/events/global/tor_connection_status_changed_event.dart' - as _i19; + as _i20; import 'package:stackwallet/services/node_service.dart' as _i2; -import 'package:stackwallet/services/tor_service.dart' as _i18; +import 'package:stackwallet/services/tor_service.dart' as _i19; import 'package:stackwallet/services/wallets.dart' as _i9; -import 'package:stackwallet/utilities/amount/amount_unit.dart' as _i15; -import 'package:stackwallet/utilities/enums/backup_frequency_type.dart' as _i14; +import 'package:stackwallet/utilities/amount/amount_unit.dart' as _i16; +import 'package:stackwallet/utilities/enums/backup_frequency_type.dart' as _i15; import 'package:stackwallet/utilities/enums/sync_type_enum.dart' as _i13; import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart' as _i7; @@ -27,12 +28,14 @@ import 'package:stackwallet/wallets/isar/models/wallet_info.dart' as _i11; import 'package:stackwallet/wallets/wallet/wallet.dart' as _i5; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart' as _i6; -import 'package:tor_ffi_plugin/tor_ffi_plugin.dart' as _i20; +import 'package:tor_ffi_plugin/tor_ffi_plugin.dart' as _i21; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -323,7 +326,10 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { @override String get language => (super.noSuchMethod( Invocation.getter(#language), - returnValue: '', + returnValue: _i14.dummyValue( + this, + Invocation.getter(#language), + ), ) as String); @override set language(String? newLanguage) => super.noSuchMethod( @@ -336,7 +342,10 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { @override String get currency => (super.noSuchMethod( Invocation.getter(#currency), - returnValue: '', + returnValue: _i14.dummyValue( + this, + Invocation.getter(#currency), + ), ) as String); @override set currency(String? newCurrency) => super.noSuchMethod( @@ -446,12 +455,12 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { returnValueForMissingStub: null, ); @override - _i14.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod( + _i15.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod( Invocation.getter(#backupFrequencyType), - returnValue: _i14.BackupFrequencyType.everyTenMinutes, - ) as _i14.BackupFrequencyType); + returnValue: _i15.BackupFrequencyType.everyTenMinutes, + ) as _i15.BackupFrequencyType); @override - set backupFrequencyType(_i14.BackupFrequencyType? backupFrequencyType) => + set backupFrequencyType(_i15.BackupFrequencyType? backupFrequencyType) => super.noSuchMethod( Invocation.setter( #backupFrequencyType, @@ -545,7 +554,10 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { @override String get themeId => (super.noSuchMethod( Invocation.getter(#themeId), - returnValue: '', + returnValue: _i14.dummyValue( + this, + Invocation.getter(#themeId), + ), ) as String); @override set themeId(String? themeId) => super.noSuchMethod( @@ -558,7 +570,10 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { @override String get systemBrightnessLightThemeId => (super.noSuchMethod( Invocation.getter(#systemBrightnessLightThemeId), - returnValue: '', + returnValue: _i14.dummyValue( + this, + Invocation.getter(#systemBrightnessLightThemeId), + ), ) as String); @override set systemBrightnessLightThemeId(String? systemBrightnessLightThemeId) => @@ -572,7 +587,10 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { @override String get systemBrightnessDarkThemeId => (super.noSuchMethod( Invocation.getter(#systemBrightnessDarkThemeId), - returnValue: '', + returnValue: _i14.dummyValue( + this, + Invocation.getter(#systemBrightnessDarkThemeId), + ), ) as String); @override set systemBrightnessDarkThemeId(String? systemBrightnessDarkThemeId) => @@ -646,17 +664,17 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); @override - _i15.AmountUnit amountUnit(_i4.CryptoCurrency? coin) => (super.noSuchMethod( + _i16.AmountUnit amountUnit(_i4.CryptoCurrency? coin) => (super.noSuchMethod( Invocation.method( #amountUnit, [coin], ), - returnValue: _i15.AmountUnit.normal, - ) as _i15.AmountUnit); + returnValue: _i16.AmountUnit.normal, + ) as _i16.AmountUnit); @override void updateAmountUnit({ required _i4.CryptoCurrency? coin, - required _i15.AmountUnit? amountUnit, + required _i16.AmountUnit? amountUnit, }) => super.noSuchMethod( Invocation.method( @@ -724,7 +742,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { returnValueForMissingStub: null, ); @override - void addListener(_i16.VoidCallback? listener) => super.noSuchMethod( + void addListener(_i17.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #addListener, [listener], @@ -732,7 +750,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { returnValueForMissingStub: null, ); @override - void removeListener(_i16.VoidCallback? listener) => super.noSuchMethod( + void removeListener(_i17.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #removeListener, [listener], @@ -774,15 +792,15 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), ) as _i7.SecureStorageInterface); @override - List<_i17.NodeModel> get primaryNodes => (super.noSuchMethod( + List<_i18.NodeModel> get primaryNodes => (super.noSuchMethod( Invocation.getter(#primaryNodes), - returnValue: <_i17.NodeModel>[], - ) as List<_i17.NodeModel>); + returnValue: <_i18.NodeModel>[], + ) as List<_i18.NodeModel>); @override - List<_i17.NodeModel> get nodes => (super.noSuchMethod( + List<_i18.NodeModel> get nodes => (super.noSuchMethod( Invocation.getter(#nodes), - returnValue: <_i17.NodeModel>[], - ) as List<_i17.NodeModel>); + returnValue: <_i18.NodeModel>[], + ) as List<_i18.NodeModel>); @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), @@ -800,7 +818,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { @override _i10.Future setPrimaryNodeFor({ required _i4.CryptoCurrency? coin, - required _i17.NodeModel? node, + required _i18.NodeModel? node, bool? shouldNotifyListeners = false, }) => (super.noSuchMethod( @@ -817,30 +835,30 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); @override - _i17.NodeModel? getPrimaryNodeFor({required _i4.CryptoCurrency? currency}) => + _i18.NodeModel? getPrimaryNodeFor({required _i4.CryptoCurrency? currency}) => (super.noSuchMethod(Invocation.method( #getPrimaryNodeFor, [], {#currency: currency}, - )) as _i17.NodeModel?); + )) as _i18.NodeModel?); @override - List<_i17.NodeModel> getNodesFor(_i4.CryptoCurrency? coin) => + List<_i18.NodeModel> getNodesFor(_i4.CryptoCurrency? coin) => (super.noSuchMethod( Invocation.method( #getNodesFor, [coin], ), - returnValue: <_i17.NodeModel>[], - ) as List<_i17.NodeModel>); + returnValue: <_i18.NodeModel>[], + ) as List<_i18.NodeModel>); @override - _i17.NodeModel? getNodeById({required String? id}) => + _i18.NodeModel? getNodeById({required String? id}) => (super.noSuchMethod(Invocation.method( #getNodeById, [], {#id: id}, - )) as _i17.NodeModel?); + )) as _i18.NodeModel?); @override - List<_i17.NodeModel> failoverNodesFor( + List<_i18.NodeModel> failoverNodesFor( {required _i4.CryptoCurrency? currency}) => (super.noSuchMethod( Invocation.method( @@ -848,11 +866,11 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { [], {#currency: currency}, ), - returnValue: <_i17.NodeModel>[], - ) as List<_i17.NodeModel>); + returnValue: <_i18.NodeModel>[], + ) as List<_i18.NodeModel>); @override _i10.Future add( - _i17.NodeModel? node, + _i18.NodeModel? node, String? password, bool? shouldNotifyListeners, ) => @@ -904,7 +922,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ) as _i10.Future); @override _i10.Future edit( - _i17.NodeModel? editedNode, + _i18.NodeModel? editedNode, String? password, bool? shouldNotifyListeners, ) => @@ -930,7 +948,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); @override - void addListener(_i16.VoidCallback? listener) => super.noSuchMethod( + void addListener(_i17.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #addListener, [listener], @@ -938,7 +956,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValueForMissingStub: null, ); @override - void removeListener(_i16.VoidCallback? listener) => super.noSuchMethod( + void removeListener(_i17.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #removeListener, [listener], @@ -966,16 +984,16 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { /// A class which mocks [TorService]. /// /// See the documentation for Mockito's code generation for more information. -class MockTorService extends _i1.Mock implements _i18.TorService { +class MockTorService extends _i1.Mock implements _i19.TorService { MockTorService() { _i1.throwOnMissingStub(this); } @override - _i19.TorConnectionStatus get status => (super.noSuchMethod( + _i20.TorConnectionStatus get status => (super.noSuchMethod( Invocation.getter(#status), - returnValue: _i19.TorConnectionStatus.disconnected, - ) as _i19.TorConnectionStatus); + returnValue: _i20.TorConnectionStatus.disconnected, + ) as _i20.TorConnectionStatus); @override ({_i8.InternetAddress host, int port}) getProxyInfo() => (super.noSuchMethod( Invocation.method( @@ -996,7 +1014,7 @@ class MockTorService extends _i1.Mock implements _i18.TorService { @override void init({ required String? torDataDirPath, - _i20.Tor? mockableOverride, + _i21.Tor? mockableOverride, }) => super.noSuchMethod( Invocation.method( diff --git a/test/widget_tests/table_view/table_view_row_test.mocks.dart b/test/widget_tests/table_view/table_view_row_test.mocks.dart index 9880d1e22..09791d1f0 100644 --- a/test/widget_tests/table_view/table_view_row_test.mocks.dart +++ b/test/widget_tests/table_view/table_view_row_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/widget_tests/table_view/table_view_row_test.dart. // Do not manually edit this file. @@ -25,6 +25,8 @@ import 'package:stackwallet/wallets/wallet/wallet.dart' as _i5; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors diff --git a/test/widget_tests/trade_card_test.mocks.dart b/test/widget_tests/trade_card_test.mocks.dart index e8bb7f552..0f62658a4 100644 --- a/test/widget_tests/trade_card_test.mocks.dart +++ b/test/widget_tests/trade_card_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/widget_tests/trade_card_test.dart. // Do not manually edit this file. @@ -7,6 +7,7 @@ import 'dart:async' as _i6; import 'dart:typed_data' as _i7; import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i8; import 'package:stackwallet/db/isar/main_db.dart' as _i3; import 'package:stackwallet/models/isar/stack_theme.dart' as _i5; import 'package:stackwallet/networking/http.dart' as _i2; @@ -16,6 +17,8 @@ import 'package:stackwallet/themes/theme_service.dart' as _i4; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -168,91 +171,145 @@ class MockIThemeAssets extends _i1.Mock implements _i5.IThemeAssets { @override String get bellNew => (super.noSuchMethod( Invocation.getter(#bellNew), - returnValue: '', + returnValue: _i8.dummyValue( + this, + Invocation.getter(#bellNew), + ), ) as String); @override String get buy => (super.noSuchMethod( Invocation.getter(#buy), - returnValue: '', + returnValue: _i8.dummyValue( + this, + Invocation.getter(#buy), + ), ) as String); @override String get exchange => (super.noSuchMethod( Invocation.getter(#exchange), - returnValue: '', + returnValue: _i8.dummyValue( + this, + Invocation.getter(#exchange), + ), ) as String); @override String get personaIncognito => (super.noSuchMethod( Invocation.getter(#personaIncognito), - returnValue: '', + returnValue: _i8.dummyValue( + this, + Invocation.getter(#personaIncognito), + ), ) as String); @override String get personaEasy => (super.noSuchMethod( Invocation.getter(#personaEasy), - returnValue: '', + returnValue: _i8.dummyValue( + this, + Invocation.getter(#personaEasy), + ), ) as String); @override String get stack => (super.noSuchMethod( Invocation.getter(#stack), - returnValue: '', + returnValue: _i8.dummyValue( + this, + Invocation.getter(#stack), + ), ) as String); @override String get stackIcon => (super.noSuchMethod( Invocation.getter(#stackIcon), - returnValue: '', + returnValue: _i8.dummyValue( + this, + Invocation.getter(#stackIcon), + ), ) as String); @override String get receive => (super.noSuchMethod( Invocation.getter(#receive), - returnValue: '', + returnValue: _i8.dummyValue( + this, + Invocation.getter(#receive), + ), ) as String); @override String get receivePending => (super.noSuchMethod( Invocation.getter(#receivePending), - returnValue: '', + returnValue: _i8.dummyValue( + this, + Invocation.getter(#receivePending), + ), ) as String); @override String get receiveCancelled => (super.noSuchMethod( Invocation.getter(#receiveCancelled), - returnValue: '', + returnValue: _i8.dummyValue( + this, + Invocation.getter(#receiveCancelled), + ), ) as String); @override String get send => (super.noSuchMethod( Invocation.getter(#send), - returnValue: '', + returnValue: _i8.dummyValue( + this, + Invocation.getter(#send), + ), ) as String); @override String get sendPending => (super.noSuchMethod( Invocation.getter(#sendPending), - returnValue: '', + returnValue: _i8.dummyValue( + this, + Invocation.getter(#sendPending), + ), ) as String); @override String get sendCancelled => (super.noSuchMethod( Invocation.getter(#sendCancelled), - returnValue: '', + returnValue: _i8.dummyValue( + this, + Invocation.getter(#sendCancelled), + ), ) as String); @override String get themeSelector => (super.noSuchMethod( Invocation.getter(#themeSelector), - returnValue: '', + returnValue: _i8.dummyValue( + this, + Invocation.getter(#themeSelector), + ), ) as String); @override String get themePreview => (super.noSuchMethod( Invocation.getter(#themePreview), - returnValue: '', + returnValue: _i8.dummyValue( + this, + Invocation.getter(#themePreview), + ), ) as String); @override String get txExchange => (super.noSuchMethod( Invocation.getter(#txExchange), - returnValue: '', + returnValue: _i8.dummyValue( + this, + Invocation.getter(#txExchange), + ), ) as String); @override String get txExchangePending => (super.noSuchMethod( Invocation.getter(#txExchangePending), - returnValue: '', + returnValue: _i8.dummyValue( + this, + Invocation.getter(#txExchangePending), + ), ) as String); @override String get txExchangeFailed => (super.noSuchMethod( Invocation.getter(#txExchangeFailed), - returnValue: '', + returnValue: _i8.dummyValue( + this, + Invocation.getter(#txExchangeFailed), + ), ) as String); } diff --git a/test/widget_tests/transaction_card_test.mocks.dart b/test/widget_tests/transaction_card_test.mocks.dart index 6d3d98e0f..cc4157a10 100644 --- a/test/widget_tests/transaction_card_test.mocks.dart +++ b/test/widget_tests/transaction_card_test.mocks.dart @@ -1,31 +1,32 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/widget_tests/transaction_card_test.dart. // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i11; -import 'dart:typed_data' as _i24; -import 'dart:ui' as _i16; +import 'dart:typed_data' as _i25; +import 'dart:ui' as _i17; -import 'package:decimal/decimal.dart' as _i21; +import 'package:decimal/decimal.dart' as _i22; import 'package:isar/isar.dart' as _i9; import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i16; import 'package:stackwallet/db/isar/main_db.dart' as _i3; -import 'package:stackwallet/models/isar/models/block_explorer.dart' as _i26; +import 'package:stackwallet/models/isar/models/block_explorer.dart' as _i27; import 'package:stackwallet/models/isar/models/blockchain_data/v2/transaction_v2.dart' - as _i28; -import 'package:stackwallet/models/isar/models/contact_entry.dart' as _i25; -import 'package:stackwallet/models/isar/models/isar_models.dart' as _i27; -import 'package:stackwallet/models/isar/stack_theme.dart' as _i23; + as _i29; +import 'package:stackwallet/models/isar/models/contact_entry.dart' as _i26; +import 'package:stackwallet/models/isar/models/isar_models.dart' as _i28; +import 'package:stackwallet/models/isar/stack_theme.dart' as _i24; import 'package:stackwallet/networking/http.dart' as _i8; import 'package:stackwallet/services/locale_service.dart' as _i15; import 'package:stackwallet/services/node_service.dart' as _i2; -import 'package:stackwallet/services/price_service.dart' as _i20; +import 'package:stackwallet/services/price_service.dart' as _i21; import 'package:stackwallet/services/wallets.dart' as _i10; -import 'package:stackwallet/themes/theme_service.dart' as _i22; -import 'package:stackwallet/utilities/amount/amount_unit.dart' as _i19; -import 'package:stackwallet/utilities/enums/backup_frequency_type.dart' as _i18; -import 'package:stackwallet/utilities/enums/sync_type_enum.dart' as _i17; +import 'package:stackwallet/themes/theme_service.dart' as _i23; +import 'package:stackwallet/utilities/amount/amount_unit.dart' as _i20; +import 'package:stackwallet/utilities/enums/backup_frequency_type.dart' as _i19; +import 'package:stackwallet/utilities/enums/sync_type_enum.dart' as _i18; import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart' as _i13; import 'package:stackwallet/utilities/prefs.dart' as _i14; @@ -41,6 +42,8 @@ import 'package:tuple/tuple.dart' as _i7; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -272,7 +275,10 @@ class MockLocaleService extends _i1.Mock implements _i15.LocaleService { @override String get locale => (super.noSuchMethod( Invocation.getter(#locale), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#locale), + ), ) as String); @override bool get hasListeners => (super.noSuchMethod( @@ -290,7 +296,7 @@ class MockLocaleService extends _i1.Mock implements _i15.LocaleService { returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); @override - void addListener(_i16.VoidCallback? listener) => super.noSuchMethod( + void addListener(_i17.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #addListener, [listener], @@ -298,7 +304,7 @@ class MockLocaleService extends _i1.Mock implements _i15.LocaleService { returnValueForMissingStub: null, ); @override - void removeListener(_i16.VoidCallback? listener) => super.noSuchMethod( + void removeListener(_i17.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #removeListener, [listener], @@ -382,12 +388,12 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { returnValueForMissingStub: null, ); @override - _i17.SyncingType get syncType => (super.noSuchMethod( + _i18.SyncingType get syncType => (super.noSuchMethod( Invocation.getter(#syncType), - returnValue: _i17.SyncingType.currentWalletOnly, - ) as _i17.SyncingType); + returnValue: _i18.SyncingType.currentWalletOnly, + ) as _i18.SyncingType); @override - set syncType(_i17.SyncingType? syncType) => super.noSuchMethod( + set syncType(_i18.SyncingType? syncType) => super.noSuchMethod( Invocation.setter( #syncType, syncType, @@ -423,7 +429,10 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { @override String get language => (super.noSuchMethod( Invocation.getter(#language), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#language), + ), ) as String); @override set language(String? newLanguage) => super.noSuchMethod( @@ -436,7 +445,10 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { @override String get currency => (super.noSuchMethod( Invocation.getter(#currency), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#currency), + ), ) as String); @override set currency(String? newCurrency) => super.noSuchMethod( @@ -546,12 +558,12 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { returnValueForMissingStub: null, ); @override - _i18.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod( + _i19.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod( Invocation.getter(#backupFrequencyType), - returnValue: _i18.BackupFrequencyType.everyTenMinutes, - ) as _i18.BackupFrequencyType); + returnValue: _i19.BackupFrequencyType.everyTenMinutes, + ) as _i19.BackupFrequencyType); @override - set backupFrequencyType(_i18.BackupFrequencyType? backupFrequencyType) => + set backupFrequencyType(_i19.BackupFrequencyType? backupFrequencyType) => super.noSuchMethod( Invocation.setter( #backupFrequencyType, @@ -645,7 +657,10 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { @override String get themeId => (super.noSuchMethod( Invocation.getter(#themeId), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#themeId), + ), ) as String); @override set themeId(String? themeId) => super.noSuchMethod( @@ -658,7 +673,10 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { @override String get systemBrightnessLightThemeId => (super.noSuchMethod( Invocation.getter(#systemBrightnessLightThemeId), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#systemBrightnessLightThemeId), + ), ) as String); @override set systemBrightnessLightThemeId(String? systemBrightnessLightThemeId) => @@ -672,7 +690,10 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { @override String get systemBrightnessDarkThemeId => (super.noSuchMethod( Invocation.getter(#systemBrightnessDarkThemeId), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#systemBrightnessDarkThemeId), + ), ) as String); @override set systemBrightnessDarkThemeId(String? systemBrightnessDarkThemeId) => @@ -746,17 +767,17 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); @override - _i19.AmountUnit amountUnit(_i4.CryptoCurrency? coin) => (super.noSuchMethod( + _i20.AmountUnit amountUnit(_i4.CryptoCurrency? coin) => (super.noSuchMethod( Invocation.method( #amountUnit, [coin], ), - returnValue: _i19.AmountUnit.normal, - ) as _i19.AmountUnit); + returnValue: _i20.AmountUnit.normal, + ) as _i20.AmountUnit); @override void updateAmountUnit({ required _i4.CryptoCurrency? coin, - required _i19.AmountUnit? amountUnit, + required _i20.AmountUnit? amountUnit, }) => super.noSuchMethod( Invocation.method( @@ -824,7 +845,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { returnValueForMissingStub: null, ); @override - void addListener(_i16.VoidCallback? listener) => super.noSuchMethod( + void addListener(_i17.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #addListener, [listener], @@ -832,7 +853,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { returnValueForMissingStub: null, ); @override - void removeListener(_i16.VoidCallback? listener) => super.noSuchMethod( + void removeListener(_i17.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #removeListener, [listener], @@ -860,7 +881,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { /// A class which mocks [PriceService]. /// /// See the documentation for Mockito's code generation for more information. -class MockPriceService extends _i1.Mock implements _i20.PriceService { +class MockPriceService extends _i1.Mock implements _i21.PriceService { MockPriceService() { _i1.throwOnMissingStub(this); } @@ -868,7 +889,10 @@ class MockPriceService extends _i1.Mock implements _i20.PriceService { @override String get baseTicker => (super.noSuchMethod( Invocation.getter(#baseTicker), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#baseTicker), + ), ) as String); @override set baseTicker(String? _baseTicker) => super.noSuchMethod( @@ -898,35 +922,35 @@ class MockPriceService extends _i1.Mock implements _i20.PriceService { returnValue: false, ) as bool); @override - _i7.Tuple2<_i21.Decimal, double> getPrice(_i4.CryptoCurrency? coin) => + _i7.Tuple2<_i22.Decimal, double> getPrice(_i4.CryptoCurrency? coin) => (super.noSuchMethod( Invocation.method( #getPrice, [coin], ), - returnValue: _FakeTuple2_5<_i21.Decimal, double>( + returnValue: _FakeTuple2_5<_i22.Decimal, double>( this, Invocation.method( #getPrice, [coin], ), ), - ) as _i7.Tuple2<_i21.Decimal, double>); + ) as _i7.Tuple2<_i22.Decimal, double>); @override - _i7.Tuple2<_i21.Decimal, double> getTokenPrice(String? contractAddress) => + _i7.Tuple2<_i22.Decimal, double> getTokenPrice(String? contractAddress) => (super.noSuchMethod( Invocation.method( #getTokenPrice, [contractAddress], ), - returnValue: _FakeTuple2_5<_i21.Decimal, double>( + returnValue: _FakeTuple2_5<_i22.Decimal, double>( this, Invocation.method( #getTokenPrice, [contractAddress], ), ), - ) as _i7.Tuple2<_i21.Decimal, double>); + ) as _i7.Tuple2<_i22.Decimal, double>); @override _i11.Future updatePrice() => (super.noSuchMethod( Invocation.method( @@ -961,7 +985,7 @@ class MockPriceService extends _i1.Mock implements _i20.PriceService { returnValueForMissingStub: null, ); @override - void addListener(_i16.VoidCallback? listener) => super.noSuchMethod( + void addListener(_i17.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #addListener, [listener], @@ -969,7 +993,7 @@ class MockPriceService extends _i1.Mock implements _i20.PriceService { returnValueForMissingStub: null, ); @override - void removeListener(_i16.VoidCallback? listener) => super.noSuchMethod( + void removeListener(_i17.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #removeListener, [listener], @@ -989,7 +1013,7 @@ class MockPriceService extends _i1.Mock implements _i20.PriceService { /// A class which mocks [ThemeService]. /// /// See the documentation for Mockito's code generation for more information. -class MockThemeService extends _i1.Mock implements _i22.ThemeService { +class MockThemeService extends _i1.Mock implements _i23.ThemeService { MockThemeService() { _i1.throwOnMissingStub(this); } @@ -1019,10 +1043,10 @@ class MockThemeService extends _i1.Mock implements _i22.ThemeService { ), ) as _i3.MainDB); @override - List<_i23.StackTheme> get installedThemes => (super.noSuchMethod( + List<_i24.StackTheme> get installedThemes => (super.noSuchMethod( Invocation.getter(#installedThemes), - returnValue: <_i23.StackTheme>[], - ) as List<_i23.StackTheme>); + returnValue: <_i24.StackTheme>[], + ) as List<_i24.StackTheme>); @override void init(_i3.MainDB? db) => super.noSuchMethod( Invocation.method( @@ -1032,7 +1056,7 @@ class MockThemeService extends _i1.Mock implements _i22.ThemeService { returnValueForMissingStub: null, ); @override - _i11.Future install({required _i24.Uint8List? themeArchiveData}) => + _i11.Future install({required _i25.Uint8List? themeArchiveData}) => (super.noSuchMethod( Invocation.method( #install, @@ -1072,33 +1096,33 @@ class MockThemeService extends _i1.Mock implements _i22.ThemeService { returnValue: _i11.Future.value(false), ) as _i11.Future); @override - _i11.Future> fetchThemes() => + _i11.Future> fetchThemes() => (super.noSuchMethod( Invocation.method( #fetchThemes, [], ), - returnValue: _i11.Future>.value( - <_i22.StackThemeMetaData>[]), - ) as _i11.Future>); + returnValue: _i11.Future>.value( + <_i23.StackThemeMetaData>[]), + ) as _i11.Future>); @override - _i11.Future<_i24.Uint8List> fetchTheme( - {required _i22.StackThemeMetaData? themeMetaData}) => + _i11.Future<_i25.Uint8List> fetchTheme( + {required _i23.StackThemeMetaData? themeMetaData}) => (super.noSuchMethod( Invocation.method( #fetchTheme, [], {#themeMetaData: themeMetaData}, ), - returnValue: _i11.Future<_i24.Uint8List>.value(_i24.Uint8List(0)), - ) as _i11.Future<_i24.Uint8List>); + returnValue: _i11.Future<_i25.Uint8List>.value(_i25.Uint8List(0)), + ) as _i11.Future<_i25.Uint8List>); @override - _i23.StackTheme? getTheme({required String? themeId}) => + _i24.StackTheme? getTheme({required String? themeId}) => (super.noSuchMethod(Invocation.method( #getTheme, [], {#themeId: themeId}, - )) as _i23.StackTheme?); + )) as _i24.StackTheme?); } /// A class which mocks [MainDB]. @@ -1147,13 +1171,13 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); @override - List<_i25.ContactEntry> getContactEntries() => (super.noSuchMethod( + List<_i26.ContactEntry> getContactEntries() => (super.noSuchMethod( Invocation.method( #getContactEntries, [], ), - returnValue: <_i25.ContactEntry>[], - ) as List<_i25.ContactEntry>); + returnValue: <_i26.ContactEntry>[], + ) as List<_i26.ContactEntry>); @override _i11.Future deleteContactEntry({required String? id}) => (super.noSuchMethod( @@ -1175,15 +1199,15 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValue: _i11.Future.value(false), ) as _i11.Future); @override - _i25.ContactEntry? getContactEntry({required String? id}) => + _i26.ContactEntry? getContactEntry({required String? id}) => (super.noSuchMethod(Invocation.method( #getContactEntry, [], {#id: id}, - )) as _i25.ContactEntry?); + )) as _i26.ContactEntry?); @override _i11.Future putContactEntry( - {required _i25.ContactEntry? contactEntry}) => + {required _i26.ContactEntry? contactEntry}) => (super.noSuchMethod( Invocation.method( #putContactEntry, @@ -1193,16 +1217,16 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValue: _i11.Future.value(false), ) as _i11.Future); @override - _i26.TransactionBlockExplorer? getTransactionBlockExplorer( + _i27.TransactionBlockExplorer? getTransactionBlockExplorer( {required _i4.CryptoCurrency? cryptoCurrency}) => (super.noSuchMethod(Invocation.method( #getTransactionBlockExplorer, [], {#cryptoCurrency: cryptoCurrency}, - )) as _i26.TransactionBlockExplorer?); + )) as _i27.TransactionBlockExplorer?); @override _i11.Future putTransactionBlockExplorer( - _i26.TransactionBlockExplorer? explorer) => + _i27.TransactionBlockExplorer? explorer) => (super.noSuchMethod( Invocation.method( #putTransactionBlockExplorer, @@ -1211,13 +1235,13 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValue: _i11.Future.value(0), ) as _i11.Future); @override - _i9.QueryBuilder<_i27.Address, _i27.Address, _i9.QAfterWhereClause> + _i9.QueryBuilder<_i28.Address, _i28.Address, _i9.QAfterWhereClause> getAddresses(String? walletId) => (super.noSuchMethod( Invocation.method( #getAddresses, [walletId], ), - returnValue: _FakeQueryBuilder_8<_i27.Address, _i27.Address, + returnValue: _FakeQueryBuilder_8<_i28.Address, _i28.Address, _i9.QAfterWhereClause>( this, Invocation.method( @@ -1226,9 +1250,9 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), ), ) as _i9 - .QueryBuilder<_i27.Address, _i27.Address, _i9.QAfterWhereClause>); + .QueryBuilder<_i28.Address, _i28.Address, _i9.QAfterWhereClause>); @override - _i11.Future putAddress(_i27.Address? address) => (super.noSuchMethod( + _i11.Future putAddress(_i28.Address? address) => (super.noSuchMethod( Invocation.method( #putAddress, [address], @@ -1236,7 +1260,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValue: _i11.Future.value(0), ) as _i11.Future); @override - _i11.Future> putAddresses(List<_i27.Address>? addresses) => + _i11.Future> putAddresses(List<_i28.Address>? addresses) => (super.noSuchMethod( Invocation.method( #putAddresses, @@ -1245,7 +1269,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValue: _i11.Future>.value([]), ) as _i11.Future>); @override - _i11.Future> updateOrPutAddresses(List<_i27.Address>? addresses) => + _i11.Future> updateOrPutAddresses(List<_i28.Address>? addresses) => (super.noSuchMethod( Invocation.method( #updateOrPutAddresses, @@ -1254,7 +1278,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValue: _i11.Future>.value([]), ) as _i11.Future>); @override - _i11.Future<_i27.Address?> getAddress( + _i11.Future<_i28.Address?> getAddress( String? walletId, String? address, ) => @@ -1266,12 +1290,12 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { address, ], ), - returnValue: _i11.Future<_i27.Address?>.value(), - ) as _i11.Future<_i27.Address?>); + returnValue: _i11.Future<_i28.Address?>.value(), + ) as _i11.Future<_i28.Address?>); @override _i11.Future updateAddress( - _i27.Address? oldAddress, - _i27.Address? newAddress, + _i28.Address? oldAddress, + _i28.Address? newAddress, ) => (super.noSuchMethod( Invocation.method( @@ -1284,13 +1308,13 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValue: _i11.Future.value(0), ) as _i11.Future); @override - _i9.QueryBuilder<_i27.Transaction, _i27.Transaction, _i9.QAfterWhereClause> + _i9.QueryBuilder<_i28.Transaction, _i28.Transaction, _i9.QAfterWhereClause> getTransactions(String? walletId) => (super.noSuchMethod( Invocation.method( #getTransactions, [walletId], ), - returnValue: _FakeQueryBuilder_8<_i27.Transaction, _i27.Transaction, + returnValue: _FakeQueryBuilder_8<_i28.Transaction, _i28.Transaction, _i9.QAfterWhereClause>( this, Invocation.method( @@ -1298,10 +1322,10 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { [walletId], ), ), - ) as _i9.QueryBuilder<_i27.Transaction, _i27.Transaction, + ) as _i9.QueryBuilder<_i28.Transaction, _i28.Transaction, _i9.QAfterWhereClause>); @override - _i11.Future putTransaction(_i27.Transaction? transaction) => + _i11.Future putTransaction(_i28.Transaction? transaction) => (super.noSuchMethod( Invocation.method( #putTransaction, @@ -1311,7 +1335,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ) as _i11.Future); @override _i11.Future> putTransactions( - List<_i27.Transaction>? transactions) => + List<_i28.Transaction>? transactions) => (super.noSuchMethod( Invocation.method( #putTransactions, @@ -1320,7 +1344,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValue: _i11.Future>.value([]), ) as _i11.Future>); @override - _i11.Future<_i27.Transaction?> getTransaction( + _i11.Future<_i28.Transaction?> getTransaction( String? walletId, String? txid, ) => @@ -1332,10 +1356,10 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { txid, ], ), - returnValue: _i11.Future<_i27.Transaction?>.value(), - ) as _i11.Future<_i27.Transaction?>); + returnValue: _i11.Future<_i28.Transaction?>.value(), + ) as _i11.Future<_i28.Transaction?>); @override - _i11.Stream<_i27.Transaction?> watchTransaction({ + _i11.Stream<_i28.Transaction?> watchTransaction({ required int? id, bool? fireImmediately = false, }) => @@ -1348,10 +1372,10 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { #fireImmediately: fireImmediately, }, ), - returnValue: _i11.Stream<_i27.Transaction?>.empty(), - ) as _i11.Stream<_i27.Transaction?>); + returnValue: _i11.Stream<_i28.Transaction?>.empty(), + ) as _i11.Stream<_i28.Transaction?>); @override - _i9.QueryBuilder<_i27.UTXO, _i27.UTXO, _i9.QAfterWhereClause> getUTXOs( + _i9.QueryBuilder<_i28.UTXO, _i28.UTXO, _i9.QAfterWhereClause> getUTXOs( String? walletId) => (super.noSuchMethod( Invocation.method( @@ -1359,16 +1383,16 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { [walletId], ), returnValue: - _FakeQueryBuilder_8<_i27.UTXO, _i27.UTXO, _i9.QAfterWhereClause>( + _FakeQueryBuilder_8<_i28.UTXO, _i28.UTXO, _i9.QAfterWhereClause>( this, Invocation.method( #getUTXOs, [walletId], ), ), - ) as _i9.QueryBuilder<_i27.UTXO, _i27.UTXO, _i9.QAfterWhereClause>); + ) as _i9.QueryBuilder<_i28.UTXO, _i28.UTXO, _i9.QAfterWhereClause>); @override - _i9.QueryBuilder<_i27.UTXO, _i27.UTXO, _i9.QAfterFilterCondition> + _i9.QueryBuilder<_i28.UTXO, _i28.UTXO, _i9.QAfterFilterCondition> getUTXOsByAddress( String? walletId, String? address, @@ -1381,7 +1405,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { address, ], ), - returnValue: _FakeQueryBuilder_8<_i27.UTXO, _i27.UTXO, + returnValue: _FakeQueryBuilder_8<_i28.UTXO, _i28.UTXO, _i9.QAfterFilterCondition>( this, Invocation.method( @@ -1393,9 +1417,9 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), ), ) as _i9 - .QueryBuilder<_i27.UTXO, _i27.UTXO, _i9.QAfterFilterCondition>); + .QueryBuilder<_i28.UTXO, _i28.UTXO, _i9.QAfterFilterCondition>); @override - _i11.Future putUTXO(_i27.UTXO? utxo) => (super.noSuchMethod( + _i11.Future putUTXO(_i28.UTXO? utxo) => (super.noSuchMethod( Invocation.method( #putUTXO, [utxo], @@ -1404,7 +1428,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); @override - _i11.Future putUTXOs(List<_i27.UTXO>? utxos) => (super.noSuchMethod( + _i11.Future putUTXOs(List<_i28.UTXO>? utxos) => (super.noSuchMethod( Invocation.method( #putUTXOs, [utxos], @@ -1415,7 +1439,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { @override _i11.Future updateUTXOs( String? walletId, - List<_i27.UTXO>? utxos, + List<_i28.UTXO>? utxos, ) => (super.noSuchMethod( Invocation.method( @@ -1428,7 +1452,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValue: _i11.Future.value(false), ) as _i11.Future); @override - _i11.Stream<_i27.UTXO?> watchUTXO({ + _i11.Stream<_i28.UTXO?> watchUTXO({ required int? id, bool? fireImmediately = false, }) => @@ -1441,10 +1465,10 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { #fireImmediately: fireImmediately, }, ), - returnValue: _i11.Stream<_i27.UTXO?>.empty(), - ) as _i11.Stream<_i27.UTXO?>); + returnValue: _i11.Stream<_i28.UTXO?>.empty(), + ) as _i11.Stream<_i28.UTXO?>); @override - _i9.QueryBuilder<_i27.TransactionNote, _i27.TransactionNote, + _i9.QueryBuilder<_i28.TransactionNote, _i28.TransactionNote, _i9.QAfterWhereClause> getTransactionNotes( String? walletId) => (super.noSuchMethod( @@ -1452,18 +1476,18 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { #getTransactionNotes, [walletId], ), - returnValue: _FakeQueryBuilder_8<_i27.TransactionNote, - _i27.TransactionNote, _i9.QAfterWhereClause>( + returnValue: _FakeQueryBuilder_8<_i28.TransactionNote, + _i28.TransactionNote, _i9.QAfterWhereClause>( this, Invocation.method( #getTransactionNotes, [walletId], ), ), - ) as _i9.QueryBuilder<_i27.TransactionNote, _i27.TransactionNote, + ) as _i9.QueryBuilder<_i28.TransactionNote, _i28.TransactionNote, _i9.QAfterWhereClause>); @override - _i11.Future putTransactionNote(_i27.TransactionNote? transactionNote) => + _i11.Future putTransactionNote(_i28.TransactionNote? transactionNote) => (super.noSuchMethod( Invocation.method( #putTransactionNote, @@ -1474,7 +1498,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ) as _i11.Future); @override _i11.Future putTransactionNotes( - List<_i27.TransactionNote>? transactionNotes) => + List<_i28.TransactionNote>? transactionNotes) => (super.noSuchMethod( Invocation.method( #putTransactionNotes, @@ -1484,7 +1508,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); @override - _i11.Future<_i27.TransactionNote?> getTransactionNote( + _i11.Future<_i28.TransactionNote?> getTransactionNote( String? walletId, String? txid, ) => @@ -1496,10 +1520,10 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { txid, ], ), - returnValue: _i11.Future<_i27.TransactionNote?>.value(), - ) as _i11.Future<_i27.TransactionNote?>); + returnValue: _i11.Future<_i28.TransactionNote?>.value(), + ) as _i11.Future<_i28.TransactionNote?>); @override - _i11.Stream<_i27.TransactionNote?> watchTransactionNote({ + _i11.Stream<_i28.TransactionNote?> watchTransactionNote({ required int? id, bool? fireImmediately = false, }) => @@ -1512,27 +1536,27 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { #fireImmediately: fireImmediately, }, ), - returnValue: _i11.Stream<_i27.TransactionNote?>.empty(), - ) as _i11.Stream<_i27.TransactionNote?>); + returnValue: _i11.Stream<_i28.TransactionNote?>.empty(), + ) as _i11.Stream<_i28.TransactionNote?>); @override - _i9.QueryBuilder<_i27.AddressLabel, _i27.AddressLabel, _i9.QAfterWhereClause> + _i9.QueryBuilder<_i28.AddressLabel, _i28.AddressLabel, _i9.QAfterWhereClause> getAddressLabels(String? walletId) => (super.noSuchMethod( Invocation.method( #getAddressLabels, [walletId], ), - returnValue: _FakeQueryBuilder_8<_i27.AddressLabel, - _i27.AddressLabel, _i9.QAfterWhereClause>( + returnValue: _FakeQueryBuilder_8<_i28.AddressLabel, + _i28.AddressLabel, _i9.QAfterWhereClause>( this, Invocation.method( #getAddressLabels, [walletId], ), ), - ) as _i9.QueryBuilder<_i27.AddressLabel, _i27.AddressLabel, + ) as _i9.QueryBuilder<_i28.AddressLabel, _i28.AddressLabel, _i9.QAfterWhereClause>); @override - _i11.Future putAddressLabel(_i27.AddressLabel? addressLabel) => + _i11.Future putAddressLabel(_i28.AddressLabel? addressLabel) => (super.noSuchMethod( Invocation.method( #putAddressLabel, @@ -1541,7 +1565,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValue: _i11.Future.value(0), ) as _i11.Future); @override - int putAddressLabelSync(_i27.AddressLabel? addressLabel) => + int putAddressLabelSync(_i28.AddressLabel? addressLabel) => (super.noSuchMethod( Invocation.method( #putAddressLabelSync, @@ -1550,7 +1574,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValue: 0, ) as int); @override - _i11.Future putAddressLabels(List<_i27.AddressLabel>? addressLabels) => + _i11.Future putAddressLabels(List<_i28.AddressLabel>? addressLabels) => (super.noSuchMethod( Invocation.method( #putAddressLabels, @@ -1560,7 +1584,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); @override - _i11.Future<_i27.AddressLabel?> getAddressLabel( + _i11.Future<_i28.AddressLabel?> getAddressLabel( String? walletId, String? addressString, ) => @@ -1572,10 +1596,10 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { addressString, ], ), - returnValue: _i11.Future<_i27.AddressLabel?>.value(), - ) as _i11.Future<_i27.AddressLabel?>); + returnValue: _i11.Future<_i28.AddressLabel?>.value(), + ) as _i11.Future<_i28.AddressLabel?>); @override - _i27.AddressLabel? getAddressLabelSync( + _i28.AddressLabel? getAddressLabelSync( String? walletId, String? addressString, ) => @@ -1585,9 +1609,9 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { walletId, addressString, ], - )) as _i27.AddressLabel?); + )) as _i28.AddressLabel?); @override - _i11.Stream<_i27.AddressLabel?> watchAddressLabel({ + _i11.Stream<_i28.AddressLabel?> watchAddressLabel({ required int? id, bool? fireImmediately = false, }) => @@ -1600,10 +1624,10 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { #fireImmediately: fireImmediately, }, ), - returnValue: _i11.Stream<_i27.AddressLabel?>.empty(), - ) as _i11.Stream<_i27.AddressLabel?>); + returnValue: _i11.Stream<_i28.AddressLabel?>.empty(), + ) as _i11.Stream<_i28.AddressLabel?>); @override - _i11.Future updateAddressLabel(_i27.AddressLabel? addressLabel) => + _i11.Future updateAddressLabel(_i28.AddressLabel? addressLabel) => (super.noSuchMethod( Invocation.method( #updateAddressLabel, @@ -1643,7 +1667,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ) as _i11.Future); @override _i11.Future addNewTransactionData( - List<_i7.Tuple2<_i27.Transaction, _i27.Address?>>? transactionsData, + List<_i7.Tuple2<_i28.Transaction, _i28.Address?>>? transactionsData, String? walletId, ) => (super.noSuchMethod( @@ -1659,7 +1683,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ) as _i11.Future); @override _i11.Future> updateOrPutTransactionV2s( - List<_i28.TransactionV2>? transactions) => + List<_i29.TransactionV2>? transactions) => (super.noSuchMethod( Invocation.method( #updateOrPutTransactionV2s, @@ -1668,13 +1692,13 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValue: _i11.Future>.value([]), ) as _i11.Future>); @override - _i9.QueryBuilder<_i27.EthContract, _i27.EthContract, _i9.QWhere> + _i9.QueryBuilder<_i28.EthContract, _i28.EthContract, _i9.QWhere> getEthContracts() => (super.noSuchMethod( Invocation.method( #getEthContracts, [], ), - returnValue: _FakeQueryBuilder_8<_i27.EthContract, _i27.EthContract, + returnValue: _FakeQueryBuilder_8<_i28.EthContract, _i28.EthContract, _i9.QWhere>( this, Invocation.method( @@ -1683,24 +1707,24 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), ), ) as _i9 - .QueryBuilder<_i27.EthContract, _i27.EthContract, _i9.QWhere>); + .QueryBuilder<_i28.EthContract, _i28.EthContract, _i9.QWhere>); @override - _i11.Future<_i27.EthContract?> getEthContract(String? contractAddress) => + _i11.Future<_i28.EthContract?> getEthContract(String? contractAddress) => (super.noSuchMethod( Invocation.method( #getEthContract, [contractAddress], ), - returnValue: _i11.Future<_i27.EthContract?>.value(), - ) as _i11.Future<_i27.EthContract?>); + returnValue: _i11.Future<_i28.EthContract?>.value(), + ) as _i11.Future<_i28.EthContract?>); @override - _i27.EthContract? getEthContractSync(String? contractAddress) => + _i28.EthContract? getEthContractSync(String? contractAddress) => (super.noSuchMethod(Invocation.method( #getEthContractSync, [contractAddress], - )) as _i27.EthContract?); + )) as _i28.EthContract?); @override - _i11.Future putEthContract(_i27.EthContract? contract) => + _i11.Future putEthContract(_i28.EthContract? contract) => (super.noSuchMethod( Invocation.method( #putEthContract, @@ -1709,7 +1733,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValue: _i11.Future.value(0), ) as _i11.Future); @override - _i11.Future putEthContracts(List<_i27.EthContract>? contracts) => + _i11.Future putEthContracts(List<_i28.EthContract>? contracts) => (super.noSuchMethod( Invocation.method( #putEthContracts, @@ -1733,7 +1757,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { /// A class which mocks [IThemeAssets]. /// /// See the documentation for Mockito's code generation for more information. -class MockIThemeAssets extends _i1.Mock implements _i23.IThemeAssets { +class MockIThemeAssets extends _i1.Mock implements _i24.IThemeAssets { MockIThemeAssets() { _i1.throwOnMissingStub(this); } @@ -1741,91 +1765,145 @@ class MockIThemeAssets extends _i1.Mock implements _i23.IThemeAssets { @override String get bellNew => (super.noSuchMethod( Invocation.getter(#bellNew), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#bellNew), + ), ) as String); @override String get buy => (super.noSuchMethod( Invocation.getter(#buy), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#buy), + ), ) as String); @override String get exchange => (super.noSuchMethod( Invocation.getter(#exchange), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#exchange), + ), ) as String); @override String get personaIncognito => (super.noSuchMethod( Invocation.getter(#personaIncognito), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#personaIncognito), + ), ) as String); @override String get personaEasy => (super.noSuchMethod( Invocation.getter(#personaEasy), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#personaEasy), + ), ) as String); @override String get stack => (super.noSuchMethod( Invocation.getter(#stack), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#stack), + ), ) as String); @override String get stackIcon => (super.noSuchMethod( Invocation.getter(#stackIcon), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#stackIcon), + ), ) as String); @override String get receive => (super.noSuchMethod( Invocation.getter(#receive), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#receive), + ), ) as String); @override String get receivePending => (super.noSuchMethod( Invocation.getter(#receivePending), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#receivePending), + ), ) as String); @override String get receiveCancelled => (super.noSuchMethod( Invocation.getter(#receiveCancelled), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#receiveCancelled), + ), ) as String); @override String get send => (super.noSuchMethod( Invocation.getter(#send), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#send), + ), ) as String); @override String get sendPending => (super.noSuchMethod( Invocation.getter(#sendPending), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#sendPending), + ), ) as String); @override String get sendCancelled => (super.noSuchMethod( Invocation.getter(#sendCancelled), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#sendCancelled), + ), ) as String); @override String get themeSelector => (super.noSuchMethod( Invocation.getter(#themeSelector), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#themeSelector), + ), ) as String); @override String get themePreview => (super.noSuchMethod( Invocation.getter(#themePreview), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#themePreview), + ), ) as String); @override String get txExchange => (super.noSuchMethod( Invocation.getter(#txExchange), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#txExchange), + ), ) as String); @override String get txExchangePending => (super.noSuchMethod( Invocation.getter(#txExchangePending), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#txExchangePending), + ), ) as String); @override String get txExchangeFailed => (super.noSuchMethod( Invocation.getter(#txExchangeFailed), - returnValue: '', + returnValue: _i16.dummyValue( + this, + Invocation.getter(#txExchangeFailed), + ), ) as String); } diff --git a/test/widget_tests/wallet_card_test.mocks.dart b/test/widget_tests/wallet_card_test.mocks.dart index d984f4aef..1c8afe8db 100644 --- a/test/widget_tests/wallet_card_test.mocks.dart +++ b/test/widget_tests/wallet_card_test.mocks.dart @@ -1,20 +1,21 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/widget_tests/wallet_card_test.dart. // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i8; -import 'dart:typed_data' as _i16; -import 'dart:ui' as _i13; +import 'dart:typed_data' as _i17; +import 'dart:ui' as _i14; import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i13; import 'package:stackwallet/db/isar/main_db.dart' as _i3; -import 'package:stackwallet/models/isar/stack_theme.dart' as _i15; +import 'package:stackwallet/models/isar/stack_theme.dart' as _i16; import 'package:stackwallet/networking/http.dart' as _i6; import 'package:stackwallet/services/locale_service.dart' as _i12; import 'package:stackwallet/services/node_service.dart' as _i2; import 'package:stackwallet/services/wallets.dart' as _i7; -import 'package:stackwallet/themes/theme_service.dart' as _i14; +import 'package:stackwallet/themes/theme_service.dart' as _i15; import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart' as _i10; import 'package:stackwallet/utilities/prefs.dart' as _i11; @@ -27,6 +28,8 @@ import 'package:stackwallet/wallets/wallet/wallet.dart' as _i5; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -206,7 +209,10 @@ class MockLocaleService extends _i1.Mock implements _i12.LocaleService { @override String get locale => (super.noSuchMethod( Invocation.getter(#locale), - returnValue: '', + returnValue: _i13.dummyValue( + this, + Invocation.getter(#locale), + ), ) as String); @override bool get hasListeners => (super.noSuchMethod( @@ -224,7 +230,7 @@ class MockLocaleService extends _i1.Mock implements _i12.LocaleService { returnValueForMissingStub: _i8.Future.value(), ) as _i8.Future); @override - void addListener(_i13.VoidCallback? listener) => super.noSuchMethod( + void addListener(_i14.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #addListener, [listener], @@ -232,7 +238,7 @@ class MockLocaleService extends _i1.Mock implements _i12.LocaleService { returnValueForMissingStub: null, ); @override - void removeListener(_i13.VoidCallback? listener) => super.noSuchMethod( + void removeListener(_i14.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #removeListener, [listener], @@ -260,7 +266,7 @@ class MockLocaleService extends _i1.Mock implements _i12.LocaleService { /// A class which mocks [ThemeService]. /// /// See the documentation for Mockito's code generation for more information. -class MockThemeService extends _i1.Mock implements _i14.ThemeService { +class MockThemeService extends _i1.Mock implements _i15.ThemeService { MockThemeService() { _i1.throwOnMissingStub(this); } @@ -290,10 +296,10 @@ class MockThemeService extends _i1.Mock implements _i14.ThemeService { ), ) as _i3.MainDB); @override - List<_i15.StackTheme> get installedThemes => (super.noSuchMethod( + List<_i16.StackTheme> get installedThemes => (super.noSuchMethod( Invocation.getter(#installedThemes), - returnValue: <_i15.StackTheme>[], - ) as List<_i15.StackTheme>); + returnValue: <_i16.StackTheme>[], + ) as List<_i16.StackTheme>); @override void init(_i3.MainDB? db) => super.noSuchMethod( Invocation.method( @@ -303,7 +309,7 @@ class MockThemeService extends _i1.Mock implements _i14.ThemeService { returnValueForMissingStub: null, ); @override - _i8.Future install({required _i16.Uint8List? themeArchiveData}) => + _i8.Future install({required _i17.Uint8List? themeArchiveData}) => (super.noSuchMethod( Invocation.method( #install, @@ -343,31 +349,31 @@ class MockThemeService extends _i1.Mock implements _i14.ThemeService { returnValue: _i8.Future.value(false), ) as _i8.Future); @override - _i8.Future> fetchThemes() => + _i8.Future> fetchThemes() => (super.noSuchMethod( Invocation.method( #fetchThemes, [], ), - returnValue: _i8.Future>.value( - <_i14.StackThemeMetaData>[]), - ) as _i8.Future>); + returnValue: _i8.Future>.value( + <_i15.StackThemeMetaData>[]), + ) as _i8.Future>); @override - _i8.Future<_i16.Uint8List> fetchTheme( - {required _i14.StackThemeMetaData? themeMetaData}) => + _i8.Future<_i17.Uint8List> fetchTheme( + {required _i15.StackThemeMetaData? themeMetaData}) => (super.noSuchMethod( Invocation.method( #fetchTheme, [], {#themeMetaData: themeMetaData}, ), - returnValue: _i8.Future<_i16.Uint8List>.value(_i16.Uint8List(0)), - ) as _i8.Future<_i16.Uint8List>); + returnValue: _i8.Future<_i17.Uint8List>.value(_i17.Uint8List(0)), + ) as _i8.Future<_i17.Uint8List>); @override - _i15.StackTheme? getTheme({required String? themeId}) => + _i16.StackTheme? getTheme({required String? themeId}) => (super.noSuchMethod(Invocation.method( #getTheme, [], {#themeId: themeId}, - )) as _i15.StackTheme?); + )) as _i16.StackTheme?); } diff --git a/test/widget_tests/wallet_info_row/sub_widgets/wallet_info_row_balance_future_test.dart b/test/widget_tests/wallet_info_row/sub_widgets/wallet_info_row_balance_future_test.dart index a3161a73e..470f89225 100644 --- a/test/widget_tests/wallet_info_row/sub_widgets/wallet_info_row_balance_future_test.dart +++ b/test/widget_tests/wallet_info_row/sub_widgets/wallet_info_row_balance_future_test.dart @@ -2,12 +2,15 @@ import 'package:mockito/annotations.dart'; import 'package:stackwallet/services/node_service.dart'; import 'package:stackwallet/services/wallets.dart'; -@GenerateMocks([ - Wallets, -], customMocks: [ - MockSpec(returnNullOnMissingStub: true), - // MockSpec(returnNullOnMissingStub: true), -]) +@GenerateMocks( + [ + Wallets, + ], + customMocks: [ + MockSpec(), + // MockSpec(), + ], +) void main() { // testWidgets("Test wallet info row balance loads correctly", // (widgetTester) async { diff --git a/test/widget_tests/wallet_info_row/sub_widgets/wallet_info_row_balance_future_test.mocks.dart b/test/widget_tests/wallet_info_row/sub_widgets/wallet_info_row_balance_future_test.mocks.dart index b94f2e713..2084bd833 100644 --- a/test/widget_tests/wallet_info_row/sub_widgets/wallet_info_row_balance_future_test.mocks.dart +++ b/test/widget_tests/wallet_info_row/sub_widgets/wallet_info_row_balance_future_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/widget_tests/wallet_info_row/sub_widgets/wallet_info_row_balance_future_test.dart. // Do not manually edit this file. @@ -23,6 +23,8 @@ import 'package:stackwallet/wallets/wallet/wallet.dart' as _i5; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -196,6 +198,10 @@ class MockWallets extends _i1.Mock implements _i7.Wallets { /// /// See the documentation for Mockito's code generation for more information. class MockNodeService extends _i1.Mock implements _i2.NodeService { + MockNodeService() { + _i1.throwOnMissingStub(this); + } + @override _i6.SecureStorageInterface get secureStorageInterface => (super.noSuchMethod( Invocation.getter(#secureStorageInterface), diff --git a/test/widget_tests/wallet_info_row/wallet_info_row_test.dart b/test/widget_tests/wallet_info_row/wallet_info_row_test.dart index d4f97e600..a9fc6756a 100644 --- a/test/widget_tests/wallet_info_row/wallet_info_row_test.dart +++ b/test/widget_tests/wallet_info_row/wallet_info_row_test.dart @@ -1,16 +1,18 @@ import 'package:mockito/annotations.dart'; import 'package:stackwallet/services/node_service.dart'; import 'package:stackwallet/services/wallets.dart'; -import 'package:stackwallet/services/wallets_service.dart'; import 'package:stackwallet/themes/theme_service.dart'; -@GenerateMocks([ - Wallets, - ThemeService, -], customMocks: [ - MockSpec(returnNullOnMissingStub: true), - // MockSpec(returnNullOnMissingStub: true), -]) +@GenerateMocks( + [ + Wallets, + ThemeService, + ], + customMocks: [ + MockSpec(), + // MockSpec(), + ], +) void main() { // testWidgets("Test wallet info row displays correctly", (widgetTester) async { // final wallets = MockWallets(); diff --git a/test/widget_tests/wallet_info_row/wallet_info_row_test.mocks.dart b/test/widget_tests/wallet_info_row/wallet_info_row_test.mocks.dart index 9c90bcdcd..057284d51 100644 --- a/test/widget_tests/wallet_info_row/wallet_info_row_test.mocks.dart +++ b/test/widget_tests/wallet_info_row/wallet_info_row_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in stackwallet/test/widget_tests/wallet_info_row/wallet_info_row_test.dart. // Do not manually edit this file. @@ -27,6 +27,8 @@ import 'package:stackwallet/wallets/wallet/wallet.dart' as _i5; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors @@ -325,6 +327,10 @@ class MockThemeService extends _i1.Mock implements _i12.ThemeService { /// /// See the documentation for Mockito's code generation for more information. class MockNodeService extends _i1.Mock implements _i2.NodeService { + MockNodeService() { + _i1.throwOnMissingStub(this); + } + @override _i7.SecureStorageInterface get secureStorageInterface => (super.noSuchMethod( Invocation.getter(#secureStorageInterface), From b63b34327601e6c75a23f719a000c971a49b345e Mon Sep 17 00:00:00 2001 From: Julian Date: Tue, 28 May 2024 09:56:40 -0600 Subject: [PATCH 053/100] windows symlink fixes --- scripts/app_config/shared/link_assets.sh | 25 +++++++++++++----------- scripts/build_app.sh | 2 +- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/scripts/app_config/shared/link_assets.sh b/scripts/app_config/shared/link_assets.sh index c443b1262..90074e459 100755 --- a/scripts/app_config/shared/link_assets.sh +++ b/scripts/app_config/shared/link_assets.sh @@ -2,12 +2,13 @@ set -x -e -if [ $# -ne 1 ]; then - echo "Usage: $0 " +if [ $# -ne 2 ]; then + echo "Usage: $0 " exit 1 fi SELECT_ASSETS_DIR=$1 +APP_BUILD_PLATFORM=$2 # declare full paths ASSET_SOURCES_DIR="${APP_PROJECT_ROOT_DIR}/asset_sources" @@ -16,14 +17,16 @@ ASSETS_DIR="${APP_PROJECT_ROOT_DIR}/assets" # finally update symlinks -rm -f "${ASSETS_DIR}/default_themes" -ln -s "${ASSET_SOURCES_DIR}/bundled_themes/${SELECT_ASSETS_DIR}" "${ASSETS_DIR}/default_themes" +for dirname in "default_themes" "icon" "lottie" "in_app_logo_icons"; do + LINK_SOURCE_DIR="${ASSET_SOURCES_DIR}/${dirname}/${SELECT_ASSETS_DIR}" -rm -f "${ASSETS_DIR}/icon" -ln -s "${ASSET_SOURCES_DIR}/icon/${SELECT_ASSETS_DIR}" "${ASSETS_DIR}/icon" + rm -f "${ASSETS_DIR}/${dirname}" -rm -f "${ASSETS_DIR}/lottie" -ln -s "${ASSET_SOURCES_DIR}/lottie/${SELECT_ASSETS_DIR}" "${ASSETS_DIR}/lottie" - -rm -f "${ASSETS_DIR}/in_app_logo_icons" -ln -s "${ASSET_SOURCES_DIR}/in_app_logo_icons/${SELECT_ASSETS_DIR}" "${ASSETS_DIR}/in_app_logo_icons" + if [[ "${APP_BUILD_PLATFORM}" = 'windows' ]]; then + LINK_SOURCE_DIR_WIN_PATH_VERSION=$(wslpath -w "${LINK_SOURCE_DIR}") + LINK_NAME_WIN_PATH_VERSION=$(wslpath -w "${ASSETS_DIR}") + cmd.exe /c mklink /D "${LINK_NAME_WIN_PATH_VERSION}\\${dirname}" "${LINK_SOURCE_DIR_WIN_PATH_VERSION}" + else + ln -s "${LINK_SOURCE_DIR}" "${ASSETS_DIR}/${dirname}" + fi +done diff --git a/scripts/build_app.sh b/scripts/build_app.sh index 7f89a202f..aa569226a 100755 --- a/scripts/build_app.sh +++ b/scripts/build_app.sh @@ -92,7 +92,7 @@ if printf '%s\0' "${APP_NAMED_IDS[@]}" | grep -Fxqz -- "${APP_NAMED_ID}"; then cp "${T_PUBSPEC}" "${ACTUAL_PUBSPEC}" fi "${APP_PROJECT_ROOT_DIR}/scripts/app_config/shared/update_version.sh" -v "${APP_VERSION_STRING}" -b "${APP_BUILD_NUMBER}" - "${APP_PROJECT_ROOT_DIR}/scripts/app_config/shared/link_assets.sh" "${APP_NAMED_ID}" + "${APP_PROJECT_ROOT_DIR}/scripts/app_config/shared/link_assets.sh" "${APP_NAMED_ID}" "${APP_BUILD_PLATFORM}" # shellcheck disable=SC1090 source "${APP_PROJECT_ROOT_DIR}/scripts/app_config/configure_${APP_NAMED_ID}.sh" "${APP_PROJECT_ROOT_DIR}/scripts/app_config/platforms/${APP_BUILD_PLATFORM}/platform_config.sh" From b4478e77c6c37816f6de763f4b3e0f01a7a283a8 Mon Sep 17 00:00:00 2001 From: Julian Date: Tue, 28 May 2024 10:30:41 -0600 Subject: [PATCH 054/100] ios project file fix that got lost somewhere in merging --- .../ios/Runner.xcodeproj/project.pbxproj | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/scripts/app_config/templates/ios/Runner.xcodeproj/project.pbxproj b/scripts/app_config/templates/ios/Runner.xcodeproj/project.pbxproj index c793dd2fe..fdfcd1bfa 100644 --- a/scripts/app_config/templates/ios/Runner.xcodeproj/project.pbxproj +++ b/scripts/app_config/templates/ios/Runner.xcodeproj/project.pbxproj @@ -18,6 +18,8 @@ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; B49D91439948369648AB0603 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51604430FD0FD1FA5C4767A0 /* Pods_Runner.framework */; }; + CE6B5DF12BF26AAA00CF1F44 /* monero_libwallet2_api_c.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE6B5DEF2BF26AAA00CF1F44 /* monero_libwallet2_api_c.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + CE6B5DF22BF26AAA00CF1F44 /* wownero_libwallet2_api_c.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE6B5DF02BF26AAA00CF1F44 /* wownero_libwallet2_api_c.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -32,6 +34,17 @@ name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; + CE6B5DEA2BF26A3300CF1F44 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + CE6B5DF12BF26AAA00CF1F44 /* monero_libwallet2_api_c.dylib in CopyFiles */, + CE6B5DF22BF26AAA00CF1F44 /* wownero_libwallet2_api_c.dylib in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -62,6 +75,8 @@ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B999088F2ABE1E170012A442 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; + CE6B5DEF2BF26AAA00CF1F44 /* monero_libwallet2_api_c.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = monero_libwallet2_api_c.dylib; sourceTree = ""; }; + CE6B5DF02BF26AAA00CF1F44 /* wownero_libwallet2_api_c.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = wownero_libwallet2_api_c.dylib; sourceTree = ""; }; E6F536731AC506735EB76340 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -122,6 +137,8 @@ 97C146E51CF9000F007C117D = { isa = PBXGroup; children = ( + CE6B5DEF2BF26AAA00CF1F44 /* monero_libwallet2_api_c.dylib */, + CE6B5DF02BF26AAA00CF1F44 /* wownero_libwallet2_api_c.dylib */, 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, @@ -177,6 +194,7 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, FD1CA371131604E6658D4146 /* [CP] Embed Pods Frameworks */, + CE6B5DEA2BF26A3300CF1F44 /* CopyFiles */, ); buildRules = ( ); @@ -307,9 +325,6 @@ "${BUILT_PRODUCTS_DIR}/barcode_scan2/barcode_scan2.framework", "${BUILT_PRODUCTS_DIR}/coinlib_flutter/secp256k1.framework", "${BUILT_PRODUCTS_DIR}/connectivity_plus/connectivity_plus.framework", - "${BUILT_PRODUCTS_DIR}/cw_monero/cw_monero.framework", - "${BUILT_PRODUCTS_DIR}/cw_shared_external/cw_shared_external.framework", - "${BUILT_PRODUCTS_DIR}/cw_wownero/cw_wownero.framework", "${BUILT_PRODUCTS_DIR}/device_info_plus/device_info_plus.framework", "${BUILT_PRODUCTS_DIR}/devicelocale/devicelocale.framework", "${BUILT_PRODUCTS_DIR}/file_picker/file_picker.framework", @@ -343,9 +358,6 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/barcode_scan2.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/secp256k1.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/connectivity_plus.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/cw_monero.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/cw_shared_external.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/cw_wownero.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/device_info_plus.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/devicelocale.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_picker.framework", From 6ac468c55c44b7a6c9967ad40de174d5aad537fc Mon Sep 17 00:00:00 2001 From: Julian Date: Tue, 28 May 2024 11:14:06 -0600 Subject: [PATCH 055/100] rename dir --- .../stack_duo/dark.zip | Bin .../stack_duo/light.zip | Bin .../stack_wallet/dark.zip | Bin .../stack_wallet/light.zip | Bin 4 files changed, 0 insertions(+), 0 deletions(-) rename asset_sources/{bundled_themes => default_themes}/stack_duo/dark.zip (100%) rename asset_sources/{bundled_themes => default_themes}/stack_duo/light.zip (100%) rename asset_sources/{bundled_themes => default_themes}/stack_wallet/dark.zip (100%) rename asset_sources/{bundled_themes => default_themes}/stack_wallet/light.zip (100%) diff --git a/asset_sources/bundled_themes/stack_duo/dark.zip b/asset_sources/default_themes/stack_duo/dark.zip similarity index 100% rename from asset_sources/bundled_themes/stack_duo/dark.zip rename to asset_sources/default_themes/stack_duo/dark.zip diff --git a/asset_sources/bundled_themes/stack_duo/light.zip b/asset_sources/default_themes/stack_duo/light.zip similarity index 100% rename from asset_sources/bundled_themes/stack_duo/light.zip rename to asset_sources/default_themes/stack_duo/light.zip diff --git a/asset_sources/bundled_themes/stack_wallet/dark.zip b/asset_sources/default_themes/stack_wallet/dark.zip similarity index 100% rename from asset_sources/bundled_themes/stack_wallet/dark.zip rename to asset_sources/default_themes/stack_wallet/dark.zip diff --git a/asset_sources/bundled_themes/stack_wallet/light.zip b/asset_sources/default_themes/stack_wallet/light.zip similarity index 100% rename from asset_sources/bundled_themes/stack_wallet/light.zip rename to asset_sources/default_themes/stack_wallet/light.zip From fb429b12f8ba9e89ede0e7788008c55fdfae643b Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 28 May 2024 14:37:43 -0600 Subject: [PATCH 056/100] add sqlite lib --- linux/flutter/generated_plugin_registrant.cc | 4 ++++ linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 ++ pubspec.lock | 16 ++++++++++++++++ scripts/app_config/templates/pubspec.template | 2 ++ windows/flutter/generated_plugin_registrant.cc | 3 +++ windows/flutter/generated_plugins.cmake | 1 + 7 files changed, 29 insertions(+) diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index b174939fa..499e835f2 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) isar_flutter_libs_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "IsarFlutterLibsPlugin"); isar_flutter_libs_plugin_register_with_registrar(isar_flutter_libs_registrar); + g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin"); + sqlite3_flutter_libs_plugin_register_with_registrar(sqlite3_flutter_libs_registrar); g_autoptr(FlPluginRegistrar) stack_wallet_backup_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "StackWalletBackupPlugin"); stack_wallet_backup_plugin_register_with_registrar(stack_wallet_backup_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index e1af526f4..a8bc74800 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -9,6 +9,7 @@ list(APPEND FLUTTER_PLUGIN_LIST flutter_libmonero flutter_secure_storage_linux isar_flutter_libs + sqlite3_flutter_libs stack_wallet_backup url_launcher_linux window_size diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 681a96548..762e46f56 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -17,6 +17,7 @@ import lelantus import package_info_plus import path_provider_foundation import share_plus +import sqlite3_flutter_libs import stack_wallet_backup import url_launcher_macos import wakelock_macos @@ -35,6 +36,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) + Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin")) StackWalletBackupPlugin.register(with: registry.registrar(forPlugin: "StackWalletBackupPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) WakelockMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockMacosPlugin")) diff --git a/pubspec.lock b/pubspec.lock index a9cfa08cf..4caa61444 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1633,6 +1633,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" + sqlite3: + dependency: "direct main" + description: + name: sqlite3 + sha256: b384f598b813b347c5a7e5ffad82cbaff1bec3d1561af267041e66f6f0899295 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + sqlite3_flutter_libs: + dependency: "direct main" + description: + name: sqlite3_flutter_libs + sha256: "1e62698dc1ab396152ccaf3b3990d826244e9f3c8c39b51805f209adcd6dbea3" + url: "https://pub.dev" + source: hosted + version: "0.5.22" stack_trace: dependency: transitive description: diff --git a/scripts/app_config/templates/pubspec.template b/scripts/app_config/templates/pubspec.template index a77535133..71410f213 100644 --- a/scripts/app_config/templates/pubspec.template +++ b/scripts/app_config/templates/pubspec.template @@ -183,6 +183,8 @@ dependencies: ref: a83e375678eb22fe544dc125d29bbec0fb833882 path: packages/solana calendar_date_picker2: ^1.0.2 + sqlite3: ^2.4.3 + sqlite3_flutter_libs: ^0.5.22 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index f886b2cc1..d8d4b219f 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); SharePlusWindowsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); + Sqlite3FlutterLibsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin")); StackWalletBackupPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("StackWalletBackupPluginCApi")); UrlLauncherWindowsRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 02d70698f..b00cd149f 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -10,6 +10,7 @@ list(APPEND FLUTTER_PLUGIN_LIST isar_flutter_libs permission_handler_windows share_plus + sqlite3_flutter_libs stack_wallet_backup url_launcher_windows window_size From 3eb5a0a39c2bab81e62bb50e85ac6d33fce14c13 Mon Sep 17 00:00:00 2001 From: julian Date: Wed, 29 May 2024 13:29:45 -0600 Subject: [PATCH 057/100] Cache Spark anonymity sets in sqlite --- lib/db/db_version_migration.dart | 23 ++ lib/db/hive/db.dart | 17 +- lib/db/sqlite/firo_cache.dart | 330 ++++++++++++++++++ .../cached_electrumx_client.dart | 64 ---- lib/electrumx_rpc/electrumx_client.dart | 15 +- lib/main.dart | 2 + .../global_settings_view/hidden_settings.dart | 50 +-- lib/utilities/constants.dart | 2 +- lib/utilities/stack_file_system.dart | 13 + lib/wallets/wallet/impl/firo_wallet.dart | 31 +- lib/wallets/wallet/wallet.dart | 7 + .../spark_interface.dart | 220 ++++++------ 12 files changed, 550 insertions(+), 224 deletions(-) create mode 100644 lib/db/sqlite/firo_cache.dart diff --git a/lib/db/db_version_migration.dart b/lib/db/db_version_migration.dart index fe563f5ea..8fc4c6963 100644 --- a/lib/db/db_version_migration.dart +++ b/lib/db/db_version_migration.dart @@ -422,6 +422,20 @@ class DbVersionMigrator with WalletDB { // try to continue migrating return await migrate(12, secureStore: secureStore); + case 12: + // migrate + await _v12(secureStore); + + // update version + await DB.instance.put( + boxName: DB.boxNameDBInfo, + key: "hive_data_version", + value: 13, + ); + + // try to continue migrating + return await migrate(13, secureStore: secureStore); + default: // finally return return; @@ -701,4 +715,13 @@ class DbVersionMigrator with WalletDB { Future _v11(SecureStorageInterface secureStore) async { await migrateWalletsToIsar(secureStore: secureStore); } + + Future _v12(SecureStorageInterface secureStore) async { + await DB.instance.deleteBoxFromDisk( + boxName: "firo_anonymitySetSparkCache", + ); + await DB.instance.deleteBoxFromDisk( + boxName: "firoTestNet_anonymitySetSparkCache", + ); + } } diff --git a/lib/db/hive/db.dart b/lib/db/hive/db.dart index 3f1c86cb7..7fe515c4a 100644 --- a/lib/db/hive/db.dart +++ b/lib/db/hive/db.dart @@ -13,6 +13,7 @@ import 'dart:isolate'; import 'package:cw_core/wallet_info.dart' as xmr; import 'package:hive/hive.dart'; import 'package:mutex/mutex.dart'; + import '../../app_config.dart'; import '../../models/exchange/response_objects/trade.dart'; import '../../models/node_model.dart'; @@ -55,8 +56,6 @@ class DB { // firo only String _boxNameSetCache({required CryptoCurrency currency}) => "${currency.identifier}_anonymitySetCache"; - String _boxNameSetSparkCache({required CryptoCurrency currency}) => - "${currency.identifier}_anonymitySetSparkCache"; String _boxNameUsedSerialsCache({required CryptoCurrency currency}) => "${currency.identifier}_usedSerialsCache"; String _boxNameSparkUsedCoinsTagsCache({required CryptoCurrency currency}) => @@ -81,7 +80,6 @@ class DB { final Map> _txCacheBoxes = {}; final Map> _setCacheBoxes = {}; - final Map> _setSparkCacheBoxes = {}; final Map> _usedSerialsCacheBoxes = {}; final Map> _getSparkUsedCoinsTagsCacheBoxes = {}; @@ -213,16 +211,6 @@ class DB { await Hive.openBox(_boxNameSetCache(currency: currency)); } - Future> getSparkAnonymitySetCacheBox({ - required CryptoCurrency currency, - }) async { - if (_setSparkCacheBoxes[currency.identifier]?.isOpen != true) { - _setSparkCacheBoxes.remove(currency.identifier); - } - return _setSparkCacheBoxes[currency.identifier] ??= - await Hive.openBox(_boxNameSetSparkCache(currency: currency)); - } - Future closeAnonymitySetCacheBox({ required CryptoCurrency currency, }) async { @@ -266,9 +254,6 @@ class DB { await deleteAll(boxName: _boxNameTxCache(currency: currency)); if (currency is Firo) { await deleteAll(boxName: _boxNameSetCache(currency: currency)); - await deleteAll( - boxName: _boxNameSetSparkCache(currency: currency), - ); await deleteAll( boxName: _boxNameUsedSerialsCache(currency: currency), ); diff --git a/lib/db/sqlite/firo_cache.dart b/lib/db/sqlite/firo_cache.dart new file mode 100644 index 000000000..c8ac015e8 --- /dev/null +++ b/lib/db/sqlite/firo_cache.dart @@ -0,0 +1,330 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:sqlite3/sqlite3.dart'; + +import '../../electrumx_rpc/electrumx_client.dart'; +import '../../utilities/logger.dart'; +import '../../utilities/stack_file_system.dart'; + +/// Temporary debugging log function for this file +void _debugLog(Object? object) { + if (kDebugMode) { + Logging.instance.log( + object, + level: LogLevel.Fatal, + ); + } +} + +/// Wrapper class for [FiroCache] as [FiroCache] should eventually be handled in a +/// background isolate and [FiroCacheCoordinator] should manage that isolate +abstract class FiroCacheCoordinator { + static Future init() => _FiroCache.init(); + + static Future runFetchAndUpdateSparkAnonSetCacheForGroupId( + int groupId, + ElectrumXClient client, + ) async { + final blockhashResult = + await FiroCacheCoordinator.getLatestSetInfoForGroupId( + groupId, + ); + final blockHash = blockhashResult?.blockHash ?? ""; + + final json = await client.getSparkAnonymitySet( + coinGroupId: groupId.toString(), + startBlockHash: blockHash, + ); + + await _FiroCache._updateWith(json, groupId); + } + + static Future getSetCoinsForGroupId( + int groupId, { + int? newerThanTimeStamp, + }) async { + return await _FiroCache._getSetCoinsForGroupId( + groupId, + newerThanTimeStamp: newerThanTimeStamp, + ); + } + + static Future< + ({ + String blockHash, + String setHash, + int timestampUTC, + })?> getLatestSetInfoForGroupId( + int groupId, + ) async { + final result = await _FiroCache._getLatestSetInfoForGroupId(groupId); + + if (result.isEmpty) { + return null; + } + + return ( + blockHash: result.first["blockHash"] as String, + setHash: result.first["setHash"] as String, + timestampUTC: result.first["timestampUTC"] as int, + ); + } +} + +abstract class _FiroCache { + static const String sqliteDbFileName = "firo_ex_cache.sqlite3"; + + static Database? _db; + static Database get db { + if (_db == null) { + throw Exception( + "FiroCache.init() must be called before accessing FiroCache.db!", + ); + } + return _db!; + } + + static Future? _initFuture; + static Future init() => _initFuture ??= _init(); + + static Future _init() async { + final sqliteDir = await StackFileSystem.applicationSQLiteDirectory(); + + final file = File("${sqliteDir.path}/$sqliteDbFileName"); + + final exists = await file.exists(); + if (!exists) { + await _createDb(file.path); + } + + _db = sqlite3.open( + file.path, + mode: OpenMode.readWrite, + ); + } + + static Future _createDb(String file) async { + final db = sqlite3.open( + file, + mode: OpenMode.readWriteCreate, + ); + + db.execute( + """ + CREATE TABLE SparkSet ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, + blockHash TEXT NOT NULL, + setHash TEXT NOT NULL, + groupId INTEGER NOT NULL, + UNIQUE (blockHash, setHash, groupId) + ); + + CREATE TABLE SparkCoin ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, + serialized TEXT NOT NULL, + txHash TEXT NOT NULL, + context TEXT NOT NULL, + UNIQUE(serialized, txHash, context) + ); + + CREATE TABLE SparkSetCoins ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, + timestampUTC INTEGER NOT NULL, + setId INTEGER NOT NULL, + coinId INTEGER NOT NULL, + FOREIGN KEY (setId) REFERENCES SparkSet(id), + FOREIGN KEY (coinId) REFERENCES SparkCoin(id) + ); + """, + ); + + db.dispose(); + } + + // =========================================================================== + // =============== Spark anonymity set queries =============================== + + static Future _getSetCoinsForGroupId( + int groupId, { + int? newerThanTimeStamp, + }) async { + String query = """ + SELECT sc.id, sc.serialized, sc.txHash, sc.context + FROM SparkSetCoins AS ssc + JOIN SparkSet AS ss ON ssc.setId = ss.id + JOIN SparkCoin AS sc ON ssc.coinId = sc.id + WHERE ss.groupId = $groupId + """; + + if (newerThanTimeStamp != null) { + query += " AND ssc.timestampUTC" + " > $newerThanTimeStamp"; + } + + return db.select("$query;"); + } + + static Future _getLatestSetInfoForGroupId( + int groupId, + ) async { + final query = """ + SELECT ss.blockHash, ss.setHash, ssc.timestampUTC + FROM SparkSet ss + JOIN SparkSetCoins ssc ON ss.id = ssc.setId + WHERE ss.groupId = $groupId + ORDER BY ssc.timestampUTC DESC + LIMIT 1; + """; + + return db.select("$query;"); + } + + // =========================================================================== + // =========================================================================== + + static int _upCount = 0; + + /// update the sqlite cache + /// Expected json format: + /// { + /// "blockHash": "someBlockHash", + /// "setHash": "someSetHash", + /// "coins": [ + /// ["serliazed1", "hash1", "context1"], + /// ["serliazed2", "hash2", "context2"], + /// ... + /// ["serliazed3", "hash3", "context3"], + /// ["serliazed4", "hash4", "context4"], + /// ], + /// } + /// + /// returns true if successful, otherwise false + static Future _updateWith( + Map json, + int groupId, + ) async { + final start = DateTime.now(); + _upCount++; + final blockHash = json["blockHash"] as String; + final setHash = json["setHash"] as String; + + _debugLog( + "$_upCount _updateWith() called where groupId=$groupId," + " blockHash=$blockHash, setHash=$setHash", + ); + + final checkResult = db.select( + """ + SELECT * + FROM SparkSet + WHERE blockHash = ? AND setHash = ? AND groupId = ?; + """, + [ + blockHash, + setHash, + groupId, + ], + ); + + _debugLog("$_upCount _updateWith() called where checkResult=$checkResult"); + + if (checkResult.isNotEmpty) { + _debugLog( + "$_upCount _updateWith() duration = ${DateTime.now().difference(start)}", + ); + // already up to date + return true; + } + + if ((json["coins"] as List).isEmpty) { + _debugLog("$_upCount _updateWith() called where json[coins] is Empty"); + _debugLog( + "$_upCount _updateWith() duration = ${DateTime.now().difference(start)}", + ); + // no coins to actually insert + return true; + } + + final coins = (json["coins"] as List) + .map( + (e) => [ + e[0] as String, + e[1] as String, + e[2] as String, + ], + ) + .toList(); + + final timestamp = DateTime.now().toUtc().millisecondsSinceEpoch ~/ 1000; + + db.execute("BEGIN;"); + try { + db.execute( + """ + INSERT INTO SparkSet (blockHash, setHash, groupId) + VALUES (?, ?, ?); + """, + [blockHash, setHash, groupId], + ); + final setId = db.lastInsertRowId; + + for (final coin in coins) { + int coinId; + try { + db.execute( + """ + INSERT INTO SparkCoin (serialized, txHash, context) + VALUES (?, ?, ?); + """, + coin, + ); + coinId = db.lastInsertRowId; + } on SqliteException catch (e) { + if (e.extendedResultCode == 2067) { + final result = db.select( + """ + SELECT id + FROM SparkCoin + WHERE serialized = ? AND txHash = ? AND context = ?; + """, + coin, + ); + coinId = result.first["id"] as int; + } else { + rethrow; + } + } + + db.execute( + """ + INSERT INTO SparkSetCoins (timestampUTC, setId, coinId) + VALUES (?, ?, ?); + """, + [timestamp, setId, coinId], + ); + } + + db.execute("COMMIT;"); + _debugLog("$_upCount _updateWith() COMMITTED"); + _debugLog( + "$_upCount _updateWith() duration = ${DateTime.now().difference(start)}", + ); + return true; + } catch (e, s) { + db.execute("ROLLBACK;"); + _debugLog("$_upCount _updateWith() ROLLBACK"); + _debugLog( + "$_upCount _updateWith() duration = ${DateTime.now().difference(start)}", + ); + // NOTE THIS LOGGER MUST BE CALLED ON MAIN ISOLATE FOR NOW + Logging.instance.log( + "$e\n$s", + level: LogLevel.Error, + ); + } + + return false; + } +} diff --git a/lib/electrumx_rpc/cached_electrumx_client.dart b/lib/electrumx_rpc/cached_electrumx_client.dart index fb4ff4a87..c2dbbb6b8 100644 --- a/lib/electrumx_rpc/cached_electrumx_client.dart +++ b/lib/electrumx_rpc/cached_electrumx_client.dart @@ -116,70 +116,6 @@ class CachedElectrumXClient { } } - Future> getSparkAnonymitySet({ - required String groupId, - String blockhash = "", - required CryptoCurrency cryptoCurrency, - required bool useOnlyCacheIfNotEmpty, - }) async { - try { - final box = await DB.instance.getSparkAnonymitySetCacheBox( - currency: cryptoCurrency, - ); - final cachedSet = box.get(groupId) as Map?; - - Map set; - - // null check to see if there is a cached set - if (cachedSet == null) { - set = { - "coinGroupID": int.parse(groupId), - "blockHash": blockhash, - "setHash": "", - "coins": [], - }; - } else { - set = Map.from(cachedSet); - if (useOnlyCacheIfNotEmpty) { - return set; - } - } - - final newSet = await electrumXClient.getSparkAnonymitySet( - coinGroupId: groupId, - startBlockHash: set["blockHash"] as String, - ); - - // update set with new data - if (newSet["setHash"] != "" && set["setHash"] != newSet["setHash"]) { - set["setHash"] = newSet["setHash"]; - set["blockHash"] = newSet["blockHash"]; - for (int i = (newSet["coins"] as List).length - 1; i >= 0; i--) { - // TODO verify this is correct (or append?) - if ((set["coins"] as List) - .where((e) => e[0] == newSet["coins"][i][0]) - .isEmpty) { - set["coins"].insert(0, newSet["coins"][i]); - } - } - // save set to db - await box.put(groupId, set); - Logging.instance.log( - "Updated current anonymity set for ${cryptoCurrency.identifier} with group ID $groupId", - level: LogLevel.Info, - ); - } - - return set; - } catch (e, s) { - Logging.instance.log( - "Failed to process CachedElectrumX.getSparkAnonymitySet(): $e\n$s", - level: LogLevel.Error, - ); - rethrow; - } - } - String base64ToHex(String source) => base64Decode(LineSplitter.split(source).join()) .map((e) => e.toRadixString(16).padLeft(2, '0')) diff --git a/lib/electrumx_rpc/electrumx_client.dart b/lib/electrumx_rpc/electrumx_client.dart index afe44ec14..6d473a71f 100644 --- a/lib/electrumx_rpc/electrumx_client.dart +++ b/lib/electrumx_rpc/electrumx_client.dart @@ -20,7 +20,8 @@ import 'package:event_bus/event_bus.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_libsparkmobile/flutter_libsparkmobile.dart'; import 'package:mutex/mutex.dart'; -import 'client_manager.dart'; +import 'package:stream_channel/stream_channel.dart'; + import '../exceptions/electrumx/no_such_transaction.dart'; import '../services/event_bus/events/global/tor_connection_status_changed_event.dart'; import '../services/event_bus/events/global/tor_status_changed_event.dart'; @@ -29,7 +30,7 @@ import '../services/tor_service.dart'; import '../utilities/logger.dart'; import '../utilities/prefs.dart'; import '../wallets/crypto_currency/crypto_currency.dart'; -import 'package:stream_channel/stream_channel.dart'; +import 'client_manager.dart'; class WifiOnlyException implements Exception {} @@ -910,10 +911,7 @@ class ElectrumXClient { String? requestID, }) async { try { - Logging.instance.log( - "attempting to fetch spark.getsparkanonymityset...", - level: LogLevel.Info, - ); + final start = DateTime.now(); await _checkElectrumAdapter(); final Map response = await (getElectrumAdapter() as FiroElectrumClient) @@ -922,7 +920,10 @@ class ElectrumXClient { startBlockHash: startBlockHash, ); Logging.instance.log( - "Fetching spark.getsparkanonymityset finished", + "Finished ElectrumXClient.getSparkAnonymitySet(coinGroupId" + "=$coinGroupId, startBlockHash=$startBlockHash). " + "" + "Duration=${DateTime.now().difference(start)}", level: LogLevel.Info, ); return response; diff --git a/lib/main.dart b/lib/main.dart index 52370d13d..ae7d26466 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -35,6 +35,7 @@ import 'app_config.dart'; import 'db/db_version_migration.dart'; import 'db/hive/db.dart'; import 'db/isar/main_db.dart'; +import 'db/sqlite/firo_cache.dart'; import 'models/exchange/change_now/exchange_transaction.dart'; import 'models/exchange/change_now/exchange_transaction_status.dart'; import 'models/exchange/response_objects/trade.dart'; @@ -200,6 +201,7 @@ void main(List args) async { } await StackFileSystem.initThemesDir(); + await FiroCacheCoordinator.init(); // Desktop migrate handled elsewhere (currently desktop_login_view.dart) if (!Util.isDesktop) { diff --git a/lib/pages/settings_views/global_settings_view/hidden_settings.dart b/lib/pages/settings_views/global_settings_view/hidden_settings.dart index 973dab913..b389f31a3 100644 --- a/lib/pages/settings_views/global_settings_view/hidden_settings.dart +++ b/lib/pages/settings_views/global_settings_view/hidden_settings.dart @@ -13,6 +13,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; + import '../../../notifications/show_flush_bar.dart'; import '../../../providers/global/debug_service_provider.dart'; import '../../../providers/providers.dart'; @@ -284,28 +285,33 @@ class HiddenSettings extends StatelessWidget { ); }, ), - const SizedBox( - height: 12, - ), - Consumer( - builder: (_, ref, __) { - return GestureDetector( - onTap: () async { - // - }, - child: RoundedWhiteContainer( - child: Text( - "Do nothing", - style: STextStyles.button(context).copyWith( - color: Theme.of(context) - .extension()! - .accentColorDark, - ), - ), - ), - ); - }, - ), + // const SizedBox( + // height: 12, + // ), + // Consumer( + // builder: (_, ref, __) { + // return GestureDetector( + // onTap: () async { + // await showLoading( + // whileFuture: FiroCache.init(), + // context: context, + // rootNavigator: true, + // message: "initializing firo cache", + // ); + // }, + // child: RoundedWhiteContainer( + // child: Text( + // "init firo_cache", + // style: STextStyles.button(context).copyWith( + // color: Theme.of(context) + // .extension()! + // .accentColorDark, + // ), + // ), + // ), + // ); + // }, + // ), ], ), ), diff --git a/lib/utilities/constants.dart b/lib/utilities/constants.dart index d68760139..6b64d4a73 100644 --- a/lib/utilities/constants.dart +++ b/lib/utilities/constants.dart @@ -40,7 +40,7 @@ abstract class Constants { // Enable Logger.print statements static const bool disableLogger = false; - static const int currentDataVersion = 12; + static const int currentDataVersion = 13; static const int rescanV1 = 1; diff --git a/lib/utilities/stack_file_system.dart b/lib/utilities/stack_file_system.dart index 3675813d9..281bd2a8f 100644 --- a/lib/utilities/stack_file_system.dart +++ b/lib/utilities/stack_file_system.dart @@ -91,6 +91,19 @@ abstract class StackFileSystem { } } + static Future applicationSQLiteDirectory() async { + final root = await applicationRootDirectory(); + if (Util.isDesktop) { + final dir = Directory("${root.path}/sqlite"); + if (!dir.existsSync()) { + await dir.create(); + } + return dir; + } else { + return root; + } + } + static Future applicationTorDirectory() async { final root = await applicationRootDirectory(); if (Util.isDesktop) { diff --git a/lib/wallets/wallet/impl/firo_wallet.dart b/lib/wallets/wallet/impl/firo_wallet.dart index dce8cd9fa..918ec563b 100644 --- a/lib/wallets/wallet/impl/firo_wallet.dart +++ b/lib/wallets/wallet/impl/firo_wallet.dart @@ -6,6 +6,7 @@ import 'package:decimal/decimal.dart'; import 'package:flutter_libsparkmobile/flutter_libsparkmobile.dart'; import 'package:isar/isar.dart'; +import '../../../db/sqlite/firo_cache.dart'; import '../../../models/isar/models/blockchain_data/v2/input_v2.dart'; import '../../../models/isar/models/blockchain_data/v2/output_v2.dart'; import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; @@ -587,6 +588,8 @@ class FiroWallet extends Bip39HDWallet @override Future recover({required bool isRescan}) async { + groupIdTimestampUTCMap = {}; + final start = DateTime.now(); final root = await getRootHDNode(); final List addresses})>> receiveFutures = @@ -620,11 +623,15 @@ class FiroWallet extends Bip39HDWallet // spark final latestSparkCoinId = await electrumXClient.getSparkLatestCoinId(); - final sparkAnonSetFuture = electrumXCachedClient.getSparkAnonymitySet( - groupId: latestSparkCoinId.toString(), - cryptoCurrency: info.coin, - useOnlyCacheIfNotEmpty: false, - ); + final List> sparkAnonSetFutures = []; + for (int i = 1; i <= latestSparkCoinId; i++) { + sparkAnonSetFutures.add( + FiroCacheCoordinator.runFetchAndUpdateSparkAnonSetCacheForGroupId( + i, + electrumXClient, + ), + ); + } final sparkUsedCoinTagsFuture = electrumXCachedClient.getSparkUsedCoinsTags( cryptoCurrency: info.coin, @@ -739,8 +746,8 @@ class FiroWallet extends Bip39HDWallet final futureResults = await Future.wait([ usedSerialNumbersFuture, setDataMapFuture, - sparkAnonSetFuture, sparkUsedCoinTagsFuture, + ...sparkAnonSetFutures, ]); // lelantus @@ -748,8 +755,7 @@ class FiroWallet extends Bip39HDWallet final setDataMap = futureResults[1] as Map; // spark - final sparkAnonymitySet = futureResults[2] as Map; - final sparkSpentCoinTags = futureResults[3] as Set; + final sparkSpentCoinTags = futureResults[2] as Set; if (Util.isDesktop) { await Future.wait([ @@ -759,8 +765,8 @@ class FiroWallet extends Bip39HDWallet setDataMap: setDataMap, ), recoverSparkWallet( - anonymitySet: sparkAnonymitySet, spentCoinTags: sparkSpentCoinTags, + latestSparkCoinId: latestSparkCoinId, ), ]); } else { @@ -770,13 +776,18 @@ class FiroWallet extends Bip39HDWallet setDataMap: setDataMap, ); await recoverSparkWallet( - anonymitySet: sparkAnonymitySet, spentCoinTags: sparkSpentCoinTags, + latestSparkCoinId: latestSparkCoinId, ); } }); unawaited(refresh()); + Logging.instance.log( + "Firo recover for " + "${info.name}: ${DateTime.now().difference(start)}", + level: LogLevel.Info, + ); } catch (e, s) { Logging.instance.log( "Exception rethrown from electrumx_mixin recover(): $e\n$s", diff --git a/lib/wallets/wallet/wallet.dart b/lib/wallets/wallet/wallet.dart index 8fa0b9ddf..dd96aa1f4 100644 --- a/lib/wallets/wallet/wallet.dart +++ b/lib/wallets/wallet/wallet.dart @@ -473,6 +473,7 @@ abstract class Wallet { if (refreshMutex.isLocked) { return; } + final start = DateTime.now(); try { // this acquire should be almost instant due to above check. @@ -608,6 +609,12 @@ abstract class Wallet { ); } finally { refreshMutex.release(); + + Logging.instance.log( + "Refresh for " + "${info.name}: ${DateTime.now().difference(start)}", + level: LogLevel.Info, + ); } } diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart index 591869cfe..8b94e941d 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart @@ -7,6 +7,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter_libsparkmobile/flutter_libsparkmobile.dart'; import 'package:isar/isar.dart'; +import '../../../db/sqlite/firo_cache.dart'; import '../../../models/balance.dart'; import '../../../models/isar/models/blockchain_data/v2/input_v2.dart'; import '../../../models/isar/models/blockchain_data/v2/output_v2.dart'; @@ -259,17 +260,39 @@ mixin SparkInterface final List> setMaps = []; final List<({int groupId, String blockHash})> idAndBlockHashes = []; for (int i = 1; i <= currentId; i++) { - final set = await electrumXCachedClient.getSparkAnonymitySet( - groupId: i.toString(), - cryptoCurrency: info.coin, - useOnlyCacheIfNotEmpty: true, + final resultSet = await FiroCacheCoordinator.getSetCoinsForGroupId(i); + if (resultSet.isEmpty) { + continue; + } + + final info = await FiroCacheCoordinator.getLatestSetInfoForGroupId( + i, ); - set["coinGroupID"] = i; - setMaps.add(set); + if (info == null) { + throw Exception("The `info` should never be null here"); + } + + final Map setData = { + "blockHash": info.blockHash, + "setHash": info.setHash, + "coinGroupID": i, + "coins": resultSet + .map( + (row) => [ + row["serialized"] as String, + row["txHash"] as String, + row["context"] as String, + ], + ) + .toList(), + }; + + setData["coinGroupID"] = i; + setMaps.add(setData); idAndBlockHashes.add( ( groupId: i, - blockHash: set["blockHash"] as String, + blockHash: setData["blockHash"] as String, ), ); } @@ -607,78 +630,12 @@ mixin SparkInterface } Future refreshSparkData() async { - final sparkAddresses = await mainDB.isar.addresses - .where() - .walletIdEqualTo(walletId) - .filter() - .typeEqualTo(AddressType.spark) - .findAll(); - - final Set paths = - sparkAddresses.map((e) => e.derivationPath!.value).toSet(); - try { - final latestSparkCoinId = await electrumXClient.getSparkLatestCoinId(); - - final anonymitySetFuture = electrumXCachedClient.getSparkAnonymitySet( - groupId: latestSparkCoinId.toString(), - cryptoCurrency: info.coin, - useOnlyCacheIfNotEmpty: false, - ); - - final spentCoinTagsFuture = electrumXCachedClient.getSparkUsedCoinsTags( + final spentCoinTags = await electrumXCachedClient.getSparkUsedCoinsTags( cryptoCurrency: info.coin, ); - final futureResults = await Future.wait([ - anonymitySetFuture, - spentCoinTagsFuture, - ]); - - final anonymitySet = futureResults[0] as Map; - final spentCoinTags = futureResults[1] as Set; - - final List myCoins = []; - - if (anonymitySet["coins"] is List && - (anonymitySet["coins"] as List).isNotEmpty) { - final root = await getRootHDNode(); - final privateKeyHexSet = paths - .map( - (e) => root.derivePath(e).privateKey.data.toHex, - ) - .toSet(); - - final identifiedCoins = await compute( - _identifyCoins, - ( - anonymitySetCoins: anonymitySet["coins"] as List, - groupId: latestSparkCoinId, - spentCoinTags: spentCoinTags, - privateKeyHexSet: privateKeyHexSet, - walletId: walletId, - isTestNet: cryptoCurrency.network == CryptoCurrencyNetwork.test, - ), - ); - - myCoins.addAll(identifiedCoins); - } - - // check current coins - final currentCoins = await mainDB.isar.sparkCoins - .where() - .walletIdEqualToAnyLTagHash(walletId) - .filter() - .isUsedEqualTo(false) - .findAll(); - for (final coin in currentCoins) { - if (spentCoinTags.contains(coin.lTagHash)) { - myCoins.add(coin.copyWith(isUsed: true)); - } - } - - // update wallet spark coins in isar - await _addOrUpdateSparkCoins(myCoins); + await _checkAndUpdateCoins(spentCoinTags, true); // refresh spark balance await refreshSparkBalance(); @@ -734,11 +691,14 @@ mixin SparkInterface ); } + // TODO: look into persistence for this? + Map groupIdTimestampUTCMap = {}; + /// Should only be called within the standard wallet [recover] function due to /// mutex locking. Otherwise behaviour MAY be undefined. Future recoverSparkWallet({ - required Map anonymitySet, required Set spentCoinTags, + required int latestSparkCoinId, }) async { // generate spark addresses if non existing if (await getCurrentReceivingSparkAddress() == null) { @@ -746,35 +706,8 @@ mixin SparkInterface await mainDB.putAddress(address); } - final sparkAddresses = await mainDB.isar.addresses - .where() - .walletIdEqualTo(walletId) - .filter() - .typeEqualTo(AddressType.spark) - .findAll(); - - final Set paths = - sparkAddresses.map((e) => e.derivationPath!.value).toSet(); - try { - final root = await getRootHDNode(); - final privateKeyHexSet = - paths.map((e) => root.derivePath(e).privateKey.data.toHex).toSet(); - - final myCoins = await compute( - _identifyCoins, - ( - anonymitySetCoins: anonymitySet["coins"] as List, - groupId: anonymitySet["coinGroupID"] as int, - spentCoinTags: spentCoinTags, - privateKeyHexSet: privateKeyHexSet, - walletId: walletId, - isTestNet: cryptoCurrency.network == CryptoCurrencyNetwork.test, - ), - ); - - // update wallet spark coins in isar - await _addOrUpdateSparkCoins(myCoins); + await _checkAndUpdateCoins(spentCoinTags, false); // refresh spark balance await refreshSparkBalance(); @@ -787,6 +720,85 @@ mixin SparkInterface } } + Future _checkAndUpdateCoins( + Set spentCoinTags, + bool checkUseds, + ) async { + final sparkAddresses = await mainDB.isar.addresses + .where() + .walletIdEqualTo(walletId) + .filter() + .typeEqualTo(AddressType.spark) + .findAll(); + final root = await getRootHDNode(); + final Set privateKeyHexSet = sparkAddresses + .map( + (e) => root.derivePath(e.derivationPath!.value).privateKey.data.toHex, + ) + .toSet(); + + List? currentCoins; + if (checkUseds) { + currentCoins = await mainDB.isar.sparkCoins + .where() + .walletIdEqualToAnyLTagHash(walletId) + .filter() + .isUsedEqualTo(false) + .findAll(); + } + + final latestSparkCoinId = await electrumXClient.getSparkLatestCoinId(); + for (int i = 1; i <= latestSparkCoinId; i++) { + final lastCheckedTimeStampUTC = groupIdTimestampUTCMap[i] ?? 0; + final info = await FiroCacheCoordinator.getLatestSetInfoForGroupId( + i, + ); + final anonymitySetResult = + await FiroCacheCoordinator.getSetCoinsForGroupId( + i, + newerThanTimeStamp: lastCheckedTimeStampUTC, + ); + final coinsRaw = anonymitySetResult + .map( + (row) => [ + row["serialized"] as String, + row["txHash"] as String, + row["context"] as String, + ], + ) + .toList(); + + if (coinsRaw.isNotEmpty) { + final myCoins = await compute( + _identifyCoins, + ( + anonymitySetCoins: coinsRaw, + groupId: i, + spentCoinTags: spentCoinTags, + privateKeyHexSet: privateKeyHexSet, + walletId: walletId, + isTestNet: cryptoCurrency.network == CryptoCurrencyNetwork.test, + ), + ); + + if (checkUseds && currentCoins != null) { + for (final coin in currentCoins) { + if (spentCoinTags.contains(coin.lTagHash)) { + myCoins.add(coin.copyWith(isUsed: true)); + } + } + } + + // update wallet spark coins in isar + await _addOrUpdateSparkCoins(myCoins); + } + groupIdTimestampUTCMap[i] = max( + lastCheckedTimeStampUTC, + info?.timestampUTC ?? lastCheckedTimeStampUTC, + ); + } + } + // modelled on CSparkWallet::CreateSparkMintTransactions https://github.com/firoorg/firo/blob/39c41e5e7ec634ced3700fe3f4f5509dc2e480d0/src/spark/sparkwallet.cpp#L752 Future> _createSparkMintTransactions({ required List availableUtxos, From ef96f3b76c3fc1e7a42b835879142e7dde4a0f2f Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 29 May 2024 17:45:42 -0500 Subject: [PATCH 058/100] Add enableLelantusScanning bool and restore/rescan logic Squashed commit msgs: add (currently unused) bool enableLelantusScanning to WalletInfo only do Lelantus things if Lelantus is enabled 6ac468 --- lib/wallets/isar/models/wallet_info.dart | 1 + lib/wallets/wallet/impl/firo_wallet.dart | 89 +++++++++++++++--------- 2 files changed, 58 insertions(+), 32 deletions(-) diff --git a/lib/wallets/isar/models/wallet_info.dart b/lib/wallets/isar/models/wallet_info.dart index a84fe4348..351229c29 100644 --- a/lib/wallets/isar/models/wallet_info.dart +++ b/lib/wallets/isar/models/wallet_info.dart @@ -507,4 +507,5 @@ abstract class WalletInfoKeys { static const String tezosDerivationPath = "tezosDerivationPathKey"; static const String lelantusCoinIsarRescanRequired = "lelantusCoinIsarRescanRequired"; + static const String enableLelantusScanning = "enableLelantusScanning"; } diff --git a/lib/wallets/wallet/impl/firo_wallet.dart b/lib/wallets/wallet/impl/firo_wallet.dart index dce8cd9fa..510d08cfb 100644 --- a/lib/wallets/wallet/impl/firo_wallet.dart +++ b/lib/wallets/wallet/impl/firo_wallet.dart @@ -610,13 +610,25 @@ class FiroWallet extends Bip39HDWallet await mainDB.deleteWalletBlockchainData(walletId); } + // Parse otherDataJsonString to get the enableLelantusScanning value. + bool? enableLelantusScanning = false; + if (info.otherDataJsonString != null) { + final otherDataJson = json.decode(info.otherDataJsonString!); + enableLelantusScanning = + otherDataJson["enableLelantusScanning"] as bool? ?? false; + } + // lelantus - final latestSetId = await electrumXClient.getLelantusLatestCoinId(); - final setDataMapFuture = getSetDataMap(latestSetId); - final usedSerialNumbersFuture = - electrumXCachedClient.getUsedCoinSerials( - cryptoCurrency: info.coin, - ); + int? latestSetId; + Future>? setDataMapFuture; + Future>? usedSerialNumbersFuture; + if (enableLelantusScanning) { + latestSetId = await electrumXClient.getLelantusLatestCoinId(); + setDataMapFuture = getSetDataMap(latestSetId); + usedSerialNumbersFuture = electrumXCachedClient.getUsedCoinSerials( + cryptoCurrency: info.coin, + ); + } // spark final latestSparkCoinId = await electrumXClient.getSparkLatestCoinId(); @@ -736,39 +748,52 @@ class FiroWallet extends Bip39HDWallet updateUTXOs(), ]); - final futureResults = await Future.wait([ - usedSerialNumbersFuture, - setDataMapFuture, - sparkAnonSetFuture, - sparkUsedCoinTagsFuture, - ]); + List> futures = []; + + futures.add(sparkAnonSetFuture); + futures.add(sparkUsedCoinTagsFuture); + if (enableLelantusScanning) { + futures.add(usedSerialNumbersFuture!); + futures.add(setDataMapFuture!); + } + + final futureResults = await Future.wait(futures); // lelantus - final usedSerialsSet = (futureResults[0] as List).toSet(); - final setDataMap = futureResults[1] as Map; + Set? usedSerialsSet; + Map? setDataMap; + if (enableLelantusScanning) { + usedSerialsSet = (futureResults[2] as List).toSet(); + setDataMap = futureResults[3] as Map; + } // spark - final sparkAnonymitySet = futureResults[2] as Map; - final sparkSpentCoinTags = futureResults[3] as Set; + final sparkAnonymitySet = futureResults[0] as Map; + final sparkSpentCoinTags = futureResults[1] as Set; if (Util.isDesktop) { - await Future.wait([ - recoverLelantusWallet( - latestSetId: latestSetId, - usedSerialNumbers: usedSerialsSet, - setDataMap: setDataMap, - ), - recoverSparkWallet( - anonymitySet: sparkAnonymitySet, - spentCoinTags: sparkSpentCoinTags, - ), - ]); + List> futures = []; + if (enableLelantusScanning) { + futures.add(recoverLelantusWallet( + latestSetId: latestSetId!, + usedSerialNumbers: usedSerialsSet!, + setDataMap: setDataMap!, + )); + } + futures.add(recoverSparkWallet( + anonymitySet: sparkAnonymitySet, + spentCoinTags: sparkSpentCoinTags, + )); + + await Future.wait(futures); } else { - await recoverLelantusWallet( - latestSetId: latestSetId, - usedSerialNumbers: usedSerialsSet, - setDataMap: setDataMap, - ); + if (enableLelantusScanning) { + await recoverLelantusWallet( + latestSetId: latestSetId!, + usedSerialNumbers: usedSerialsSet!, + setDataMap: setDataMap!, + ); + } await recoverSparkWallet( anonymitySet: sparkAnonymitySet, spentCoinTags: sparkSpentCoinTags, From 32561b56943c5060053838387086df003a25e812 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 29 May 2024 17:46:21 -0500 Subject: [PATCH 059/100] Scan for Lelantus transactions desktop UI Squashed commit msgs: WIP add lelantus checkmark to restore options view, pass to restore view make bool optional, pass it style restore option text --- .../restore_options_view.dart | 32 ++++++++- .../restore_wallet_view.dart | 25 ++++--- .../more_features/more_features_dialog.dart | 67 +++++++++++++++++++ lib/route_generator.dart | 7 +- 4 files changed, 119 insertions(+), 12 deletions(-) diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart index e04ed9959..8e6459649 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart @@ -72,6 +72,9 @@ class _RestoreOptionsViewState extends ConsumerState { bool get supportsMnemonicPassphrase => coin.hasMnemonicPassphraseSupport; + bool enableLelantusScanning = false; + bool get supportsLelantus => coin is Firo; + @override void initState() { walletName = widget.walletName; @@ -107,12 +110,13 @@ class _RestoreOptionsViewState extends ConsumerState { if (mounted) { await Navigator.of(context).pushNamed( RestoreWalletView.routeName, - arguments: Tuple5( + arguments: Tuple6( walletName, coin, ref.read(mnemonicWordCountStateProvider.state).state, _restoreFromDate, passwordController.text, + enableLelantusScanning, ), ); } @@ -437,6 +441,32 @@ class _RestoreOptionsViewState extends ConsumerState { color: Colors.transparent, child: Column( children: [ + Row( + children: [ + Checkbox( + value: enableLelantusScanning, + onChanged: (bool? newValue) { + setState(() { + enableLelantusScanning = newValue ?? true; + }); + }, + ), + Text( + 'Scan for Lelantus transactions', + style: isDesktop + ? STextStyles.desktopTextExtraSmall(context) + .copyWith( + color: Theme.of(context) + .extension()! + .textSubtitle1, + ) + : STextStyles.itemSubtitle(context), + ), + ], + ), + const SizedBox( + height: 8, + ), ClipRRect( borderRadius: BorderRadius.circular( Constants.size.circularBorderRadius, diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart index 9daf9d300..d7fcd7264 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart @@ -22,15 +22,9 @@ import 'package:flutter_libmonero/monero/monero.dart' as libxmr; import 'package:flutter_libmonero/wownero/wownero.dart' as libwow; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:wakelock/wakelock.dart'; + import '../../../notifications/show_flush_bar.dart'; -import '../add_token_view/edit_wallet_tokens_view.dart'; -import 'confirm_recovery_dialog.dart'; -import 'sub_widgets/restore_failed_dialog.dart'; -import 'sub_widgets/restore_succeeded_dialog.dart'; -import 'sub_widgets/restoring_dialog.dart'; -import '../select_wallet_for_token_view.dart'; -import '../verify_recovery_phrase_view/verify_recovery_phrase_view.dart'; -import '../../home_view/home_view.dart'; import '../../../pages_desktop_specific/desktop_home_view.dart'; import '../../../pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart'; import '../../../providers/db/main_db_provider.dart'; @@ -64,7 +58,14 @@ import '../../../widgets/icon_widgets/qrcode_icon.dart'; import '../../../widgets/table_view/table_view.dart'; import '../../../widgets/table_view/table_view_cell.dart'; import '../../../widgets/table_view/table_view_row.dart'; -import 'package:wakelock/wakelock.dart'; +import '../../home_view/home_view.dart'; +import '../add_token_view/edit_wallet_tokens_view.dart'; +import '../select_wallet_for_token_view.dart'; +import '../verify_recovery_phrase_view/verify_recovery_phrase_view.dart'; +import 'confirm_recovery_dialog.dart'; +import 'sub_widgets/restore_failed_dialog.dart'; +import 'sub_widgets/restore_succeeded_dialog.dart'; +import 'sub_widgets/restoring_dialog.dart'; class RestoreWalletView extends ConsumerStatefulWidget { const RestoreWalletView({ @@ -74,6 +75,7 @@ class RestoreWalletView extends ConsumerStatefulWidget { required this.seedWordsLength, required this.mnemonicPassphrase, required this.restoreFromDate, + this.enableLelantusScanning = false, this.barcodeScanner = const BarcodeScannerWrapper(), this.clipboard = const ClipboardWrapper(), }); @@ -85,6 +87,7 @@ class RestoreWalletView extends ConsumerStatefulWidget { final String mnemonicPassphrase; final int seedWordsLength; final DateTime restoreFromDate; + final bool enableLelantusScanning; final BarcodeScannerInterface barcodeScanner; final ClipboardInterface clipboard; @@ -256,6 +259,8 @@ class _RestoreWalletViewState extends ConsumerState { otherDataJsonString = jsonEncode( { WalletInfoKeys.lelantusCoinIsarRescanRequired: false, + WalletInfoKeys.enableLelantusScanning: + widget.enableLelantusScanning, }, ); } @@ -331,6 +336,8 @@ class _RestoreWalletViewState extends ConsumerState { mnemonic: mnemonic, ); + // TODO [prio=high]: Update wallet with widget.enableLelantusScanning. + // TODO: extract interface with isRestore param switch (wallet.runtimeType) { case const (EpiccashWallet): diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart index 10366c646..03f4db1c7 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart @@ -8,9 +8,13 @@ * */ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; + +import '../../../../../providers/db/main_db_provider.dart'; import '../../../../../providers/global/prefs_provider.dart'; import '../../../../../providers/global/wallets_provider.dart'; import '../../../../../themes/stack_colors.dart'; @@ -18,10 +22,12 @@ import '../../../../../utilities/assets.dart'; import '../../../../../utilities/text_styles.dart'; import '../../../../../wallets/crypto_currency/coins/banano.dart'; import '../../../../../wallets/crypto_currency/coins/firo.dart'; +import '../../../../../wallets/isar/models/wallet_info.dart'; import '../../../../../wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart'; import '../../../../../wallets/wallet/wallet_mixin_interfaces/coin_control_interface.dart'; import '../../../../../wallets/wallet/wallet_mixin_interfaces/ordinals_interface.dart'; import '../../../../../wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart'; +import '../../../../../widgets/custom_buttons/draggable_switch_button.dart'; import '../../../../../widgets/desktop/desktop_dialog.dart'; import '../../../../../widgets/desktop/desktop_dialog_close_button.dart'; import '../../../../../widgets/rounded_container.dart'; @@ -53,6 +59,8 @@ class MoreFeaturesDialog extends ConsumerStatefulWidget { } class _MoreFeaturesDialogState extends ConsumerState { + bool? enableLelantusScanning = false; + @override Widget build(BuildContext context) { final wallet = ref.watch( @@ -61,6 +69,13 @@ class _MoreFeaturesDialogState extends ConsumerState { ), ); + // Parse otherDataJsonString to get the enableLelantusScanning value. + if (wallet.info.otherDataJsonString != null) { + final otherDataJson = json.decode(wallet.info.otherDataJsonString!); + enableLelantusScanning = + otherDataJson["enableLelantusScanning"] as bool? ?? false; + } + final coinControlPrefEnabled = ref.watch( prefsChangeNotifierProvider.select( (value) => value.enableCoinControl, @@ -136,6 +151,58 @@ class _MoreFeaturesDialogState extends ConsumerState { iconAsset: Assets.svg.cashFusion, onPressed: () => widget.onFusionPressed?.call(), ), + if (wallet.info.coin is Firo) + Padding( + padding: const EdgeInsets.symmetric( + vertical: 6, + horizontal: 32, + ), + child: RoundedContainer( + color: Colors.transparent, + borderColor: Theme.of(context) + .extension()! + .textFieldDefaultBG, + child: Row( + children: [ + SizedBox(width: 3), + SizedBox( + height: 20, + width: 40, + child: DraggableSwitchButton( + isOn: enableLelantusScanning ?? false, + onValueChanged: (newValue) { + // Toggle enableLelantusScanning in wallet info. + wallet.info.updateOtherData(newEntries: { + WalletInfoKeys.enableLelantusScanning: + !(enableLelantusScanning ?? false) + }, isar: ref.read(mainDBProvider).isar).then((value) { + // Should setState be used here? + enableLelantusScanning = + !(enableLelantusScanning ?? false); + }); + }, + ), + ), + const SizedBox( + width: 16, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Scan for Lelantus transactions", + style: STextStyles.w600_20(context), + ), + // Text( + // detail, + // style: STextStyles.desktopTextExtraExtraSmall(context), + // ), + ], + ), + ], + ), + ), + ), const SizedBox( height: 28, ), diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 735b8681e..516c1ddb8 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -11,6 +11,8 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:isar/isar.dart'; +import 'package:tuple/tuple.dart'; + import 'models/add_wallet_list_entity/add_wallet_list_entity.dart'; import 'models/add_wallet_list_entity/sub_classes/eth_token_entity.dart'; import 'models/buy/response_objects/quote.dart'; @@ -194,7 +196,6 @@ import 'wallets/models/tx_data.dart'; import 'wallets/wallet/wallet.dart'; import 'widgets/choose_coin_view.dart'; import 'widgets/frost_scaffold.dart'; -import 'package:tuple/tuple.dart'; /* * This file contains all the routes for the app. @@ -1390,7 +1391,8 @@ class RouteGenerator { return _routeError("${settings.name} invalid args: ${args.toString()}"); case RestoreWalletView.routeName: - if (args is Tuple5) { + if (args + is Tuple6) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, builder: (_) => RestoreWalletView( @@ -1399,6 +1401,7 @@ class RouteGenerator { seedWordsLength: args.item3, restoreFromDate: args.item4, mnemonicPassphrase: args.item5, + enableLelantusScanning: args.item6 ?? false, ), settings: RouteSettings( name: settings.name, From 8374d300358057943a3af316d3171b29d16a8f10 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 29 May 2024 19:42:14 -0500 Subject: [PATCH 060/100] Lelantus settings mobile UI and cleanup --- .../restore_wallet_view.dart | 2 - .../lelantus_settings_view.dart | 146 ++++++++++++++++++ .../wallet_settings_wallet_settings_view.dart | 42 ++++- lib/route_generator.dart | 13 ++ pubspec.lock | 2 +- 5 files changed, 199 insertions(+), 6 deletions(-) create mode 100644 lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/lelantus_settings_view.dart diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart index d7fcd7264..a7768b061 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart @@ -336,8 +336,6 @@ class _RestoreWalletViewState extends ConsumerState { mnemonic: mnemonic, ); - // TODO [prio=high]: Update wallet with widget.enableLelantusScanning. - // TODO: extract interface with isRestore param switch (wallet.runtimeType) { case const (EpiccashWallet): diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/lelantus_settings_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/lelantus_settings_view.dart new file mode 100644 index 000000000..58632c1cd --- /dev/null +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/lelantus_settings_view.dart @@ -0,0 +1,146 @@ +/* + * This file is part of Stack Wallet. + * + * Copyright (c) 2023 Cypher Stack + * All Rights Reserved. + * The code is distributed under GPLv3 license, see LICENSE file for details. + * Generated by Cypher Stack on 2023-05-26 + * + */ + +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../../../providers/db/main_db_provider.dart'; +import '../../../../providers/global/wallets_provider.dart'; +import '../../../../themes/stack_colors.dart'; +import '../../../../utilities/text_styles.dart'; +import '../../../../wallets/crypto_currency/crypto_currency.dart'; +import '../../../../wallets/isar/models/wallet_info.dart'; +import '../../../../wallets/wallet/wallet.dart'; +import '../../../../widgets/background.dart'; +import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; +import '../../../../widgets/custom_buttons/draggable_switch_button.dart'; + +class LelantusSettingsView extends ConsumerStatefulWidget { + const LelantusSettingsView({ + super.key, + required this.walletId, + }); + + static const String routeName = "/lelantusSettings"; + + final String walletId; + + @override + ConsumerState createState() => + _LelantusSettingsViewState(); +} + +class _LelantusSettingsViewState extends ConsumerState { + late final TextEditingController _controller; + late final String walletId; + + final _focusNode = FocusNode(); + + bool _isInitialized = false; + Wallet? wallet; + bool? enableLelantusScanning = false; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + if (!_isInitialized) { + // Get the wallet. + wallet = ref.watch( + pWallets.select( + (value) => value.getWallet(widget.walletId), + ), + ); + + // Parse otherDataJsonString to get the enableLelantusScanning value. + if (wallet?.info.otherDataJsonString != null) { + final otherDataJson = json.decode(wallet!.info.otherDataJsonString!); + enableLelantusScanning = + otherDataJson["enableLelantusScanning"] as bool? ?? false; + } + + _isInitialized = true; // Ensure this logic runs only once + } + } + + @override + void dispose() { + _controller.dispose(); + _focusNode.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Background( + child: Scaffold( + backgroundColor: Theme.of(context).extension()!.background, + appBar: AppBar( + leading: AppBarBackButton( + onPressed: () { + Navigator.of(context).pop(); + }, + ), + title: Text( + "Lelantus settings", + style: STextStyles.navBarTitle(context), + ), + ), + body: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Row( + children: [ + SizedBox( + height: 20, + width: 40, + child: DraggableSwitchButton( + isOn: enableLelantusScanning ?? false, + onValueChanged: (newValue) { + // Toggle enableLelantusScanning in wallet info. + wallet?.info.updateOtherData(newEntries: { + WalletInfoKeys.enableLelantusScanning: + !(enableLelantusScanning ?? false) + }, isar: ref.read(mainDBProvider).isar).then((value) { + // Should setState be used here? + enableLelantusScanning = + !(enableLelantusScanning ?? false); + }); + }, + ), + ), + const SizedBox( + width: 16, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Scan for Lelantus transactions", + style: STextStyles.smallMed12(context), + ), + // Text( + // detail, + // style: STextStyles.desktopTextExtraExtraSmall(context), + // ), + ], + ), + ], + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart index a3c3a2969..7c2884258 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart @@ -10,9 +10,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import '../../../pinpad_views/lock_screen_view.dart'; -import 'delete_wallet_warning_view.dart'; -import 'rename_wallet_view.dart'; + import '../../../../route_generator.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/constants.dart'; @@ -22,6 +20,10 @@ import '../../../../widgets/background.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../../widgets/rounded_white_container.dart'; import '../../../../widgets/stack_dialog.dart'; +import '../../../pinpad_views/lock_screen_view.dart'; +import 'delete_wallet_warning_view.dart'; +import 'lelantus_settings_view.dart'; +import 'rename_wallet_view.dart'; class WalletSettingsWalletSettingsView extends ConsumerWidget { const WalletSettingsWalletSettingsView({ @@ -180,6 +182,40 @@ class WalletSettingsWalletSettingsView extends ConsumerWidget { ), ), ), + const SizedBox( + height: 8, + ), + RoundedWhiteContainer( + padding: const EdgeInsets.all(0), + child: RawMaterialButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + onPressed: () { + Navigator.of(context).pushNamed( + LelantusSettingsView.routeName, + arguments: walletId, + ); + }, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 12.0, + vertical: 20, + ), + child: Row( + children: [ + Text( + "Lelantus settings", + style: STextStyles.titleBold12(context), + ), + ], + ), + ), + ), + ), ], ), ), diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 516c1ddb8..27d1eb4f7 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -129,6 +129,7 @@ import 'pages/settings_views/wallet_settings_view/wallet_settings_view.dart'; import 'pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/change_representative_view.dart'; import 'pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_recovery_phrase_view.dart'; import 'pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_warning_view.dart'; +import 'pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/lelantus_settings_view.dart'; import 'pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/rename_wallet_view.dart'; import 'pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart'; import 'pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart'; @@ -1953,6 +1954,18 @@ class RouteGenerator { } return _routeError("${settings.name} invalid args: ${args.toString()}"); + case LelantusSettingsView.routeName: + if (args is String) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => LelantusSettingsView(walletId: args), + settings: RouteSettings( + name: settings.name, + ), + ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); + // == Desktop specific routes ============================================ case CreatePasswordView.routeName: if (args is bool) { diff --git a/pubspec.lock b/pubspec.lock index a9cfa08cf..e8c94e9a2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1194,7 +1194,7 @@ packages: source: hosted version: "0.2.0" monero: - dependency: "direct main" + dependency: transitive description: path: "." ref: "6a17a405a1a260fa228b2f4fc94044088a4335ac" From cbe9919e67b3f80b7568b26245d397379f640cae Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 29 May 2024 19:44:24 -0500 Subject: [PATCH 061/100] Refresh Lelantus data appropriately according to the enableLelantusScanning WalletInfo setting (stored in otherDataJsonString) --- lib/wallets/wallet/wallet.dart | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/wallets/wallet/wallet.dart b/lib/wallets/wallet/wallet.dart index 8fa0b9ddf..83698a819 100644 --- a/lib/wallets/wallet/wallet.dart +++ b/lib/wallets/wallet/wallet.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'package:isar/isar.dart'; import 'package:meta/meta.dart'; @@ -558,7 +559,16 @@ abstract class Wallet { // TODO: [prio=low] handle this differently. Extra modification of this file for coin specific functionality should be avoided. if (this is LelantusInterface) { - await (this as LelantusInterface).refreshLelantusData(); + // Parse otherDataJsonString to get the enableLelantusScanning value. + bool enableLelantusScanning = false; + if (this.info.otherDataJsonString != null) { + final otherDataJson = json.decode(this.info.otherDataJsonString!); + enableLelantusScanning = + otherDataJson["enableLelantusScanning"] as bool? ?? false; + } + if (enableLelantusScanning) { + await (this as LelantusInterface).refreshLelantusData(); + } } GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.90, walletId)); From c7c34803fa8883e3b442774fcac36c2c740ec7dd Mon Sep 17 00:00:00 2001 From: julian Date: Thu, 30 May 2024 10:14:33 -0600 Subject: [PATCH 062/100] use libsparkmobile caching branch --- pubspec.lock | 4 ++-- scripts/app_config/templates/pubspec.template | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 4caa61444..ccd30906c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -689,8 +689,8 @@ packages: dependency: "direct main" description: path: "." - ref: "439727b278250c61a291f5335c298c0f2d952517" - resolved-ref: "439727b278250c61a291f5335c298c0f2d952517" + ref: "7a11d0cadf8c7a6a5d5144dab18cef9536aa5943" + resolved-ref: "7a11d0cadf8c7a6a5d5144dab18cef9536aa5943" url: "https://github.com/cypherstack/flutter_libsparkmobile.git" source: git version: "0.0.1" diff --git a/scripts/app_config/templates/pubspec.template b/scripts/app_config/templates/pubspec.template index 71410f213..c9e284ce9 100644 --- a/scripts/app_config/templates/pubspec.template +++ b/scripts/app_config/templates/pubspec.template @@ -33,7 +33,7 @@ dependencies: flutter_libsparkmobile: git: url: https://github.com/cypherstack/flutter_libsparkmobile.git - ref: 439727b278250c61a291f5335c298c0f2d952517 + ref: 7a11d0cadf8c7a6a5d5144dab18cef9536aa5943 flutter_libmonero: path: ./crypto_plugins/flutter_libmonero From c7e7643fe56cb26fae47dba843632241cf3394da Mon Sep 17 00:00:00 2001 From: julian Date: Thu, 30 May 2024 12:46:41 -0600 Subject: [PATCH 063/100] chunked list extension --- lib/utilities/extensions/impl/list.dart | 19 ++++++++++++++ test/utilities/extensions/list_test.dart | 33 ++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 lib/utilities/extensions/impl/list.dart create mode 100644 test/utilities/extensions/list_test.dart diff --git a/lib/utilities/extensions/impl/list.dart b/lib/utilities/extensions/impl/list.dart new file mode 100644 index 000000000..c802f2704 --- /dev/null +++ b/lib/utilities/extensions/impl/list.dart @@ -0,0 +1,19 @@ +extension ListExt on List { + List> chunked({required int chunkSize}) { + final remainder = length % chunkSize; + final count = length ~/ chunkSize; + final List> result = []; + + int i = 0; + while (i < count) { + result.add(sublist(i, i + chunkSize)); + i++; + } + + if (remainder > 0) { + result.add(sublist(i, i + remainder)); + } + + return result; + } +} diff --git a/test/utilities/extensions/list_test.dart b/test/utilities/extensions/list_test.dart new file mode 100644 index 000000000..594328034 --- /dev/null +++ b/test/utilities/extensions/list_test.dart @@ -0,0 +1,33 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:stackwallet/utilities/extensions/extensions.dart'; + +void main() { + test("Empty list", () { + final List list = []; + expect( + list.chunked(chunkSize: 3).isEmpty, + true, + ); + }); + + test("No remainder", () { + final List list = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + final chunked = list.chunked(chunkSize: 3); + expect(chunked.length == 3, true); + expect( + chunked.map((e) => e.length == 3).reduce((v, e) => v && e), + true, + ); + }); + + test("Some remainder", () { + final List list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + final chunked = list.chunked(chunkSize: 3); + expect(chunked.length == 4, true); + expect(chunked.last.length == 1, true); + expect( + chunked.map((e) => e.length == 3).reduce((v, e) => v && e), + false, + ); + }); +} From d99231c973cac216f4a832230f2a27b13292ecc8 Mon Sep 17 00:00:00 2001 From: julian Date: Thu, 30 May 2024 15:09:26 -0600 Subject: [PATCH 064/100] fix spark anon set fetch using the reverse hex of the blockhash given to us by an earlier call of that same electrumx method --- lib/db/sqlite/firo_cache.dart | 2 +- lib/utilities/extensions/impl/string.dart | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/db/sqlite/firo_cache.dart b/lib/db/sqlite/firo_cache.dart index c8ac015e8..f77e4da15 100644 --- a/lib/db/sqlite/firo_cache.dart +++ b/lib/db/sqlite/firo_cache.dart @@ -35,7 +35,7 @@ abstract class FiroCacheCoordinator { final json = await client.getSparkAnonymitySet( coinGroupId: groupId.toString(), - startBlockHash: blockHash, + startBlockHash: blockHash.toHexReversedFromBase64, ); await _FiroCache._updateWith(json, groupId); diff --git a/lib/utilities/extensions/impl/string.dart b/lib/utilities/extensions/impl/string.dart index d3e50fa78..6a25f23b6 100644 --- a/lib/utilities/extensions/impl/string.dart +++ b/lib/utilities/extensions/impl/string.dart @@ -14,6 +14,7 @@ import 'dart:typed_data'; import 'package:dart_bs58/dart_bs58.dart'; import 'package:dart_bs58check/dart_bs58check.dart'; import 'package:hex/hex.dart'; + import '../extensions.dart'; extension StringExtensions on String { @@ -27,4 +28,14 @@ extension StringExtensions on String { Uint8List get toUint8ListFromBase58CheckEncoded => bs58check.decode(this); BigInt get toBigIntFromHex => toUint8ListFromHex.toBigInt; + + String get toHexFromBase64 => base64Decode(LineSplitter.split(this).join()) + .map((e) => e.toRadixString(16).padLeft(2, '0')) + .join(); + + String get toHexReversedFromBase64 => + base64Decode(LineSplitter.split(this).join()) + .reversed + .map((e) => e.toRadixString(16).padLeft(2, '0')) + .join(); } From 08f01d3141d9334efa13b5376c04bc70116c04b3 Mon Sep 17 00:00:00 2001 From: julian Date: Thu, 30 May 2024 15:10:56 -0600 Subject: [PATCH 065/100] cache used spark tags in sqlite as well --- lib/db/db_version_migration.dart | 14 +- lib/db/hive/db.dart | 17 -- lib/db/sqlite/firo_cache.dart | 270 ++++++++++++++++-- .../cached_electrumx_client.dart | 47 --- lib/electrumx_rpc/electrumx_client.dart | 28 +- lib/utilities/extensions/extensions.dart | 1 + lib/wallets/wallet/impl/firo_wallet.dart | 11 +- .../spark_interface.dart | 128 ++++++--- 8 files changed, 357 insertions(+), 159 deletions(-) diff --git a/lib/db/db_version_migration.dart b/lib/db/db_version_migration.dart index 8fc4c6963..b5da9f505 100644 --- a/lib/db/db_version_migration.dart +++ b/lib/db/db_version_migration.dart @@ -717,11 +717,13 @@ class DbVersionMigrator with WalletDB { } Future _v12(SecureStorageInterface secureStore) async { - await DB.instance.deleteBoxFromDisk( - boxName: "firo_anonymitySetSparkCache", - ); - await DB.instance.deleteBoxFromDisk( - boxName: "firoTestNet_anonymitySetSparkCache", - ); + for (final identifier in ["firo", "firoTestNet"]) { + await DB.instance.deleteBoxFromDisk( + boxName: "${identifier}_anonymitySetSparkCache", + ); + await DB.instance.deleteBoxFromDisk( + boxName: "${identifier}_sparkUsedCoinsTagsCache", + ); + } } } diff --git a/lib/db/hive/db.dart b/lib/db/hive/db.dart index 7fe515c4a..2e9b5435b 100644 --- a/lib/db/hive/db.dart +++ b/lib/db/hive/db.dart @@ -58,8 +58,6 @@ class DB { "${currency.identifier}_anonymitySetCache"; String _boxNameUsedSerialsCache({required CryptoCurrency currency}) => "${currency.identifier}_usedSerialsCache"; - String _boxNameSparkUsedCoinsTagsCache({required CryptoCurrency currency}) => - "${currency.identifier}_sparkUsedCoinsTagsCache"; Box? _boxNodeModels; Box? _boxPrimaryNodes; @@ -229,18 +227,6 @@ class DB { ); } - Future> getSparkUsedCoinsTagsCacheBox({ - required CryptoCurrency currency, - }) async { - if (_getSparkUsedCoinsTagsCacheBoxes[currency.identifier]?.isOpen != true) { - _getSparkUsedCoinsTagsCacheBoxes.remove(currency.identifier); - } - return _getSparkUsedCoinsTagsCacheBoxes[currency.identifier] ??= - await Hive.openBox( - _boxNameSparkUsedCoinsTagsCache(currency: currency), - ); - } - Future closeUsedSerialsCacheBox({ required CryptoCurrency currency, }) async { @@ -257,9 +243,6 @@ class DB { await deleteAll( boxName: _boxNameUsedSerialsCache(currency: currency), ); - await deleteAll( - boxName: _boxNameSparkUsedCoinsTagsCache(currency: currency), - ); } } diff --git a/lib/db/sqlite/firo_cache.dart b/lib/db/sqlite/firo_cache.dart index f77e4da15..a6c2882a0 100644 --- a/lib/db/sqlite/firo_cache.dart +++ b/lib/db/sqlite/firo_cache.dart @@ -2,9 +2,11 @@ import 'dart:async'; import 'dart:io'; import 'package:flutter/foundation.dart'; +import 'package:flutter_libsparkmobile/flutter_libsparkmobile.dart'; import 'package:sqlite3/sqlite3.dart'; import '../../electrumx_rpc/electrumx_client.dart'; +import '../../utilities/extensions/extensions.dart'; import '../../utilities/logger.dart'; import '../../utilities/stack_file_system.dart'; @@ -18,11 +20,31 @@ void _debugLog(Object? object) { } } -/// Wrapper class for [FiroCache] as [FiroCache] should eventually be handled in a +List _ffiHashTagsComputeWrapper(List base64Tags) { + return LibSpark.hashTags(base64Tags: base64Tags); +} + +/// Wrapper class for [_FiroCache] as [_FiroCache] should eventually be handled in a /// background isolate and [FiroCacheCoordinator] should manage that isolate abstract class FiroCacheCoordinator { static Future init() => _FiroCache.init(); + static Future runFetchAndUpdateSparkUsedCoinTags( + ElectrumXClient client, + ) async { + final count = await FiroCacheCoordinator.getUsedCoinTagsLastAddedRowId(); + final unhashedTags = await client.getSparkUnhashedUsedCoinsTags( + startNumber: count, + ); + if (unhashedTags.isNotEmpty) { + final hashedTags = await compute( + _ffiHashTagsComputeWrapper, + unhashedTags, + ); + await _FiroCache._updateSparkUsedTagsWith(hashedTags); + } + } + static Future runFetchAndUpdateSparkAnonSetCacheForGroupId( int groupId, ElectrumXClient client, @@ -38,7 +60,36 @@ abstract class FiroCacheCoordinator { startBlockHash: blockHash.toHexReversedFromBase64, ); - await _FiroCache._updateWith(json, groupId); + await _FiroCache._updateSparkAnonSetCoinsWith(json, groupId); + } + + // =========================================================================== + + static Future> getUsedCoinTags(int startNumber) async { + final result = await _FiroCache._getSparkUsedCoinTags( + startNumber, + ); + return result.map((e) => e["tag"] as String).toSet(); + } + + /// This should be the equivalent of counting the number of tags in the db. + /// Assuming the integrity of the data. Faster than actually calling count on + /// a table where no records have been deleted. None should be deleted from + /// this table in practice. + static Future getUsedCoinTagsLastAddedRowId() async { + final result = await _FiroCache._getUsedCoinTagsLastAddedRowId(); + if (result.isEmpty) { + return 0; + } + return result.first["highestId"] as int? ?? 0; + } + + static Future checkTagIsUsed( + String tag, + ) async { + return await _FiroCache._checkTagIsUsed( + tag, + ); } static Future getSetCoinsForGroupId( @@ -71,6 +122,14 @@ abstract class FiroCacheCoordinator { timestampUTC: result.first["timestampUTC"] as int, ); } + + static Future checkSetInfoForGroupIdExists( + int groupId, + ) async { + return await _FiroCache._checkSetInfoForGroupIdExists( + groupId, + ); + } } abstract class _FiroCache { @@ -137,6 +196,11 @@ abstract class _FiroCache { FOREIGN KEY (setId) REFERENCES SparkSet(id), FOREIGN KEY (coinId) REFERENCES SparkCoin(id) ); + + CREATE TABLE SparkUsedCoinTags ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, + tag TEXT NOT NULL UNIQUE + ); """, ); @@ -181,10 +245,64 @@ abstract class _FiroCache { return db.select("$query;"); } - // =========================================================================== - // =========================================================================== + static Future _checkSetInfoForGroupIdExists( + int groupId, + ) async { + final query = """ + SELECT EXISTS ( + SELECT 1 + FROM SparkSet + WHERE groupId = $groupId + ) AS setExists; + """; - static int _upCount = 0; + return db.select("$query;").first["setExists"] == 1; + } + + // =========================================================================== + // =============== Spark used coin tags queries ============================== + + static Future _getSparkUsedCoinTags( + int startNumber, + ) async { + String query = """ + SELECT tag + FROM SparkUsedCoinTags + """; + + if (startNumber > 0) { + query += " WHERE id >= $startNumber"; + } + + return db.select("$query;"); + } + + static Future _getUsedCoinTagsLastAddedRowId() async { + const query = """ + SELECT MAX(id) AS highestId + FROM SparkUsedCoinTags; + """; + + return db.select("$query;"); + } + + static Future _checkTagIsUsed(String tag) async { + final query = """ + SELECT EXISTS ( + SELECT 1 + FROM SparkUsedCoinTags + WHERE tag = '$tag' + ) AS tagExists; + """; + + return db.select("$query;").first["tagExists"] == 1; + } + + // =========================================================================== + // ================== write to spark used tags cache ========================= + + // debug log counter var + static int _updateTagsCount = 0; /// update the sqlite cache /// Expected json format: @@ -201,20 +319,123 @@ abstract class _FiroCache { /// } /// /// returns true if successful, otherwise false - static Future _updateWith( + static Future _updateSparkUsedTagsWith( + List tags, + ) async { + final start = DateTime.now(); + _updateTagsCount++; + + if (tags.isEmpty) { + _debugLog( + "$_updateTagsCount _updateSparkUsedTagsWith(tags) called " + "where tags is empty", + ); + _debugLog( + "$_updateTagsCount _updateSparkUsedTagsWith() " + "duration = ${DateTime.now().difference(start)}", + ); + // nothing to add, return early + return true; + } else if (tags.length <= 10) { + _debugLog("$_updateTagsCount _updateSparkUsedTagsWith() called where " + "tags.length=${tags.length}, tags: $tags,"); + } else { + _debugLog( + "$_updateTagsCount _updateSparkUsedTagsWith() called where" + " tags.length=${tags.length}," + " first 5 tags: ${tags.sublist(0, 5)}," + " last 5 tags: ${tags.sublist(tags.length - 5, tags.length)}", + ); + } + + db.execute("BEGIN;"); + try { + for (final tag in tags) { + db.execute( + """ + INSERT OR IGNORE INTO SparkUsedCoinTags (tag) + VALUES (?); + """, + [tag], + ); + } + + db.execute("COMMIT;"); + _debugLog("$_updateTagsCount _updateSparkUsedTagsWith() COMMITTED"); + _debugLog( + "$_updateTagsCount _updateSparkUsedTagsWith() " + "duration = ${DateTime.now().difference(start)}", + ); + return true; + } catch (e, s) { + db.execute("ROLLBACK;"); + _debugLog("$_updateTagsCount _updateSparkUsedTagsWith() ROLLBACK"); + _debugLog( + "$_updateTagsCount _updateSparkUsedTagsWith() " + "duration = ${DateTime.now().difference(start)}", + ); + // NOTE THIS LOGGER MUST BE CALLED ON MAIN ISOLATE FOR NOW + Logging.instance.log( + "$e\n$s", + level: LogLevel.Error, + ); + } + + return false; + } + + // =========================================================================== + // ================== write to spark anon set cache ========================== + + // debug log counter var + static int _updateAnonSetCount = 0; + + /// update the sqlite cache + /// Expected json format: + /// { + /// "blockHash": "someBlockHash", + /// "setHash": "someSetHash", + /// "coins": [ + /// ["serliazed1", "hash1", "context1"], + /// ["serliazed2", "hash2", "context2"], + /// ... + /// ["serliazed3", "hash3", "context3"], + /// ["serliazed4", "hash4", "context4"], + /// ], + /// } + /// + /// returns true if successful, otherwise false + static Future _updateSparkAnonSetCoinsWith( Map json, int groupId, ) async { final start = DateTime.now(); - _upCount++; + _updateAnonSetCount++; final blockHash = json["blockHash"] as String; final setHash = json["setHash"] as String; + final coinsRaw = json["coins"] as List; _debugLog( - "$_upCount _updateWith() called where groupId=$groupId," - " blockHash=$blockHash, setHash=$setHash", + "$_updateAnonSetCount _updateSparkAnonSetCoinsWith() " + "called where groupId=$groupId, " + "blockHash=$blockHash (${blockHash.toHexReversedFromBase64}), " + "setHash=$setHash, " + "coins.length: ${coinsRaw.isEmpty ? 0 : coinsRaw.length}", ); + if ((json["coins"] as List).isEmpty) { + _debugLog( + "$_updateAnonSetCount _updateSparkAnonSetCoinsWith()" + " called where json[coins] is Empty", + ); + _debugLog( + "$_updateAnonSetCount _updateSparkAnonSetCoinsWith()" + " duration = ${DateTime.now().difference(start)}", + ); + // no coins to actually insert + return true; + } + final checkResult = db.select( """ SELECT * @@ -228,26 +449,21 @@ abstract class _FiroCache { ], ); - _debugLog("$_upCount _updateWith() called where checkResult=$checkResult"); + _debugLog( + "$_updateAnonSetCount _updateSparkAnonSetCoinsWith()" + " called where checkResult=$checkResult", + ); if (checkResult.isNotEmpty) { _debugLog( - "$_upCount _updateWith() duration = ${DateTime.now().difference(start)}", + "$_updateAnonSetCount _updateSparkAnonSetCoinsWith()" + " duration = ${DateTime.now().difference(start)}", ); // already up to date return true; } - if ((json["coins"] as List).isEmpty) { - _debugLog("$_upCount _updateWith() called where json[coins] is Empty"); - _debugLog( - "$_upCount _updateWith() duration = ${DateTime.now().difference(start)}", - ); - // no coins to actually insert - return true; - } - - final coins = (json["coins"] as List) + final coins = coinsRaw .map( (e) => [ e[0] as String, @@ -307,16 +523,20 @@ abstract class _FiroCache { } db.execute("COMMIT;"); - _debugLog("$_upCount _updateWith() COMMITTED"); _debugLog( - "$_upCount _updateWith() duration = ${DateTime.now().difference(start)}", + "$_updateAnonSetCount _updateSparkAnonSetCoinsWith() COMMITTED", + ); + _debugLog( + "$_updateAnonSetCount _updateSparkAnonSetCoinsWith() duration" + " = ${DateTime.now().difference(start)}", ); return true; } catch (e, s) { db.execute("ROLLBACK;"); - _debugLog("$_upCount _updateWith() ROLLBACK"); + _debugLog("$_updateAnonSetCount _updateSparkAnonSetCoinsWith() ROLLBACK"); _debugLog( - "$_upCount _updateWith() duration = ${DateTime.now().difference(start)}", + "$_updateAnonSetCount _updateSparkAnonSetCoinsWith()" + " duration = ${DateTime.now().difference(start)}", ); // NOTE THIS LOGGER MUST BE CALLED ON MAIN ISOLATE FOR NOW Logging.instance.log( diff --git a/lib/electrumx_rpc/cached_electrumx_client.dart b/lib/electrumx_rpc/cached_electrumx_client.dart index c2dbbb6b8..8b1ff10c8 100644 --- a/lib/electrumx_rpc/cached_electrumx_client.dart +++ b/lib/electrumx_rpc/cached_electrumx_client.dart @@ -220,53 +220,6 @@ class CachedElectrumXClient { } } - Future> getSparkUsedCoinsTags({ - required CryptoCurrency cryptoCurrency, - }) async { - try { - final box = await DB.instance.getSparkUsedCoinsTagsCacheBox( - currency: cryptoCurrency, - ); - - final _list = box.get("tags") as List?; - - final Set cachedTags = - _list == null ? {} : List.from(_list).toSet(); - - final startNumber = max( - 0, - cachedTags.length - 100, // 100 being some arbitrary buffer - ); - - final newTags = await electrumXClient.getSparkUsedCoinsTags( - startNumber: startNumber, - ); - - // ensure we are getting some overlap so we know we are not missing any - if (cachedTags.isNotEmpty && newTags.isNotEmpty) { - assert(cachedTags.intersection(newTags).isNotEmpty); - } - - // Make newTags an Iterable. - final Iterable iterableTags = newTags.map((e) => e.toString()); - - cachedTags.addAll(iterableTags); - - await box.put( - "tags", - cachedTags.toList(), - ); - - return cachedTags; - } catch (e, s) { - Logging.instance.log( - "Failed to process CachedElectrumX.getSparkUsedCoinsTags(): $e\n$s", - level: LogLevel.Error, - ); - rethrow; - } - } - /// Clear all cached transactions for the specified coin Future clearSharedTransactionCache({ required CryptoCurrency cryptoCurrency, diff --git a/lib/electrumx_rpc/electrumx_client.dart b/lib/electrumx_rpc/electrumx_client.dart index 6d473a71f..1e6018a95 100644 --- a/lib/electrumx_rpc/electrumx_client.dart +++ b/lib/electrumx_rpc/electrumx_client.dart @@ -17,8 +17,6 @@ import 'package:electrum_adapter/electrum_adapter.dart' as electrum_adapter; import 'package:electrum_adapter/electrum_adapter.dart'; import 'package:electrum_adapter/methods/specific/firo.dart'; import 'package:event_bus/event_bus.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter_libsparkmobile/flutter_libsparkmobile.dart'; import 'package:mutex/mutex.dart'; import 'package:stream_channel/stream_channel.dart'; @@ -922,7 +920,7 @@ class ElectrumXClient { Logging.instance.log( "Finished ElectrumXClient.getSparkAnonymitySet(coinGroupId" "=$coinGroupId, startBlockHash=$startBlockHash). " - "" + "coins.length: ${(response["coins"] as List?)?.length}" "Duration=${DateTime.now().difference(start)}", level: LogLevel.Info, ); @@ -934,16 +932,12 @@ class ElectrumXClient { /// Takes [startNumber], if it is 0, we get the full set, /// otherwise the used tags after that number - Future> getSparkUsedCoinsTags({ + Future> getSparkUnhashedUsedCoinsTags({ String? requestID, required int startNumber, }) async { try { - // Use electrum_adapter package's getSparkUsedCoinsTags method. - Logging.instance.log( - "attempting to fetch spark.getusedcoinstags...", - level: LogLevel.Info, - ); + final start = DateTime.now(); await _checkElectrumAdapter(); final Map response = await (getElectrumAdapter() as FiroElectrumClient) @@ -955,8 +949,16 @@ class ElectrumXClient { level: LogLevel.Info, ); final map = Map.from(response); - final set = Set.from(map["tags"] as List); - return await compute(_ffiHashTagsComputeWrapper, set); + final tags = List.from(map["tags"] as List); + + Logging.instance.log( + "Finished ElectrumXClient.getSparkUnhashedUsedCoinsTags(startNumber" + "=$startNumber). " + "Duration=${DateTime.now().difference(start)}", + level: LogLevel.Info, + ); + + return tags; } catch (e) { Logging.instance.log(e, level: LogLevel.Error); rethrow; @@ -1093,7 +1095,3 @@ class ElectrumXClient { } } } - -Set _ffiHashTagsComputeWrapper(Set base64Tags) { - return LibSpark.hashTags(base64Tags: base64Tags); -} diff --git a/lib/utilities/extensions/extensions.dart b/lib/utilities/extensions/extensions.dart index a798c002a..678e12844 100644 --- a/lib/utilities/extensions/extensions.dart +++ b/lib/utilities/extensions/extensions.dart @@ -13,5 +13,6 @@ export 'impl/box_shadow.dart'; export 'impl/cl_transaction.dart'; export 'impl/contract_abi.dart'; export 'impl/gradient.dart'; +export 'impl/list.dart'; export 'impl/string.dart'; export 'impl/uint8_list.dart'; diff --git a/lib/wallets/wallet/impl/firo_wallet.dart b/lib/wallets/wallet/impl/firo_wallet.dart index 918ec563b..03f175b99 100644 --- a/lib/wallets/wallet/impl/firo_wallet.dart +++ b/lib/wallets/wallet/impl/firo_wallet.dart @@ -588,7 +588,9 @@ class FiroWallet extends Bip39HDWallet @override Future recover({required bool isRescan}) async { + // reset last checked values groupIdTimestampUTCMap = {}; + final start = DateTime.now(); final root = await getRootHDNode(); @@ -633,8 +635,8 @@ class FiroWallet extends Bip39HDWallet ); } final sparkUsedCoinTagsFuture = - electrumXCachedClient.getSparkUsedCoinsTags( - cryptoCurrency: info.coin, + FiroCacheCoordinator.runFetchAndUpdateSparkUsedCoinTags( + electrumXClient, ); // receiving addresses @@ -754,9 +756,6 @@ class FiroWallet extends Bip39HDWallet final usedSerialsSet = (futureResults[0] as List).toSet(); final setDataMap = futureResults[1] as Map; - // spark - final sparkSpentCoinTags = futureResults[2] as Set; - if (Util.isDesktop) { await Future.wait([ recoverLelantusWallet( @@ -765,7 +764,6 @@ class FiroWallet extends Bip39HDWallet setDataMap: setDataMap, ), recoverSparkWallet( - spentCoinTags: sparkSpentCoinTags, latestSparkCoinId: latestSparkCoinId, ), ]); @@ -776,7 +774,6 @@ class FiroWallet extends Bip39HDWallet setDataMap: setDataMap, ); await recoverSparkWallet( - spentCoinTags: sparkSpentCoinTags, latestSparkCoinId: latestSparkCoinId, ); } diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart index 8b94e941d..5b2c2c601 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart @@ -631,12 +631,41 @@ mixin SparkInterface Future refreshSparkData() async { try { - final spentCoinTags = await electrumXCachedClient.getSparkUsedCoinsTags( - cryptoCurrency: info.coin, + // start by checking if any previous sets are missing from db and add the + // missing groupIds to the list if sets to check and update + final latestGroupId = await electrumXClient.getSparkLatestCoinId(); + final List groupIds = []; + if (latestGroupId > 1) { + for (int id = 1; id < latestGroupId; id++) { + final setExists = + await FiroCacheCoordinator.checkSetInfoForGroupIdExists( + id, + ); + if (!setExists) { + groupIds.add(id); + } + } + } + groupIds.add(latestGroupId); + + // start fetch and update process for each set groupId as required + final possibleFutures = groupIds.map( + (e) => + FiroCacheCoordinator.runFetchAndUpdateSparkAnonSetCacheForGroupId( + e, + electrumXClient, + ), ); - await _checkAndUpdateCoins(spentCoinTags, true); + // wait for each fetch and update to complete + await Future.wait([ + ...possibleFutures, + FiroCacheCoordinator.runFetchAndUpdateSparkUsedCoinTags( + electrumXClient, + ), + ]); + await _checkAndUpdateCoins(); // refresh spark balance await refreshSparkBalance(); } catch (e, s) { @@ -697,7 +726,6 @@ mixin SparkInterface /// Should only be called within the standard wallet [recover] function due to /// mutex locking. Otherwise behaviour MAY be undefined. Future recoverSparkWallet({ - required Set spentCoinTags, required int latestSparkCoinId, }) async { // generate spark addresses if non existing @@ -707,7 +735,7 @@ mixin SparkInterface } try { - await _checkAndUpdateCoins(spentCoinTags, false); + await _checkAndUpdateCoins(); // refresh spark balance await refreshSparkBalance(); @@ -720,10 +748,7 @@ mixin SparkInterface } } - Future _checkAndUpdateCoins( - Set spentCoinTags, - bool checkUseds, - ) async { + Future _checkAndUpdateCoins() async { final sparkAddresses = await mainDB.isar.addresses .where() .walletIdEqualTo(walletId) @@ -737,15 +762,7 @@ mixin SparkInterface ) .toSet(); - List? currentCoins; - if (checkUseds) { - currentCoins = await mainDB.isar.sparkCoins - .where() - .walletIdEqualToAnyLTagHash(walletId) - .filter() - .isUsedEqualTo(false) - .findAll(); - } + final Map>> rawCoinsBySetId = {}; final latestSparkCoinId = await electrumXClient.getSparkLatestCoinId(); for (int i = 1; i <= latestSparkCoinId; i++) { @@ -769,34 +786,62 @@ mixin SparkInterface .toList(); if (coinsRaw.isNotEmpty) { - final myCoins = await compute( - _identifyCoins, - ( - anonymitySetCoins: coinsRaw, - groupId: i, - spentCoinTags: spentCoinTags, - privateKeyHexSet: privateKeyHexSet, - walletId: walletId, - isTestNet: cryptoCurrency.network == CryptoCurrencyNetwork.test, - ), - ); - - if (checkUseds && currentCoins != null) { - for (final coin in currentCoins) { - if (spentCoinTags.contains(coin.lTagHash)) { - myCoins.add(coin.copyWith(isUsed: true)); - } - } - } - - // update wallet spark coins in isar - await _addOrUpdateSparkCoins(myCoins); + rawCoinsBySetId[i] = coinsRaw; } + groupIdTimestampUTCMap[i] = max( lastCheckedTimeStampUTC, info?.timestampUTC ?? lastCheckedTimeStampUTC, ); } + + final List newlyIdCoins = []; + for (final groupId in rawCoinsBySetId.keys) { + final myCoins = await compute( + _identifyCoins, + ( + anonymitySetCoins: rawCoinsBySetId[groupId]!, + groupId: groupId, + privateKeyHexSet: privateKeyHexSet, + walletId: walletId, + isTestNet: cryptoCurrency.network == CryptoCurrencyNetwork.test, + ), + ); + newlyIdCoins.addAll(myCoins); + } + + await _checkAndMarkCoinsUsedInDB(coinsNotInDbYet: newlyIdCoins); + } + + Future _checkAndMarkCoinsUsedInDB({ + List coinsNotInDbYet = const [], + }) async { + final List coins = await mainDB.isar.sparkCoins + .where() + .walletIdEqualToAnyLTagHash(walletId) + .filter() + .isUsedEqualTo(false) + .findAll(); + + final List coinsToWrite = []; + + final spentCoinTags = await FiroCacheCoordinator.getUsedCoinTags(0); + + for (final coin in coins) { + if (spentCoinTags.contains(coin.lTagHash)) { + coinsToWrite.add(coin.copyWith(isUsed: true)); + } + } + for (final coin in coinsNotInDbYet) { + if (spentCoinTags.contains(coin.lTagHash)) { + coinsToWrite.add(coin.copyWith(isUsed: true)); + } else { + coinsToWrite.add(coin); + } + } + + // update wallet spark coins in isar + await _addOrUpdateSparkCoins(coinsToWrite); } // modelled on CSparkWallet::CreateSparkMintTransactions https://github.com/firoorg/firo/blob/39c41e5e7ec634ced3700fe3f4f5509dc2e480d0/src/spark/sparkwallet.cpp#L752 @@ -1713,7 +1758,6 @@ Future> _identifyCoins( ({ List anonymitySetCoins, int groupId, - Set spentCoinTags, Set privateKeyHexSet, String walletId, bool isTestNet, @@ -1756,7 +1800,7 @@ Future> _identifyCoins( SparkCoin( walletId: args.walletId, type: coinType, - isUsed: args.spentCoinTags.contains(coin.lTagHash!), + isUsed: false, groupId: args.groupId, nonce: coin.nonceHex?.toUint8ListFromHex, address: coin.address!, From e5d8dff6cd9a4d6116fdd9a2336b6f8725582081 Mon Sep 17 00:00:00 2001 From: julian Date: Thu, 30 May 2024 15:17:50 -0600 Subject: [PATCH 066/100] add debug log level value to enum --- lib/db/sqlite/firo_cache.dart | 2 +- lib/models/isar/models/log.g.dart | 2 + lib/utilities/enums/log_level_enum.dart | 3 +- test/cached_electrumx_test.mocks.dart | 8 ++-- ...allet_settings_view_screen_test.mocks.dart | 32 --------------- .../bitcoin/bitcoin_wallet_test.mocks.dart | 40 ++----------------- .../bitcoincash_wallet_test.mocks.dart | 40 ++----------------- .../dogecoin/dogecoin_wallet_test.mocks.dart | 40 ++----------------- .../namecoin/namecoin_wallet_test.mocks.dart | 40 ++----------------- .../particl/particl_wallet_test.mocks.dart | 40 ++----------------- 10 files changed, 29 insertions(+), 218 deletions(-) diff --git a/lib/db/sqlite/firo_cache.dart b/lib/db/sqlite/firo_cache.dart index a6c2882a0..63595316a 100644 --- a/lib/db/sqlite/firo_cache.dart +++ b/lib/db/sqlite/firo_cache.dart @@ -15,7 +15,7 @@ void _debugLog(Object? object) { if (kDebugMode) { Logging.instance.log( object, - level: LogLevel.Fatal, + level: LogLevel.Debug, ); } } diff --git a/lib/models/isar/models/log.g.dart b/lib/models/isar/models/log.g.dart index 332e91d3c..8a4cf22b1 100644 --- a/lib/models/isar/models/log.g.dart +++ b/lib/models/isar/models/log.g.dart @@ -124,12 +124,14 @@ const _LoglogLevelEnumValueMap = { r'Warning': r'Warning', r'Error': r'Error', r'Fatal': r'Fatal', + r'Debug': r'Debug', }; const _LoglogLevelValueEnumMap = { r'Info': LogLevel.Info, r'Warning': LogLevel.Warning, r'Error': LogLevel.Error, r'Fatal': LogLevel.Fatal, + r'Debug': LogLevel.Debug, }; Id _logGetId(Log object) { diff --git a/lib/utilities/enums/log_level_enum.dart b/lib/utilities/enums/log_level_enum.dart index 6e5e14a8f..5fb7f9e52 100644 --- a/lib/utilities/enums/log_level_enum.dart +++ b/lib/utilities/enums/log_level_enum.dart @@ -14,5 +14,6 @@ enum LogLevel { Info, Warning, Error, - Fatal; + Fatal, + Debug; } diff --git a/test/cached_electrumx_test.mocks.dart b/test/cached_electrumx_test.mocks.dart index 5ae1523da..e8bdc2e53 100644 --- a/test/cached_electrumx_test.mocks.dart +++ b/test/cached_electrumx_test.mocks.dart @@ -428,21 +428,21 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { _i7.Future>.value({}), ) as _i7.Future>); @override - _i7.Future> getSparkUsedCoinsTags({ + _i7.Future> getSparkUnhashedUsedCoinsTags({ String? requestID, required int? startNumber, }) => (super.noSuchMethod( Invocation.method( - #getSparkUsedCoinsTags, + #getSparkUnhashedUsedCoinsTags, [], { #requestID: requestID, #startNumber: startNumber, }, ), - returnValue: _i7.Future>.value({}), - ) as _i7.Future>); + returnValue: _i7.Future>.value([]), + ) as _i7.Future>); @override _i7.Future>> getSparkMintMetaData({ String? requestID, diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart index 446894293..e4c45b863 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart @@ -78,27 +78,6 @@ class MockCachedElectrumXClient extends _i1.Mock _i4.Future>.value({}), ) as _i4.Future>); @override - _i4.Future> getSparkAnonymitySet({ - required String? groupId, - String? blockhash = r'', - required _i5.CryptoCurrency? cryptoCurrency, - required bool? useOnlyCacheIfNotEmpty, - }) => - (super.noSuchMethod( - Invocation.method( - #getSparkAnonymitySet, - [], - { - #groupId: groupId, - #blockhash: blockhash, - #cryptoCurrency: cryptoCurrency, - #useOnlyCacheIfNotEmpty: useOnlyCacheIfNotEmpty, - }, - ), - returnValue: - _i4.Future>.value({}), - ) as _i4.Future>); - @override String base64ToHex(String? source) => (super.noSuchMethod( Invocation.method( #base64ToHex, @@ -162,17 +141,6 @@ class MockCachedElectrumXClient extends _i1.Mock returnValue: _i4.Future>.value([]), ) as _i4.Future>); @override - _i4.Future> getSparkUsedCoinsTags( - {required _i5.CryptoCurrency? cryptoCurrency}) => - (super.noSuchMethod( - Invocation.method( - #getSparkUsedCoinsTags, - [], - {#cryptoCurrency: cryptoCurrency}, - ), - returnValue: _i4.Future>.value({}), - ) as _i4.Future>); - @override _i4.Future clearSharedTransactionCache( {required _i5.CryptoCurrency? cryptoCurrency}) => (super.noSuchMethod( diff --git a/test/services/coins/bitcoin/bitcoin_wallet_test.mocks.dart b/test/services/coins/bitcoin/bitcoin_wallet_test.mocks.dart index 776e6874b..af94be20b 100644 --- a/test/services/coins/bitcoin/bitcoin_wallet_test.mocks.dart +++ b/test/services/coins/bitcoin/bitcoin_wallet_test.mocks.dart @@ -425,21 +425,21 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { _i6.Future>.value({}), ) as _i6.Future>); @override - _i6.Future> getSparkUsedCoinsTags({ + _i6.Future> getSparkUnhashedUsedCoinsTags({ String? requestID, required int? startNumber, }) => (super.noSuchMethod( Invocation.method( - #getSparkUsedCoinsTags, + #getSparkUnhashedUsedCoinsTags, [], { #requestID: requestID, #startNumber: startNumber, }, ), - returnValue: _i6.Future>.value({}), - ) as _i6.Future>); + returnValue: _i6.Future>.value([]), + ) as _i6.Future>); @override _i6.Future>> getSparkMintMetaData({ String? requestID, @@ -559,27 +559,6 @@ class MockCachedElectrumXClient extends _i1.Mock _i6.Future>.value({}), ) as _i6.Future>); @override - _i6.Future> getSparkAnonymitySet({ - required String? groupId, - String? blockhash = r'', - required _i2.CryptoCurrency? cryptoCurrency, - required bool? useOnlyCacheIfNotEmpty, - }) => - (super.noSuchMethod( - Invocation.method( - #getSparkAnonymitySet, - [], - { - #groupId: groupId, - #blockhash: blockhash, - #cryptoCurrency: cryptoCurrency, - #useOnlyCacheIfNotEmpty: useOnlyCacheIfNotEmpty, - }, - ), - returnValue: - _i6.Future>.value({}), - ) as _i6.Future>); - @override String base64ToHex(String? source) => (super.noSuchMethod( Invocation.method( #base64ToHex, @@ -643,17 +622,6 @@ class MockCachedElectrumXClient extends _i1.Mock returnValue: _i6.Future>.value([]), ) as _i6.Future>); @override - _i6.Future> getSparkUsedCoinsTags( - {required _i2.CryptoCurrency? cryptoCurrency}) => - (super.noSuchMethod( - Invocation.method( - #getSparkUsedCoinsTags, - [], - {#cryptoCurrency: cryptoCurrency}, - ), - returnValue: _i6.Future>.value({}), - ) as _i6.Future>); - @override _i6.Future clearSharedTransactionCache( {required _i2.CryptoCurrency? cryptoCurrency}) => (super.noSuchMethod( diff --git a/test/services/coins/bitcoincash/bitcoincash_wallet_test.mocks.dart b/test/services/coins/bitcoincash/bitcoincash_wallet_test.mocks.dart index a2238f324..85565e3cf 100644 --- a/test/services/coins/bitcoincash/bitcoincash_wallet_test.mocks.dart +++ b/test/services/coins/bitcoincash/bitcoincash_wallet_test.mocks.dart @@ -425,21 +425,21 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { _i6.Future>.value({}), ) as _i6.Future>); @override - _i6.Future> getSparkUsedCoinsTags({ + _i6.Future> getSparkUnhashedUsedCoinsTags({ String? requestID, required int? startNumber, }) => (super.noSuchMethod( Invocation.method( - #getSparkUsedCoinsTags, + #getSparkUnhashedUsedCoinsTags, [], { #requestID: requestID, #startNumber: startNumber, }, ), - returnValue: _i6.Future>.value({}), - ) as _i6.Future>); + returnValue: _i6.Future>.value([]), + ) as _i6.Future>); @override _i6.Future>> getSparkMintMetaData({ String? requestID, @@ -559,27 +559,6 @@ class MockCachedElectrumXClient extends _i1.Mock _i6.Future>.value({}), ) as _i6.Future>); @override - _i6.Future> getSparkAnonymitySet({ - required String? groupId, - String? blockhash = r'', - required _i2.CryptoCurrency? cryptoCurrency, - required bool? useOnlyCacheIfNotEmpty, - }) => - (super.noSuchMethod( - Invocation.method( - #getSparkAnonymitySet, - [], - { - #groupId: groupId, - #blockhash: blockhash, - #cryptoCurrency: cryptoCurrency, - #useOnlyCacheIfNotEmpty: useOnlyCacheIfNotEmpty, - }, - ), - returnValue: - _i6.Future>.value({}), - ) as _i6.Future>); - @override String base64ToHex(String? source) => (super.noSuchMethod( Invocation.method( #base64ToHex, @@ -643,17 +622,6 @@ class MockCachedElectrumXClient extends _i1.Mock returnValue: _i6.Future>.value([]), ) as _i6.Future>); @override - _i6.Future> getSparkUsedCoinsTags( - {required _i2.CryptoCurrency? cryptoCurrency}) => - (super.noSuchMethod( - Invocation.method( - #getSparkUsedCoinsTags, - [], - {#cryptoCurrency: cryptoCurrency}, - ), - returnValue: _i6.Future>.value({}), - ) as _i6.Future>); - @override _i6.Future clearSharedTransactionCache( {required _i2.CryptoCurrency? cryptoCurrency}) => (super.noSuchMethod( diff --git a/test/services/coins/dogecoin/dogecoin_wallet_test.mocks.dart b/test/services/coins/dogecoin/dogecoin_wallet_test.mocks.dart index f80e8a840..03e117e4b 100644 --- a/test/services/coins/dogecoin/dogecoin_wallet_test.mocks.dart +++ b/test/services/coins/dogecoin/dogecoin_wallet_test.mocks.dart @@ -425,21 +425,21 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { _i6.Future>.value({}), ) as _i6.Future>); @override - _i6.Future> getSparkUsedCoinsTags({ + _i6.Future> getSparkUnhashedUsedCoinsTags({ String? requestID, required int? startNumber, }) => (super.noSuchMethod( Invocation.method( - #getSparkUsedCoinsTags, + #getSparkUnhashedUsedCoinsTags, [], { #requestID: requestID, #startNumber: startNumber, }, ), - returnValue: _i6.Future>.value({}), - ) as _i6.Future>); + returnValue: _i6.Future>.value([]), + ) as _i6.Future>); @override _i6.Future>> getSparkMintMetaData({ String? requestID, @@ -559,27 +559,6 @@ class MockCachedElectrumXClient extends _i1.Mock _i6.Future>.value({}), ) as _i6.Future>); @override - _i6.Future> getSparkAnonymitySet({ - required String? groupId, - String? blockhash = r'', - required _i2.CryptoCurrency? cryptoCurrency, - required bool? useOnlyCacheIfNotEmpty, - }) => - (super.noSuchMethod( - Invocation.method( - #getSparkAnonymitySet, - [], - { - #groupId: groupId, - #blockhash: blockhash, - #cryptoCurrency: cryptoCurrency, - #useOnlyCacheIfNotEmpty: useOnlyCacheIfNotEmpty, - }, - ), - returnValue: - _i6.Future>.value({}), - ) as _i6.Future>); - @override String base64ToHex(String? source) => (super.noSuchMethod( Invocation.method( #base64ToHex, @@ -643,17 +622,6 @@ class MockCachedElectrumXClient extends _i1.Mock returnValue: _i6.Future>.value([]), ) as _i6.Future>); @override - _i6.Future> getSparkUsedCoinsTags( - {required _i2.CryptoCurrency? cryptoCurrency}) => - (super.noSuchMethod( - Invocation.method( - #getSparkUsedCoinsTags, - [], - {#cryptoCurrency: cryptoCurrency}, - ), - returnValue: _i6.Future>.value({}), - ) as _i6.Future>); - @override _i6.Future clearSharedTransactionCache( {required _i2.CryptoCurrency? cryptoCurrency}) => (super.noSuchMethod( diff --git a/test/services/coins/namecoin/namecoin_wallet_test.mocks.dart b/test/services/coins/namecoin/namecoin_wallet_test.mocks.dart index 9f13d167b..4e57a7657 100644 --- a/test/services/coins/namecoin/namecoin_wallet_test.mocks.dart +++ b/test/services/coins/namecoin/namecoin_wallet_test.mocks.dart @@ -425,21 +425,21 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { _i6.Future>.value({}), ) as _i6.Future>); @override - _i6.Future> getSparkUsedCoinsTags({ + _i6.Future> getSparkUnhashedUsedCoinsTags({ String? requestID, required int? startNumber, }) => (super.noSuchMethod( Invocation.method( - #getSparkUsedCoinsTags, + #getSparkUnhashedUsedCoinsTags, [], { #requestID: requestID, #startNumber: startNumber, }, ), - returnValue: _i6.Future>.value({}), - ) as _i6.Future>); + returnValue: _i6.Future>.value([]), + ) as _i6.Future>); @override _i6.Future>> getSparkMintMetaData({ String? requestID, @@ -559,27 +559,6 @@ class MockCachedElectrumXClient extends _i1.Mock _i6.Future>.value({}), ) as _i6.Future>); @override - _i6.Future> getSparkAnonymitySet({ - required String? groupId, - String? blockhash = r'', - required _i2.CryptoCurrency? cryptoCurrency, - required bool? useOnlyCacheIfNotEmpty, - }) => - (super.noSuchMethod( - Invocation.method( - #getSparkAnonymitySet, - [], - { - #groupId: groupId, - #blockhash: blockhash, - #cryptoCurrency: cryptoCurrency, - #useOnlyCacheIfNotEmpty: useOnlyCacheIfNotEmpty, - }, - ), - returnValue: - _i6.Future>.value({}), - ) as _i6.Future>); - @override String base64ToHex(String? source) => (super.noSuchMethod( Invocation.method( #base64ToHex, @@ -643,17 +622,6 @@ class MockCachedElectrumXClient extends _i1.Mock returnValue: _i6.Future>.value([]), ) as _i6.Future>); @override - _i6.Future> getSparkUsedCoinsTags( - {required _i2.CryptoCurrency? cryptoCurrency}) => - (super.noSuchMethod( - Invocation.method( - #getSparkUsedCoinsTags, - [], - {#cryptoCurrency: cryptoCurrency}, - ), - returnValue: _i6.Future>.value({}), - ) as _i6.Future>); - @override _i6.Future clearSharedTransactionCache( {required _i2.CryptoCurrency? cryptoCurrency}) => (super.noSuchMethod( diff --git a/test/services/coins/particl/particl_wallet_test.mocks.dart b/test/services/coins/particl/particl_wallet_test.mocks.dart index 5c6f92792..9cb9db169 100644 --- a/test/services/coins/particl/particl_wallet_test.mocks.dart +++ b/test/services/coins/particl/particl_wallet_test.mocks.dart @@ -425,21 +425,21 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { _i6.Future>.value({}), ) as _i6.Future>); @override - _i6.Future> getSparkUsedCoinsTags({ + _i6.Future> getSparkUnhashedUsedCoinsTags({ String? requestID, required int? startNumber, }) => (super.noSuchMethod( Invocation.method( - #getSparkUsedCoinsTags, + #getSparkUnhashedUsedCoinsTags, [], { #requestID: requestID, #startNumber: startNumber, }, ), - returnValue: _i6.Future>.value({}), - ) as _i6.Future>); + returnValue: _i6.Future>.value([]), + ) as _i6.Future>); @override _i6.Future>> getSparkMintMetaData({ String? requestID, @@ -559,27 +559,6 @@ class MockCachedElectrumXClient extends _i1.Mock _i6.Future>.value({}), ) as _i6.Future>); @override - _i6.Future> getSparkAnonymitySet({ - required String? groupId, - String? blockhash = r'', - required _i2.CryptoCurrency? cryptoCurrency, - required bool? useOnlyCacheIfNotEmpty, - }) => - (super.noSuchMethod( - Invocation.method( - #getSparkAnonymitySet, - [], - { - #groupId: groupId, - #blockhash: blockhash, - #cryptoCurrency: cryptoCurrency, - #useOnlyCacheIfNotEmpty: useOnlyCacheIfNotEmpty, - }, - ), - returnValue: - _i6.Future>.value({}), - ) as _i6.Future>); - @override String base64ToHex(String? source) => (super.noSuchMethod( Invocation.method( #base64ToHex, @@ -643,17 +622,6 @@ class MockCachedElectrumXClient extends _i1.Mock returnValue: _i6.Future>.value([]), ) as _i6.Future>); @override - _i6.Future> getSparkUsedCoinsTags( - {required _i2.CryptoCurrency? cryptoCurrency}) => - (super.noSuchMethod( - Invocation.method( - #getSparkUsedCoinsTags, - [], - {#cryptoCurrency: cryptoCurrency}, - ), - returnValue: _i6.Future>.value({}), - ) as _i6.Future>); - @override _i6.Future clearSharedTransactionCache( {required _i2.CryptoCurrency? cryptoCurrency}) => (super.noSuchMethod( From 0f98c0be2c55a1831683c0b02eb1b161ef48bde0 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Thu, 30 May 2024 17:50:09 -0500 Subject: [PATCH 067/100] Use WalletInfoKeys (plus standardize Lelantus') and make UI var private --- .../lelantus_settings_view.dart | 3 ++- .../more_features/more_features_dialog.dart | 15 ++++++++------- lib/wallets/isar/models/wallet_info.dart | 2 +- lib/wallets/wallet/impl/firo_wallet.dart | 3 ++- lib/wallets/wallet/wallet.dart | 3 ++- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/lelantus_settings_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/lelantus_settings_view.dart index 58632c1cd..ad53ccacf 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/lelantus_settings_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/lelantus_settings_view.dart @@ -64,7 +64,8 @@ class _LelantusSettingsViewState extends ConsumerState { if (wallet?.info.otherDataJsonString != null) { final otherDataJson = json.decode(wallet!.info.otherDataJsonString!); enableLelantusScanning = - otherDataJson["enableLelantusScanning"] as bool? ?? false; + otherDataJson[WalletInfoKeys.enableLelantusScanning] as bool? ?? + false; } _isInitialized = true; // Ensure this logic runs only once diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart index 03f4db1c7..f1a9815c4 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart @@ -59,7 +59,7 @@ class MoreFeaturesDialog extends ConsumerStatefulWidget { } class _MoreFeaturesDialogState extends ConsumerState { - bool? enableLelantusScanning = false; + bool _enableLelantusScanning = false; @override Widget build(BuildContext context) { @@ -72,8 +72,9 @@ class _MoreFeaturesDialogState extends ConsumerState { // Parse otherDataJsonString to get the enableLelantusScanning value. if (wallet.info.otherDataJsonString != null) { final otherDataJson = json.decode(wallet.info.otherDataJsonString!); - enableLelantusScanning = - otherDataJson["enableLelantusScanning"] as bool? ?? false; + _enableLelantusScanning = + otherDataJson[WalletInfoKeys.enableLelantusScanning] as bool? ?? + false; } final coinControlPrefEnabled = ref.watch( @@ -169,16 +170,16 @@ class _MoreFeaturesDialogState extends ConsumerState { height: 20, width: 40, child: DraggableSwitchButton( - isOn: enableLelantusScanning ?? false, + isOn: _enableLelantusScanning, onValueChanged: (newValue) { // Toggle enableLelantusScanning in wallet info. wallet.info.updateOtherData(newEntries: { WalletInfoKeys.enableLelantusScanning: - !(enableLelantusScanning ?? false) + !(_enableLelantusScanning) }, isar: ref.read(mainDBProvider).isar).then((value) { // Should setState be used here? - enableLelantusScanning = - !(enableLelantusScanning ?? false); + _enableLelantusScanning = + !(_enableLelantusScanning); }); }, ), diff --git a/lib/wallets/isar/models/wallet_info.dart b/lib/wallets/isar/models/wallet_info.dart index 351229c29..a2b7ae126 100644 --- a/lib/wallets/isar/models/wallet_info.dart +++ b/lib/wallets/isar/models/wallet_info.dart @@ -507,5 +507,5 @@ abstract class WalletInfoKeys { static const String tezosDerivationPath = "tezosDerivationPathKey"; static const String lelantusCoinIsarRescanRequired = "lelantusCoinIsarRescanRequired"; - static const String enableLelantusScanning = "enableLelantusScanning"; + static const String enableLelantusScanning = "enableLelantusScanningKey"; } diff --git a/lib/wallets/wallet/impl/firo_wallet.dart b/lib/wallets/wallet/impl/firo_wallet.dart index 510d08cfb..3da17a3ff 100644 --- a/lib/wallets/wallet/impl/firo_wallet.dart +++ b/lib/wallets/wallet/impl/firo_wallet.dart @@ -615,7 +615,8 @@ class FiroWallet extends Bip39HDWallet if (info.otherDataJsonString != null) { final otherDataJson = json.decode(info.otherDataJsonString!); enableLelantusScanning = - otherDataJson["enableLelantusScanning"] as bool? ?? false; + otherDataJson[WalletInfoKeys.enableLelantusScanning] as bool? ?? + false; } // lelantus diff --git a/lib/wallets/wallet/wallet.dart b/lib/wallets/wallet/wallet.dart index 83698a819..56469071c 100644 --- a/lib/wallets/wallet/wallet.dart +++ b/lib/wallets/wallet/wallet.dart @@ -564,7 +564,8 @@ abstract class Wallet { if (this.info.otherDataJsonString != null) { final otherDataJson = json.decode(this.info.otherDataJsonString!); enableLelantusScanning = - otherDataJson["enableLelantusScanning"] as bool? ?? false; + otherDataJson[WalletInfoKeys.enableLelantusScanning] as bool? ?? + false; } if (enableLelantusScanning) { await (this as LelantusInterface).refreshLelantusData(); From d37d86759d73afbdf1a8455ba7ac71d7e390663c Mon Sep 17 00:00:00 2001 From: sneurlax Date: Thu, 30 May 2024 18:17:26 -0500 Subject: [PATCH 068/100] Lock toggling lelantus scanning behind a mutex --- .../lelantus_settings_view.dart | 30 ++++++++++++------- .../more_features/more_features_dialog.dart | 24 ++++++++++----- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/lelantus_settings_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/lelantus_settings_view.dart index ad53ccacf..1f8c342b0 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/lelantus_settings_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/lelantus_settings_view.dart @@ -47,7 +47,8 @@ class _LelantusSettingsViewState extends ConsumerState { bool _isInitialized = false; Wallet? wallet; - bool? enableLelantusScanning = false; + bool _enableLelantusScanning = false; + bool _isUpdatingLelantusScanning = false; @override void didChangeDependencies() { @@ -63,7 +64,7 @@ class _LelantusSettingsViewState extends ConsumerState { // Parse otherDataJsonString to get the enableLelantusScanning value. if (wallet?.info.otherDataJsonString != null) { final otherDataJson = json.decode(wallet!.info.otherDataJsonString!); - enableLelantusScanning = + _enableLelantusScanning = otherDataJson[WalletInfoKeys.enableLelantusScanning] as bool? ?? false; } @@ -106,16 +107,23 @@ class _LelantusSettingsViewState extends ConsumerState { height: 20, width: 40, child: DraggableSwitchButton( - isOn: enableLelantusScanning ?? false, - onValueChanged: (newValue) { + isOn: _enableLelantusScanning, + onValueChanged: (newValue) async { + if (_isUpdatingLelantusScanning) return; + _isUpdatingLelantusScanning = true; // Lock mutex. + // Toggle enableLelantusScanning in wallet info. - wallet?.info.updateOtherData(newEntries: { - WalletInfoKeys.enableLelantusScanning: - !(enableLelantusScanning ?? false) - }, isar: ref.read(mainDBProvider).isar).then((value) { - // Should setState be used here? - enableLelantusScanning = - !(enableLelantusScanning ?? false); + await wallet?.info.updateOtherData( + newEntries: { + WalletInfoKeys.enableLelantusScanning: + !_enableLelantusScanning, + }, + isar: ref.read(mainDBProvider).isar, + ); + + setState(() { + _enableLelantusScanning = !_enableLelantusScanning; + _isUpdatingLelantusScanning = false; // Free mutex. }); }, ), diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart index f1a9815c4..1ab2a6781 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart @@ -60,6 +60,7 @@ class MoreFeaturesDialog extends ConsumerStatefulWidget { class _MoreFeaturesDialogState extends ConsumerState { bool _enableLelantusScanning = false; + bool _isUpdatingLelantusScanning = false; // Mutex. @override Widget build(BuildContext context) { @@ -171,15 +172,22 @@ class _MoreFeaturesDialogState extends ConsumerState { width: 40, child: DraggableSwitchButton( isOn: _enableLelantusScanning, - onValueChanged: (newValue) { + onValueChanged: (newValue) async { + if (_isUpdatingLelantusScanning) return; + _isUpdatingLelantusScanning = true; // Lock mutex. + // Toggle enableLelantusScanning in wallet info. - wallet.info.updateOtherData(newEntries: { - WalletInfoKeys.enableLelantusScanning: - !(_enableLelantusScanning) - }, isar: ref.read(mainDBProvider).isar).then((value) { - // Should setState be used here? - _enableLelantusScanning = - !(_enableLelantusScanning); + await wallet.info.updateOtherData( + newEntries: { + WalletInfoKeys.enableLelantusScanning: + !_enableLelantusScanning, + }, + isar: ref.read(mainDBProvider).isar, + ); + + setState(() { + _enableLelantusScanning = !_enableLelantusScanning; + _isUpdatingLelantusScanning = false; // Free mutex. }); }, ), From 0454c88c4a9b3682c630f8e30244a4b8d0e0d5ac Mon Sep 17 00:00:00 2001 From: julian Date: Thu, 30 May 2024 17:17:35 -0600 Subject: [PATCH 069/100] modify cache db schema --- lib/db/sqlite/firo_cache.dart | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/lib/db/sqlite/firo_cache.dart b/lib/db/sqlite/firo_cache.dart index 63595316a..47cd680b3 100644 --- a/lib/db/sqlite/firo_cache.dart +++ b/lib/db/sqlite/firo_cache.dart @@ -177,6 +177,7 @@ abstract class _FiroCache { blockHash TEXT NOT NULL, setHash TEXT NOT NULL, groupId INTEGER NOT NULL, + timestampUTC INTEGER NOT NULL, UNIQUE (blockHash, setHash, groupId) ); @@ -190,7 +191,6 @@ abstract class _FiroCache { CREATE TABLE SparkSetCoins ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, - timestampUTC INTEGER NOT NULL, setId INTEGER NOT NULL, coinId INTEGER NOT NULL, FOREIGN KEY (setId) REFERENCES SparkSet(id), @@ -215,15 +215,15 @@ abstract class _FiroCache { int? newerThanTimeStamp, }) async { String query = """ - SELECT sc.id, sc.serialized, sc.txHash, sc.context - FROM SparkSetCoins AS ssc - JOIN SparkSet AS ss ON ssc.setId = ss.id + SELECT sc.serialized, sc.txHash, sc.context + FROM SparkSet AS ss + JOIN SparkSetCoins AS ssc ON ss.id = ssc.setId JOIN SparkCoin AS sc ON ssc.coinId = sc.id WHERE ss.groupId = $groupId """; if (newerThanTimeStamp != null) { - query += " AND ssc.timestampUTC" + query += " AND ss.timestampUTC" " > $newerThanTimeStamp"; } @@ -234,11 +234,10 @@ abstract class _FiroCache { int groupId, ) async { final query = """ - SELECT ss.blockHash, ss.setHash, ssc.timestampUTC + SELECT ss.blockHash, ss.setHash, ss.timestampUTC FROM SparkSet ss - JOIN SparkSetCoins ssc ON ss.id = ssc.setId WHERE ss.groupId = $groupId - ORDER BY ssc.timestampUTC DESC + ORDER BY ss.timestampUTC DESC LIMIT 1; """; @@ -479,16 +478,17 @@ abstract class _FiroCache { try { db.execute( """ - INSERT INTO SparkSet (blockHash, setHash, groupId) - VALUES (?, ?, ?); + INSERT INTO SparkSet (blockHash, setHash, groupId, timestampUTC) + VALUES (?, ?, ?, ?); """, - [blockHash, setHash, groupId], + [blockHash, setHash, groupId, timestamp], ); final setId = db.lastInsertRowId; for (final coin in coins) { int coinId; try { + // try to insert and get row id db.execute( """ INSERT INTO SparkCoin (serialized, txHash, context) @@ -498,6 +498,8 @@ abstract class _FiroCache { ); coinId = db.lastInsertRowId; } on SqliteException catch (e) { + // if there already is a matching coin in the db + // just grab its row id if (e.extendedResultCode == 2067) { final result = db.select( """ @@ -513,12 +515,13 @@ abstract class _FiroCache { } } + // finally add the row id to the newly added set db.execute( """ - INSERT INTO SparkSetCoins (timestampUTC, setId, coinId) - VALUES (?, ?, ?); + INSERT INTO SparkSetCoins (setId, coinId) + VALUES (?, ?); """, - [timestamp, setId, coinId], + [setId, coinId], ); } From 0acc2f2a081cd1f445823d72150e5244f43d2f41 Mon Sep 17 00:00:00 2001 From: julian Date: Thu, 30 May 2024 19:03:15 -0600 Subject: [PATCH 070/100] use extension method --- .../wallet/wallet_mixin_interfaces/spark_interface.dart | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart index 5b2c2c601..426217fac 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart @@ -1687,12 +1687,6 @@ mixin SparkInterface ); } -String base64ToReverseHex(String source) => - base64Decode(LineSplitter.split(source).join()) - .reversed - .map((e) => e.toRadixString(16).padLeft(2, '0')) - .join(); - /// Top level function which should be called wrapped in [compute] Future< ({ @@ -1774,7 +1768,7 @@ Future> _identifyCoins( } final serializedCoinB64 = data[0]; - final txHash = base64ToReverseHex(data[1]); + final txHash = data[1].toHexReversedFromBase64; final contextB64 = data[2]; final coin = LibSpark.identifyAndRecoverCoin( From 1e425e7848ae98850f7d0d52a980520d59ef4835 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Thu, 30 May 2024 21:58:03 -0500 Subject: [PATCH 071/100] use CheckboxTextButton *eye twitches* --- .../restore_options_view.dart | 30 +++++-------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart index 8e6459649..1ba3cef79 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart @@ -25,6 +25,7 @@ import '../../../../utilities/util.dart'; import '../../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../../widgets/conditional_parent.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; +import '../../../../widgets/custom_buttons/checkbox_text_button.dart'; import '../../../../widgets/date_picker/date_picker.dart'; import '../../../../widgets/desktop/desktop_app_bar.dart'; import '../../../../widgets/desktop/desktop_scaffold.dart'; @@ -441,28 +442,13 @@ class _RestoreOptionsViewState extends ConsumerState { color: Colors.transparent, child: Column( children: [ - Row( - children: [ - Checkbox( - value: enableLelantusScanning, - onChanged: (bool? newValue) { - setState(() { - enableLelantusScanning = newValue ?? true; - }); - }, - ), - Text( - 'Scan for Lelantus transactions', - style: isDesktop - ? STextStyles.desktopTextExtraSmall(context) - .copyWith( - color: Theme.of(context) - .extension()! - .textSubtitle1, - ) - : STextStyles.itemSubtitle(context), - ), - ], + CheckboxTextButton( + label: "Scan for Lelantus transactions", + onChanged: (newValue) { + setState(() { + enableLelantusScanning = newValue ?? true; + }); + }, ), const SizedBox( height: 8, From e06023d0fc851d4039fd7f806f6b0df96c0ce284 Mon Sep 17 00:00:00 2001 From: julian Date: Thu, 30 May 2024 21:38:42 -0600 Subject: [PATCH 072/100] extra info when run in debug mode --- .../wallet_view/desktop_wallet_view.dart | 74 +++++++++++++++++-- lib/utilities/wallet_tools.dart | 55 ++++++++++++++ 2 files changed, 121 insertions(+), 8 deletions(-) create mode 100644 lib/utilities/wallet_tools.dart diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart index 83dc57784..a8160899f 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart @@ -16,13 +16,17 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:isar/isar.dart'; +import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; +import '../../../models/isar/models/isar_models.dart'; import '../../../pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart'; import '../../../pages/token_view/my_tokens_view.dart'; import '../../../pages/wallet_view/sub_widgets/transactions_list.dart'; import '../../../pages/wallet_view/transaction_views/all_transactions_view.dart'; import '../../../pages/wallet_view/transaction_views/tx_v2/all_transactions_v2_view.dart'; import '../../../pages/wallet_view/transaction_views/tx_v2/transaction_v2_list.dart'; +import '../../../providers/db/main_db_provider.dart'; import '../../../providers/global/active_wallet_provider.dart'; import '../../../providers/global/auto_swb_service_provider.dart'; import '../../../providers/providers.dart'; @@ -35,6 +39,7 @@ import '../../../utilities/assets.dart'; import '../../../utilities/enums/backup_frequency_type.dart'; import '../../../utilities/enums/sync_type_enum.dart'; import '../../../utilities/text_styles.dart'; +import '../../../utilities/wallet_tools.dart'; import '../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../wallets/wallet/impl/banano_wallet.dart'; import '../../../widgets/custom_buttons/app_bar_icon_button.dart'; @@ -207,17 +212,70 @@ class _DesktopWalletViewState extends ConsumerState { ), if (kDebugMode) const Spacer(), if (kDebugMode) - Row( + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, children: [ - const Text( - "Debug Height:", + Row( + children: [ + const Text( + "dbgHeight: ", + ), + const SizedBox( + width: 2, + ), + Text( + ref + .watch(pWalletChainHeight(widget.walletId)) + .toString(), + ), + ], ), - const SizedBox( - width: 2, - ), - Text( - ref.watch(pWalletChainHeight(widget.walletId)).toString(), + Row( + children: [ + const Text( + "dbgTxCount: ", + ), + const SizedBox( + width: 2, + ), + Text( + wallet.isarTransactionVersion == 2 + ? ref + .watch(mainDBProvider) + .isar + .transactionV2s + .where() + .walletIdEqualTo(widget.walletId) + .countSync() + .toString() + : ref + .watch(mainDBProvider) + .isar + .transactions + .where() + .walletIdEqualTo(widget.walletId) + .countSync() + .toString(), + ), + ], ), + if (wallet.isarTransactionVersion == 2) + Row( + children: [ + const Text( + "dbgBal: ", + ), + const SizedBox( + width: 2, + ), + Text( + WalletDevTools.checkFiroTransactionTally( + widget.walletId, + ), + ), + ], + ), ], ), const Spacer(), diff --git a/lib/utilities/wallet_tools.dart b/lib/utilities/wallet_tools.dart new file mode 100644 index 000000000..cbc3c616c --- /dev/null +++ b/lib/utilities/wallet_tools.dart @@ -0,0 +1,55 @@ +import 'package:isar/isar.dart'; + +import '../db/isar/main_db.dart'; +import '../models/isar/models/blockchain_data/v2/transaction_v2.dart'; +import '../wallets/crypto_currency/crypto_currency.dart'; +import 'amount/amount.dart'; +import 'amount/amount_formatter.dart'; +import 'amount/amount_unit.dart'; + +abstract class WalletDevTools { + static String checkFiroTransactionTally(String walletId) { + final amtFmt = AmountFormatter( + unit: AmountUnit.normal, + locale: "en_US", + coin: Firo(CryptoCurrencyNetwork.main), + maxDecimals: 8, + ); + + final all = MainDB.instance.isar.transactionV2s + .where() + .walletIdEqualTo(walletId) + .findAllSync(); + + final totalCount = all.length; + + BigInt runningBalance = BigInt.zero; + for (final tx in all) { + final ownIns = tx.inputs + .where((e) => e.walletOwns) + .map((e) => e.value) + .fold(BigInt.zero, (p, e) => p + e); + runningBalance -= ownIns; + + final ownOuts = tx.outputs + .where((e) => e.walletOwns) + .map((e) => e.value) + .fold(BigInt.zero, (p, e) => p + e); + runningBalance += ownOuts; + } + + final balanceAccordingToTxHistory = Amount( + rawValue: runningBalance, + fractionDigits: 8, + ); + + print("======== $walletId ============="); + print("totalTxCount: $totalCount"); + print( + "balanceAccordingToTxns: ${amtFmt.format(balanceAccordingToTxHistory)}", + ); + print("=================================================="); + + return amtFmt.format(balanceAccordingToTxHistory); + } +} From 1f0798619af2f87b240e94895563b84244c6af0e Mon Sep 17 00:00:00 2001 From: julian Date: Thu, 30 May 2024 22:06:48 -0600 Subject: [PATCH 073/100] save spark set cache scanned timestamps --- lib/wallets/isar/models/wallet_info.dart | 2 ++ lib/wallets/wallet/impl/firo_wallet.dart | 16 ++++++++------ .../spark_interface.dart | 21 ++++++++++++++----- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/lib/wallets/isar/models/wallet_info.dart b/lib/wallets/isar/models/wallet_info.dart index a2b7ae126..55fc5e8a6 100644 --- a/lib/wallets/isar/models/wallet_info.dart +++ b/lib/wallets/isar/models/wallet_info.dart @@ -508,4 +508,6 @@ abstract class WalletInfoKeys { static const String lelantusCoinIsarRescanRequired = "lelantusCoinIsarRescanRequired"; static const String enableLelantusScanning = "enableLelantusScanningKey"; + static const String firoSparkCacheSetTimestampCache = + "firoSparkCacheSetTimestampCacheKey"; } diff --git a/lib/wallets/wallet/impl/firo_wallet.dart b/lib/wallets/wallet/impl/firo_wallet.dart index 3e8acd0f3..3372e11bd 100644 --- a/lib/wallets/wallet/impl/firo_wallet.dart +++ b/lib/wallets/wallet/impl/firo_wallet.dart @@ -589,7 +589,12 @@ class FiroWallet extends Bip39HDWallet @override Future recover({required bool isRescan}) async { // reset last checked values - groupIdTimestampUTCMap = {}; + await info.updateOtherData( + newEntries: { + WalletInfoKeys.firoSparkCacheSetTimestampCache: {}, + }, + isar: mainDB.isar, + ); final start = DateTime.now(); final root = await getRootHDNode(); @@ -753,7 +758,6 @@ class FiroWallet extends Bip39HDWallet updateUTXOs(), ]); - final List> futures = []; if (enableLelantusScanning) { futures.add(lelantusFutures[0]); @@ -776,10 +780,10 @@ class FiroWallet extends Bip39HDWallet await Future.wait([ if (enableLelantusScanning) recoverLelantusWallet( - latestSetId: latestSetId!, - usedSerialNumbers: usedSerialsSet!, - setDataMap: setDataMap!, - ), + latestSetId: latestSetId!, + usedSerialNumbers: usedSerialsSet!, + setDataMap: setDataMap!, + ), recoverSparkWallet( latestSparkCoinId: latestSparkCoinId, ), diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart index 426217fac..b0409c899 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart @@ -21,6 +21,7 @@ import '../../../utilities/logger.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../crypto_currency/interfaces/electrumx_currency_interface.dart'; import '../../isar/models/spark_coin.dart'; +import '../../isar/models/wallet_info.dart'; import '../../models/tx_data.dart'; import '../intermediate/bip39_hd_wallet.dart'; import 'electrumx_interface.dart'; @@ -720,9 +721,6 @@ mixin SparkInterface ); } - // TODO: look into persistence for this? - Map groupIdTimestampUTCMap = {}; - /// Should only be called within the standard wallet [recover] function due to /// mutex locking. Otherwise behaviour MAY be undefined. Future recoverSparkWallet({ @@ -764,9 +762,15 @@ mixin SparkInterface final Map>> rawCoinsBySetId = {}; + final groupIdTimestampUTCMap = + info.otherData[WalletInfoKeys.firoSparkCacheSetTimestampCache] + as Map? ?? + {}; + final latestSparkCoinId = await electrumXClient.getSparkLatestCoinId(); for (int i = 1; i <= latestSparkCoinId; i++) { - final lastCheckedTimeStampUTC = groupIdTimestampUTCMap[i] ?? 0; + final lastCheckedTimeStampUTC = + groupIdTimestampUTCMap[i.toString()] as int? ?? 0; final info = await FiroCacheCoordinator.getLatestSetInfoForGroupId( i, ); @@ -789,12 +793,19 @@ mixin SparkInterface rawCoinsBySetId[i] = coinsRaw; } - groupIdTimestampUTCMap[i] = max( + groupIdTimestampUTCMap[i.toString()] = max( lastCheckedTimeStampUTC, info?.timestampUTC ?? lastCheckedTimeStampUTC, ); } + await info.updateOtherData( + newEntries: { + WalletInfoKeys.firoSparkCacheSetTimestampCache: groupIdTimestampUTCMap, + }, + isar: mainDB.isar, + ); + final List newlyIdCoins = []; for (final groupId in rawCoinsBySetId.keys) { final myCoins = await compute( From 16acbc366b4184e6102430d43653a63e14a9ecca Mon Sep 17 00:00:00 2001 From: julian Date: Fri, 31 May 2024 11:36:37 -0600 Subject: [PATCH 074/100] remove uninstantiated and unused variables and clean up a bit --- .../lelantus_settings_view.dart | 79 +++++-------------- pubspec.lock | 2 +- 2 files changed, 22 insertions(+), 59 deletions(-) diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/lelantus_settings_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/lelantus_settings_view.dart index 1f8c342b0..56e726731 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/lelantus_settings_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/lelantus_settings_view.dart @@ -8,18 +8,14 @@ * */ -import 'dart:convert'; - import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../providers/db/main_db_provider.dart'; -import '../../../../providers/global/wallets_provider.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/text_styles.dart'; -import '../../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../../wallets/isar/models/wallet_info.dart'; -import '../../../../wallets/wallet/wallet.dart'; +import '../../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../../widgets/background.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../../widgets/custom_buttons/draggable_switch_button.dart'; @@ -40,46 +36,26 @@ class LelantusSettingsView extends ConsumerStatefulWidget { } class _LelantusSettingsViewState extends ConsumerState { - late final TextEditingController _controller; - late final String walletId; - - final _focusNode = FocusNode(); - - bool _isInitialized = false; - Wallet? wallet; - bool _enableLelantusScanning = false; bool _isUpdatingLelantusScanning = false; - @override - void didChangeDependencies() { - super.didChangeDependencies(); - if (!_isInitialized) { - // Get the wallet. - wallet = ref.watch( - pWallets.select( - (value) => value.getWallet(widget.walletId), - ), + Future _switchToggled(bool newValue) async { + if (_isUpdatingLelantusScanning) return; + _isUpdatingLelantusScanning = true; // Lock mutex. + + try { + // Toggle enableLelantusScanning in wallet info. + await ref.read(pWalletInfo(widget.walletId)).updateOtherData( + newEntries: { + WalletInfoKeys.enableLelantusScanning: newValue, + }, + isar: ref.read(mainDBProvider).isar, ); - - // Parse otherDataJsonString to get the enableLelantusScanning value. - if (wallet?.info.otherDataJsonString != null) { - final otherDataJson = json.decode(wallet!.info.otherDataJsonString!); - _enableLelantusScanning = - otherDataJson[WalletInfoKeys.enableLelantusScanning] as bool? ?? - false; - } - - _isInitialized = true; // Ensure this logic runs only once + } finally { + // ensure _isUpdatingLelantusScanning is set to false no matter what + _isUpdatingLelantusScanning = false; } } - @override - void dispose() { - _controller.dispose(); - _focusNode.dispose(); - super.dispose(); - } - @override Widget build(BuildContext context) { return Background( @@ -107,25 +83,12 @@ class _LelantusSettingsViewState extends ConsumerState { height: 20, width: 40, child: DraggableSwitchButton( - isOn: _enableLelantusScanning, - onValueChanged: (newValue) async { - if (_isUpdatingLelantusScanning) return; - _isUpdatingLelantusScanning = true; // Lock mutex. - - // Toggle enableLelantusScanning in wallet info. - await wallet?.info.updateOtherData( - newEntries: { - WalletInfoKeys.enableLelantusScanning: - !_enableLelantusScanning, - }, - isar: ref.read(mainDBProvider).isar, - ); - - setState(() { - _enableLelantusScanning = !_enableLelantusScanning; - _isUpdatingLelantusScanning = false; // Free mutex. - }); - }, + isOn: ref.watch( + pWalletInfo(widget.walletId) + .select((value) => value.otherData), + )[WalletInfoKeys.enableLelantusScanning] as bool? ?? + false, + onValueChanged: _switchToggled, ), ), const SizedBox( diff --git a/pubspec.lock b/pubspec.lock index 77e310a23..ccd30906c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1194,7 +1194,7 @@ packages: source: hosted version: "0.2.0" monero: - dependency: transitive + dependency: "direct main" description: path: "." ref: "6a17a405a1a260fa228b2f4fc94044088a4335ac" From de949efbff5c545c2b630a1cbc56a79567ba9f1d Mon Sep 17 00:00:00 2001 From: julian Date: Fri, 31 May 2024 11:59:43 -0600 Subject: [PATCH 075/100] show firo sparl electrumx data cache size and implement clearing of that cache when all electrumx cache for firo is cleared --- lib/db/sqlite/firo_cache.dart | 45 ++++++++++++++ .../wallet_settings_view.dart | 7 ++- .../spark_info.dart | 59 +++++++++++++++++++ .../wallet_settings_wallet_settings_view.dart | 34 +++++++++++ lib/route_generator.dart | 10 ++++ 5 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/spark_info.dart diff --git a/lib/db/sqlite/firo_cache.dart b/lib/db/sqlite/firo_cache.dart index 47cd680b3..d04eed56e 100644 --- a/lib/db/sqlite/firo_cache.dart +++ b/lib/db/sqlite/firo_cache.dart @@ -29,6 +29,34 @@ List _ffiHashTagsComputeWrapper(List base64Tags) { abstract class FiroCacheCoordinator { static Future init() => _FiroCache.init(); + static Future clearSharedCache() async { + return await _FiroCache._deleteAllCache(); + } + + static Future getSparkCacheSize() async { + final dir = await StackFileSystem.applicationSQLiteDirectory(); + final cacheFile = File("${dir.path}/${_FiroCache.sqliteDbFileName}"); + final int bytes; + if (await cacheFile.exists()) { + bytes = await cacheFile.length(); + } else { + bytes = 0; + } + + if (bytes < 1024) { + return '$bytes B'; + } else if (bytes < 1048576) { + final double kbSize = bytes / 1024; + return '${kbSize.toStringAsFixed(2)} KB'; + } else if (bytes < 1073741824) { + final double mbSize = bytes / 1048576; + return '${mbSize.toStringAsFixed(2)} MB'; + } else { + final double gbSize = bytes / 1073741824; + return '${gbSize.toStringAsFixed(2)} GB'; + } + } + static Future runFetchAndUpdateSparkUsedCoinTags( ElectrumXClient client, ) async { @@ -164,6 +192,23 @@ abstract class _FiroCache { ); } + static Future _deleteAllCache() async { + final start = DateTime.now(); + db.execute( + """ + DELETE FROM SparkSet; + DELETE FROM SparkCoin; + DELETE FROM SparkSetCoins; + DELETE FROM SparkUsedCoinTags; + VACUUM; + """, + ); + _debugLog( + "_deleteAllCache() " + "duration = ${DateTime.now().difference(start)}", + ); + } + static Future _createDb(String file) async { final db = sqlite3.open( file, diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart index 1af5c6e6b..004bd57df 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart @@ -16,6 +16,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:tuple/tuple.dart'; import '../../../db/hive/db.dart'; +import '../../../db/sqlite/firo_cache.dart'; import '../../../models/epicbox_config_model.dart'; import '../../../notifications/show_flush_bar.dart'; import '../../../providers/global/wallets_provider.dart'; @@ -413,7 +414,8 @@ class _WalletSettingsViewState extends ConsumerState { ), ); - if (result == "OK" && mounted) { + if (result == "OK" && + context.mounted) { await showLoading( whileFuture: Future.wait( [ @@ -426,6 +428,9 @@ class _WalletSettingsViewState extends ConsumerState { .clearSharedTransactionCache( currency: coin, ), + if (coin is Firo) + FiroCacheCoordinator + .clearSharedCache(), ], ), context: context, diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/spark_info.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/spark_info.dart new file mode 100644 index 000000000..7cb9b91f3 --- /dev/null +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/spark_info.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../../../db/sqlite/firo_cache.dart'; +import '../../../../themes/stack_colors.dart'; +import '../../../../utilities/text_styles.dart'; +import '../../../../widgets/background.dart'; +import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; +import '../../../../widgets/detail_item.dart'; + +class SparkInfoView extends ConsumerWidget { + const SparkInfoView({ + super.key, + }); + + static const String routeName = "/sparkInfo"; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Background( + child: Scaffold( + backgroundColor: Theme.of(context).extension()!.background, + appBar: AppBar( + leading: AppBarBackButton( + onPressed: () { + Navigator.of(context).pop(); + }, + ), + title: Text( + "Spark Info", + style: STextStyles.navBarTitle(context), + ), + ), + body: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + FutureBuilder( + future: FiroCacheCoordinator.getSparkCacheSize(), + builder: (_, snapshot) { + String detail = "Loading..."; + if (snapshot.connectionState == ConnectionState.done) { + detail = snapshot.data ?? detail; + } + + return DetailItem( + title: "Spark electrumx cache size", + detail: detail, + ); + }, + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart index 7c2884258..9c874514e 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart @@ -24,6 +24,7 @@ import '../../../pinpad_views/lock_screen_view.dart'; import 'delete_wallet_warning_view.dart'; import 'lelantus_settings_view.dart'; import 'rename_wallet_view.dart'; +import 'spark_info.dart'; class WalletSettingsWalletSettingsView extends ConsumerWidget { const WalletSettingsWalletSettingsView({ @@ -216,6 +217,39 @@ class WalletSettingsWalletSettingsView extends ConsumerWidget { ), ), ), + const SizedBox( + height: 8, + ), + RoundedWhiteContainer( + padding: const EdgeInsets.all(0), + child: RawMaterialButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + onPressed: () { + Navigator.of(context).pushNamed( + SparkInfoView.routeName, + ); + }, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 12.0, + vertical: 20, + ), + child: Row( + children: [ + Text( + "Spark info", + style: STextStyles.titleBold12(context), + ), + ], + ), + ), + ), + ), ], ), ), diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 27d1eb4f7..ccedb0a6a 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -131,6 +131,7 @@ import 'pages/settings_views/wallet_settings_view/wallet_settings_wallet_setting import 'pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_warning_view.dart'; import 'pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/lelantus_settings_view.dart'; import 'pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/rename_wallet_view.dart'; +import 'pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/spark_info.dart'; import 'pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart'; import 'pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart'; import 'pages/special/firo_rescan_recovery_error_dialog.dart'; @@ -1966,6 +1967,15 @@ class RouteGenerator { } return _routeError("${settings.name} invalid args: ${args.toString()}"); + case SparkInfoView.routeName: + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => const SparkInfoView(), + settings: RouteSettings( + name: settings.name, + ), + ); + // == Desktop specific routes ============================================ case CreatePasswordView.routeName: if (args is bool) { From 744107b3eb17895d693e3fbb6ed2ec519f78b22e Mon Sep 17 00:00:00 2001 From: julian Date: Fri, 31 May 2024 16:37:25 -0600 Subject: [PATCH 076/100] compartmentalize the sqlite firo cache code and add a background isolate worker to handle some processing as well as cache db writes --- lib/db/sqlite/firo_cache.dart | 520 +--------------------- lib/db/sqlite/firo_cache_coordinator.dart | 162 +++++++ lib/db/sqlite/firo_cache_reader.dart | 103 +++++ lib/db/sqlite/firo_cache_worker.dart | 120 +++++ lib/db/sqlite/firo_cache_writer.dart | 169 +++++++ 5 files changed, 576 insertions(+), 498 deletions(-) create mode 100644 lib/db/sqlite/firo_cache_coordinator.dart create mode 100644 lib/db/sqlite/firo_cache_reader.dart create mode 100644 lib/db/sqlite/firo_cache_worker.dart create mode 100644 lib/db/sqlite/firo_cache_writer.dart diff --git a/lib/db/sqlite/firo_cache.dart b/lib/db/sqlite/firo_cache.dart index d04eed56e..7197690a8 100644 --- a/lib/db/sqlite/firo_cache.dart +++ b/lib/db/sqlite/firo_cache.dart @@ -1,15 +1,23 @@ import 'dart:async'; import 'dart:io'; +import 'dart:isolate'; import 'package:flutter/foundation.dart'; import 'package:flutter_libsparkmobile/flutter_libsparkmobile.dart'; +import 'package:mutex/mutex.dart'; import 'package:sqlite3/sqlite3.dart'; +import 'package:uuid/uuid.dart'; import '../../electrumx_rpc/electrumx_client.dart'; import '../../utilities/extensions/extensions.dart'; import '../../utilities/logger.dart'; import '../../utilities/stack_file_system.dart'; +part 'firo_cache_coordinator.dart'; +part 'firo_cache_reader.dart'; +part 'firo_cache_writer.dart'; +part 'firo_cache_worker.dart'; + /// Temporary debugging log function for this file void _debugLog(Object? object) { if (kDebugMode) { @@ -20,146 +28,6 @@ void _debugLog(Object? object) { } } -List _ffiHashTagsComputeWrapper(List base64Tags) { - return LibSpark.hashTags(base64Tags: base64Tags); -} - -/// Wrapper class for [_FiroCache] as [_FiroCache] should eventually be handled in a -/// background isolate and [FiroCacheCoordinator] should manage that isolate -abstract class FiroCacheCoordinator { - static Future init() => _FiroCache.init(); - - static Future clearSharedCache() async { - return await _FiroCache._deleteAllCache(); - } - - static Future getSparkCacheSize() async { - final dir = await StackFileSystem.applicationSQLiteDirectory(); - final cacheFile = File("${dir.path}/${_FiroCache.sqliteDbFileName}"); - final int bytes; - if (await cacheFile.exists()) { - bytes = await cacheFile.length(); - } else { - bytes = 0; - } - - if (bytes < 1024) { - return '$bytes B'; - } else if (bytes < 1048576) { - final double kbSize = bytes / 1024; - return '${kbSize.toStringAsFixed(2)} KB'; - } else if (bytes < 1073741824) { - final double mbSize = bytes / 1048576; - return '${mbSize.toStringAsFixed(2)} MB'; - } else { - final double gbSize = bytes / 1073741824; - return '${gbSize.toStringAsFixed(2)} GB'; - } - } - - static Future runFetchAndUpdateSparkUsedCoinTags( - ElectrumXClient client, - ) async { - final count = await FiroCacheCoordinator.getUsedCoinTagsLastAddedRowId(); - final unhashedTags = await client.getSparkUnhashedUsedCoinsTags( - startNumber: count, - ); - if (unhashedTags.isNotEmpty) { - final hashedTags = await compute( - _ffiHashTagsComputeWrapper, - unhashedTags, - ); - await _FiroCache._updateSparkUsedTagsWith(hashedTags); - } - } - - static Future runFetchAndUpdateSparkAnonSetCacheForGroupId( - int groupId, - ElectrumXClient client, - ) async { - final blockhashResult = - await FiroCacheCoordinator.getLatestSetInfoForGroupId( - groupId, - ); - final blockHash = blockhashResult?.blockHash ?? ""; - - final json = await client.getSparkAnonymitySet( - coinGroupId: groupId.toString(), - startBlockHash: blockHash.toHexReversedFromBase64, - ); - - await _FiroCache._updateSparkAnonSetCoinsWith(json, groupId); - } - - // =========================================================================== - - static Future> getUsedCoinTags(int startNumber) async { - final result = await _FiroCache._getSparkUsedCoinTags( - startNumber, - ); - return result.map((e) => e["tag"] as String).toSet(); - } - - /// This should be the equivalent of counting the number of tags in the db. - /// Assuming the integrity of the data. Faster than actually calling count on - /// a table where no records have been deleted. None should be deleted from - /// this table in practice. - static Future getUsedCoinTagsLastAddedRowId() async { - final result = await _FiroCache._getUsedCoinTagsLastAddedRowId(); - if (result.isEmpty) { - return 0; - } - return result.first["highestId"] as int? ?? 0; - } - - static Future checkTagIsUsed( - String tag, - ) async { - return await _FiroCache._checkTagIsUsed( - tag, - ); - } - - static Future getSetCoinsForGroupId( - int groupId, { - int? newerThanTimeStamp, - }) async { - return await _FiroCache._getSetCoinsForGroupId( - groupId, - newerThanTimeStamp: newerThanTimeStamp, - ); - } - - static Future< - ({ - String blockHash, - String setHash, - int timestampUTC, - })?> getLatestSetInfoForGroupId( - int groupId, - ) async { - final result = await _FiroCache._getLatestSetInfoForGroupId(groupId); - - if (result.isEmpty) { - return null; - } - - return ( - blockHash: result.first["blockHash"] as String, - setHash: result.first["setHash"] as String, - timestampUTC: result.first["timestampUTC"] as int, - ); - } - - static Future checkSetInfoForGroupIdExists( - int groupId, - ) async { - return await _FiroCache._checkSetInfoForGroupIdExists( - groupId, - ); - } -} - abstract class _FiroCache { static const String sqliteDbFileName = "firo_ex_cache.sqlite3"; @@ -218,14 +86,14 @@ abstract class _FiroCache { db.execute( """ CREATE TABLE SparkSet ( - id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, - blockHash TEXT NOT NULL, - setHash TEXT NOT NULL, - groupId INTEGER NOT NULL, - timestampUTC INTEGER NOT NULL, - UNIQUE (blockHash, setHash, groupId) + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, + blockHash TEXT NOT NULL, + setHash TEXT NOT NULL, + groupId INTEGER NOT NULL, + timestampUTC INTEGER NOT NULL, + UNIQUE (blockHash, setHash, groupId) ); - + CREATE TABLE SparkCoin ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, serialized TEXT NOT NULL, @@ -233,366 +101,22 @@ abstract class _FiroCache { context TEXT NOT NULL, UNIQUE(serialized, txHash, context) ); - + CREATE TABLE SparkSetCoins ( - id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, - setId INTEGER NOT NULL, - coinId INTEGER NOT NULL, - FOREIGN KEY (setId) REFERENCES SparkSet(id), - FOREIGN KEY (coinId) REFERENCES SparkCoin(id) + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, + setId INTEGER NOT NULL, + coinId INTEGER NOT NULL, + FOREIGN KEY (setId) REFERENCES SparkSet(id), + FOREIGN KEY (coinId) REFERENCES SparkCoin(id) ); CREATE TABLE SparkUsedCoinTags ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, tag TEXT NOT NULL UNIQUE ); - """, + """, ); db.dispose(); } - - // =========================================================================== - // =============== Spark anonymity set queries =============================== - - static Future _getSetCoinsForGroupId( - int groupId, { - int? newerThanTimeStamp, - }) async { - String query = """ - SELECT sc.serialized, sc.txHash, sc.context - FROM SparkSet AS ss - JOIN SparkSetCoins AS ssc ON ss.id = ssc.setId - JOIN SparkCoin AS sc ON ssc.coinId = sc.id - WHERE ss.groupId = $groupId - """; - - if (newerThanTimeStamp != null) { - query += " AND ss.timestampUTC" - " > $newerThanTimeStamp"; - } - - return db.select("$query;"); - } - - static Future _getLatestSetInfoForGroupId( - int groupId, - ) async { - final query = """ - SELECT ss.blockHash, ss.setHash, ss.timestampUTC - FROM SparkSet ss - WHERE ss.groupId = $groupId - ORDER BY ss.timestampUTC DESC - LIMIT 1; - """; - - return db.select("$query;"); - } - - static Future _checkSetInfoForGroupIdExists( - int groupId, - ) async { - final query = """ - SELECT EXISTS ( - SELECT 1 - FROM SparkSet - WHERE groupId = $groupId - ) AS setExists; - """; - - return db.select("$query;").first["setExists"] == 1; - } - - // =========================================================================== - // =============== Spark used coin tags queries ============================== - - static Future _getSparkUsedCoinTags( - int startNumber, - ) async { - String query = """ - SELECT tag - FROM SparkUsedCoinTags - """; - - if (startNumber > 0) { - query += " WHERE id >= $startNumber"; - } - - return db.select("$query;"); - } - - static Future _getUsedCoinTagsLastAddedRowId() async { - const query = """ - SELECT MAX(id) AS highestId - FROM SparkUsedCoinTags; - """; - - return db.select("$query;"); - } - - static Future _checkTagIsUsed(String tag) async { - final query = """ - SELECT EXISTS ( - SELECT 1 - FROM SparkUsedCoinTags - WHERE tag = '$tag' - ) AS tagExists; - """; - - return db.select("$query;").first["tagExists"] == 1; - } - - // =========================================================================== - // ================== write to spark used tags cache ========================= - - // debug log counter var - static int _updateTagsCount = 0; - - /// update the sqlite cache - /// Expected json format: - /// { - /// "blockHash": "someBlockHash", - /// "setHash": "someSetHash", - /// "coins": [ - /// ["serliazed1", "hash1", "context1"], - /// ["serliazed2", "hash2", "context2"], - /// ... - /// ["serliazed3", "hash3", "context3"], - /// ["serliazed4", "hash4", "context4"], - /// ], - /// } - /// - /// returns true if successful, otherwise false - static Future _updateSparkUsedTagsWith( - List tags, - ) async { - final start = DateTime.now(); - _updateTagsCount++; - - if (tags.isEmpty) { - _debugLog( - "$_updateTagsCount _updateSparkUsedTagsWith(tags) called " - "where tags is empty", - ); - _debugLog( - "$_updateTagsCount _updateSparkUsedTagsWith() " - "duration = ${DateTime.now().difference(start)}", - ); - // nothing to add, return early - return true; - } else if (tags.length <= 10) { - _debugLog("$_updateTagsCount _updateSparkUsedTagsWith() called where " - "tags.length=${tags.length}, tags: $tags,"); - } else { - _debugLog( - "$_updateTagsCount _updateSparkUsedTagsWith() called where" - " tags.length=${tags.length}," - " first 5 tags: ${tags.sublist(0, 5)}," - " last 5 tags: ${tags.sublist(tags.length - 5, tags.length)}", - ); - } - - db.execute("BEGIN;"); - try { - for (final tag in tags) { - db.execute( - """ - INSERT OR IGNORE INTO SparkUsedCoinTags (tag) - VALUES (?); - """, - [tag], - ); - } - - db.execute("COMMIT;"); - _debugLog("$_updateTagsCount _updateSparkUsedTagsWith() COMMITTED"); - _debugLog( - "$_updateTagsCount _updateSparkUsedTagsWith() " - "duration = ${DateTime.now().difference(start)}", - ); - return true; - } catch (e, s) { - db.execute("ROLLBACK;"); - _debugLog("$_updateTagsCount _updateSparkUsedTagsWith() ROLLBACK"); - _debugLog( - "$_updateTagsCount _updateSparkUsedTagsWith() " - "duration = ${DateTime.now().difference(start)}", - ); - // NOTE THIS LOGGER MUST BE CALLED ON MAIN ISOLATE FOR NOW - Logging.instance.log( - "$e\n$s", - level: LogLevel.Error, - ); - } - - return false; - } - - // =========================================================================== - // ================== write to spark anon set cache ========================== - - // debug log counter var - static int _updateAnonSetCount = 0; - - /// update the sqlite cache - /// Expected json format: - /// { - /// "blockHash": "someBlockHash", - /// "setHash": "someSetHash", - /// "coins": [ - /// ["serliazed1", "hash1", "context1"], - /// ["serliazed2", "hash2", "context2"], - /// ... - /// ["serliazed3", "hash3", "context3"], - /// ["serliazed4", "hash4", "context4"], - /// ], - /// } - /// - /// returns true if successful, otherwise false - static Future _updateSparkAnonSetCoinsWith( - Map json, - int groupId, - ) async { - final start = DateTime.now(); - _updateAnonSetCount++; - final blockHash = json["blockHash"] as String; - final setHash = json["setHash"] as String; - final coinsRaw = json["coins"] as List; - - _debugLog( - "$_updateAnonSetCount _updateSparkAnonSetCoinsWith() " - "called where groupId=$groupId, " - "blockHash=$blockHash (${blockHash.toHexReversedFromBase64}), " - "setHash=$setHash, " - "coins.length: ${coinsRaw.isEmpty ? 0 : coinsRaw.length}", - ); - - if ((json["coins"] as List).isEmpty) { - _debugLog( - "$_updateAnonSetCount _updateSparkAnonSetCoinsWith()" - " called where json[coins] is Empty", - ); - _debugLog( - "$_updateAnonSetCount _updateSparkAnonSetCoinsWith()" - " duration = ${DateTime.now().difference(start)}", - ); - // no coins to actually insert - return true; - } - - final checkResult = db.select( - """ - SELECT * - FROM SparkSet - WHERE blockHash = ? AND setHash = ? AND groupId = ?; - """, - [ - blockHash, - setHash, - groupId, - ], - ); - - _debugLog( - "$_updateAnonSetCount _updateSparkAnonSetCoinsWith()" - " called where checkResult=$checkResult", - ); - - if (checkResult.isNotEmpty) { - _debugLog( - "$_updateAnonSetCount _updateSparkAnonSetCoinsWith()" - " duration = ${DateTime.now().difference(start)}", - ); - // already up to date - return true; - } - - final coins = coinsRaw - .map( - (e) => [ - e[0] as String, - e[1] as String, - e[2] as String, - ], - ) - .toList(); - - final timestamp = DateTime.now().toUtc().millisecondsSinceEpoch ~/ 1000; - - db.execute("BEGIN;"); - try { - db.execute( - """ - INSERT INTO SparkSet (blockHash, setHash, groupId, timestampUTC) - VALUES (?, ?, ?, ?); - """, - [blockHash, setHash, groupId, timestamp], - ); - final setId = db.lastInsertRowId; - - for (final coin in coins) { - int coinId; - try { - // try to insert and get row id - db.execute( - """ - INSERT INTO SparkCoin (serialized, txHash, context) - VALUES (?, ?, ?); - """, - coin, - ); - coinId = db.lastInsertRowId; - } on SqliteException catch (e) { - // if there already is a matching coin in the db - // just grab its row id - if (e.extendedResultCode == 2067) { - final result = db.select( - """ - SELECT id - FROM SparkCoin - WHERE serialized = ? AND txHash = ? AND context = ?; - """, - coin, - ); - coinId = result.first["id"] as int; - } else { - rethrow; - } - } - - // finally add the row id to the newly added set - db.execute( - """ - INSERT INTO SparkSetCoins (setId, coinId) - VALUES (?, ?); - """, - [setId, coinId], - ); - } - - db.execute("COMMIT;"); - _debugLog( - "$_updateAnonSetCount _updateSparkAnonSetCoinsWith() COMMITTED", - ); - _debugLog( - "$_updateAnonSetCount _updateSparkAnonSetCoinsWith() duration" - " = ${DateTime.now().difference(start)}", - ); - return true; - } catch (e, s) { - db.execute("ROLLBACK;"); - _debugLog("$_updateAnonSetCount _updateSparkAnonSetCoinsWith() ROLLBACK"); - _debugLog( - "$_updateAnonSetCount _updateSparkAnonSetCoinsWith()" - " duration = ${DateTime.now().difference(start)}", - ); - // NOTE THIS LOGGER MUST BE CALLED ON MAIN ISOLATE FOR NOW - Logging.instance.log( - "$e\n$s", - level: LogLevel.Error, - ); - } - - return false; - } } diff --git a/lib/db/sqlite/firo_cache_coordinator.dart b/lib/db/sqlite/firo_cache_coordinator.dart new file mode 100644 index 000000000..d2e4796f6 --- /dev/null +++ b/lib/db/sqlite/firo_cache_coordinator.dart @@ -0,0 +1,162 @@ +part of 'firo_cache.dart'; + +/// Wrapper class for [_FiroCache] as [_FiroCache] should eventually be handled in a +/// background isolate and [FiroCacheCoordinator] should manage that isolate +abstract class FiroCacheCoordinator { + static _FiroCacheWorker? _worker; + + static bool _init = false; + static Future init() async { + if (_init) { + return; + } + _init = true; + await _FiroCache.init(); + _worker = await _FiroCacheWorker.spawn(); + } + + static Future clearSharedCache() async { + return await _FiroCache._deleteAllCache(); + } + + static Future getSparkCacheSize() async { + final dir = await StackFileSystem.applicationSQLiteDirectory(); + final cacheFile = File("${dir.path}/${_FiroCache.sqliteDbFileName}"); + final int bytes; + if (await cacheFile.exists()) { + bytes = await cacheFile.length(); + } else { + bytes = 0; + } + + if (bytes < 1024) { + return '$bytes B'; + } else if (bytes < 1048576) { + final double kbSize = bytes / 1024; + return '${kbSize.toStringAsFixed(2)} KB'; + } else if (bytes < 1073741824) { + final double mbSize = bytes / 1048576; + return '${mbSize.toStringAsFixed(2)} MB'; + } else { + final double gbSize = bytes / 1073741824; + return '${gbSize.toStringAsFixed(2)} GB'; + } + } + + static Future runFetchAndUpdateSparkUsedCoinTags( + ElectrumXClient client, + ) async { + final count = await FiroCacheCoordinator.getUsedCoinTagsLastAddedRowId(); + final unhashedTags = await client.getSparkUnhashedUsedCoinsTags( + startNumber: count, + ); + if (unhashedTags.isNotEmpty) { + await _worker!.runTask( + FCTask( + func: FCFuncName._updateSparkUsedTagsWith, + data: unhashedTags, + ), + ); + } + } + + static Future runFetchAndUpdateSparkAnonSetCacheForGroupId( + int groupId, + ElectrumXClient client, + ) async { + final blockhashResult = + await FiroCacheCoordinator.getLatestSetInfoForGroupId( + groupId, + ); + final blockHash = blockhashResult?.blockHash ?? ""; + + final json = await client.getSparkAnonymitySet( + coinGroupId: groupId.toString(), + startBlockHash: blockHash.toHexReversedFromBase64, + ); + + await _worker!.runTask( + FCTask( + func: FCFuncName._updateSparkAnonSetCoinsWith, + data: (groupId, json), + ), + ); + } + + // =========================================================================== + + static Future> getUsedCoinTags(int startNumber) async { + final result = await _Reader._getSparkUsedCoinTags( + startNumber, + db: _FiroCache.db, + ); + return result.map((e) => e["tag"] as String).toSet(); + } + + /// This should be the equivalent of counting the number of tags in the db. + /// Assuming the integrity of the data. Faster than actually calling count on + /// a table where no records have been deleted. None should be deleted from + /// this table in practice. + static Future getUsedCoinTagsLastAddedRowId() async { + final result = await _Reader._getUsedCoinTagsLastAddedRowId( + db: _FiroCache.db, + ); + if (result.isEmpty) { + return 0; + } + return result.first["highestId"] as int? ?? 0; + } + + static Future checkTagIsUsed( + String tag, + ) async { + return await _Reader._checkTagIsUsed( + tag, + db: _FiroCache.db, + ); + } + + static Future getSetCoinsForGroupId( + int groupId, { + int? newerThanTimeStamp, + }) async { + return await _Reader._getSetCoinsForGroupId( + groupId, + db: _FiroCache.db, + newerThanTimeStamp: newerThanTimeStamp, + ); + } + + static Future< + ({ + String blockHash, + String setHash, + int timestampUTC, + })?> getLatestSetInfoForGroupId( + int groupId, + ) async { + final result = await _Reader._getLatestSetInfoForGroupId( + groupId, + db: _FiroCache.db, + ); + + if (result.isEmpty) { + return null; + } + + return ( + blockHash: result.first["blockHash"] as String, + setHash: result.first["setHash"] as String, + timestampUTC: result.first["timestampUTC"] as int, + ); + } + + static Future checkSetInfoForGroupIdExists( + int groupId, + ) async { + return await _Reader._checkSetInfoForGroupIdExists( + groupId, + db: _FiroCache.db, + ); + } +} diff --git a/lib/db/sqlite/firo_cache_reader.dart b/lib/db/sqlite/firo_cache_reader.dart new file mode 100644 index 000000000..10af03922 --- /dev/null +++ b/lib/db/sqlite/firo_cache_reader.dart @@ -0,0 +1,103 @@ +part of 'firo_cache.dart'; + +/// Keep all fetch queries in this separate file +abstract class _Reader { + // =========================================================================== + // =============== Spark anonymity set queries =============================== + + static Future _getSetCoinsForGroupId( + int groupId, { + required Database db, + int? newerThanTimeStamp, + }) async { + String query = """ + SELECT sc.serialized, sc.txHash, sc.context + FROM SparkSet AS ss + JOIN SparkSetCoins AS ssc ON ss.id = ssc.setId + JOIN SparkCoin AS sc ON ssc.coinId = sc.id + WHERE ss.groupId = $groupId + """; + + if (newerThanTimeStamp != null) { + query += " AND ss.timestampUTC" + " > $newerThanTimeStamp"; + } + + return db.select("$query;"); + } + + static Future _getLatestSetInfoForGroupId( + int groupId, { + required Database db, + }) async { + final query = """ + SELECT ss.blockHash, ss.setHash, ss.timestampUTC + FROM SparkSet ss + WHERE ss.groupId = $groupId + ORDER BY ss.timestampUTC DESC + LIMIT 1; + """; + + return db.select("$query;"); + } + + static Future _checkSetInfoForGroupIdExists( + int groupId, { + required Database db, + }) async { + final query = """ + SELECT EXISTS ( + SELECT 1 + FROM SparkSet + WHERE groupId = $groupId + ) AS setExists; + """; + + return db.select("$query;").first["setExists"] == 1; + } + + // =========================================================================== + // =============== Spark used coin tags queries ============================== + + static Future _getSparkUsedCoinTags( + int startNumber, { + required Database db, + }) async { + String query = """ + SELECT tag + FROM SparkUsedCoinTags + """; + + if (startNumber > 0) { + query += " WHERE id >= $startNumber"; + } + + return db.select("$query;"); + } + + static Future _getUsedCoinTagsLastAddedRowId({ + required Database db, + }) async { + const query = """ + SELECT MAX(id) AS highestId + FROM SparkUsedCoinTags; + """; + + return db.select("$query;"); + } + + static Future _checkTagIsUsed( + String tag, { + required Database db, + }) async { + final query = """ + SELECT EXISTS ( + SELECT 1 + FROM SparkUsedCoinTags + WHERE tag = '$tag' + ) AS tagExists; + """; + + return db.select("$query;").first["tagExists"] == 1; + } +} diff --git a/lib/db/sqlite/firo_cache_worker.dart b/lib/db/sqlite/firo_cache_worker.dart new file mode 100644 index 000000000..d29018b8d --- /dev/null +++ b/lib/db/sqlite/firo_cache_worker.dart @@ -0,0 +1,120 @@ +part of 'firo_cache.dart'; + +enum FCFuncName { + _updateSparkAnonSetCoinsWith, + _updateSparkUsedTagsWith, +} + +class FCTask { + final id = const Uuid().v4(); + final FCFuncName func; + final dynamic data; + + FCTask({required this.func, required this.data}); +} + +class _FiroCacheWorker { + final SendPort _commands; + final ReceivePort _responses; + final Map> _activeRequests = {}; + + Future runTask(FCTask task) async { + final completer = Completer.sync(); + _activeRequests[task.id] = completer; + _commands.send(task); + return await completer.future; + } + + static Future<_FiroCacheWorker> spawn() async { + final sqliteDir = await StackFileSystem.applicationSQLiteDirectory(); + final dbFilePath = "${sqliteDir.path}/${_FiroCache.sqliteDbFileName}"; + + final initPort = RawReceivePort(); + final connection = Completer<(ReceivePort, SendPort)>.sync(); + + initPort.handler = (dynamic initialMessage) { + final commandPort = initialMessage as SendPort; + connection.complete( + ( + ReceivePort.fromRawReceivePort(initPort), + commandPort, + ), + ); + }; + + try { + await Isolate.spawn( + _startWorkerIsolate, + (initPort.sendPort, dbFilePath), + ); + } catch (_) { + initPort.close(); + rethrow; + } + + final (receivePort, sendPort) = await connection.future; + + return _FiroCacheWorker._(receivePort, sendPort); + } + + _FiroCacheWorker._(this._responses, this._commands) { + _responses.listen(_handleResponsesFromIsolate); + } + + void _handleResponsesFromIsolate(dynamic message) { + final (id, error) = message as (String, Object?); + final completer = _activeRequests.remove(id)!; + + if (error != null) { + completer.completeError(error); + } else { + completer.complete(id); + } + } + + static void _handleCommandsToIsolate( + ReceivePort receivePort, + SendPort sendPort, + Database db, + Mutex mutex, + ) { + receivePort.listen((message) { + final task = message as FCTask; + + mutex.protect(() async { + try { + final FCResult result; + switch (task.func) { + case FCFuncName._updateSparkAnonSetCoinsWith: + final data = task.data as (int, Map); + result = _updateSparkAnonSetCoinsWith(db, data.$2, data.$1); + break; + + case FCFuncName._updateSparkUsedTagsWith: + result = _updateSparkUsedTagsWith(db, task.data as List); + break; + } + + if (result.success) { + sendPort.send((task.id, null)); + } else { + sendPort.send((task.id, result.error!)); + } + } catch (e) { + sendPort.send((task.id, e)); + } + }); + }); + } + + static void _startWorkerIsolate((SendPort, String) args) { + final receivePort = ReceivePort(); + args.$1.send(receivePort.sendPort); + final mutex = Mutex(); + final db = sqlite3.open( + args.$2, + mode: OpenMode.readWrite, + ); + _handleCommandsToIsolate(receivePort, args.$1, db, mutex); + } +} diff --git a/lib/db/sqlite/firo_cache_writer.dart b/lib/db/sqlite/firo_cache_writer.dart new file mode 100644 index 000000000..0bf1d938b --- /dev/null +++ b/lib/db/sqlite/firo_cache_writer.dart @@ -0,0 +1,169 @@ +part of 'firo_cache.dart'; + +class FCResult { + final bool success; + final Object? error; + + FCResult({required this.success, this.error}); +} + +// =========================================================================== +// ================== write to spark used tags cache ========================= + +/// update the sqlite cache +/// Expected json format: +/// returns true if successful, otherwise some exception +FCResult _updateSparkUsedTagsWith( + Database db, + List tags, +) { + // hash the tags here since this function is called in a background isolate + final hashedTags = LibSpark.hashTags(base64Tags: tags); + + if (hashedTags.isEmpty) { + // nothing to add, return early + return FCResult(success: true); + } + + db.execute("BEGIN;"); + try { + for (final tag in hashedTags) { + db.execute( + """ + INSERT OR IGNORE INTO SparkUsedCoinTags (tag) + VALUES (?); + """, + [tag], + ); + } + + db.execute("COMMIT;"); + + return FCResult(success: true); + } catch (e) { + db.execute("ROLLBACK;"); + return FCResult(success: false, error: e); + } +} + +// =========================================================================== +// ================== write to spark anon set cache ========================== + +/// update the sqlite cache +/// Expected json format: +/// { +/// "blockHash": "someBlockHash", +/// "setHash": "someSetHash", +/// "coins": [ +/// ["serliazed1", "hash1", "context1"], +/// ["serliazed2", "hash2", "context2"], +/// ... +/// ["serliazed3", "hash3", "context3"], +/// ["serliazed4", "hash4", "context4"], +/// ], +/// } +/// +/// returns true if successful, otherwise false +FCResult _updateSparkAnonSetCoinsWith( + Database db, + Map json, + int groupId, +) { + final blockHash = json["blockHash"] as String; + final setHash = json["setHash"] as String; + final coinsRaw = json["coins"] as List; + + if (coinsRaw.isEmpty) { + // no coins to actually insert + return FCResult(success: true); + } + + final checkResult = db.select( + """ + SELECT * + FROM SparkSet + WHERE blockHash = ? AND setHash = ? AND groupId = ?; + """, + [ + blockHash, + setHash, + groupId, + ], + ); + + if (checkResult.isNotEmpty) { + // already up to date + return FCResult(success: true); + } + + final coins = coinsRaw + .map( + (e) => [ + e[0] as String, + e[1] as String, + e[2] as String, + ], + ) + .toList(); + + final timestamp = DateTime.now().toUtc().millisecondsSinceEpoch ~/ 1000; + + db.execute("BEGIN;"); + try { + db.execute( + """ + INSERT INTO SparkSet (blockHash, setHash, groupId, timestampUTC) + VALUES (?, ?, ?, ?); + """, + [blockHash, setHash, groupId, timestamp], + ); + final setId = db.lastInsertRowId; + + for (final coin in coins) { + int coinId; + try { + // try to insert and get row id + db.execute( + """ + INSERT INTO SparkCoin (serialized, txHash, context) + VALUES (?, ?, ?); + """, + coin, + ); + coinId = db.lastInsertRowId; + } on SqliteException catch (e) { + // if there already is a matching coin in the db + // just grab its row id + if (e.extendedResultCode == 2067) { + final result = db.select( + """ + SELECT id + FROM SparkCoin + WHERE serialized = ? AND txHash = ? AND context = ?; + """, + coin, + ); + coinId = result.first["id"] as int; + } else { + rethrow; + } + } + + // finally add the row id to the newly added set + db.execute( + """ + INSERT INTO SparkSetCoins (setId, coinId) + VALUES (?, ?); + """, + [setId, coinId], + ); + } + + db.execute("COMMIT;"); + + return FCResult(success: true); + } catch (e) { + db.execute("ROLLBACK;"); + return FCResult(success: false, error: e); + } +} From 676ab60c6ff326e0bb3b5b189c89c698312b8107 Mon Sep 17 00:00:00 2001 From: julian Date: Fri, 31 May 2024 16:54:32 -0600 Subject: [PATCH 077/100] show loading and ensure desktop password functions aren't doubled called with a lock --- .../password/create_password_view.dart | 68 ++++++++++------ .../settings_menu/security_settings.dart | 79 ++++++++++--------- 2 files changed, 85 insertions(+), 62 deletions(-) diff --git a/lib/pages_desktop_specific/password/create_password_view.dart b/lib/pages_desktop_specific/password/create_password_view.dart index 5f318a1bc..558be3077 100644 --- a/lib/pages_desktop_specific/password/create_password_view.dart +++ b/lib/pages_desktop_specific/password/create_password_view.dart @@ -23,6 +23,7 @@ import '../../themes/stack_colors.dart'; import '../../utilities/assets.dart'; import '../../utilities/constants.dart'; import '../../utilities/flutter_secure_storage_interface.dart'; +import '../../utilities/show_loading.dart'; import '../../utilities/text_styles.dart'; import '../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../widgets/desktop/desktop_app_bar.dart'; @@ -68,12 +69,7 @@ class _CreatePasswordViewState extends ConsumerState { bool _nextLock = false; - void onNextPressed() async { - if (_nextLock) { - return; - } - _nextLock = true; - + Future _onNextPressed() async { final String passphrase = passwordController.text; final String repeatPassphrase = passwordRepeatController.text; @@ -85,7 +81,6 @@ class _CreatePasswordViewState extends ConsumerState { context: context, ), ); - _nextLock = false; return; } if (passphrase != repeatPassphrase) { @@ -96,19 +91,31 @@ class _CreatePasswordViewState extends ConsumerState { context: context, ), ); - _nextLock = false; return; } try { - if (await ref.read(storageCryptoHandlerProvider).hasPassword()) { - throw Exception( - "Tried creating a new password and attempted to overwrite an existing entry!", - ); + whileFuture() async { + if (await ref.read(storageCryptoHandlerProvider).hasPassword()) { + throw Exception( + "Tried creating a new password and attempted to overwrite an existing entry!", + ); + } + + await ref.read(storageCryptoHandlerProvider).initFromNew(passphrase); + await (ref.read(secureStoreProvider).store as DesktopSecureStore) + .init(); } - await ref.read(storageCryptoHandlerProvider).initFromNew(passphrase); - await (ref.read(secureStoreProvider).store as DesktopSecureStore).init(); + await showLoading( + whileFuture: whileFuture(), + context: context, + message: "Initializing...", + rootNavigator: true, + onException: (e) { + throw e; + }, + ); // load default nodes now as node service requires storage handler to exist @@ -116,14 +123,15 @@ class _CreatePasswordViewState extends ConsumerState { await ref.read(nodeServiceChangeNotifierProvider).updateDefaults(); } } catch (e) { - unawaited( - showFloatingFlushBar( - type: FlushBarType.warning, - message: "Error: $e", - context: context, - ), - ); - _nextLock = false; + if (mounted) { + unawaited( + showFloatingFlushBar( + type: FlushBarType.warning, + message: "Error: $e", + context: context, + ), + ); + } return; } @@ -152,7 +160,19 @@ class _CreatePasswordViewState extends ConsumerState { ), ); } - _nextLock = false; + } + + void _onNextPressedWrapper() async { + if (_nextLock) { + return; + } + _nextLock = true; + + try { + await _onNextPressed(); + } finally { + _nextLock = false; + } } @override @@ -464,7 +484,7 @@ class _CreatePasswordViewState extends ConsumerState { : Theme.of(context) .extension()! .getPrimaryDisabledButtonStyle(context), - onPressed: nextEnabled ? onNextPressed : null, + onPressed: nextEnabled ? _onNextPressedWrapper : null, child: Text( "Next", style: nextEnabled diff --git a/lib/pages_desktop_specific/settings/settings_menu/security_settings.dart b/lib/pages_desktop_specific/settings/settings_menu/security_settings.dart index 190740b3f..3b227f1d3 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/security_settings.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/security_settings.dart @@ -21,6 +21,7 @@ import '../../../providers/desktop/storage_crypto_handler_provider.dart'; import '../../../themes/stack_colors.dart'; import '../../../utilities/assets.dart'; import '../../../utilities/constants.dart'; +import '../../../utilities/show_loading.dart'; import '../../../utilities/text_styles.dart'; import '../../../widgets/desktop/primary_button.dart'; import '../../../widgets/progress_bar.dart'; @@ -62,7 +63,8 @@ class _SecuritySettings extends ConsumerState { String passwordFeedback = "Add another word or two. Uncommon words are better. Use a few words, avoid common phrases. No need for symbols, digits, or uppercase letters."; - Future attemptChangePW() async { + bool _changePWLock = false; + Future<(bool, FlushBarType, String)> _attemptChangePW() async { final String pw = passwordCurrentController.text; final String pwNew = passwordController.text; final String pwNewRepeat = passwordRepeatController.text; @@ -74,14 +76,7 @@ class _SecuritySettings extends ConsumerState { if (pwNew != pwNewRepeat) { await Future.delayed(const Duration(seconds: 1)); - unawaited( - showFloatingFlushBar( - type: FlushBarType.warning, - message: "New passphrase does not match!", - context: context, - ), - ); - return false; + return (false, FlushBarType.warning, "New passphrase does not match!"); } else { final success = await ref.read(storageCryptoHandlerProvider).changePassphrase( @@ -92,38 +87,21 @@ class _SecuritySettings extends ConsumerState { if (success) { await Future.delayed(const Duration(seconds: 1)); - unawaited( - showFloatingFlushBar( - type: FlushBarType.success, - message: "Passphrase successfully changed", - context: context, - ), + return ( + true, + FlushBarType.success, + "Passphrase successfully changed" ); - return true; } else { await Future.delayed(const Duration(seconds: 1)); - unawaited( - showFloatingFlushBar( - type: FlushBarType.warning, - message: "Passphrase change failed", - context: context, - ), - ); - return false; + return (false, FlushBarType.warning, "Passphrase change failed"); } } } else { await Future.delayed(const Duration(seconds: 1)); - unawaited( - showFloatingFlushBar( - type: FlushBarType.warning, - message: "Current passphrase is not valid!", - context: context, - ), - ); - return false; + return (false, FlushBarType.warning, "Current passphrase is not valid!"); } } @@ -522,12 +500,37 @@ class _SecuritySettings extends ConsumerState { enabled: shouldEnableSave, label: "Save changes", onPressed: () async { - final didChangePW = - await attemptChangePW(); - if (didChangePW) { - setState(() { - changePassword = false; - }); + if (_changePWLock) { + return; + } + _changePWLock = true; + + try { + final (didChangePW, type, message) = + (await showLoading( + whileFuture: _attemptChangePW(), + context: context, + message: "Updating...", + rootNavigator: true, + ))!; + + if (mounted) { + unawaited( + showFloatingFlushBar( + type: type, + message: message, + context: context, + ), + ); + } + + if (didChangePW == true) { + setState(() { + changePassword = false; + }); + } + } finally { + _changePWLock = false; } }, ), From 39d571e94c80aea92fd059edfd884bcb0ea15341 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 3 Jun 2024 13:35:08 -0600 Subject: [PATCH 078/100] consistent scripts --- scripts/android/build_all.sh | 2 +- scripts/android/build_all_duo.sh | 10 +++++----- scripts/ios/build_all.sh | 10 +++++----- scripts/ios/build_all_duo.sh | 10 +++++----- scripts/linux/build_all_duo.sh | 8 ++++---- scripts/macos/build_all.sh | 2 +- scripts/macos/build_all_duo.sh | 10 +++++----- scripts/windows/build_all_duo.sh | 6 +++--- 8 files changed, 29 insertions(+), 29 deletions(-) diff --git a/scripts/android/build_all.sh b/scripts/android/build_all.sh index 715adbebb..7cb2e083d 100755 --- a/scripts/android/build_all.sh +++ b/scripts/android/build_all.sh @@ -14,7 +14,7 @@ PLUGINS_DIR=../../crypto_plugins (cd "${PLUGINS_DIR}"/flutter_liblelantus/scripts/android && ./build_all.sh ) (cd "${PLUGINS_DIR}"/flutter_libepiccash/scripts/android && ./build_all.sh ) -(cd "${PLUGINS_DIR}"/flutter_libmonero/scripts/android/ && ./build_all.sh ) +(cd "${PLUGINS_DIR}"/flutter_libmonero/scripts/android/ && ./build_all.sh ) set_rust_to_1720 (cd "${PLUGINS_DIR}"/frostdart/scripts/android && ./build_all.sh ) diff --git a/scripts/android/build_all_duo.sh b/scripts/android/build_all_duo.sh index bca175541..aec2ebbb3 100755 --- a/scripts/android/build_all_duo.sh +++ b/scripts/android/build_all_duo.sh @@ -14,11 +14,11 @@ mkdir -p build PLUGINS_DIR=../../crypto_plugins -(cd "${PLUGINS_DIR}"/flutter_liblelantus/scripts/android && ./build_all.sh ) & -(cd "${PLUGINS_DIR}"/flutter_libepiccash/scripts/android && ./build_all.sh ) & -(cd "${PLUGINS_DIR}"/flutter_libmonero/scripts/android/ && ./build_all.sh ) && -set_rust_to_1720 && -(cd "${PLUGINS_DIR}"/frostdart/scripts/android && ./build_all.sh ) & +(cd "${PLUGINS_DIR}"/flutter_liblelantus/scripts/android && ./build_all.sh ) +(cd "${PLUGINS_DIR}"/flutter_libepiccash/scripts/android && ./build_all.sh ) +(cd "${PLUGINS_DIR}"/flutter_libmonero/scripts/android/ && ./build_all.sh ) +set_rust_to_1720 +(cd "${PLUGINS_DIR}"/frostdart/scripts/android && ./build_all.sh ) wait echo "Done building" diff --git a/scripts/ios/build_all.sh b/scripts/ios/build_all.sh index 595d6aba1..b34724561 100755 --- a/scripts/ios/build_all.sh +++ b/scripts/ios/build_all.sh @@ -14,11 +14,11 @@ rustup target add x86_64-apple-ios rustup target add aarch64-apple-ios rustup target add x86_64-apple-ios -(cd ../../crypto_plugins/flutter_liblelantus/scripts/ios && ./build_all.sh ) & -(cd ../../crypto_plugins/flutter_libepiccash/scripts/ios && ./build_all.sh ) & -(cd ../../crypto_plugins/flutter_libmonero/scripts/ios/ && ./build_all.sh ) && -set_rust_to_1720 && -(cd ../../crypto_plugins/frostdart/scripts/ios && ./build_all.sh ) & +(cd ../../crypto_plugins/flutter_liblelantus/scripts/ios && ./build_all.sh ) +(cd ../../crypto_plugins/flutter_libepiccash/scripts/ios && ./build_all.sh ) +(cd ../../crypto_plugins/flutter_libmonero/scripts/ios/ && ./build_all.sh ) +set_rust_to_1720 +(cd ../../crypto_plugins/frostdart/scripts/ios && ./build_all.sh ) wait echo "Done building" diff --git a/scripts/ios/build_all_duo.sh b/scripts/ios/build_all_duo.sh index c16884680..387f85f81 100755 --- a/scripts/ios/build_all_duo.sh +++ b/scripts/ios/build_all_duo.sh @@ -16,11 +16,11 @@ rustup target add x86_64-apple-ios rustup target add aarch64-apple-ios rustup target add x86_64-apple-ios -(cd ../../crypto_plugins/flutter_liblelantus/scripts/ios && ./build_all.sh ) & -(cd ../../crypto_plugins/flutter_libepiccash/scripts/ios && ./build_all.sh ) & -(cd ../../crypto_plugins/flutter_libmonero/scripts/ios/ && ./build_all.sh ) && -set_rust_to_1720 && -(cd ../../crypto_plugins/frostdart/scripts/ios && ./build_all.sh ) & +(cd ../../crypto_plugins/flutter_liblelantus/scripts/ios && ./build_all.sh ) +(cd ../../crypto_plugins/flutter_libepiccash/scripts/ios && ./build_all.sh ) +(cd ../../crypto_plugins/flutter_libmonero/scripts/ios/ && ./build_all.sh ) +set_rust_to_1720 +(cd ../../crypto_plugins/frostdart/scripts/ios && ./build_all.sh ) wait echo "Done building" diff --git a/scripts/linux/build_all_duo.sh b/scripts/linux/build_all_duo.sh index 77ccdf592..e7397bdc5 100755 --- a/scripts/linux/build_all_duo.sh +++ b/scripts/linux/build_all_duo.sh @@ -14,11 +14,11 @@ set_rust_to_1671 # flutter-elinux build linux --dart-define="IS_ARM=true" mkdir -p build ./build_secure_storage_deps.sh & -(cd ../../crypto_plugins/flutter_liblelantus/scripts/linux && ./build_all.sh ) & -(cd ../../crypto_plugins/flutter_libepiccash/scripts/linux && ./build_all.sh ) & -(cd ../../crypto_plugins/flutter_libmonero/scripts/linux && ./build_monero_all.sh && ./build_sharedfile.sh ) +(cd ../../crypto_plugins/flutter_liblelantus/scripts/linux && ./build_all.sh ) +(cd ../../crypto_plugins/flutter_libepiccash/scripts/linux && ./build_all.sh ) +(cd ../../crypto_plugins/flutter_libmonero/scripts/linux && ./build_all.sh ) set_rust_to_1720 -(cd ../../crypto_plugins/frostdart/scripts/linux && ./build_all.sh ) & +(cd ../../crypto_plugins/frostdart/scripts/linux && ./build_all.sh ) ./build_secp256k1.sh diff --git a/scripts/macos/build_all.sh b/scripts/macos/build_all.sh index 56c3a76ab..e3a58b45f 100755 --- a/scripts/macos/build_all.sh +++ b/scripts/macos/build_all.sh @@ -8,7 +8,7 @@ set_rust_to_1671 (cd ../../crypto_plugins/flutter_liblelantus/scripts/macos && ./build_all.sh ) (cd ../../crypto_plugins/flutter_libepiccash/scripts/macos && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmonero/scripts/macos/ && ./build_all.sh ) +(cd ../../crypto_plugins/flutter_libmonero/scripts/macos/ && ./build_all.sh ) set_rust_to_1720 (cd ../../crypto_plugins/frostdart/scripts/macos && ./build_all.sh ) diff --git a/scripts/macos/build_all_duo.sh b/scripts/macos/build_all_duo.sh index f5cfbeeb1..6f70f4371 100755 --- a/scripts/macos/build_all_duo.sh +++ b/scripts/macos/build_all_duo.sh @@ -8,11 +8,11 @@ set -x -e source ../rust_version.sh set_rust_to_1671 -(cd ../../crypto_plugins/flutter_liblelantus/scripts/macos && ./build_all.sh ) & -(cd ../../crypto_plugins/flutter_libepiccash/scripts/macos && ./build_all.sh ) & -(cd ../../crypto_plugins/flutter_libmonero/scripts/macos/ && ./build_all.sh ) && -set_rust_to_1720 && -(cd ../../crypto_plugins/frostdart/scripts/macos && ./build_all.sh ) & +(cd ../../crypto_plugins/flutter_liblelantus/scripts/macos && ./build_all.sh ) +(cd ../../crypto_plugins/flutter_libepiccash/scripts/macos && ./build_all.sh ) +(cd ../../crypto_plugins/flutter_libmonero/scripts/macos/ && ./build_all.sh ) +set_rust_to_1720 +(cd ../../crypto_plugins/frostdart/scripts/macos && ./build_all.sh ) wait echo "Done building" diff --git a/scripts/windows/build_all_duo.sh b/scripts/windows/build_all_duo.sh index 6262793dd..80303f28b 100755 --- a/scripts/windows/build_all_duo.sh +++ b/scripts/windows/build_all_duo.sh @@ -9,11 +9,11 @@ source ../rust_version.sh set_rust_to_1671 mkdir -p build -(cd ../../crypto_plugins/flutter_libepiccash/scripts/windows && ./build_all.sh ) & -(cd ../../crypto_plugins/flutter_liblelantus/scripts/windows && ./build_all.sh ) & +(cd ../../crypto_plugins/flutter_libepiccash/scripts/windows && ./build_all.sh ) +(cd ../../crypto_plugins/flutter_liblelantus/scripts/windows && ./build_all.sh ) (cd ../../crypto_plugins/flutter_libmonero/scripts/windows && ./build_all.sh) set_rust_to_1720 -(cd ../../crypto_plugins/frostdart/scripts/windows && ./build_all.sh ) & +(cd ../../crypto_plugins/frostdart/scripts/windows && ./build_all.sh ) ./build_secp256k1_wsl.sh From c3e604472bf7a731f8e1be0e02e040427c2c2880 Mon Sep 17 00:00:00 2001 From: Julian Date: Mon, 3 Jun 2024 14:23:17 -0600 Subject: [PATCH 079/100] sqlite macos --- macos/Podfile.lock | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 04bb85e54..f7100bab5 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -34,6 +34,21 @@ PODS: - ReachabilitySwift (5.0.0) - share_plus (0.0.1): - FlutterMacOS + - sqlite3 (3.46.0): + - sqlite3/common (= 3.46.0) + - sqlite3/common (3.46.0) + - sqlite3/fts5 (3.46.0): + - sqlite3/common + - sqlite3/perf-threadsafe (3.46.0): + - sqlite3/common + - sqlite3/rtree (3.46.0): + - sqlite3/common + - sqlite3_flutter_libs (0.0.1): + - FlutterMacOS + - sqlite3 (~> 3.46.0) + - sqlite3/fts5 + - sqlite3/perf-threadsafe + - sqlite3/rtree - stack_wallet_backup (0.0.1): - FlutterMacOS - tor_ffi_plugin (0.0.1) @@ -61,6 +76,7 @@ DEPENDENCIES: - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`) + - sqlite3_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/macos`) - stack_wallet_backup (from `Flutter/ephemeral/.symlinks/plugins/stack_wallet_backup/macos`) - tor_ffi_plugin (from `Flutter/ephemeral/.symlinks/plugins/tor_ffi_plugin/macos`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) @@ -70,6 +86,7 @@ DEPENDENCIES: SPEC REPOS: trunk: - ReachabilitySwift + - sqlite3 EXTERNAL SOURCES: coinlib_flutter: @@ -104,6 +121,8 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin share_plus: :path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos + sqlite3_flutter_libs: + :path: Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/macos stack_wallet_backup: :path: Flutter/ephemeral/.symlinks/plugins/stack_wallet_backup/macos tor_ffi_plugin: @@ -133,6 +152,8 @@ SPEC CHECKSUMS: path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7 + sqlite3: 154b084339ede06960a5b3c8160066adc9176b7d + sqlite3_flutter_libs: 1be4459672f8168ded2d8667599b8e3ca5e72b83 stack_wallet_backup: 6ebc60b1bdcf11cf1f1cbad9aa78332e1e15778c tor_ffi_plugin: 2566c1ed174688cca560fa0c64b7a799c66f07cb url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95 From 3e116a50e49770cb0b25ed7800d17bb5c17eb9df Mon Sep 17 00:00:00 2001 From: Julian Date: Mon, 3 Jun 2024 14:23:55 -0600 Subject: [PATCH 080/100] override low uptime git package url --- pubspec.lock | 2 +- scripts/app_config/templates/pubspec.template | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index ccd30906c..68029cb28 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1199,7 +1199,7 @@ packages: path: "." ref: "6a17a405a1a260fa228b2f4fc94044088a4335ac" resolved-ref: "6a17a405a1a260fa228b2f4fc94044088a4335ac" - url: "https://git.mrcyjanek.net/mrcyjanek/monero.dart" + url: "https://www.github.com/mrcyjanek/monero.dart" source: git version: "0.0.0" mutex: diff --git a/scripts/app_config/templates/pubspec.template b/scripts/app_config/templates/pubspec.template index c9e284ce9..43a82bae6 100644 --- a/scripts/app_config/templates/pubspec.template +++ b/scripts/app_config/templates/pubspec.template @@ -49,7 +49,7 @@ dependencies: monero: git: - url: https://git.mrcyjanek.net/mrcyjanek/monero.dart + url: https://www.github.com/mrcyjanek/monero.dart ref: 6a17a405a1a260fa228b2f4fc94044088a4335ac flutter_libepiccash: @@ -211,6 +211,11 @@ flutter_native_splash: dependency_overrides: + monero: + git: + url: https://www.github.com/mrcyjanek/monero.dart + ref: 6a17a405a1a260fa228b2f4fc94044088a4335ac + bip47: git: url: https://github.com/cypherstack/bip47.git From 3b3448d12cea3d5c48e052ffce7f46fc7e17428c Mon Sep 17 00:00:00 2001 From: Julian Date: Mon, 3 Jun 2024 14:50:16 -0600 Subject: [PATCH 081/100] ecash default server fix/update --- lib/wallets/crypto_currency/coins/ecash.dart | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/wallets/crypto_currency/coins/ecash.dart b/lib/wallets/crypto_currency/coins/ecash.dart index f20b9a52c..593bb34f5 100644 --- a/lib/wallets/crypto_currency/coins/ecash.dart +++ b/lib/wallets/crypto_currency/coins/ecash.dart @@ -282,10 +282,8 @@ class Ecash extends Bip39HDCurrency with ElectrumXCurrencyInterface { switch (network) { case CryptoCurrencyNetwork.main: return NodeModel( - // host: "ecash.stackwallet.com", - // port: 59002, - host: "electrum.bitcoinabc.org", - port: 50002, + host: "ecash.stackwallet.com", + port: 59002, name: DefaultNodes.defaultName, id: DefaultNodes.buildId(this), useSSL: true, From c3a2e19890b7039cad504cec71de074afb88bd2e Mon Sep 17 00:00:00 2001 From: Julian Date: Mon, 3 Jun 2024 15:16:23 -0600 Subject: [PATCH 082/100] ios pod lock for reference --- ios/Podfile.lock | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 198e44f2d..cff46cb7b 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -83,6 +83,21 @@ PODS: - SDWebImage/Core (5.13.2) - share_plus (0.0.1): - Flutter + - sqlite3 (3.46.0): + - sqlite3/common (= 3.46.0) + - sqlite3/common (3.46.0) + - sqlite3/fts5 (3.46.0): + - sqlite3/common + - sqlite3/perf-threadsafe (3.46.0): + - sqlite3/common + - sqlite3/rtree (3.46.0): + - sqlite3/common + - sqlite3_flutter_libs (0.0.1): + - Flutter + - sqlite3 (~> 3.46.0) + - sqlite3/fts5 + - sqlite3/perf-threadsafe + - sqlite3/rtree - stack_wallet_backup (0.0.1): - Flutter - SwiftProtobuf (1.19.0) @@ -117,6 +132,7 @@ DEPENDENCIES: - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`) + - sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`) - stack_wallet_backup (from `.symlinks/plugins/stack_wallet_backup/ios`) - tor_ffi_plugin (from `.symlinks/plugins/tor_ffi_plugin/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) @@ -129,6 +145,7 @@ SPEC REPOS: - MTBBarcodeScanner - ReachabilitySwift - SDWebImage + - sqlite3 - SwiftProtobuf - SwiftyGif @@ -177,6 +194,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/permission_handler_apple/ios" share_plus: :path: ".symlinks/plugins/share_plus/ios" + sqlite3_flutter_libs: + :path: ".symlinks/plugins/sqlite3_flutter_libs/ios" stack_wallet_backup: :path: ".symlinks/plugins/stack_wallet_backup/ios" tor_ffi_plugin: @@ -203,7 +222,7 @@ SPEC CHECKSUMS: flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be frostdart: 4c72b69ccac2f13ede744107db046a125acce597 - integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4 + integration_test: 13825b8a9334a850581300559b8839134b124670 isar_flutter_libs: b69f437aeab9c521821c3f376198c4371fa21073 lelantus: 417f0221260013dfc052cae9cf4b741b6479edba local_auth: 1740f55d7af0a2e2a8684ce225fe79d8931e808c @@ -214,6 +233,8 @@ SPEC CHECKSUMS: ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 SDWebImage: 72f86271a6f3139cc7e4a89220946489d4b9a866 share_plus: 599aa54e4ea31d4b4c0e9c911bcc26c55e791028 + sqlite3: 154b084339ede06960a5b3c8160066adc9176b7d + sqlite3_flutter_libs: 0d611efdf6d1c9297d5ab03dab21b75aeebdae31 stack_wallet_backup: 5b8563aba5d8ffbf2ce1944331ff7294a0ec7c03 SwiftProtobuf: 6ef3f0e422ef90d6605ca20b21a94f6c1324d6b3 SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780 From 671e6324028bdc54dfc3731e68dce135c21eef05 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 3 Jun 2024 15:17:43 -0600 Subject: [PATCH 083/100] wallet order --- scripts/app_config/configure_stack_wallet.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/app_config/configure_stack_wallet.sh b/scripts/app_config/configure_stack_wallet.sh index cd0b463b5..dc43feb4a 100755 --- a/scripts/app_config/configure_stack_wallet.sh +++ b/scripts/app_config/configure_stack_wallet.sh @@ -44,24 +44,24 @@ const ({String light, String dark})? _appIconAsset = null; final List _supportedCoins = List.unmodifiable([ Bitcoin(CryptoCurrencyNetwork.main), - BitcoinFrost(CryptoCurrencyNetwork.main), - Litecoin(CryptoCurrencyNetwork.main), + Monero(CryptoCurrencyNetwork.main), + Banano(CryptoCurrencyNetwork.main), Bitcoincash(CryptoCurrencyNetwork.main), + BitcoinFrost(CryptoCurrencyNetwork.main), Dogecoin(CryptoCurrencyNetwork.main), - Epiccash(CryptoCurrencyNetwork.main), Ecash(CryptoCurrencyNetwork.main), + Epiccash(CryptoCurrencyNetwork.main), Ethereum(CryptoCurrencyNetwork.main), Firo(CryptoCurrencyNetwork.main), - Monero(CryptoCurrencyNetwork.main), + Litecoin(CryptoCurrencyNetwork.main), + Nano(CryptoCurrencyNetwork.main), + Namecoin(CryptoCurrencyNetwork.main), Particl(CryptoCurrencyNetwork.main), Peercoin(CryptoCurrencyNetwork.main), Solana(CryptoCurrencyNetwork.main), Stellar(CryptoCurrencyNetwork.main), Tezos(CryptoCurrencyNetwork.main), Wownero(CryptoCurrencyNetwork.main), - Namecoin(CryptoCurrencyNetwork.main), - Nano(CryptoCurrencyNetwork.main), - Banano(CryptoCurrencyNetwork.main), Bitcoin(CryptoCurrencyNetwork.test), BitcoinFrost(CryptoCurrencyNetwork.test), Litecoin(CryptoCurrencyNetwork.test), From 8430cc92bd95ed71bc42416b99f9a93fb3c0df07 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 3 Jun 2024 16:36:50 -0600 Subject: [PATCH 084/100] reset coin control selected utxos on desktop --- .../my_stack_view/wallet_view/desktop_wallet_view.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart index a8160899f..3be835d0b 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart @@ -48,6 +48,7 @@ import '../../../widgets/desktop/desktop_app_bar.dart'; import '../../../widgets/desktop/desktop_scaffold.dart'; import '../../../widgets/hover_text_field.dart'; import '../../../widgets/rounded_white_container.dart'; +import '../../coin_control/desktop_coin_control_use_dialog.dart'; import 'sub_widgets/desktop_wallet_features.dart'; import 'sub_widgets/desktop_wallet_summary.dart'; import 'sub_widgets/my_wallet.dart'; @@ -134,7 +135,10 @@ class _DesktopWalletViewState extends ConsumerState { widget.eventBus != null ? widget.eventBus! : GlobalEventBus.instance; WidgetsBinding.instance.addPostFrameCallback( - (_) => ref.read(currentWalletIdProvider.notifier).state = wallet.walletId, + (_) { + ref.read(currentWalletIdProvider.notifier).state = wallet.walletId; + ref.read(desktopUseUTXOs.notifier).state = {}; + }, ); if (!wallet.shouldAutoSync) { From cba62243bb2930072a1290dcc77f23d04f1bb37e Mon Sep 17 00:00:00 2001 From: sneurlax Date: Tue, 4 Jun 2024 02:52:44 -0500 Subject: [PATCH 085/100] use flutter_file_picker 8.0.3 + muttsu_623's Android patch you may still need to `dos2unix scripts/windows/build_all.sh` as necessary --- scripts/app_config/templates/pubspec.template | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/app_config/templates/pubspec.template b/scripts/app_config/templates/pubspec.template index a77535133..9e086eb28 100644 --- a/scripts/app_config/templates/pubspec.template +++ b/scripts/app_config/templates/pubspec.template @@ -236,8 +236,8 @@ dependency_overrides: file_picker: git: - url: https://github.com/muttsu-623/flutter_file_picker.git - ref: f0930d9fa79d347b2a0e25a7de4f5a4a88a9a907 + url: https://github.com/cypherstack/flutter_file_picker.git + ref: 9abc0930081c9859884e073bd25ab88b2114d9e7 crypto: 3.0.2 analyzer: ^5.2.0 From 05503b04705619ae200be882123d3e7d7b3c4aa4 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 4 Jun 2024 09:16:57 -0600 Subject: [PATCH 086/100] fix wow connection --- .../add_edit_node_view.dart | 15 +++++++++--- .../test_monero_node_connection.dart | 5 ++-- lib/wallets/wallet/impl/monero_wallet.dart | 2 ++ lib/wallets/wallet/impl/wownero_wallet.dart | 2 ++ pubspec.lock | 24 +++++++++---------- 5 files changed, 31 insertions(+), 17 deletions(-) diff --git a/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart b/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart index 4ccaa47bf..478440c99 100644 --- a/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart +++ b/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart @@ -29,6 +29,7 @@ import '../../../../utilities/text_styles.dart'; import '../../../../utilities/util.dart'; import '../../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../../wallets/crypto_currency/intermediate/cryptonote_currency.dart'; +import '../../../../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; import '../../../../widgets/background.dart'; import '../../../../widgets/conditional_parent.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; @@ -221,7 +222,7 @@ class _AddEditNodeViewState extends ConsumerState { // strip unused path String address = formData.host!; - if (coin is Monero || coin is Wownero) { + if (coin is CwBasedInterface) { if (address.startsWith("http")) { final uri = Uri.parse(address); address = "${uri.scheme}://${uri.host}"; @@ -332,7 +333,7 @@ class _AddEditNodeViewState extends ConsumerState { FocusScope.of(context).unfocus(); await Future.delayed(const Duration(milliseconds: 75)); } - if (mounted) { + if (context.mounted) { Navigator.of(context).pop(); } }, @@ -836,6 +837,14 @@ class _NodeFormState extends ConsumerState { } else { enableSSLCheckbox = true; } + } else if (widget.coin is CwBasedInterface) { + if (newValue.startsWith("https://")) { + _useSSL = true; + } else if (newValue.startsWith("http://")) { + _useSSL = false; + } else { + _useSSL = true; + } } _updateState(); setState(() {}); @@ -1043,7 +1052,7 @@ class _NodeFormState extends ConsumerState { ), ], ), - if (widget.coin is Monero || widget.coin is Wownero) + if (widget.coin is CwBasedInterface) Row( children: [ GestureDetector( diff --git a/lib/utilities/test_monero_node_connection.dart b/lib/utilities/test_monero_node_connection.dart index d6fe05cd5..bc9c95a69 100644 --- a/lib/utilities/test_monero_node_connection.dart +++ b/lib/utilities/test_monero_node_connection.dart @@ -12,11 +12,12 @@ import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; -import 'format.dart'; -import 'logger.dart'; + import '../widgets/desktop/primary_button.dart'; import '../widgets/desktop/secondary_button.dart'; import '../widgets/stack_dialog.dart'; +import 'format.dart'; +import 'logger.dart'; class MoneroNodeConnectionResponse { final X509Certificate? cert; diff --git a/lib/wallets/wallet/impl/monero_wallet.dart b/lib/wallets/wallet/impl/monero_wallet.dart index 757c553f1..71dc2e238 100644 --- a/lib/wallets/wallet/impl/monero_wallet.dart +++ b/lib/wallets/wallet/impl/monero_wallet.dart @@ -201,6 +201,7 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { uri: "$host:${node.port}", type: WalletType.monero, trusted: node.trusted ?? false, + useSSL: node.useSSL, ), socksProxyAddress: proxy == null ? null : "${proxy.host.address}:${proxy.port}", @@ -212,6 +213,7 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { uri: "$host:${node.port}", type: WalletType.monero, trusted: node.trusted ?? false, + useSSL: node.useSSL, ), socksProxyAddress: proxy == null ? null : "${proxy.host.address}:${proxy.port}", diff --git a/lib/wallets/wallet/impl/wownero_wallet.dart b/lib/wallets/wallet/impl/wownero_wallet.dart index 847b8b811..e8d8c3646 100644 --- a/lib/wallets/wallet/impl/wownero_wallet.dart +++ b/lib/wallets/wallet/impl/wownero_wallet.dart @@ -190,6 +190,7 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { uri: "$host:${node.port}", type: WalletType.wownero, trusted: node.trusted ?? false, + useSSL: node.useSSL, ), socksProxyAddress: proxy == null ? null : "${proxy.host.address}:${proxy.port}", @@ -201,6 +202,7 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { uri: "$host:${node.port}", type: WalletType.wownero, trusted: node.trusted ?? false, + useSSL: node.useSSL, ), socksProxyAddress: proxy == null ? null : "${proxy.host.address}:${proxy.port}", diff --git a/pubspec.lock b/pubspec.lock index 68029cb28..47dc7ef13 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -346,10 +346,10 @@ packages: dependency: transitive description: name: cross_file - sha256: fd832b5384d0d6da4f6df60b854d33accaaeb63aa9e10e736a87381f08dee2cb + sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32" url: "https://pub.dev" source: hosted - version: "0.3.3+5" + version: "0.3.4+1" crypto: dependency: "direct main" description: @@ -616,11 +616,11 @@ packages: dependency: "direct main" description: path: "." - ref: f0930d9fa79d347b2a0e25a7de4f5a4a88a9a907 - resolved-ref: f0930d9fa79d347b2a0e25a7de4f5a4a88a9a907 - url: "https://github.com/muttsu-623/flutter_file_picker.git" + ref: "9abc0930081c9859884e073bd25ab88b2114d9e7" + resolved-ref: "9abc0930081c9859884e073bd25ab88b2114d9e7" + url: "https://github.com/cypherstack/flutter_file_picker.git" source: git - version: "5.3.3" + version: "8.0.3" fixnum: dependency: transitive description: @@ -746,10 +746,10 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: f185ac890306b5779ecbd611f52502d8d4d63d27703ef73161ca0407e815f02c + sha256: "8cf40eebf5dec866a6d1956ad7b4f7016e6c0cc69847ab946833b7d43743809f" url: "https://pub.dev" source: hosted - version: "2.0.16" + version: "2.0.19" flutter_riverpod: dependency: "direct main" description: @@ -1390,10 +1390,10 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.8" pointycastle: dependency: "direct main" description: @@ -2065,10 +2065,10 @@ packages: dependency: transitive description: name: win32 - sha256: c97defd418eef4ec88c0d1652cdce84b9f7b63dd7198e266d06ac1710d527067 + sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8" url: "https://pub.dev" source: hosted - version: "5.0.8" + version: "5.2.0" win32_registry: dependency: transitive description: From 5eb276b6fcc77ca2eb05230fc5cd31ace0d42d16 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 4 Jun 2024 09:16:57 -0600 Subject: [PATCH 087/100] fix wow connection --- .../add_edit_node_view.dart | 15 +++++++++--- .../test_monero_node_connection.dart | 5 ++-- lib/wallets/wallet/impl/monero_wallet.dart | 2 ++ lib/wallets/wallet/impl/wownero_wallet.dart | 2 ++ pubspec.lock | 24 +++++++++---------- 5 files changed, 31 insertions(+), 17 deletions(-) diff --git a/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart b/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart index 4ccaa47bf..478440c99 100644 --- a/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart +++ b/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart @@ -29,6 +29,7 @@ import '../../../../utilities/text_styles.dart'; import '../../../../utilities/util.dart'; import '../../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../../wallets/crypto_currency/intermediate/cryptonote_currency.dart'; +import '../../../../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; import '../../../../widgets/background.dart'; import '../../../../widgets/conditional_parent.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; @@ -221,7 +222,7 @@ class _AddEditNodeViewState extends ConsumerState { // strip unused path String address = formData.host!; - if (coin is Monero || coin is Wownero) { + if (coin is CwBasedInterface) { if (address.startsWith("http")) { final uri = Uri.parse(address); address = "${uri.scheme}://${uri.host}"; @@ -332,7 +333,7 @@ class _AddEditNodeViewState extends ConsumerState { FocusScope.of(context).unfocus(); await Future.delayed(const Duration(milliseconds: 75)); } - if (mounted) { + if (context.mounted) { Navigator.of(context).pop(); } }, @@ -836,6 +837,14 @@ class _NodeFormState extends ConsumerState { } else { enableSSLCheckbox = true; } + } else if (widget.coin is CwBasedInterface) { + if (newValue.startsWith("https://")) { + _useSSL = true; + } else if (newValue.startsWith("http://")) { + _useSSL = false; + } else { + _useSSL = true; + } } _updateState(); setState(() {}); @@ -1043,7 +1052,7 @@ class _NodeFormState extends ConsumerState { ), ], ), - if (widget.coin is Monero || widget.coin is Wownero) + if (widget.coin is CwBasedInterface) Row( children: [ GestureDetector( diff --git a/lib/utilities/test_monero_node_connection.dart b/lib/utilities/test_monero_node_connection.dart index d6fe05cd5..bc9c95a69 100644 --- a/lib/utilities/test_monero_node_connection.dart +++ b/lib/utilities/test_monero_node_connection.dart @@ -12,11 +12,12 @@ import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; -import 'format.dart'; -import 'logger.dart'; + import '../widgets/desktop/primary_button.dart'; import '../widgets/desktop/secondary_button.dart'; import '../widgets/stack_dialog.dart'; +import 'format.dart'; +import 'logger.dart'; class MoneroNodeConnectionResponse { final X509Certificate? cert; diff --git a/lib/wallets/wallet/impl/monero_wallet.dart b/lib/wallets/wallet/impl/monero_wallet.dart index 757c553f1..71dc2e238 100644 --- a/lib/wallets/wallet/impl/monero_wallet.dart +++ b/lib/wallets/wallet/impl/monero_wallet.dart @@ -201,6 +201,7 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { uri: "$host:${node.port}", type: WalletType.monero, trusted: node.trusted ?? false, + useSSL: node.useSSL, ), socksProxyAddress: proxy == null ? null : "${proxy.host.address}:${proxy.port}", @@ -212,6 +213,7 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { uri: "$host:${node.port}", type: WalletType.monero, trusted: node.trusted ?? false, + useSSL: node.useSSL, ), socksProxyAddress: proxy == null ? null : "${proxy.host.address}:${proxy.port}", diff --git a/lib/wallets/wallet/impl/wownero_wallet.dart b/lib/wallets/wallet/impl/wownero_wallet.dart index 847b8b811..e8d8c3646 100644 --- a/lib/wallets/wallet/impl/wownero_wallet.dart +++ b/lib/wallets/wallet/impl/wownero_wallet.dart @@ -190,6 +190,7 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { uri: "$host:${node.port}", type: WalletType.wownero, trusted: node.trusted ?? false, + useSSL: node.useSSL, ), socksProxyAddress: proxy == null ? null : "${proxy.host.address}:${proxy.port}", @@ -201,6 +202,7 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { uri: "$host:${node.port}", type: WalletType.wownero, trusted: node.trusted ?? false, + useSSL: node.useSSL, ), socksProxyAddress: proxy == null ? null : "${proxy.host.address}:${proxy.port}", diff --git a/pubspec.lock b/pubspec.lock index e8c94e9a2..aac36a767 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -346,10 +346,10 @@ packages: dependency: transitive description: name: cross_file - sha256: fd832b5384d0d6da4f6df60b854d33accaaeb63aa9e10e736a87381f08dee2cb + sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32" url: "https://pub.dev" source: hosted - version: "0.3.3+5" + version: "0.3.4+1" crypto: dependency: "direct main" description: @@ -616,11 +616,11 @@ packages: dependency: "direct main" description: path: "." - ref: f0930d9fa79d347b2a0e25a7de4f5a4a88a9a907 - resolved-ref: f0930d9fa79d347b2a0e25a7de4f5a4a88a9a907 - url: "https://github.com/muttsu-623/flutter_file_picker.git" + ref: "9abc0930081c9859884e073bd25ab88b2114d9e7" + resolved-ref: "9abc0930081c9859884e073bd25ab88b2114d9e7" + url: "https://github.com/cypherstack/flutter_file_picker.git" source: git - version: "5.3.3" + version: "8.0.3" fixnum: dependency: transitive description: @@ -746,10 +746,10 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: f185ac890306b5779ecbd611f52502d8d4d63d27703ef73161ca0407e815f02c + sha256: "8cf40eebf5dec866a6d1956ad7b4f7016e6c0cc69847ab946833b7d43743809f" url: "https://pub.dev" source: hosted - version: "2.0.16" + version: "2.0.19" flutter_riverpod: dependency: "direct main" description: @@ -1390,10 +1390,10 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.8" pointycastle: dependency: "direct main" description: @@ -2049,10 +2049,10 @@ packages: dependency: transitive description: name: win32 - sha256: c97defd418eef4ec88c0d1652cdce84b9f7b63dd7198e266d06ac1710d527067 + sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8" url: "https://pub.dev" source: hosted - version: "5.0.8" + version: "5.2.0" win32_registry: dependency: transitive description: From b7a4f7c29eb5d91bfa7426f80042c2e67d72d1fa Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 4 Jun 2024 09:40:45 -0600 Subject: [PATCH 088/100] fix initial xmr/wow address not showing up on first wallet open --- lib/wallets/wallet/impl/monero_wallet.dart | 12 ++++++++++++ lib/wallets/wallet/impl/wownero_wallet.dart | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/lib/wallets/wallet/impl/monero_wallet.dart b/lib/wallets/wallet/impl/monero_wallet.dart index 71dc2e238..9bcb4c3d7 100644 --- a/lib/wallets/wallet/impl/monero_wallet.dart +++ b/lib/wallets/wallet/impl/monero_wallet.dart @@ -126,6 +126,18 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { await updateNode(); + Address? currentAddress = await getCurrentReceivingAddress(); + if (currentAddress == null) { + currentAddress = addressFor(index: 0); + await mainDB.updateOrPutAddresses([currentAddress]); + } + if (info.cachedReceivingAddress != currentAddress.value) { + await info.updateReceivingAddress( + newAddress: currentAddress.value, + isar: mainDB.isar, + ); + } + await CwBasedInterface.cwWalletBase?.startSync(); unawaited(refresh()); autoSaveTimer?.cancel(); diff --git a/lib/wallets/wallet/impl/wownero_wallet.dart b/lib/wallets/wallet/impl/wownero_wallet.dart index e8d8c3646..ced514aa3 100644 --- a/lib/wallets/wallet/impl/wownero_wallet.dart +++ b/lib/wallets/wallet/impl/wownero_wallet.dart @@ -452,6 +452,18 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { await updateNode(); + Address? currentAddress = await getCurrentReceivingAddress(); + if (currentAddress == null) { + currentAddress = addressFor(index: 0); + await mainDB.updateOrPutAddresses([currentAddress]); + } + if (info.cachedReceivingAddress != currentAddress.value) { + await info.updateReceivingAddress( + newAddress: currentAddress.value, + isar: mainDB.isar, + ); + } + await (CwBasedInterface.cwWalletBase as WowneroWalletBase?)?.startSync(); unawaited(refresh()); autoSaveTimer?.cancel(); From 7cef4c45ebc43aefbc4f8f3ef0ba6d14ffea76c6 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 4 Jun 2024 09:40:45 -0600 Subject: [PATCH 089/100] fix initial xmr/wow address not showing up on first wallet open --- lib/wallets/wallet/impl/monero_wallet.dart | 12 ++++++++++++ lib/wallets/wallet/impl/wownero_wallet.dart | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/lib/wallets/wallet/impl/monero_wallet.dart b/lib/wallets/wallet/impl/monero_wallet.dart index 71dc2e238..9bcb4c3d7 100644 --- a/lib/wallets/wallet/impl/monero_wallet.dart +++ b/lib/wallets/wallet/impl/monero_wallet.dart @@ -126,6 +126,18 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { await updateNode(); + Address? currentAddress = await getCurrentReceivingAddress(); + if (currentAddress == null) { + currentAddress = addressFor(index: 0); + await mainDB.updateOrPutAddresses([currentAddress]); + } + if (info.cachedReceivingAddress != currentAddress.value) { + await info.updateReceivingAddress( + newAddress: currentAddress.value, + isar: mainDB.isar, + ); + } + await CwBasedInterface.cwWalletBase?.startSync(); unawaited(refresh()); autoSaveTimer?.cancel(); diff --git a/lib/wallets/wallet/impl/wownero_wallet.dart b/lib/wallets/wallet/impl/wownero_wallet.dart index e8d8c3646..ced514aa3 100644 --- a/lib/wallets/wallet/impl/wownero_wallet.dart +++ b/lib/wallets/wallet/impl/wownero_wallet.dart @@ -452,6 +452,18 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { await updateNode(); + Address? currentAddress = await getCurrentReceivingAddress(); + if (currentAddress == null) { + currentAddress = addressFor(index: 0); + await mainDB.updateOrPutAddresses([currentAddress]); + } + if (info.cachedReceivingAddress != currentAddress.value) { + await info.updateReceivingAddress( + newAddress: currentAddress.value, + isar: mainDB.isar, + ); + } + await (CwBasedInterface.cwWalletBase as WowneroWalletBase?)?.startSync(); unawaited(refresh()); autoSaveTimer?.cancel(); From ed15482442b1e925e658eafc4e596bebcad2c59a Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 4 Jun 2024 10:08:37 -0600 Subject: [PATCH 090/100] enable experimental firo public funds coin control --- .../send_view/frost_ms/frost_send_view.dart | 6 +++++- lib/pages/send_view/send_view.dart | 7 ++++++- .../wallet_view/sub_widgets/desktop_send.dart | 17 ++++++++++++----- lib/wallets/wallet/impl/firo_wallet.dart | 7 ++++++- 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/lib/pages/send_view/frost_ms/frost_send_view.dart b/lib/pages/send_view/frost_ms/frost_send_view.dart index a5851977c..a150ac726 100644 --- a/lib/pages/send_view/frost_ms/frost_send_view.dart +++ b/lib/pages/send_view/frost_ms/frost_send_view.dart @@ -20,6 +20,7 @@ import '../../../frost_route_generator.dart'; import '../../../models/isar/models/isar_models.dart'; import '../../../providers/frost_wallet/frost_wallet_providers.dart'; import '../../../providers/providers.dart'; +import '../../../providers/wallet/public_private_balance_state_provider.dart'; import '../../../themes/coin_icon_provider.dart'; import '../../../themes/stack_colors.dart'; import '../../../utilities/amount/amount.dart'; @@ -234,7 +235,10 @@ class _FrostSendViewState extends ConsumerState { prefsChangeNotifierProvider.select( (value) => value.enableCoinControl, ), - ); + ) && + (coin is Firo + ? ref.watch(publicPrivateBalanceStateProvider) == FiroType.public + : true); return ConditionalParent( condition: !Util.isDesktop, diff --git a/lib/pages/send_view/send_view.dart b/lib/pages/send_view/send_view.dart index 623962845..8d1f12c5d 100644 --- a/lib/pages/send_view/send_view.dart +++ b/lib/pages/send_view/send_view.dart @@ -998,10 +998,15 @@ class _SendViewState extends ConsumerState { prefsChangeNotifierProvider.select( (value) => value.enableCoinControl, ), - ); + ) && + (coin is Firo + ? ref.watch(publicPrivateBalanceStateProvider) == FiroType.public + : true); if (isFiro) { ref.listen(publicPrivateBalanceStateProvider, (previous, next) { + selectedUTXOs = {}; + if (ref.read(pSendAmount) == null) { setState(() { _calculateFeesFuture = calculateFees( diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart index 43f5eb557..6c7c28296 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart @@ -17,16 +17,13 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; + import '../../../../models/isar/models/contact_entry.dart'; import '../../../../models/paynym/paynym_account_lite.dart'; import '../../../../models/send_view_auto_fill_data.dart'; import '../../../../pages/send_view/confirm_transaction_view.dart'; import '../../../../pages/send_view/sub_widgets/building_transaction_dialog.dart'; import '../../../../pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart'; -import '../../../coin_control/desktop_coin_control_use_dialog.dart'; -import '../../../desktop_home_view.dart'; -import 'address_book_address_chooser/address_book_address_chooser.dart'; -import 'desktop_fee_dropdown.dart'; import '../../../../providers/providers.dart'; import '../../../../providers/ui/fee_rate_type_state_provider.dart'; import '../../../../providers/ui/preview_tx_button_state_provider.dart'; @@ -70,6 +67,10 @@ import '../../../../widgets/icon_widgets/x_icon.dart'; import '../../../../widgets/rounded_container.dart'; import '../../../../widgets/stack_text_field.dart'; import '../../../../widgets/textfield_icon_button.dart'; +import '../../../coin_control/desktop_coin_control_use_dialog.dart'; +import '../../../desktop_home_view.dart'; +import 'address_book_address_chooser/address_book_address_chooser.dart'; +import 'desktop_fee_dropdown.dart'; class DesktopSend extends ConsumerStatefulWidget { const DesktopSend({ @@ -947,7 +948,10 @@ class _DesktopSendState extends ConsumerState { (value) => value.enableCoinControl, ), ) && - ref.watch(pWallets).getWallet(walletId) is CoinControlInterface; + ref.watch(pWallets).getWallet(walletId) is CoinControlInterface && + (coin is Firo + ? ref.watch(publicPrivateBalanceStateProvider) == FiroType.public + : true); return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -1042,6 +1046,9 @@ class _DesktopSendState extends ConsumerState { ], onChanged: (value) { if (value is FiroType) { + if (value != FiroType.public) { + ref.read(desktopUseUTXOs.state).state = {}; + } setState(() { ref.read(publicPrivateBalanceStateProvider.state).state = value; diff --git a/lib/wallets/wallet/impl/firo_wallet.dart b/lib/wallets/wallet/impl/firo_wallet.dart index 3372e11bd..9359043be 100644 --- a/lib/wallets/wallet/impl/firo_wallet.dart +++ b/lib/wallets/wallet/impl/firo_wallet.dart @@ -21,6 +21,7 @@ import '../../isar/models/spark_coin.dart'; import '../../isar/models/wallet_info.dart'; import '../../models/tx_data.dart'; import '../intermediate/bip39_hd_wallet.dart'; +import '../wallet_mixin_interfaces/coin_control_interface.dart'; import '../wallet_mixin_interfaces/electrumx_interface.dart'; import '../wallet_mixin_interfaces/lelantus_interface.dart'; import '../wallet_mixin_interfaces/spark_interface.dart'; @@ -28,7 +29,11 @@ import '../wallet_mixin_interfaces/spark_interface.dart'; const sparkStartBlock = 819300; // (approx 18 Jan 2024) class FiroWallet extends Bip39HDWallet - with ElectrumXInterface, LelantusInterface, SparkInterface { + with + ElectrumXInterface, + LelantusInterface, + SparkInterface, + CoinControlInterface { // IMPORTANT: The order of the above mixins matters. // SparkInterface MUST come after LelantusInterface. From ed6a4f7c408ae400f6954c8604f8a0157ff029c9 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 4 Jun 2024 12:27:33 -0600 Subject: [PATCH 091/100] libmonero ref update --- crypto_plugins/flutter_libmonero | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero index 81a4f74ea..4b87151d4 160000 --- a/crypto_plugins/flutter_libmonero +++ b/crypto_plugins/flutter_libmonero @@ -1 +1 @@ -Subproject commit 81a4f74ea068d3d1026c8e564ee9b0b28cee20c4 +Subproject commit 4b87151d4914606b911f738a8236a6e54a6d8ecb From 8a6b45df7e94097463e3149f1428f0a000eb4661 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 4 Jun 2024 15:46:12 -0600 Subject: [PATCH 092/100] firo dbg bal display fix --- .../my_stack_view/wallet_view/desktop_wallet_view.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart index 3be835d0b..022c0640a 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart @@ -42,6 +42,7 @@ import '../../../utilities/text_styles.dart'; import '../../../utilities/wallet_tools.dart'; import '../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../wallets/wallet/impl/banano_wallet.dart'; +import '../../../wallets/wallet/impl/firo_wallet.dart'; import '../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../widgets/custom_buttons/blue_text_button.dart'; import '../../../widgets/desktop/desktop_app_bar.dart'; @@ -264,7 +265,8 @@ class _DesktopWalletViewState extends ConsumerState { ), ], ), - if (wallet.isarTransactionVersion == 2) + if (wallet.isarTransactionVersion == 2 && + wallet is FiroWallet) Row( children: [ const Text( From debcb71d89d0e4cdaaf8b526f9eec53332e52cc3 Mon Sep 17 00:00:00 2001 From: julian Date: Wed, 5 Jun 2024 09:13:47 -0600 Subject: [PATCH 093/100] WIP campfire scripts --- scripts/android/build_all_campfire.sh | 22 +++++++ scripts/app_config/configure_campfire.sh | 59 +++++++++++++++++++ .../templates/configure_template_files.sh | 10 ++-- scripts/build_app.sh | 4 +- scripts/ios/build_all_campfire.sh | 28 +++++++++ scripts/linux/build_all_campfire.sh | 24 ++++++++ scripts/macos/build_all_campfire.sh | 19 ++++++ scripts/windows/build_all_campfire.sh | 19 ++++++ 8 files changed, 180 insertions(+), 5 deletions(-) create mode 100755 scripts/android/build_all_campfire.sh create mode 100755 scripts/app_config/configure_campfire.sh create mode 100755 scripts/ios/build_all_campfire.sh create mode 100755 scripts/linux/build_all_campfire.sh create mode 100755 scripts/macos/build_all_campfire.sh create mode 100755 scripts/windows/build_all_campfire.sh diff --git a/scripts/android/build_all_campfire.sh b/scripts/android/build_all_campfire.sh new file mode 100755 index 000000000..7cb2e083d --- /dev/null +++ b/scripts/android/build_all_campfire.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -x -e + +# libepiccash requires old rust +source ../rust_version.sh +set_rust_to_1671 + +mkdir -p build +. ./config.sh +./install_ndk.sh + +PLUGINS_DIR=../../crypto_plugins + +(cd "${PLUGINS_DIR}"/flutter_liblelantus/scripts/android && ./build_all.sh ) +(cd "${PLUGINS_DIR}"/flutter_libepiccash/scripts/android && ./build_all.sh ) +(cd "${PLUGINS_DIR}"/flutter_libmonero/scripts/android/ && ./build_all.sh ) +set_rust_to_1720 +(cd "${PLUGINS_DIR}"/frostdart/scripts/android && ./build_all.sh ) + +wait +echo "Done building" diff --git a/scripts/app_config/configure_campfire.sh b/scripts/app_config/configure_campfire.sh new file mode 100755 index 000000000..03135118e --- /dev/null +++ b/scripts/app_config/configure_campfire.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash + +set -x -e + +# Configure files for Duo. +APP_BUILD_PLATFORM=$1 + +export NEW_NAME="Campfire" +if [[ "$APP_BUILD_PLATFORM" != "ios" ]]; then + export NEW_APP_ID="com.cypherstack.campfire" +else + # for some reason this was different in the old campfire code for ios + export NEW_APP_ID="com.cypherstack.campfirefirowallet" +fi +export NEW_APP_ID_CAMEL="com.cypherstack.campfire" +export NEW_APP_ID_SNAKE="com.cypherstack.campfire" +export NEW_BASIC_NAME="campfire" + +NEW_PUBSPEC_NAME="paymint" # paymint used in original pubspec for some reason +PUBSPEC_FILE="${APP_PROJECT_ROOT_DIR}/pubspec.yaml" + +# String replacements. +if [[ "$(uname)" == 'Darwin' ]]; then + # macos specific sed + sed -i '' "s/name: PLACEHOLDER/name: ${NEW_PUBSPEC_NAME}/g" "${PUBSPEC_FILE}" + sed -i '' "s/description: PLACEHOLDER/description: ${NEW_NAME}/g" "${PUBSPEC_FILE}" +else + sed -i "s/name: PLACEHOLDER/name: ${NEW_PUBSPEC_NAME}/g" "${PUBSPEC_FILE}" + sed -i "s/description: PLACEHOLDER/description: ${NEW_NAME}/g" "${PUBSPEC_FILE}" +fi + +pushd "${APP_PROJECT_ROOT_DIR}" +BUILT_COMMIT_HASH=$(git log -1 --pretty=format:"%H") +popd + +APP_CONFIG_DART_FILE="${APP_PROJECT_ROOT_DIR}/lib/app_config.g.dart" +rm -f "$APP_CONFIG_DART_FILE" +cat << EOF > "$APP_CONFIG_DART_FILE" +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'app_config.dart'; + +const _prefix = "Campfire"; +const _separator = ""; +const _suffix = ""; +const _appDataDirName = "campfire"; +const _commitHash = "$BUILT_COMMIT_HASH"; + +const ({String light, String dark})? _appIconAsset = ( + light: "assets/in_app_logo_icons/stack-duo-icon_light.svg", + dark: "assets/in_app_logo_icons/stack-duo-icon_dark.svg", +); + +final List _supportedCoins = List.unmodifiable([ + Firo(CryptoCurrencyNetwork.main), + Firo(CryptoCurrencyNetwork.test), +]); + +EOF \ No newline at end of file diff --git a/scripts/app_config/templates/configure_template_files.sh b/scripts/app_config/templates/configure_template_files.sh index 9acf5b6f4..35cb26219 100755 --- a/scripts/app_config/templates/configure_template_files.sh +++ b/scripts/app_config/templates/configure_template_files.sh @@ -54,13 +54,15 @@ TEMPLATE_FILES=( "${WIN_TF_2}" ) -if [ ! -f "${ACTUAL_PUBSPEC}" ]; then - cp "${T_PUBSPEC}" "${ACTUAL_PUBSPEC}" +if [ -f "${ACTUAL_PUBSPEC}" ]; then + rm "${ACTUAL_PUBSPEC}" fi +cp "${T_PUBSPEC}" "${ACTUAL_PUBSPEC}" for TF in "${TEMPLATE_FILES[@]}"; do FILE="${APP_PROJECT_ROOT_DIR}/${TF}" - if [ ! -f "${FILE}" ]; then - cp -rp "${TEMPLATES_DIR}/${TF}" "${FILE}" + if [ -f "${FILE}" ]; then + rm "${FILE}" fi + cp -rp "${TEMPLATES_DIR}/${TF}" "${FILE}" done \ No newline at end of file diff --git a/scripts/build_app.sh b/scripts/build_app.sh index aa569226a..612c4097f 100755 --- a/scripts/build_app.sh +++ b/scripts/build_app.sh @@ -94,7 +94,7 @@ if printf '%s\0' "${APP_NAMED_IDS[@]}" | grep -Fxqz -- "${APP_NAMED_ID}"; then "${APP_PROJECT_ROOT_DIR}/scripts/app_config/shared/update_version.sh" -v "${APP_VERSION_STRING}" -b "${APP_BUILD_NUMBER}" "${APP_PROJECT_ROOT_DIR}/scripts/app_config/shared/link_assets.sh" "${APP_NAMED_ID}" "${APP_BUILD_PLATFORM}" # shellcheck disable=SC1090 - source "${APP_PROJECT_ROOT_DIR}/scripts/app_config/configure_${APP_NAMED_ID}.sh" + source "${APP_PROJECT_ROOT_DIR}/scripts/app_config/configure_${APP_NAMED_ID}.sh" "${APP_BUILD_PLATFORM}" "${APP_PROJECT_ROOT_DIR}/scripts/app_config/platforms/${APP_BUILD_PLATFORM}/platform_config.sh" if [[ "$APP_BUILD_PLATFORM" != "linux" ]]; then @@ -111,6 +111,8 @@ if [ "$BUILD_CRYPTO_PLUGINS" -eq 0 ]; then ./build_all.sh elif [[ "$APP_NAMED_ID" = "stack_duo" ]]; then ./build_all_duo.sh + elif [[ "$APP_NAMED_ID" = "campfire" ]]; then + ./build_all_campfire.sh else echo "Invalid app id: ${APP_NAMED_ID}" exit 1 diff --git a/scripts/ios/build_all_campfire.sh b/scripts/ios/build_all_campfire.sh new file mode 100755 index 000000000..b34724561 --- /dev/null +++ b/scripts/ios/build_all_campfire.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +set -x -e + +# libepiccash requires old rust +source ../rust_version.sh +set_rust_to_1671 + +# ensure ios rust triples are there +rustup target add aarch64-apple-ios +rustup target add x86_64-apple-ios + +# ensure ios rust triples are there +rustup target add aarch64-apple-ios +rustup target add x86_64-apple-ios + +(cd ../../crypto_plugins/flutter_liblelantus/scripts/ios && ./build_all.sh ) +(cd ../../crypto_plugins/flutter_libepiccash/scripts/ios && ./build_all.sh ) +(cd ../../crypto_plugins/flutter_libmonero/scripts/ios/ && ./build_all.sh ) +set_rust_to_1720 +(cd ../../crypto_plugins/frostdart/scripts/ios && ./build_all.sh ) + +wait +echo "Done building" + +# ensure ios rust triples are there +rustup target add aarch64-apple-ios +rustup target add x86_64-apple-ios diff --git a/scripts/linux/build_all_campfire.sh b/scripts/linux/build_all_campfire.sh new file mode 100755 index 000000000..c9542798e --- /dev/null +++ b/scripts/linux/build_all_campfire.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +set -x -e + +# libepiccash requires old rust +source ../rust_version.sh +set_rust_to_1671 + +# for arm +# flutter-elinux clean +# flutter-elinux pub get +# flutter-elinux build linux --dart-define="IS_ARM=true" +mkdir -p build +./build_secure_storage_deps.sh +(cd ../../crypto_plugins/flutter_liblelantus/scripts/linux && ./build_all.sh ) +(cd ../../crypto_plugins/flutter_libepiccash/scripts/linux && ./build_all.sh ) +(cd ../../crypto_plugins/flutter_libmonero/scripts/linux && ./build_all.sh ) +set_rust_to_1720 +(cd ../../crypto_plugins/frostdart/scripts/linux && ./build_all.sh ) + +./build_secp256k1.sh + +wait +echo "Done building" diff --git a/scripts/macos/build_all_campfire.sh b/scripts/macos/build_all_campfire.sh new file mode 100755 index 000000000..e3a58b45f --- /dev/null +++ b/scripts/macos/build_all_campfire.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +set -x -e + +# libepiccash requires old rust +source ../rust_version.sh +set_rust_to_1671 + +(cd ../../crypto_plugins/flutter_liblelantus/scripts/macos && ./build_all.sh ) +(cd ../../crypto_plugins/flutter_libepiccash/scripts/macos && ./build_all.sh ) +(cd ../../crypto_plugins/flutter_libmonero/scripts/macos/ && ./build_all.sh ) +set_rust_to_1720 +(cd ../../crypto_plugins/frostdart/scripts/macos && ./build_all.sh ) + +wait +echo "Done building" + +# set rust (back) to a more recent stable release to allow stack wallet to build tor +set_rust_to_1720 diff --git a/scripts/windows/build_all_campfire.sh b/scripts/windows/build_all_campfire.sh new file mode 100755 index 000000000..fc6ac37b1 --- /dev/null +++ b/scripts/windows/build_all_campfire.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +set -x -e + +# libepiccash requires old rust +source ../rust_version.sh +set_rust_to_1671 + +mkdir -p build +(cd ../../crypto_plugins/flutter_libepiccash/scripts/windows && ./build_all.sh ) +(cd ../../crypto_plugins/flutter_liblelantus/scripts/windows && ./build_all.sh ) +(cd ../../crypto_plugins/flutter_libmonero/scripts/windows && ./build_all.sh) +set_rust_to_1720 +(cd ../../crypto_plugins/frostdart/scripts/windows && ./build_all.sh ) + +./build_secp256k1_wsl.sh + +wait +echo "Done building" From 3dac77b8d6d489e81af2d91a4a604c0dfa3a76c6 Mon Sep 17 00:00:00 2001 From: julian Date: Wed, 5 Jun 2024 10:51:18 -0600 Subject: [PATCH 094/100] WIP mempool electrumx cals --- lib/electrumx_rpc/electrumx_client.dart | 59 +++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/lib/electrumx_rpc/electrumx_client.dart b/lib/electrumx_rpc/electrumx_client.dart index 1e6018a95..594e73fe3 100644 --- a/lib/electrumx_rpc/electrumx_client.dart +++ b/lib/electrumx_rpc/electrumx_client.dart @@ -930,6 +930,7 @@ class ElectrumXClient { } } + // TODO: update when we get new call to include tx hashes in response /// Takes [startNumber], if it is 0, we get the full set, /// otherwise the used tags after that number Future> getSparkUnhashedUsedCoinsTags({ @@ -1024,6 +1025,64 @@ class ElectrumXClient { } } + /// Returns the txids of the current transactions found in the mempool + Future> getMempoolTxids({ + String? requestID, + }) async { + try { + final start = DateTime.now(); + final response = await request( + requestID: requestID, + command: "spark.getmempooltxids", + ); + + // TODO verify once server is live + final txids = List.from(response as List).toSet(); + // final map = Map.from(response as Map); + // final txids = List.from(map["tags"] as List).toSet(); + + Logging.instance.log( + "Finished ElectrumXClient.getMempoolTxids(). " + "Duration=${DateTime.now().difference(start)}", + level: LogLevel.Info, + ); + + return txids; + } catch (e) { + Logging.instance.log(e, level: LogLevel.Error); + rethrow; + } + } + + /// Returns the txids of the current transactions found in the mempool + Future> getMempoolSparkData({ + String? requestID, + required List txids, + }) async { + try { + final start = DateTime.now(); + final response = await request( + requestID: requestID, + command: "spark.getmempooltxs", + args: txids, + ); + + // TODO verify once server is live + final map = Map.from(response as Map); + + Logging.instance.log( + "Finished ElectrumXClient.getMempoolSparkData(txids: $txids). " + "Duration=${DateTime.now().difference(start)}", + level: LogLevel.Info, + ); + + return map; + } catch (e) { + Logging.instance.log(e, level: LogLevel.Error); + rethrow; + } + } + // =========================================================================== /// Get the current fee rate. From f5e9187c443d53251bd672ae7f2f931e41d43d82 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 5 Jun 2024 12:24:16 -0500 Subject: [PATCH 095/100] add campfire assets use some stack_wallet assets as appropriate --- .../default_themes/campfire/light.zip | Bin 0 -> 897669 bytes asset_sources/icon/campfire/app_icon_alpha.png | Bin 0 -> 58920 bytes asset_sources/icon/campfire/icon.png | Bin 0 -> 53110 bytes asset_sources/icon/campfire/macos-icon.png | Bin 0 -> 58920 bytes asset_sources/icon/campfire/splash.png | Bin 0 -> 33590 bytes .../campfire/campfire-icon_dark.svg | 9 +++++++++ .../campfire/campfire-icon_light.svg | 9 +++++++++ .../lottie/campfire/arrow_rotate.json | 1 + asset_sources/lottie/campfire/icon_send.json | 1 + asset_sources/lottie/campfire/loader.json | 1 + .../lottie/campfire/loader_and_checkmark.json | 1 + .../lottie/campfire/onion_animation.json | 1 + .../campfire/app_icon-playstore.png | Bin 0 -> 53110 bytes 13 files changed, 23 insertions(+) create mode 100644 asset_sources/default_themes/campfire/light.zip create mode 100644 asset_sources/icon/campfire/app_icon_alpha.png create mode 100644 asset_sources/icon/campfire/icon.png create mode 100644 asset_sources/icon/campfire/macos-icon.png create mode 100644 asset_sources/icon/campfire/splash.png create mode 100644 asset_sources/in_app_logo_icons/campfire/campfire-icon_dark.svg create mode 100644 asset_sources/in_app_logo_icons/campfire/campfire-icon_light.svg create mode 100644 asset_sources/lottie/campfire/arrow_rotate.json create mode 100644 asset_sources/lottie/campfire/icon_send.json create mode 100644 asset_sources/lottie/campfire/loader.json create mode 100644 asset_sources/lottie/campfire/loader_and_checkmark.json create mode 100644 asset_sources/lottie/campfire/onion_animation.json create mode 100644 asset_sources/other/playstore_icon/campfire/app_icon-playstore.png diff --git a/asset_sources/default_themes/campfire/light.zip b/asset_sources/default_themes/campfire/light.zip new file mode 100644 index 0000000000000000000000000000000000000000..d94ce2ac8660b38e4e2889e3ed0e2add9a124c89 GIT binary patch literal 897669 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 E;Ev?^0SIu+4v^r6`fl2QQUv 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 z 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 z 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�Hk=GSMv^jSonQ^$Q?y>j28X%yM_wXj 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 z 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 f?utDs+~i|n0p-FJ$4WcG$oo5ZuttP=^tj@A06 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�GsGu>$Bc`LTo{pdL$Yl0#< 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¬tJ}CUy!-EkLIu6CFpy#OsyG= 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�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

FXM$pL@3^{u4>uLxxs= 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Ðe#t(0?PU4coOgXC5`*kn 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≺rx~m$&YsGYRG%&&ieS1Osy+k}iJzzq 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 DK32U; literal 0 HcmV?d00001 diff --git a/asset_sources/icon/campfire/icon.png b/asset_sources/icon/campfire/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..bea9f072eeca32ad13487c086c38b68d68fce027 GIT binary patch literal 53110 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 DK32U; literal 0 HcmV?d00001 diff --git a/asset_sources/icon/campfire/splash.png b/asset_sources/icon/campfire/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..72b1c4a0afc976d1c36ceb6ce6a0a1b042ace5cf GIT binary patch literal 33590 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ß^*0<5yRlra=n)M|XlQqGVhH{c1uSEDU1V!(0jPn|Y+ z-w+N_T3*k=Jn&J{sS1E1;Sd29iAqOSCOcMoE|efMSKe?IM(uycug0k`lm`c5@cqMx zq_hyXrTs;dLgSBP3)%dA-b{EF$oDwuTZzH~yRnCQrn!gUG;kq}FX_Kd9#Z+a%U!<% zgz4$9!Z3#%OT5i(q>2!$u^JUn-om>t7QLbmzGDW1+w}v8_E2ygvw$Gjm=2{B6OF*N zQub}Z2Qd1==*`&b%kRFWJOq&uRmVedbjIQ0M095;!E4I-WobujVbm3V3ZXFPC8k}W zh9wY?1xbmKeo9J5xJT941!wsn3$ad-ONx%tYP5-XVL&15t1*3)X$m+sOk=hnEDzS{`S9VuM!r%MSRRi+YaWd_yUo(l<>Qn+9^*h0+~JPN7sAb$jRR&y%Br1BV3U+Zr52p{FH5aa6A9lvj~r zUy6Av2qn!}lAZ_A`6C2(;AxkZc_ntaz2BjgC&T3^YT})%dc8J|l4YFL%C**Z-s1(` zW^*vkmNYaq1+fWi7YqpeQ3fw`j(s@(c^ogZ4JBJH+ERQsUkVk1YObE2c|C*PBnSFo z_ZCr)FsMXOzIqR5RF&bF)|#9gm^&C&k$T%mkF#9Tum| z6jakoq#WKWJ>I1ONYJ4-Yc?5pF3}6Fqp>9nb?|vk%)?D)Av7xa>#e3}sPI3;H z-)7Vc2j;KU<5HI65{rq34rLJ&-(Fua))6=UQA-2}laA0lBoHZdv^vdPEJnCiVMTT8 zASe>K5GX?iN|i7^&fWdZH+qpsXlYUEpKX3iB|>1aTy9`r5Ll47EGIqu$rN6OVWMw8 zOeMuLOP$gBI$T~ zQ$;l+cdB?jA)8q8$Nzdx5A+Ve9-v);Jbr`qm0AfwaTq{v-l$d@M@brv&(x&GB4h!t z7`%HhS#B|2jNxJ>h5vWJuYoq%ojQ(s0|3z=i#Fmpn0@LD4$jh9Qrx%eoB9PSFjr7@~XuqB#MSbK=aP&CKHY0 zc>i&ft#%weKsNqWh_!*@5o}h&5z*{-IPH^57p!KsY1j12ZD)GySsvaNJ_|tEwX>M| z=@&sDC25FsHLN>6Q{%xfC3%%aV0rv!V_BxtlOvcB@H)akv791~Xo&rJaO(!mG9g2a zLDmbA3JSjrCyWbXUD)^im#p4CL}MVN{|JqjPB5;S48p(^kp1TXAh7ifrw70!{N*;Vo`6w&O1*%yz6#Mlep4rkGoc zpO38@nbktwC!uK@5yU_DX(v62UOFzLg+?EtPv@_XzJZ?ZpkgkanMp(b!J{GgKIcG5 ze;2Yr5{hhoA+HR!tnZd5b@O2+VYW9)%Llic{+D7=cN~B^`{xcsFYQ& zriRxN;aDsL2 + + + + + + + + diff --git a/asset_sources/in_app_logo_icons/campfire/campfire-icon_light.svg b/asset_sources/in_app_logo_icons/campfire/campfire-icon_light.svg new file mode 100644 index 000000000..e5c58d8ec --- /dev/null +++ b/asset_sources/in_app_logo_icons/campfire/campfire-icon_light.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/asset_sources/lottie/campfire/arrow_rotate.json b/asset_sources/lottie/campfire/arrow_rotate.json new file mode 100644 index 000000000..c729d2e7a --- /dev/null +++ b/asset_sources/lottie/campfire/arrow_rotate.json @@ -0,0 +1 @@ +{"v":"5.10.2","fr":30,"ip":0,"op":60,"w":30,"h":30,"nm":"arrow-rotate","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"arrow-rotate","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":44,"s":[200]},{"t":60,"s":[360]}],"ix":10},"p":{"a":0,"k":[15,15,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-0.828],[0,0],[3.389,0],[1.441,-4.074],[-0.781,-0.277],[-0.276,0.778],[-3.22,0],[-1.369,-1.823],[0,0],[0,-0.83],[-0.83,0],[0,0],[-0.023,0],[0,0],[0,0.83],[0,0],[0.83,0]],"o":[[0,0],[-1.964,-2.437],[-4.533,0],[-0.276,0.741],[0.781,0.277],[1.031,-2.916],[2.494,0],[0,0],[-0.83,0],[0,0.83],[0,0],[0.023,0],[0,0],[0.83,0],[0,0],[0,-0.828],[-0.83,0]],"v":[[8.25,-8.25],[8.25,-6.497],[-0.042,-10.5],[-9.902,-3.502],[-8.988,-1.584],[-7.073,-2.498],[-0.042,-7.5],[6,-4.5],[4.5,-4.5],[3,-3],[4.5,-1.5],[8.452,-1.5],[8.522,-1.5],[9.75,-1.5],[11.25,-3],[11.25,-8.25],[9.75,-9.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[-0.828,0],[0,0.83],[0,0],[-3.347,0],[-1.439,4.073],[0.783,0.277],[0.277,-0.778],[3.262,0],[1.411,1.823],[0,0],[0,0.83],[0.83,0],[0,0],[0,-0.83]],"o":[[0,0.83],[0.828,0],[0,0],[1.922,2.438],[4.575,0],[0.277,-0.783],[-0.778,-0.277],[-1.031,2.916],[-2.452,0],[0,0],[0.83,0],[0,-0.83],[0,0],[-0.828,0],[0,0]],"v":[[-11.25,8.25],[-9.75,9.75],[-8.25,8.25],[-8.25,6.497],[0,10.5],[9.9,3.502],[8.986,1.584],[7.073,2.498],[0,7.5],[-6.042,4.5],[-4.5,4.5],[-3,3],[-4.5,1.5],[-9.75,1.5],[-11.25,3]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.137254908681,0.137254908681,0.137254908681,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/asset_sources/lottie/campfire/icon_send.json b/asset_sources/lottie/campfire/icon_send.json new file mode 100644 index 000000000..7dc1f7c2d --- /dev/null +++ b/asset_sources/lottie/campfire/icon_send.json @@ -0,0 +1 @@ +{"v":"5.10.2","fr":30,"ip":0,"op":100,"w":24,"h":24,"nm":"icon-send","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"MASK","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[12,12,0],"ix":2,"l":2},"a":{"a":0,"k":[0.125,0.125,0],"ix":1,"l":2},"s":{"a":0,"k":[87.368,87.368,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[23.75,23.75],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0.125,0.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Arrow","tt":1,"tp":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[-1.009,25.009,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":33,"s":[11.695,12.306,0],"to":[0,0,0],"ti":[0,0,0]},{"t":53,"s":[24.398,-0.397,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,-0.69],[-0.69,0],[0,0],[0,0],[-0.488,-0.488],[-0.488,0.488],[0,0],[0,0],[-0.69,0],[0,0.69],[0,0],[0.234,0.234],[0.332,0]],"o":[[-0.69,0],[0,0.69],[0,0],[0,0],[-0.488,0.488],[0.488,0.488],[0,0],[0,0],[0,0.69],[0.69,0],[0,0],[0,-0.332],[-0.234,-0.234],[0,0]],"v":[[-2,-5.25],[-3.25,-4],[-2,-2.75],[0.982,-2.75],[-4.884,3.116],[-4.884,4.884],[-3.116,4.884],[2.75,-0.982],[2.75,2],[4,3.25],[5.25,2],[5.25,-4],[4.884,-4.884],[4,-5.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.137254908681,0.137254908681,0.137254908681,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector (Stroke)","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Outline","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[12,12,0],"ix":2,"l":2},"a":{"a":0,"k":[12,0.063,0],"ix":1,"l":2},"s":{"a":0,"k":[90.104,90.104,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.137254901961,0.137254901961,0.137254901961,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2.5,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[12,0.063],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":58,"s":[0]},{"t":97,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":58,"s":[100]},{"t":97,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":100,"st":0,"ct":1,"bm":0}],"markers":[{"tm":0,"cm":"{\r\n\"name\":\"SEGMENT 1\"\r\n}","dr":0},{"tm":53,"cm":"{\r\n\"name\":\"SEGMENT 2\"\r\n}","dr":0},{"tm":97,"cm":"{\r\n\"name\":\"SEGMENT 3\"\r\n}","dr":0}]} \ No newline at end of file diff --git a/asset_sources/lottie/campfire/loader.json b/asset_sources/lottie/campfire/loader.json new file mode 100644 index 000000000..dae536282 --- /dev/null +++ b/asset_sources/lottie/campfire/loader.json @@ -0,0 +1 @@ +{"v":"5.10.2","fr":30,"ip":0,"op":93,"w":700,"h":700,"nm":"Stack-duo-animation","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":42,"s":[0]},{"t":55,"s":[180]}],"ix":10},"p":{"a":0,"k":[350,350,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":101,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"gray ring Outlines","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":1,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":7,"s":[70]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":79,"s":[70]},{"t":85,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":1,"s":[199,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"t":27,"s":[-63.24,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"t":42,"s":[-63.24,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"t":55,"s":[-63.24,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":61,"s":[-63.24,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"t":65,"s":[-63.24,0,0],"to":[0,0,0],"ti":[0,0,0]},{"t":85,"s":[-198.24,0,0]}],"ix":2,"l":2},"a":{"a":0,"k":[128,128,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[0.119,0.119,5.929]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":1,"s":[70,70,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0.233,0.233,-2.61]},"t":18,"s":[80,80,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":27,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":42,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,-2.367]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":55,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,7.852]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":56,"s":[100,100,100]},{"t":85,"s":[70,70,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[27.84,-7.24],[5.65,0],[0,35.77],[-35.77,0],[-5.22,-1.37],[0,-30.12]],"o":[[-5.22,1.37],[-35.77,0],[0,-35.77],[5.65,0],[27.84,7.24],[0,30.12]],"v":[[16.36,62.67],[0,64.76],[-64.77,0],[0,-64.76],[16.36,-62.67],[64.76,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[38.26,22.13],[23.31,0],[0,-70.69],[-70.7,0],[-18.83,10.89],[0,47.38]],"o":[[-18.83,-10.89],[-70.7,0],[0,70.69],[23.31,0],[38.26,-22.13],[0,-47.38]],"v":[[63.99,-110.88],[0,-128],[-128,0],[0,128],[63.99,110.88],[128,0]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.576470588235,0.576470588235,0.576470588235,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[128,128],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":6,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":93,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"black ball Outlines","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":1,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":7,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":79,"s":[100]},{"t":85,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":1,"s":[-199.4,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"t":27,"s":[64.76,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"t":42,"s":[64.76,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"t":55,"s":[64.76,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":61,"s":[64.76,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"t":65,"s":[64.76,0,0],"to":[0,0,0],"ti":[0,0,0]},{"t":85,"s":[196.76,0,0]}],"ix":2,"l":2},"a":{"a":0,"k":[128.25,128.25,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":1,"s":[70,70,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":18,"s":[80,80,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":27,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":42,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,-2.367]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":55,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,7.852]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":56,"s":[100,100,100]},{"t":85,"s":[70,70,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-70.69],[70.69,0],[18.83,10.89],[11.26,20.03],[0,22.77],[-10.43,18.52],[-19.87,11.49],[-23.31,0]],"o":[[0,70.69],[-23.31,0],[-19.87,-11.49],[-10.43,-18.52],[0,-22.77],[11.26,-20.03],[18.83,-10.89],[70.69,0]],"v":[[128,0],[0,128],[-63.99,110.88],[-111.62,62.67],[-128,0],[-111.62,-62.67],[-63.99,-110.88],[0,-128]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[128.25,128.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":93,"st":0,"ct":1,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/asset_sources/lottie/campfire/loader_and_checkmark.json b/asset_sources/lottie/campfire/loader_and_checkmark.json new file mode 100644 index 000000000..406f52030 --- /dev/null +++ b/asset_sources/lottie/campfire/loader_and_checkmark.json @@ -0,0 +1 @@ +{"v":"5.10.2","fr":30,"ip":0,"op":130,"w":24,"h":24,"nm":"Loader","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Arrow","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":115,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":117,"s":[80]},{"t":122,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[12,12,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":117,"s":[30,30,100]},{"t":122,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[4.75,-3],[-1.25,3],[-4.75,-0.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2.5,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":100,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":117,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":119,"s":[30]},{"t":122,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":130,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Fill","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":115,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":117,"s":[30]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":119,"s":[70]},{"t":122,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[12,12,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":117,"s":[241.821,241.821,100]},{"t":122,"s":[1511.821,1511.821,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.647058823529,0.470588235294,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[6.243,6.243],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":130,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Сircle green","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":73,"s":[0]},{"t":74,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[12,12,0],"ix":2,"l":2},"a":{"a":0,"k":[0,-0.063,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.754]},"t":0,"s":[113.562,113.562,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":111,"s":[113.562,113.562,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":115,"s":[90,90,100]},{"t":122,"s":[113.562,113.562,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[19.125,19.125],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.647058823529,0.470588235294,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-0.063],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":74,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":87,"s":[0]},{"t":111,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":74,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":87,"s":[55]},{"t":111,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.647058823529,0.470588235294,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":130,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Сircle black 2 turn","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":36,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":37,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":74,"s":[100]},{"t":75,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[12,12,0],"ix":2,"l":2},"a":{"a":0,"k":[0,-0.063,0],"ix":1,"l":2},"s":{"a":0,"k":[113.562,113.562,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[19.125,19.125],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.137254901961,0.137254901961,0.137254901961,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-0.063],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":37,"s":[0.2]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":50,"s":[0.2]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":57,"s":[27.5]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":72,"s":[99]},{"t":74,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":37,"s":[0.1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":50,"s":[55]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":57,"s":[73]},{"t":74,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.137254908681,0.137254908681,0.137254908681,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":130,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Сircle black 1 turn","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":37,"s":[100]},{"t":38,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[12,12,0],"ix":2,"l":2},"a":{"a":0,"k":[0,-0.063,0],"ix":1,"l":2},"s":{"a":0,"k":[113.562,113.562,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[19.125,19.125],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.137254901961,0.137254901961,0.137254901961,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-0.063],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":13,"s":[0.2]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[27.5]},{"t":37,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":13,"s":[55]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[73]},{"t":37,"s":[99]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.137254908681,0.137254908681,0.137254908681,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":130,"st":0,"ct":1,"bm":0}],"markers":[{"tm":0,"cm":"{\r\n\"name\":\"marker 1\"\r\n}","dr":0},{"tm":74,"cm":"{\r\n\"name\":\"marker 2\"\r\n}","dr":0},{"tm":130,"cm":"{\r\n\"name\":\"marker 3\"\r\n}","dr":0}]} \ No newline at end of file diff --git a/asset_sources/lottie/campfire/onion_animation.json b/asset_sources/lottie/campfire/onion_animation.json new file mode 100644 index 000000000..9988b5a7c --- /dev/null +++ b/asset_sources/lottie/campfire/onion_animation.json @@ -0,0 +1 @@ +{"v":"5.10.2","fr":25,"ip":0,"op":436,"w":180,"h":180,"nm":"onion-character-animation","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"failed","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150.3,44.3,0],"ix":2,"l":2},"a":{"a":0,"k":[17.188,17.188,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":393,"s":[0,0,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":408,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":414,"s":[108,108,100]},{"t":429,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-6.919,6.919],[6.919,-6.919]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.878431432387,0.415686304429,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[17.188,17.187],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-6.919,-6.919],[6.919,6.919]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.878431432387,0.415686304429,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[17.188,17.188],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-8.388,0],[0,-8.388],[8.388,0],[0,8.387]],"o":[[8.388,0],[0,8.387],[-8.388,0],[0,-8.388]],"v":[[0,-15.188],[15.188,0.001],[0,15.188],[-15.188,0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.513725490196,0.513725490196,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[17.188,17.188],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"globe","sr":1,"ks":{"o":{"a":1,"k":[{"t":195,"s":[100],"h":1},{"t":213,"s":[0],"h":1},{"t":378,"s":[100],"h":1},{"t":427,"s":[0],"h":1}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150.25,44.25,0],"ix":2,"l":2},"a":{"a":0,"k":[17.188,17.188,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":32,"s":[0,0,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":47,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":53,"s":[108,108,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":68,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":328,"s":[100,100,100]},{"t":341,"s":[0,0,100],"h":1},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":378,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":390,"s":[100,100,100]},{"t":427,"s":[0,0,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[2.001,17.795],[32.376,17.795]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[17.33,2],[17.33,32.375]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-4.233,0],[-1.818,-2.345]],"o":[[1.817,-2.345],[4.233,0],[0,0]],"v":[[-9.721,1.975],[-0.001,-1.975],[9.721,1.975]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[17.188,27.059],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-4.589,0],[-1.622,2.672]],"o":[[1.622,2.672],[4.589,0],[0,0]],"v":[[-10.14,-2.278],[0,2.278],[10.14,-2.278]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[17.188,8.227],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.026,0],[0,-8.388],[4.026,0],[0,8.387]],"o":[[4.026,0],[0,8.387],[-4.026,0],[0,-8.388]],"v":[[0,-15.188],[7.29,0.001],[0,15.188],[-7.29,0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[17.188,17.188],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 5","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-8.388,0],[0,-8.388],[8.388,0],[0,8.387]],"o":[[8.388,0],[0,8.387],[-8.388,0],[0,-8.388]],"v":[[0,-15.188],[15.188,0.001],[0,15.188],[-15.188,0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.878431432387,0.415686304429,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[17.188,17.188],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 6","np":3,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"globe 2","sr":1,"ks":{"o":{"a":1,"k":[{"t":195,"s":[0],"h":1},{"t":213,"s":[100],"h":1}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150.25,44.25,0],"ix":2,"l":2},"a":{"a":0,"k":[17.188,17.188,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":32,"s":[0,0,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":47,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":53,"s":[108,108,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":68,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":328,"s":[100,100,100]},{"t":341,"s":[0,0,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[2.001,17.795],[32.376,17.795]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[17.33,2],[17.33,32.375]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-4.233,0],[-1.818,-2.345]],"o":[[1.817,-2.345],[4.233,0],[0,0]],"v":[[-9.721,1.975],[-0.001,-1.975],[9.721,1.975]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[17.188,27.059],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-4.589,0],[-1.622,2.672]],"o":[[1.622,2.672],[4.589,0],[0,0]],"v":[[-10.14,-2.278],[0,2.278],[10.14,-2.278]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[17.188,8.227],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.026,0],[0,-8.388],[4.026,0],[0,8.387]],"o":[[4.026,0],[0,8.387],[-4.026,0],[0,-8.388]],"v":[[0,-15.188],[7.29,0.001],[0,15.188],[-7.29,0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[17.188,17.188],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 5","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-8.388,0],[0,-8.388],[8.388,0],[0,8.387]],"o":[[8.388,0],[0,8.387],[-8.388,0],[0,-8.388]],"v":[[0,-15.188],[15.188,0.001],[0,15.188],[-15.188,0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.549019607843,0.901960784314,0.803921568627,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[17.188,17.188],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 6","np":3,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":3,"nm":"null-indicator","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":38,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":75,"s":[153.988]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":165,"s":[514]},{"t":211,"s":[720],"h":1},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":378,"s":[514]},{"t":424,"s":[720]}],"ix":10},"p":{"a":0,"k":[150.125,44.219,0],"ix":2,"l":2},"a":{"a":0,"k":[60.125,-45.781,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"indicator-left ","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50.969,-46.99,0],"ix":2,"l":2},"a":{"a":0,"k":[12.968,22.698,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":40,"s":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":45,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":51,"s":[108,108,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":66,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":191,"s":[100,100,100]},{"t":218,"s":[0,0,100],"h":1},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":378,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":385,"s":[100,100,100]},{"t":402,"s":[0,0,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.932,0],[0,-0.931],[0.932,0],[0,0.932]],"o":[[0.932,0],[0,0.932],[-0.932,0],[0,-0.931]],"v":[[0,-1.688],[1.687,-0.001],[0,1.688],[-1.687,-0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.878431432387,0.415686304429,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[22.249,3.688],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,-10.616],[-8.447,-2.437]],"o":[[-10.394,0.857],[0,9.232],[0,0]],"v":[[9.281,-19.82],[-9.281,0.361],[5.343,19.82]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[11.281,23.576],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"indicator-right ","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[69.531,-44.51,0],"ix":2,"l":2},"a":{"a":0,"k":[12.968,22.698,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":40,"s":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":45,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":51,"s":[108,108,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":66,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":191,"s":[100,100,100]},{"t":218,"s":[0,0,100],"h":1},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":378,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":385,"s":[100,100,100]},{"t":402,"s":[0,0,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.932,0],[0,-0.931],[0.932,0],[0,0.932]],"o":[[0.932,0],[0,0.932],[-0.932,0],[0,-0.931]],"v":[[0,-1.688],[1.687,-0.001],[0,1.688],[-1.687,-0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.878431432387,0.415686304429,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[3.687,41.708],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,-9.232],[10.395,-0.857]],"o":[[8.447,2.438],[0,10.615],[0,0]],"v":[[-5.344,-19.82],[9.281,-0.361],[-9.281,19.82]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[14.655,21.819],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"network-small","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":235,"s":[100]},{"t":243,"s":[0],"h":1},{"t":265,"s":[100],"h":1},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":319,"s":[100]},{"t":327,"s":[0],"h":1}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[168.65,44.817,0],"ix":2,"l":2},"a":{"a":0,"k":[3.912,13.78,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":211,"s":[0,0,100]},{"t":222,"s":[100,100,100],"h":1},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":265,"s":[0,0,100]},{"t":276,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,-4.365],[2.407,-3.331]],"o":[[2.348,3.306],[0,4.424],[0,0]],"v":[[-1.816,-11.78],[1.913,-0.067],[-1.913,11.78]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[3.913,13.78],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"network-small","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":235,"s":[100]},{"t":243,"s":[0],"h":1},{"t":265,"s":[100],"h":1},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":319,"s":[100]},{"t":327,"s":[0],"h":1}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[131.975,44.817,0],"ix":2,"l":2},"a":{"a":0,"k":[3.912,13.78,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":211,"s":[0,0,100]},{"t":222,"s":[100,100,100],"h":1},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":265,"s":[0,0,100]},{"t":276,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,-4.365],[-2.407,-3.331]],"o":[[-2.348,3.306],[0,4.424],[0,0]],"v":[[1.816,-11.78],[-1.913,-0.067],[1.913,11.78]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[3.912,13.78],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"network-large","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":246,"s":[100]},{"t":254,"s":[0],"h":1},{"t":276,"s":[100],"h":1},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":308,"s":[100]},{"t":316,"s":[0],"h":1}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[172.999,44.817,0],"ix":2,"l":2},"a":{"a":0,"k":[4.625,17.738,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":222,"s":[0,0,100]},{"t":233,"s":[100,100,100],"h":1},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":276,"s":[0,0,100]},{"t":287,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,-5.868],[3.298,-4.406]],"o":[[3.238,4.383],[0,5.927],[0,0]],"v":[[-2.527,-15.738],[2.626,-0.066],[-2.626,15.738]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[4.625,17.737],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"network-large","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":246,"s":[100]},{"t":254,"s":[0],"h":1},{"t":276,"s":[100],"h":1},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":308,"s":[100]},{"t":316,"s":[0],"h":1}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[127.626,44.817,0],"ix":2,"l":2},"a":{"a":0,"k":[4.625,17.738,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":222,"s":[0,0,100]},{"t":233,"s":[100,100,100],"h":1},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":276,"s":[0,0,100]},{"t":287,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,-5.868],[-3.298,-4.406]],"o":[[-3.238,4.383],[0,5.927],[0,0]],"v":[[2.527,-15.738],[-2.626,-0.066],[2.626,15.738]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[4.625,17.737],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":3,"nm":"null-body ","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":216,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":225,"s":[-10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":243,"s":[10]},{"t":252,"s":[0]}],"ix":10},"p":{"a":0,"k":[90,101,0],"ix":2,"l":2},"a":{"a":0,"k":[0,11,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"hand-right ","parent":11,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":15,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":23,"s":[26]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":308,"s":[26]},{"t":317,"s":[0],"h":1},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":378,"s":[26]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":414,"s":[26]},{"t":423,"s":[0]}],"ix":10},"p":{"a":0,"k":[31.615,10.647,0],"ix":2,"l":2},"a":{"a":0,"k":[7.62,2.379,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-0.469,-0.257],[14.905,-11.105],[-2.867,9.613],[-4.387,7.038]],"o":[[-0.146,-0.515],[4.244,2.323],[0,0],[0,0],[0,0]],"v":[[-6.294,-13.745],[-5.426,-14.434],[-3.238,7.488],[-8.799,5.079],[-3.74,-4.723]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.572549019608,0.478431402468,0.839215746113,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[13.667,16.691],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"hand-left ","parent":11,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":15,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":23,"s":[-26]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":308,"s":[-26]},{"t":317,"s":[0],"h":1},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":378,"s":[-26]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":414,"s":[-26]},{"t":423,"s":[0]}],"ix":10},"p":{"a":0,"k":[-31.366,10.897,0],"ix":2,"l":2},"a":{"a":0,"k":[19.478,2.629,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.471,-0.258],[-14.903,-11.103],[2.867,9.613],[4.387,7.037]],"o":[[0.146,-0.516],[-4.246,2.325],[0,0],[0,0],[0,0]],"v":[[6.292,-13.742],[5.421,-14.433],[3.238,7.488],[8.798,5.078],[3.738,-4.722]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.572549019608,0.478431402468,0.839215746113,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[13.665,16.691],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"eyes ","parent":11,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[0.213,-2.898,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":6,"s":[0.463,3,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":51,"s":[0.463,3,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":58,"s":[0.2,-2.898,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":79,"s":[0.2,-2.9,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":82,"s":[-6.787,-2.898,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":96,"s":[-6.8,-2.899,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":100,"s":[0.2,-2.9,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":133,"s":[0.2,-2.9,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":136,"s":[5,-9.9,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":151,"s":[5,-9.9,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":155,"s":[0.2,-2.9,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":297,"s":[0.213,-2.898,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":303,"s":[0.463,3,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":322,"s":[0.463,3,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":329,"s":[0.2,-2.898,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":408,"s":[0.213,-2.898,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":414,"s":[0.463,3,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":422,"s":[0.463,3,0],"to":[0,0,0],"ti":[0,0,0]},{"t":429,"s":[0.2,-2.898,0]}],"ix":2,"l":2},"a":{"a":0,"k":[17.045,9.606,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[1,0.833,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":19,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":23,"s":[100,7.967,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":37,"s":[100,8,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":40,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":112,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":114,"s":[100,8,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":118,"s":[100,8,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":120,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":169,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":171,"s":[100,8,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":175,"s":[100,8,100]},{"t":178,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-4.201],[1.872,0],[0,4.201],[-1.873,0]],"o":[[0,4.201],[-1.873,0],[0,-4.201],[1.872,0]],"v":[[3.391,0],[-0.001,7.606],[-3.391,0],[-0.001,-7.606]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[28.7,9.606],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-4.201],[1.873,0],[0,4.201],[-1.872,0]],"o":[[0,4.201],[-1.872,0],[0,-4.201],[1.873,0]],"v":[[3.391,0],[-0.001,7.606],[-3.391,0],[-0.001,-7.606]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[5.391,9.606],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"socket ","parent":11,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":15,"s":[31.022,33.297,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":23,"s":[23,33.297,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":308,"s":[23,33.297,0],"to":[0,0,0],"ti":[0,0,0]},{"t":318,"s":[31.022,33.297,0],"h":1},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":378,"s":[23,33.297,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":414,"s":[23,33.297,0],"to":[0,0,0],"ti":[0,0,0]},{"t":424,"s":[31.022,33.297,0]}],"ix":2,"l":2},"a":{"a":0,"k":[24.058,14.693,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-1.1,0],[0,0],[0,-1.1],[0,0],[1.1,0],[0,0],[0,1.1]],"o":[[0,-1.1],[0,0],[1.1,0],[0,0],[0,1.1],[0,0],[-1.1,0],[0,0]],"v":[[-2.435,-7.07],[-0.435,-9.07],[0.435,-9.07],[2.435,-7.07],[2.435,7.07],[0.435,9.07],[-0.435,9.07],[-2.435,7.07]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.964705942191,0.901960844152,0.658823529412,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[4.435,11.07],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,-3.981],[3.98,0],[0,0]],"o":[[0,0],[3.98,0],[0,3.981],[0,0],[0,0]],"v":[[-6.771,-7.208],[-0.438,-7.208],[6.771,0.001],[-0.438,7.208],[-6.771,7.208]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.878431432387,0.415686304429,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[10.534,11.07],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.696,0.681],[4.036,0.072],[0,0],[-17.923,-16.81],[0.72,-0.711]],"o":[[-13.443,-13.126],[0,0],[0,0],[0.739,0.693],[-0.693,0.685]],"v":[[12.516,9.362],[-15.743,-4.76],[-15.743,-7.963],[15.001,6.767],[15.023,9.345]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.768627510819,0.768627510819,0.768627510819,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30.373,17.343],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"plug ","parent":11,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":15,"s":[-26.789,33.296,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":23,"s":[-18,33.296,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":308,"s":[-18,33.296,0],"to":[0,0,0],"ti":[0,0,0]},{"t":317,"s":[-26.789,33.296,0],"h":1},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":378,"s":[-18,33.296,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":414,"s":[-18,33.296,0],"to":[0,0,0],"ti":[0,0,0]},{"t":423,"s":[-26.789,33.296,0]}],"ix":2,"l":2},"a":{"a":0,"k":[27.268,14.693,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.1,0],[0,0],[0,1.101],[0,0],[-1.1,0],[0,0],[0,-1.099]],"o":[[0,1.101],[0,0],[-1.1,0],[0,0],[0,-1.099],[0,0],[1.1,0],[0,0]],"v":[[2.435,7.07],[0.435,9.07],[-0.435,9.07],[-2.435,7.07],[-2.435,-7.07],[-0.435,-9.07],[0.435,-9.07],[2.435,-7.07]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.964705942191,0.901960844152,0.658823529412,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[43.486,11.07],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.82,0],[0,0],[0,0],[0,0],[0,-0.821]],"o":[[0,0],[0,0],[0,0],[0.82,0],[0,0.819]],"v":[[3.933,1.484],[-5.417,1.484],[-5.417,-1.484],[3.933,-1.484],[5.417,0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.768627510819,0.768627510819,0.768627510819,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[47.119,14.778],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.82,0],[0,0],[0,0],[0,0],[0,-0.82]],"o":[[0,0],[0,0],[0,0],[0.82,0],[0,0.819]],"v":[[3.933,1.484],[-5.417,1.484],[-5.417,-1.484],[3.933,-1.484],[5.417,0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.768627510819,0.768627510819,0.768627510819,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[47.119,7.363],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,3.981],[-3.981,0],[0,0]],"o":[[0,0],[-3.981,0],[0,-3.981],[0,0],[0,0]],"v":[[6.771,7.208],[0.438,7.208],[-6.771,0.001],[0.438,-7.208],[6.771,-7.208]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.878431432387,0.415686304429,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[37.387,11.07],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.696,0.681],[-4.036,0.072],[0,0],[17.924,-16.81],[-0.72,-0.711]],"o":[[13.443,-13.126],[0,0],[0,0],[-0.738,0.693],[0.693,0.685]],"v":[[-12.515,9.362],[15.742,-4.76],[15.742,-7.963],[-15.001,6.767],[-15.022,9.345]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.768627510819,0.768627510819,0.768627510819,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[17.742,17.343],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 5","np":3,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"body ","parent":11,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.017,-4.078,0],"ix":2,"l":2},"a":{"a":0,"k":[37.53,43.53,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.61,-3.511],[0.08,-0.38],[0.069,-0.28],[0.03,-0.1],[2.54,-3.51],[2.45,-1.981],[0.639,-0.45],[1.029,-0.591],[0.66,-0.32],[0.68,-0.28],[0.861,-0.28],[0.77,-0.189],[0.281,-0.069],[2.509,0],[0,0],[5.781,12],[-2.75,7.73],[-4.95,4.051],[-2.63,2.12],[-1.01,1.029],[-0.29,0.381],[0.06,1.44],[0.179,1.27],[-1,0.109],[-0.39,-0.201],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.13,-0.131],[-0.069,-0.11],[0,-0.411],[0,0],[-0.029,-0.409],[-0.02,-0.199],[-0.02,-0.159],[-0.09,-0.29],[-0.05,-0.14],[-0.04,-0.091],[-0.071,-0.109],[-0.09,-0.111],[-0.031,-0.04],[-0.55,-0.549],[-0.139,-0.141],[-2.41,-1.94],[-4.18,-5.951],[-0.83,-2.4],[-0.071,-0.241],[-0.05,-0.21],[-0.17,-0.9]],"o":[[-0.06,0.38],[-0.059,0.28],[-0.021,0.09],[-1.061,4.23],[-1.861,2.55],[-0.611,0.49],[-0.971,0.68],[-0.641,0.359],[-0.66,0.321],[-0.84,0.34],[-0.759,0.241],[-0.28,0.07],[-2.45,0.531],[0,0],[-13.32,0],[-3.54,-7.34],[2.18,-6.091],[2.62,-2.139],[2.72,-2.19],[0.551,-0.56],[1.03,-1.369],[-0.121,-2.79],[-0.161,-1.17],[0.361,-0.06],[0,0],[0,0],[0,0],[0,0],[0,0],[0.15,0.091],[0.091,0.09],[0.221,0.33],[0,0],[0,0.38],[0.011,0.201],[0.02,0.15],[0.04,0.301],[0.049,0.149],[0.04,0.09],[0.05,0.119],[0.059,0.12],[0.021,0.04],[0.279,0.37],[0.12,0.13],[1.02,0.98],[5.571,4.48],[1.461,2.089],[0.08,0.23],[0.069,0.21],[0.26,0.88],[0.679,3.5]],"v":[[34.92,12.91],[34.7,14.051],[34.511,14.889],[34.441,15.18],[28.941,26.88],[22.441,33.721],[20.561,35.139],[17.561,37.04],[15.62,38.059],[13.62,38.96],[11.07,39.889],[8.78,40.529],[7.95,40.729],[0.481,41.529],[0.45,41.529],[-31.09,21.699],[-32.78,-1.6],[-21.46,-16.561],[-13.44,-22.771],[-8.11,-27.41],[-6.9,-28.79],[-5.809,-33.45],[-6.269,-39.26],[-4.65,-41.469],[-3.51,-41.26],[0.45,-39.191],[0.481,-39.18],[0.481,-39.171],[5,-36.611],[5.71,-36.21],[6.13,-35.889],[6.38,-35.59],[6.721,-34.469],[6.721,-33.43],[6.75,-32.251],[6.8,-31.65],[6.86,-31.191],[7.061,-30.299],[7.21,-29.861],[7.33,-29.579],[7.521,-29.23],[7.74,-28.889],[7.811,-28.771],[9,-27.421],[9.4,-27.01],[14.36,-22.751],[30.3,-8.51],[33.74,-1.73],[33.971,-1.029],[34.16,-0.4],[34.811,2.27]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.572549019608,0.478431402468,0.839215746113,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[37.53,43.53],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":18,"ty":3,"nm":"null-foot-leg-right ","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":216,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":225,"s":[-16]},{"t":234,"s":[0]}],"ix":10},"p":{"a":0,"k":[104.625,119.5,0],"ix":2,"l":2},"a":{"a":0,"k":[14.625,29.5,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":"foot-right ","parent":18,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":106,"s":[-16.958]},{"t":112,"s":[0]}],"ix":10},"p":{"a":0,"k":[12.563,62.71,0],"ix":2,"l":2},"a":{"a":0,"k":[3.168,10.693,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-4.229],[0,4.229],[5.063,0]],"o":[[0,4.229],[0,-4.229],[-5.064,0]],"v":[[-9.168,1.714],[9.168,1.714],[0,-5.943]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.572549019608,0.478431402468,0.839215746113,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[11.168,7.944],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":"leg-right ","parent":18,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[14.726,61.506,0],"ix":2,"l":2},"a":{"a":0,"k":[4.042,31.23,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-2.628,14.816],[2.628,14.816],[2.628,-14.816],[-2.628,-14.816]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.572549019608,0.478431402468,0.839215746113,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[4.042,16.23],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":21,"ty":3,"nm":"null-foot-leg-left","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":234,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":243,"s":[16]},{"t":252,"s":[0]}],"ix":10},"p":{"a":0,"k":[76.125,119.75,0],"ix":2,"l":2},"a":{"a":0,"k":[-13.706,29.818,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":22,"ty":4,"nm":"foot-left ","parent":21,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":115,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":122,"s":[14]},{"t":128,"s":[0]}],"ix":10},"p":{"a":0,"k":[-11.138,62.21,0],"ix":2,"l":2},"a":{"a":0,"k":[19.667,10.193,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-4.229],[0,4.229],[-5.064,0]],"o":[[0,4.229],[0,-4.229],[5.063,0]],"v":[[9.167,1.714],[-9.167,1.714],[0,-5.943]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.572549019608,0.478431402468,0.839215746113,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[11.167,7.944],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":"leg-left ","parent":21,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-13.8,46.604,0],"ix":2,"l":2},"a":{"a":0,"k":[4.041,16.327,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[2.627,14.913],[-2.627,14.913],[-2.627,-14.913],[2.627,-14.913]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.572549019608,0.478431402468,0.839215746113,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[4.041,16.327],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":24,"ty":4,"nm":"feather-right ","parent":11,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[2.257,-40.214,0],"ix":2,"l":2},"a":{"a":0,"k":[4.979,16.819,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.92,-1.305],[-0.994,2.399],[-2.288,2.372],[4.027,-4.656],[0.654,-1.84]],"o":[[0.396,-1.953],[2.156,-5.198],[-2.176,0.596],[-2.233,2.583],[1.92,1.304]],"v":[[-1.844,8.944],[0.169,2.347],[7.605,-8.944],[-3.521,-2.013],[-7.605,5.031]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.549019607843,0.901960844152,0.803921628466,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[9.604,10.944],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":25,"ty":4,"nm":"feather-left ","parent":11,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-2.563,-41.523,0],"ix":2,"l":2},"a":{"a":0,"k":[9.913,18.048,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.773,0.237],[1.134,3.207],[1.239,1.692],[-2.752,-3.696],[0.074,-1.377]],"o":[[-0.114,-2.056],[-1.171,-3.316],[1.418,0.498],[3.356,4.509],[-1.773,-0.236]],"v":[[0.059,7.463],[-1.565,-0.649],[-5.538,-8.173],[2.182,-2.518],[5.378,8.173]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.549019607843,0.901960844152,0.803921628466,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[7.538,10.173],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":26,"ty":4,"nm":"feather-center ","parent":11,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1.25,-36.84,0],"ix":2,"l":2},"a":{"a":0,"k":[4.789,33.995,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.865,0.364],[-0.623,5.891],[-1.236,4.546],[-0.391,-6.65],[0.616,-4.418]],"o":[[-0.218,-4.542],[0.653,-6.176],[1.225,4.483],[0.367,6.262],[-1.865,-0.364]],"v":[[-3.197,15.403],[-2.745,-0.342],[0.286,-16.495],[3.048,0.332],[2.4,16.495]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.294117647059,0.290196078431,0.560784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.549019607843,0.901960844152,0.803921628466,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[5.414,18.495],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":27,"ty":4,"nm":"floor ","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[89.799,153.743,0],"ix":2,"l":2},"a":{"a":0,"k":[48.548,6.287,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-3.334],[26.675,0],[0,3.334],[-26.674,0]],"o":[[0,3.334],[-26.674,0],[0,-3.334],[26.675,0]],"v":[[48.298,0],[0,6.037],[-48.298,0],[0,-6.037]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.921568687289,0.921568687289,0.921568687289,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[48.548,6.287],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}],"markers":[{"tm":75,"cm":"connecting-start","dr":0},{"tm":165,"cm":"connecting-end","dr":0},{"tm":255,"cm":"connected-start","dr":0},{"tm":290,"cm":"connected-end","dr":0},{"tm":297,"cm":"disconnection-start","dr":0},{"tm":341,"cm":"disconnection-end","dr":0},{"tm":378,"cm":"failed-start","dr":0},{"tm":433,"cm":"failed-end","dr":0}]} \ No newline at end of file diff --git a/asset_sources/other/playstore_icon/campfire/app_icon-playstore.png b/asset_sources/other/playstore_icon/campfire/app_icon-playstore.png new file mode 100644 index 0000000000000000000000000000000000000000..bea9f072eeca32ad13487c086c38b68d68fce027 GIT binary patch literal 53110 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 Date: Wed, 5 Jun 2024 11:35:51 -0600 Subject: [PATCH 096/100] FIX: missing campfire app name id --- scripts/build_app.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build_app.sh b/scripts/build_app.sh index 612c4097f..fc56a2bc1 100755 --- a/scripts/build_app.sh +++ b/scripts/build_app.sh @@ -5,7 +5,7 @@ set -e source ./env.sh APP_PLATFORMS=("android" "ios" "macos" "linux" "windows") -APP_NAMED_IDS=("stack_wallet" "stack_duo") +APP_NAMED_IDS=("stack_wallet" "stack_duo" "campfire") # Function to display usage. usage() { From b60ac530502759aa214b4b6a11c2291bbaaa4a8a Mon Sep 17 00:00:00 2001 From: julian Date: Wed, 5 Jun 2024 12:05:22 -0600 Subject: [PATCH 097/100] app config features --- lib/app_config.dart | 8 ++++++++ scripts/app_config/configure_campfire.sh | 2 ++ scripts/app_config/configure_stack_duo.sh | 6 ++++++ scripts/app_config/configure_stack_wallet.sh | 6 ++++++ 4 files changed, 22 insertions(+) diff --git a/lib/app_config.dart b/lib/app_config.dart index 4b7003177..a9ea781c7 100644 --- a/lib/app_config.dart +++ b/lib/app_config.dart @@ -3,6 +3,12 @@ import 'wallets/crypto_currency/intermediate/frost_currency.dart'; part 'app_config.g.dart'; +enum AppFeature { + themeSelection, + buy, + swap; +} + abstract class AppConfig { static const appName = _prefix + _separator + suffix; @@ -12,6 +18,8 @@ abstract class AppConfig { static String get appDefaultDataDirName => _appDataDirName; static String get commitHash => _commitHash; + static bool hasFeature(AppFeature feature) => _features.contains(feature); + static ({String light, String dark})? get appIconAsset => _appIconAsset; static List get coins => _supportedCoins; diff --git a/scripts/app_config/configure_campfire.sh b/scripts/app_config/configure_campfire.sh index 03135118e..6bdede958 100755 --- a/scripts/app_config/configure_campfire.sh +++ b/scripts/app_config/configure_campfire.sh @@ -46,6 +46,8 @@ const _suffix = ""; const _appDataDirName = "campfire"; const _commitHash = "$BUILT_COMMIT_HASH"; +const Set _features = {}; + const ({String light, String dark})? _appIconAsset = ( light: "assets/in_app_logo_icons/stack-duo-icon_light.svg", dark: "assets/in_app_logo_icons/stack-duo-icon_dark.svg", diff --git a/scripts/app_config/configure_stack_duo.sh b/scripts/app_config/configure_stack_duo.sh index 16b68a030..56c12bd37 100755 --- a/scripts/app_config/configure_stack_duo.sh +++ b/scripts/app_config/configure_stack_duo.sh @@ -40,6 +40,12 @@ const _suffix = "Duo"; const _appDataDirName = "stackduo"; const _commitHash = "$BUILT_COMMIT_HASH"; +const Set _features = { + AppFeature.themeSelection, + AppFeature.buy, + AppFeature.swap +}; + const ({String light, String dark})? _appIconAsset = ( light: "assets/in_app_logo_icons/stack-duo-icon_light.svg", dark: "assets/in_app_logo_icons/stack-duo-icon_dark.svg", diff --git a/scripts/app_config/configure_stack_wallet.sh b/scripts/app_config/configure_stack_wallet.sh index dc43feb4a..7c0dc2a67 100755 --- a/scripts/app_config/configure_stack_wallet.sh +++ b/scripts/app_config/configure_stack_wallet.sh @@ -40,6 +40,12 @@ const _suffix = "Wallet"; const _appDataDirName = "stackwallet"; const _commitHash = "$BUILT_COMMIT_HASH"; +const Set _features = { + AppFeature.themeSelection, + AppFeature.buy, + AppFeature.swap +}; + const ({String light, String dark})? _appIconAsset = null; final List _supportedCoins = List.unmodifiable([ From b6ff0920acc2974b617079ee56019417c0e3ba32 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 4 Jun 2024 15:07:31 -0600 Subject: [PATCH 098/100] clean up coin selection somewhat --- .../electrumx_interface.dart | 498 +++++++++--------- 1 file changed, 235 insertions(+), 263 deletions(-) diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart index eda467a6a..03eb150ac 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart @@ -4,6 +4,7 @@ import 'dart:typed_data'; import 'package:coinlib_flutter/coinlib_flutter.dart' as coinlib; import 'package:isar/isar.dart'; + import '../../../electrumx_rpc/cached_electrumx_client.dart'; import '../../../electrumx_rpc/client_manager.dart'; import '../../../electrumx_rpc/electrumx_client.dart'; @@ -64,7 +65,10 @@ mixin ElectrumXInterface } Future> - _helperRecipientsConvert(List addrs, List satValues) async { + _helperRecipientsConvert( + List addrs, + List satValues, + ) async { final List<({String address, Amount amount, bool isChange})> results = []; for (int i = 0; i < addrs.length; i++) { @@ -72,7 +76,7 @@ mixin ElectrumXInterface ( address: addrs[i], amount: Amount( - rawValue: BigInt.from(satValues[i]), + rawValue: satValues[i], fractionDigits: cryptoCurrency.fractionDigits, ), isChange: (await mainDB.isar.addresses @@ -105,44 +109,47 @@ mixin ElectrumXInterface // TODO: multiple recipients one day assert(txData.recipients!.length == 1); + if (coinControl && utxos == null) { + throw Exception("Coin control used where utxos is null!"); + } + final recipientAddress = txData.recipients!.first.address; - final satoshiAmountToSend = txData.amount!.raw.toInt(); + final satoshiAmountToSend = txData.amount!.raw; final int? satsPerVByte = txData.satsPerVByte; final selectedTxFeeRate = txData.feeRateAmount!; final List availableOutputs = utxos ?? await mainDB.getUTXOs(walletId).findAll(); final currentChainHeight = await chainHeight; - final List spendableOutputs = []; - int spendableSatoshiValue = 0; - // Build list of spendable outputs and totaling their satoshi amount - for (final utxo in availableOutputs) { - if (utxo.isBlocked == false && - utxo.isConfirmed(currentChainHeight, cryptoCurrency.minConfirms) && - utxo.used != true) { - spendableOutputs.add(utxo); - spendableSatoshiValue += utxo.value; - } + final spendableOutputs = availableOutputs + .where( + (e) => + !e.isBlocked && + (e.used != true) && + e.isConfirmed(currentChainHeight, cryptoCurrency.minConfirms), + ) + .toList(); + final spendableSatoshiValue = + spendableOutputs.fold(BigInt.zero, (p, e) => p + BigInt.from(e.value)); + + if (spendableSatoshiValue < satoshiAmountToSend) { + throw Exception("Insufficient balance"); + } else if (spendableSatoshiValue == satoshiAmountToSend && !isSendAll) { + throw Exception("Insufficient balance to pay transaction fee"); } if (coinControl) { if (spendableOutputs.length < availableOutputs.length) { throw ArgumentError("Attempted to use an unavailable utxo"); } - } - - // don't care about sorting if using all utxos - if (!coinControl) { + // don't care about sorting if using all utxos + } else { // sort spendable by age (oldest first) spendableOutputs.sort( (a, b) => (b.blockTime ?? currentChainHeight) .compareTo((a.blockTime ?? currentChainHeight)), ); - // Null check operator changed to null assignment in order to resolve a - // `Null check operator used on a null value` error. currentChainHeight - // used in order to sort these unconfirmed outputs as the youngest, but we - // could just as well use currentChainHeight + 1. } Logging.instance.log( @@ -161,26 +168,10 @@ mixin ElectrumXInterface ); Logging.instance .log("satoshiAmountToSend: $satoshiAmountToSend", level: LogLevel.Info); - // If the amount the user is trying to send is smaller than the amount that they have spendable, - // then return 1, which indicates that they have an insufficient balance. - if (spendableSatoshiValue < satoshiAmountToSend) { - // return 1; - throw Exception("Insufficient balance"); - // If the amount the user wants to send is exactly equal to the amount they can spend, then return - // 2, which indicates that they are not leaving enough over to pay the transaction fee - } else if (spendableSatoshiValue == satoshiAmountToSend && !isSendAll) { - throw Exception("Insufficient balance to pay transaction fee"); - // return 2; - } - // If neither of these statements pass, we assume that the user has a spendable balance greater - // than the amount they're attempting to send. Note that this value still does not account for - // the added transaction fee, which may require an extra input and will need to be checked for - // later on. - // Possible situation right here - int satoshisBeingUsed = 0; + BigInt satoshisBeingUsed = BigInt.zero; int inputsBeingConsumed = 0; - List utxoObjectsToUse = []; + final List utxoObjectsToUse = []; if (!coinControl) { for (var i = 0; @@ -188,7 +179,7 @@ mixin ElectrumXInterface i < spendableOutputs.length; i++) { utxoObjectsToUse.add(spendableOutputs[i]); - satoshisBeingUsed += spendableOutputs[i].value; + satoshisBeingUsed += BigInt.from(spendableOutputs[i].value); inputsBeingConsumed += 1; } for (int i = 0; @@ -196,12 +187,13 @@ mixin ElectrumXInterface inputsBeingConsumed < spendableOutputs.length; i++) { utxoObjectsToUse.add(spendableOutputs[inputsBeingConsumed]); - satoshisBeingUsed += spendableOutputs[inputsBeingConsumed].value; + satoshisBeingUsed += + BigInt.from(spendableOutputs[inputsBeingConsumed].value); inputsBeingConsumed += 1; } } else { satoshisBeingUsed = spendableSatoshiValue; - utxoObjectsToUse = spendableOutputs; + utxoObjectsToUse.addAll(spendableOutputs); inputsBeingConsumed = spendableOutputs.length; } @@ -214,72 +206,20 @@ mixin ElectrumXInterface // numberOfOutputs' length must always be equal to that of recipientsArray and recipientsAmtArray final List recipientsArray = [recipientAddress]; - final List recipientsAmtArray = [satoshiAmountToSend]; + final List recipientsAmtArray = [satoshiAmountToSend]; // gather required signing data final utxoSigningData = await fetchBuildTxData(utxoObjectsToUse); if (isSendAll) { - Logging.instance - .log("Attempting to send all $cryptoCurrency", level: LogLevel.Info); - if (txData.recipients!.length != 1) { - throw Exception( - "Send all to more than one recipient not yet supported", - ); - } - - final int vSizeForOneOutput = (await buildTransaction( + return await _sendAllBuilder( + txData: txData, + recipientAddress: recipientAddress, + satoshiAmountToSend: satoshiAmountToSend, + satoshisBeingUsed: satoshisBeingUsed, utxoSigningData: utxoSigningData, - txData: txData.copyWith( - recipients: await _helperRecipientsConvert( - [recipientAddress], - [satoshisBeingUsed - 1], - ), - ), - )) - .vSize!; - int feeForOneOutput = satsPerVByte != null - ? (satsPerVByte * vSizeForOneOutput) - : estimateTxFee( - vSize: vSizeForOneOutput, - feeRatePerKB: selectedTxFeeRate, - ); - - if (satsPerVByte == null) { - final int roughEstimate = roughFeeEstimate( - spendableOutputs.length, - 1, - selectedTxFeeRate, - ).raw.toInt(); - if (feeForOneOutput < roughEstimate) { - feeForOneOutput = roughEstimate; - } - } - - final int amount = satoshiAmountToSend - feeForOneOutput; - - if (amount < 0) { - throw Exception( - "Estimated fee ($feeForOneOutput sats) is greater than balance!", - ); - } - - final data = await buildTransaction( - txData: txData.copyWith( - recipients: await _helperRecipientsConvert( - [recipientAddress], - [amount], - ), - ), - utxoSigningData: utxoSigningData, - ); - - return data.copyWith( - fee: Amount( - rawValue: BigInt.from(feeForOneOutput), - fractionDigits: cryptoCurrency.fractionDigits, - ), - usedUTXOs: utxoSigningData.map((e) => e.utxo).toList(), + satsPerVByte: satsPerVByte, + feeRatePerKB: selectedTxFeeRate, ); } @@ -290,7 +230,7 @@ mixin ElectrumXInterface txData: txData.copyWith( recipients: await _helperRecipientsConvert( [recipientAddress], - [satoshisBeingUsed - 1], + [satoshisBeingUsed - BigInt.one], ), ), )) @@ -301,6 +241,9 @@ mixin ElectrumXInterface } final int vSizeForTwoOutPuts; + + BigInt maxBI(BigInt a, BigInt b) => a > b ? a : b; + try { vSizeForTwoOutPuts = (await buildTransaction( utxoSigningData: utxoSigningData, @@ -309,7 +252,10 @@ mixin ElectrumXInterface [recipientAddress, (await getCurrentChangeAddress())!.value], [ satoshiAmountToSend, - max(0, satoshisBeingUsed - satoshiAmountToSend - 1), + maxBI( + BigInt.zero, + satoshisBeingUsed - (satoshiAmountToSend + BigInt.one), + ), ], ), ), @@ -321,53 +267,112 @@ mixin ElectrumXInterface } // Assume 1 output, only for recipient and no change - final feeForOneOutput = satsPerVByte != null - ? (satsPerVByte * vSizeForOneOutput) - : estimateTxFee( - vSize: vSizeForOneOutput, - feeRatePerKB: selectedTxFeeRate, - ); + final feeForOneOutput = BigInt.from( + satsPerVByte != null + ? (satsPerVByte * vSizeForOneOutput) + : estimateTxFee( + vSize: vSizeForOneOutput, + feeRatePerKB: selectedTxFeeRate, + ), + ); // Assume 2 outputs, one for recipient and one for change - final feeForTwoOutputs = satsPerVByte != null - ? (satsPerVByte * vSizeForTwoOutPuts) - : estimateTxFee( - vSize: vSizeForTwoOutPuts, - feeRatePerKB: selectedTxFeeRate, - ); + final feeForTwoOutputs = BigInt.from( + satsPerVByte != null + ? (satsPerVByte * vSizeForTwoOutPuts) + : estimateTxFee( + vSize: vSizeForTwoOutPuts, + feeRatePerKB: selectedTxFeeRate, + ), + ); - Logging.instance - .log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info); - Logging.instance - .log("feeForOneOutput: $feeForOneOutput", level: LogLevel.Info); + Logging.instance.log( + "feeForTwoOutputs: $feeForTwoOutputs", + level: LogLevel.Info, + ); + Logging.instance.log( + "feeForOneOutput: $feeForOneOutput", + level: LogLevel.Info, + ); - if (satoshisBeingUsed - satoshiAmountToSend > feeForOneOutput) { - if (satoshisBeingUsed - satoshiAmountToSend > - feeForOneOutput + cryptoCurrency.dustLimit.raw.toInt()) { - // Here, we know that theoretically, we may be able to include another output(change) but we first need to - // factor in the value of this output in satoshis. - final int changeOutputSize = - satoshisBeingUsed - satoshiAmountToSend - feeForTwoOutputs; - // We check to see if the user can pay for the new transaction with 2 outputs instead of one. If they can and - // the second output's size > cryptoCurrency.dustLimit satoshis, we perform the mechanics required to properly generate and use a new - // change address. - if (changeOutputSize > cryptoCurrency.dustLimit.raw.toInt() && - satoshisBeingUsed - satoshiAmountToSend - changeOutputSize == - feeForTwoOutputs) { + final difference = satoshisBeingUsed - satoshiAmountToSend; + + Future singleOutputTxn() async { + Logging.instance.log( + 'Input size: $satoshisBeingUsed', + level: LogLevel.Info, + ); + Logging.instance.log( + 'Recipient output size: $satoshiAmountToSend', + level: LogLevel.Info, + ); + Logging.instance.log( + 'Fee being paid: $difference sats', + level: LogLevel.Info, + ); + Logging.instance.log( + 'Estimated fee: $feeForOneOutput', + level: LogLevel.Info, + ); + final txnData = await buildTransaction( + utxoSigningData: utxoSigningData, + txData: txData.copyWith( + recipients: await _helperRecipientsConvert( + recipientsArray, + recipientsAmtArray, + ), + ), + ); + return txnData.copyWith( + fee: Amount( + rawValue: feeForOneOutput, + fractionDigits: cryptoCurrency.fractionDigits, + ), + usedUTXOs: utxoSigningData.map((e) => e.utxo).toList(), + ); + } + + // no change output required + if (difference == feeForOneOutput) { + Logging.instance.log('1 output in tx', level: LogLevel.Info); + return await singleOutputTxn(); + } else if (difference < feeForOneOutput) { + Logging.instance.log( + 'Cannot pay tx fee - checking for more outputs and trying again', + level: LogLevel.Warning, + ); + // try adding more outputs + if (spendableOutputs.length > inputsBeingConsumed) { + return coinSelection( + txData: txData, + isSendAll: isSendAll, + additionalOutputs: additionalOutputs + 1, + utxos: utxos, + coinControl: coinControl, + ); + } + throw Exception("Insufficient balance to pay transaction fee"); + } else { + if (difference > (feeForOneOutput + cryptoCurrency.dustLimit.raw)) { + final changeOutputSize = difference - feeForTwoOutputs; + // check if possible to add the change output + if (changeOutputSize > cryptoCurrency.dustLimit.raw && + difference - changeOutputSize == feeForTwoOutputs) { // generate new change address if current change address has been used await checkChangeAddressForTransactions(); final String newChangeAddress = (await getCurrentChangeAddress())!.value; - int feeBeingPaid = - satoshisBeingUsed - satoshiAmountToSend - changeOutputSize; + BigInt feeBeingPaid = difference - changeOutputSize; + // add change output recipientsArray.add(newChangeAddress); recipientsAmtArray.add(changeOutputSize); - // At this point, we have the outputs we're going to use, the amounts to send along with which addresses - // we intend to send these amounts to. We have enough to send instructions to build the transaction. + Logging.instance.log('2 outputs in tx', level: LogLevel.Info); - Logging.instance - .log('Input size: $satoshisBeingUsed', level: LogLevel.Info); + Logging.instance.log( + 'Input size: $satoshisBeingUsed', + level: LogLevel.Info, + ); Logging.instance.log( 'Recipient output size: $satoshiAmountToSend', level: LogLevel.Info, @@ -380,10 +385,12 @@ mixin ElectrumXInterface 'Difference (fee being paid): $feeBeingPaid sats', level: LogLevel.Info, ); - Logging.instance - .log('Estimated fee: $feeForTwoOutputs', level: LogLevel.Info); + Logging.instance.log( + 'Estimated fee: $feeForTwoOutputs', + level: LogLevel.Info, + ); - var txn = await buildTransaction( + TxData txnData = await buildTransaction( utxoSigningData: utxoSigningData, txData: txData.copyWith( recipients: await _helperRecipientsConvert( @@ -394,13 +401,12 @@ mixin ElectrumXInterface ); // make sure minimum fee is accurate if that is being used - if (txn.vSize! - feeBeingPaid == 1) { - final int changeOutputSize = - satoshisBeingUsed - satoshiAmountToSend - txn.vSize!; - feeBeingPaid = - satoshisBeingUsed - satoshiAmountToSend - changeOutputSize; + if (BigInt.from(txnData.vSize!) - feeBeingPaid == BigInt.one) { + final changeOutputSize = difference - BigInt.from(txnData.vSize!); + feeBeingPaid = difference - changeOutputSize; recipientsAmtArray.removeLast(); recipientsAmtArray.add(changeOutputSize); + Logging.instance.log( 'Adjusted Input size: $satoshisBeingUsed', level: LogLevel.Info, @@ -421,7 +427,8 @@ mixin ElectrumXInterface 'Adjusted Estimated fee: $feeForTwoOutputs', level: LogLevel.Info, ); - txn = await buildTransaction( + + txnData = await buildTransaction( utxoSigningData: utxoSigningData, txData: txData.copyWith( recipients: await _helperRecipientsConvert( @@ -432,9 +439,9 @@ mixin ElectrumXInterface ); } - return txn.copyWith( + return txnData.copyWith( fee: Amount( - rawValue: BigInt.from(feeBeingPaid), + rawValue: feeBeingPaid, fractionDigits: cryptoCurrency.fractionDigits, ), usedUTXOs: utxoSigningData.map((e) => e.utxo).toList(), @@ -442,126 +449,91 @@ mixin ElectrumXInterface } else { // Something went wrong here. It either overshot or undershot the estimated fee amount or the changeOutputSize // is smaller than or equal to cryptoCurrency.dustLimit. Revert to single output transaction. - Logging.instance.log('1 output in tx', level: LogLevel.Info); - Logging.instance - .log('Input size: $satoshisBeingUsed', level: LogLevel.Info); Logging.instance.log( - 'Recipient output size: $satoshiAmountToSend', + 'Reverting to 1 output in tx', level: LogLevel.Info, ); - Logging.instance.log( - 'Difference (fee being paid): ${satoshisBeingUsed - satoshiAmountToSend} sats', - level: LogLevel.Info, - ); - Logging.instance - .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); - final txn = await buildTransaction( - utxoSigningData: utxoSigningData, - txData: txData.copyWith( - recipients: await _helperRecipientsConvert( - recipientsArray, - recipientsAmtArray, - ), - ), - ); - return txn.copyWith( - fee: Amount( - rawValue: BigInt.from(satoshisBeingUsed - satoshiAmountToSend), - fractionDigits: cryptoCurrency.fractionDigits, - ), - usedUTXOs: utxoSigningData.map((e) => e.utxo).toList(), - ); + return await singleOutputTxn(); } - } else { - // No additional outputs needed since adding one would mean that it'd be smaller than cryptoCurrency.dustLimit sats - // which makes it uneconomical to add to the transaction. Here, we pass data directly to instruct - // the wallet to begin crafting the transaction that the user requested. - Logging.instance.log('1 output in tx', level: LogLevel.Info); - Logging.instance - .log('Input size: $satoshisBeingUsed', level: LogLevel.Info); - Logging.instance.log( - 'Recipient output size: $satoshiAmountToSend', - level: LogLevel.Info, - ); - Logging.instance.log( - 'Difference (fee being paid): ${satoshisBeingUsed - satoshiAmountToSend} sats', - level: LogLevel.Info, - ); - Logging.instance - .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); - final txn = await buildTransaction( - utxoSigningData: utxoSigningData, - txData: txData.copyWith( - recipients: await _helperRecipientsConvert( - recipientsArray, - recipientsAmtArray, - ), - ), - ); - - return txn.copyWith( - fee: Amount( - rawValue: BigInt.from(satoshisBeingUsed - satoshiAmountToSend), - fractionDigits: cryptoCurrency.fractionDigits, - ), - usedUTXOs: utxoSigningData.map((e) => e.utxo).toList(), - ); } - } else if (satoshisBeingUsed - satoshiAmountToSend == feeForOneOutput) { - // In this scenario, no additional change output is needed since inputs - outputs equal exactly - // what we need to pay for fees. Here, we pass data directly to instruct the wallet to begin - // crafting the transaction that the user requested. - Logging.instance.log('1 output in tx', level: LogLevel.Info); - Logging.instance - .log('Input size: $satoshisBeingUsed', level: LogLevel.Info); - Logging.instance.log( - 'Recipient output size: $satoshiAmountToSend', - level: LogLevel.Info, - ); - Logging.instance.log( - 'Fee being paid: ${satoshisBeingUsed - satoshiAmountToSend} sats', - level: LogLevel.Info, - ); - Logging.instance - .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); - final txn = await buildTransaction( - utxoSigningData: utxoSigningData, - txData: txData.copyWith( - recipients: await _helperRecipientsConvert( - recipientsArray, - recipientsAmtArray, - ), - ), - ); - return txn.copyWith( - fee: Amount( - rawValue: BigInt.from(feeForOneOutput), - fractionDigits: cryptoCurrency.fractionDigits, - ), - usedUTXOs: utxoSigningData.map((e) => e.utxo).toList(), - ); - } else { - // Remember that returning 2 indicates that the user does not have a sufficient balance to - // pay for the transaction fee. Ideally, at this stage, we should check if the user has any - // additional outputs they're able to spend and then recalculate fees. - Logging.instance.log( - 'Cannot pay tx fee - checking for more outputs and trying again', - level: LogLevel.Warning, - ); - // try adding more outputs - if (spendableOutputs.length > inputsBeingConsumed) { - return coinSelection( - txData: txData, - isSendAll: isSendAll, - additionalOutputs: additionalOutputs + 1, - utxos: utxos, - coinControl: coinControl, - ); - } - throw Exception("Insufficient balance to pay transaction fee"); - // return 2; } + + return txData; + } + + Future _sendAllBuilder({ + required TxData txData, + required String recipientAddress, + required BigInt satoshiAmountToSend, + required BigInt satoshisBeingUsed, + required List utxoSigningData, + required int? satsPerVByte, + required int feeRatePerKB, + }) async { + Logging.instance + .log("Attempting to send all $cryptoCurrency", level: LogLevel.Info); + if (txData.recipients!.length != 1) { + throw Exception( + "Send all to more than one recipient not yet supported", + ); + } + + final int vSizeForOneOutput = (await buildTransaction( + utxoSigningData: utxoSigningData, + txData: txData.copyWith( + recipients: await _helperRecipientsConvert( + [recipientAddress], + [satoshisBeingUsed - BigInt.one], + ), + ), + )) + .vSize!; + BigInt feeForOneOutput = BigInt.from( + satsPerVByte != null + ? (satsPerVByte * vSizeForOneOutput) + : estimateTxFee( + vSize: vSizeForOneOutput, + feeRatePerKB: feeRatePerKB, + ), + ); + + if (satsPerVByte == null) { + final roughEstimate = roughFeeEstimate( + utxoSigningData.length, + 1, + feeRatePerKB, + ).raw; + if (feeForOneOutput < roughEstimate) { + feeForOneOutput = roughEstimate; + } + } + + final amount = satoshiAmountToSend - feeForOneOutput; + + if (amount.isNegative) { + throw Exception( + "Estimated fee ($feeForOneOutput sats) is greater than balance!", + ); + } + + final data = await buildTransaction( + txData: txData.copyWith( + recipients: await _helperRecipientsConvert( + [recipientAddress], + [amount], + ), + ), + utxoSigningData: utxoSigningData, + ); + + return data.copyWith( + fee: Amount( + rawValue: feeForOneOutput, + fractionDigits: cryptoCurrency.fractionDigits, + ), + usedUTXOs: utxoSigningData.map((e) => e.utxo).toList(), + ); } Future> fetchBuildTxData( From 23db925e86e4c4346a7d72217e56257b287db612 Mon Sep 17 00:00:00 2001 From: julian Date: Wed, 5 Jun 2024 13:37:16 -0600 Subject: [PATCH 099/100] script fix --- scripts/app_config/configure_campfire.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/app_config/configure_campfire.sh b/scripts/app_config/configure_campfire.sh index 6bdede958..6298443db 100755 --- a/scripts/app_config/configure_campfire.sh +++ b/scripts/app_config/configure_campfire.sh @@ -49,8 +49,8 @@ const _commitHash = "$BUILT_COMMIT_HASH"; const Set _features = {}; const ({String light, String dark})? _appIconAsset = ( - light: "assets/in_app_logo_icons/stack-duo-icon_light.svg", - dark: "assets/in_app_logo_icons/stack-duo-icon_dark.svg", + light: "assets/in_app_logo_icons/campfire-icon_light.svg", + dark: "assets/in_app_logo_icons/campfire-icon_dark.svg", ); final List _supportedCoins = List.unmodifiable([ From 2c07f2c13bdc3461d1b37b450bca497b19ae3c9a Mon Sep 17 00:00:00 2001 From: julian Date: Wed, 5 Jun 2024 13:38:20 -0600 Subject: [PATCH 100/100] separate firo caches and add versioning --- lib/db/sqlite/firo_cache.dart | 74 +++++++++++++++---- lib/db/sqlite/firo_cache_coordinator.dart | 32 ++++---- lib/db/sqlite/firo_cache_worker.dart | 38 +++++++--- .../wallet_view/desktop_wallet_view.dart | 19 +++++ lib/utilities/stack_file_system.dart | 22 +++++- 5 files changed, 142 insertions(+), 43 deletions(-) diff --git a/lib/db/sqlite/firo_cache.dart b/lib/db/sqlite/firo_cache.dart index 7197690a8..543b63557 100644 --- a/lib/db/sqlite/firo_cache.dart +++ b/lib/db/sqlite/firo_cache.dart @@ -15,8 +15,8 @@ import '../../utilities/stack_file_system.dart'; part 'firo_cache_coordinator.dart'; part 'firo_cache_reader.dart'; -part 'firo_cache_writer.dart'; part 'firo_cache_worker.dart'; +part 'firo_cache_writer.dart'; /// Temporary debugging log function for this file void _debugLog(Object? object) { @@ -29,44 +29,73 @@ void _debugLog(Object? object) { } abstract class _FiroCache { - static const String sqliteDbFileName = "firo_ex_cache.sqlite3"; + static const int _setCacheVersion = 1; + static const int _tagsCacheVersion = 1; + static const String sparkSetCacheFileName = + "spark_set_v$_setCacheVersion.sqlite3"; + static const String sparkUsedTagsCacheFileName = + "spark_tags_v$_tagsCacheVersion.sqlite3"; - static Database? _db; - static Database get db { - if (_db == null) { + static Database? _setCacheDB; + static Database? _usedTagsCacheDB; + static Database get setCacheDB { + if (_setCacheDB == null) { throw Exception( "FiroCache.init() must be called before accessing FiroCache.db!", ); } - return _db!; + return _setCacheDB!; + } + + static Database get usedTagsCacheDB { + if (_usedTagsCacheDB == null) { + throw Exception( + "FiroCache.init() must be called before accessing FiroCache.db!", + ); + } + return _usedTagsCacheDB!; } static Future? _initFuture; static Future init() => _initFuture ??= _init(); static Future _init() async { - final sqliteDir = await StackFileSystem.applicationSQLiteDirectory(); + final sqliteDir = + await StackFileSystem.applicationFiroCacheSQLiteDirectory(); - final file = File("${sqliteDir.path}/$sqliteDbFileName"); + final sparkSetCacheFile = File("${sqliteDir.path}/$sparkSetCacheFileName"); + final sparkUsedTagsCacheFile = + File("${sqliteDir.path}/$sparkUsedTagsCacheFileName"); - final exists = await file.exists(); - if (!exists) { - await _createDb(file.path); + if (!(await sparkSetCacheFile.exists())) { + await _createSparkSetCacheDb(sparkSetCacheFile.path); + } + if (!(await sparkUsedTagsCacheFile.exists())) { + await _createSparkUsedTagsCacheDb(sparkUsedTagsCacheFile.path); } - _db = sqlite3.open( - file.path, + _setCacheDB = sqlite3.open( + sparkSetCacheFile.path, + mode: OpenMode.readWrite, + ); + _usedTagsCacheDB = sqlite3.open( + sparkUsedTagsCacheFile.path, mode: OpenMode.readWrite, ); } static Future _deleteAllCache() async { final start = DateTime.now(); - db.execute( + setCacheDB.execute( """ DELETE FROM SparkSet; DELETE FROM SparkCoin; DELETE FROM SparkSetCoins; + VACUUM; + """, + ); + usedTagsCacheDB.execute( + """ DELETE FROM SparkUsedCoinTags; VACUUM; """, @@ -77,7 +106,7 @@ abstract class _FiroCache { ); } - static Future _createDb(String file) async { + static Future _createSparkSetCacheDb(String file) async { final db = sqlite3.open( file, mode: OpenMode.readWriteCreate, @@ -109,7 +138,20 @@ abstract class _FiroCache { FOREIGN KEY (setId) REFERENCES SparkSet(id), FOREIGN KEY (coinId) REFERENCES SparkCoin(id) ); - + """, + ); + + db.dispose(); + } + + static Future _createSparkUsedTagsCacheDb(String file) async { + final db = sqlite3.open( + file, + mode: OpenMode.readWriteCreate, + ); + + db.execute( + """ CREATE TABLE SparkUsedCoinTags ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, tag TEXT NOT NULL UNIQUE diff --git a/lib/db/sqlite/firo_cache_coordinator.dart b/lib/db/sqlite/firo_cache_coordinator.dart index d2e4796f6..610504d5f 100644 --- a/lib/db/sqlite/firo_cache_coordinator.dart +++ b/lib/db/sqlite/firo_cache_coordinator.dart @@ -20,14 +20,18 @@ abstract class FiroCacheCoordinator { } static Future getSparkCacheSize() async { - final dir = await StackFileSystem.applicationSQLiteDirectory(); - final cacheFile = File("${dir.path}/${_FiroCache.sqliteDbFileName}"); - final int bytes; - if (await cacheFile.exists()) { - bytes = await cacheFile.length(); - } else { - bytes = 0; - } + final dir = await StackFileSystem.applicationFiroCacheSQLiteDirectory(); + final setCacheFile = File( + "${dir.path}/${_FiroCache.sparkSetCacheFileName}", + ); + final usedTagsCacheFile = File( + "${dir.path}/${_FiroCache.sparkUsedTagsCacheFileName}", + ); + final int bytes = + ((await setCacheFile.exists()) ? await setCacheFile.length() : 0) + + ((await usedTagsCacheFile.exists()) + ? await usedTagsCacheFile.length() + : 0); if (bytes < 1024) { return '$bytes B'; @@ -88,7 +92,7 @@ abstract class FiroCacheCoordinator { static Future> getUsedCoinTags(int startNumber) async { final result = await _Reader._getSparkUsedCoinTags( startNumber, - db: _FiroCache.db, + db: _FiroCache.usedTagsCacheDB, ); return result.map((e) => e["tag"] as String).toSet(); } @@ -99,7 +103,7 @@ abstract class FiroCacheCoordinator { /// this table in practice. static Future getUsedCoinTagsLastAddedRowId() async { final result = await _Reader._getUsedCoinTagsLastAddedRowId( - db: _FiroCache.db, + db: _FiroCache.usedTagsCacheDB, ); if (result.isEmpty) { return 0; @@ -112,7 +116,7 @@ abstract class FiroCacheCoordinator { ) async { return await _Reader._checkTagIsUsed( tag, - db: _FiroCache.db, + db: _FiroCache.usedTagsCacheDB, ); } @@ -122,7 +126,7 @@ abstract class FiroCacheCoordinator { }) async { return await _Reader._getSetCoinsForGroupId( groupId, - db: _FiroCache.db, + db: _FiroCache.setCacheDB, newerThanTimeStamp: newerThanTimeStamp, ); } @@ -137,7 +141,7 @@ abstract class FiroCacheCoordinator { ) async { final result = await _Reader._getLatestSetInfoForGroupId( groupId, - db: _FiroCache.db, + db: _FiroCache.setCacheDB, ); if (result.isEmpty) { @@ -156,7 +160,7 @@ abstract class FiroCacheCoordinator { ) async { return await _Reader._checkSetInfoForGroupIdExists( groupId, - db: _FiroCache.db, + db: _FiroCache.setCacheDB, ); } } diff --git a/lib/db/sqlite/firo_cache_worker.dart b/lib/db/sqlite/firo_cache_worker.dart index d29018b8d..a611c54b9 100644 --- a/lib/db/sqlite/firo_cache_worker.dart +++ b/lib/db/sqlite/firo_cache_worker.dart @@ -26,8 +26,10 @@ class _FiroCacheWorker { } static Future<_FiroCacheWorker> spawn() async { - final sqliteDir = await StackFileSystem.applicationSQLiteDirectory(); - final dbFilePath = "${sqliteDir.path}/${_FiroCache.sqliteDbFileName}"; + final dir = await StackFileSystem.applicationFiroCacheSQLiteDirectory(); + final setCacheFilePath = "${dir.path}/${_FiroCache.sparkSetCacheFileName}"; + final usedTagsCacheFilePath = + "${dir.path}/${_FiroCache.sparkUsedTagsCacheFileName}"; final initPort = RawReceivePort(); final connection = Completer<(ReceivePort, SendPort)>.sync(); @@ -45,7 +47,7 @@ class _FiroCacheWorker { try { await Isolate.spawn( _startWorkerIsolate, - (initPort.sendPort, dbFilePath), + (initPort.sendPort, setCacheFilePath, usedTagsCacheFilePath), ); } catch (_) { initPort.close(); @@ -75,7 +77,8 @@ class _FiroCacheWorker { static void _handleCommandsToIsolate( ReceivePort receivePort, SendPort sendPort, - Database db, + Database setCacheDb, + Database usedTagsCacheDb, Mutex mutex, ) { receivePort.listen((message) { @@ -87,11 +90,18 @@ class _FiroCacheWorker { switch (task.func) { case FCFuncName._updateSparkAnonSetCoinsWith: final data = task.data as (int, Map); - result = _updateSparkAnonSetCoinsWith(db, data.$2, data.$1); + result = _updateSparkAnonSetCoinsWith( + setCacheDb, + data.$2, + data.$1, + ); break; case FCFuncName._updateSparkUsedTagsWith: - result = _updateSparkUsedTagsWith(db, task.data as List); + result = _updateSparkUsedTagsWith( + usedTagsCacheDb, + task.data as List, + ); break; } @@ -107,14 +117,24 @@ class _FiroCacheWorker { }); } - static void _startWorkerIsolate((SendPort, String) args) { + static void _startWorkerIsolate((SendPort, String, String) args) { final receivePort = ReceivePort(); args.$1.send(receivePort.sendPort); final mutex = Mutex(); - final db = sqlite3.open( + final setCacheDb = sqlite3.open( args.$2, mode: OpenMode.readWrite, ); - _handleCommandsToIsolate(receivePort, args.$1, db, mutex); + final usedTagsCacheDb = sqlite3.open( + args.$3, + mode: OpenMode.readWrite, + ); + _handleCommandsToIsolate( + receivePort, + args.$1, + setCacheDb, + usedTagsCacheDb, + mutex, + ); } } diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart index 022c0640a..cf47215d1 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart @@ -18,6 +18,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:isar/isar.dart'; +import '../../../db/sqlite/firo_cache.dart'; import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; import '../../../models/isar/models/isar_models.dart'; import '../../../pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart'; @@ -282,6 +283,24 @@ class _DesktopWalletViewState extends ConsumerState { ), ], ), + if (wallet.isarTransactionVersion == 2 && + wallet is FiroWallet) + Row( + children: [ + const Text( + "sparkCache: ", + ), + const SizedBox( + width: 2, + ), + FutureBuilder( + future: FiroCacheCoordinator.getSparkCacheSize(), + builder: (_, snapshot) => Text( + snapshot.data ?? "", + ), + ), + ], + ), ], ), const Spacer(), diff --git a/lib/utilities/stack_file_system.dart b/lib/utilities/stack_file_system.dart index 281bd2a8f..da3d33cfe 100644 --- a/lib/utilities/stack_file_system.dart +++ b/lib/utilities/stack_file_system.dart @@ -91,10 +91,24 @@ abstract class StackFileSystem { } } - static Future applicationSQLiteDirectory() async { + // Not used in general now. See applicationFiroCacheSQLiteDirectory() + // static Future applicationSQLiteDirectory() async { + // final root = await applicationRootDirectory(); + // if (Util.isDesktop) { + // final dir = Directory("${root.path}/sqlite"); + // if (!dir.existsSync()) { + // await dir.create(); + // } + // return dir; + // } else { + // return root; + // } + // } + + static Future applicationTorDirectory() async { final root = await applicationRootDirectory(); if (Util.isDesktop) { - final dir = Directory("${root.path}/sqlite"); + final dir = Directory("${root.path}/tor"); if (!dir.existsSync()) { await dir.create(); } @@ -104,10 +118,10 @@ abstract class StackFileSystem { } } - static Future applicationTorDirectory() async { + static Future applicationFiroCacheSQLiteDirectory() async { final root = await applicationRootDirectory(); if (Util.isDesktop) { - final dir = Directory("${root.path}/tor"); + final dir = Directory("${root.path}/sqlite/firo_cache"); if (!dir.existsSync()) { await dir.create(); }

Fgh-b@56ni>4S^Kx5;gST2SO1q4@P)4aG<}Q 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%0A&j#((fHt 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�-xM_79n+o^ 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®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 {aVlDke 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