Merge branch 'widget-tests' of github.com:cypherstack/stack_wallet into widget-tests

This commit is contained in:
Likho 2022-10-19 12:09:57 +02:00
commit 5d7e22caaf
45 changed files with 1280 additions and 750 deletions

View file

@ -8,16 +8,39 @@ jobs:
- name: Prepare repository - name: Prepare repository
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
flutter-version: '3.0.5' flutter-version: '3.3.4'
channel: 'stable' channel: 'stable'
- name: Install Flutter - name: Install Flutter
uses: subosito/flutter-action@v2 uses: subosito/flutter-action@v2
- name: Setup | Rust
uses: ATiltedTree/setup-rust@v1
with:
rust-version: stable
components: clippy
- name: Checkout submodules - name: Checkout submodules
run: git submodule update --init --recursive run: git submodule update --init --recursive
- name: install dependencies
run: |
cargo install cargo-ndk
rustup target add x86_64-unknown-linux-gnu
sudo apt install -y unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake openjdk-8-jre-headless libgit2-dev clang libncurses5-dev libncursesw5-dev zlib1g-dev llvm
sudo apt install -y debhelper libclang-dev cargo rustc opencl-headers libssl-dev ocl-icd-opencl-dev
sudo apt install -y libc6-dev-i386
sudo apt install -y build-essential cmake git libgit2-dev clang libncurses5-dev libncursesw5-dev zlib1g-dev pkg-config llvm
sudo apt install -y build-essential debhelper cmake libclang-dev libncurses5-dev clang libncursesw5-dev cargo rustc opencl-headers libssl-dev pkg-config ocl-icd-opencl-dev
sudo apt install -y unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake openjdk-8-jre-headless
- name: Build Lelantus - name: Build Lelantus
run: | run: |
cd crypto_plugins/flutter_liblelantus/scripts/linux/ cd crypto_plugins/flutter_liblelantus/scripts/linux/
./build_all.sh ./build_all.sh
- name: Build Monero
run: |
cd crypto_plugins/flutter_libmonero/scripts/linux/
./build_monero_all.sh
- name: Build Epic Cash
run: |
cd crypto_plugins/flutter_libepiccash/scripts/linux/
./build_all.sh
- name: Get dependencies - name: Get dependencies
run: flutter pub get run: flutter pub get
- name: Create temp files - name: Create temp files

View file

@ -17,15 +17,22 @@ Highlights include:
- Custom Nodes. - Custom Nodes.
- Open source software. - Open source software.
## Build and run ## Building
### Prerequisites ### Prerequisites
- The only OS supported for building is Ubuntu 20.04 - The only OS supported for building is Ubuntu 20.04
- A machine with at least 100 GB of Storage - A machine with at least 100 GB of Storage
The following prerequisities can be installed with the setup script `scripts/setup.sh` or manually as described below:
- Flutter 3.0.5 [(install manually or with git, do not install with snap)](https://docs.flutter.dev/get-started/install) - Flutter 3.0.5 [(install manually or with git, do not install with snap)](https://docs.flutter.dev/get-started/install)
- Dart SDK Requirement (>=2.17.0, up until <3.0.0) - Dart SDK Requirement (>=2.17.0, up until <3.0.0)
- Android setup ([Android Studio](https://developer.android.com/studio) and subsequent dependencies) - Android setup ([Android Studio](https://developer.android.com/studio) and subsequent dependencies)
After that download the project and init the submodules ### Scripted setup
[`scripts/setup.sh`](https://github.com/cypherstack/stack_wallet/blob/main/scripts/setup.sh) is provided as a tool to set up a stock Ubuntu 20.04 installation for building: download the script and run it anywhere. This script should skip the entire [Manual setup](#manual-setup) section below and prepare you for [running](#running). It will set up the stack_wallet repository in `~/projects/stack_wallet` and build it there.
### Manual setup
After installing the prerequisites listed above, download the code and init the submodules
``` ```
git clone https://github.com/cypherstack/stack_wallet.git git clone https://github.com/cypherstack/stack_wallet.git
cd stack_wallet cd stack_wallet
@ -57,7 +64,6 @@ cd scripts
cd .. cd ..
``` ```
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 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
``` ```
sudo apt list --installed | grep boost sudo apt list --installed | grep boost
@ -76,7 +82,7 @@ cd scripts/android/
cd ../.. cd ../..
``` ```
Building plugins for testing on Linux Building plugins for Linux
``` ```
cd scripts/linux/ cd scripts/linux/
@ -85,10 +91,33 @@ cd scripts/linux/
cd ../.. cd ../..
``` ```
Finally, plug in your android device or use the emulator available via Android Studio and then run the following commands: ## Running
### Android
Plug in your android device or use the emulator available via Android Studio and then run the following commands:
``` ```
flutter pub get flutter pub get
flutter run flutter run android
``` ```
Note on Emulators: Only x86_64 emulators are supported, x86 emulators will not work Note on Emulators: Only x86_64 emulators are supported, x86 emulators will not work
### Linux
Plug in your android device or use the emulator available via Android Studio and then run the following commands:
```
flutter pub get Linux
flutter run linux
```
## Android Studio
Android Studio is the recommended IDE for development, not just for launching on Android devices and emulators but also for Linux desktop development. Install it and configure it as follows:
```
# setup android studio
sudo apt install -y openjdk-11-jdk
sudo snap install android-studio --classic
```
Use Tools > SDK Manager to install the SDK Tools > Android SDK (API 30), SDK Tools > NDK, SDK Tools > Android SDK command line tools, and SDK Tools > CMake
Then install the Flutter plugin and restart the IDE. In Android Studio's options for the Flutter language, enable auto format on save to match the project's code style. If you have problems with the Dart SDK, make sure to run `flutter` in a terminal to download it (use `source ~/.bashrc` to update your environment variables if you're still using the same terminal from which you ran `setup.sh`)
Make a Pixel 4 (API 30) x86_64 emulator with 2GB of storage space for emulation

@ -1 +1 @@
Subproject commit f74f31e2f3b4a7c11907ae5df6cd38505cd25897 Subproject commit 51f74f05d465a92e0118cf7c2bcfb049df21af42

View file

@ -243,7 +243,7 @@ SPEC CHECKSUMS:
DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
file_picker: 817ab1d8cd2da9d2da412a417162deee3500fc95 file_picker: 817ab1d8cd2da9d2da412a417162deee3500fc95
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_libepiccash: 36241aa7d3126f6521529985ccb3dc5eaf7bb317 flutter_libepiccash: 36241aa7d3126f6521529985ccb3dc5eaf7bb317
flutter_libmonero: da68a616b73dd0374a8419c684fa6b6df2c44ffe flutter_libmonero: da68a616b73dd0374a8419c684fa6b6df2c44ffe
flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743 flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743

View file

@ -198,6 +198,7 @@
CreatedOnToolsVersion = 7.3.1; CreatedOnToolsVersion = 7.3.1;
DevelopmentTeam = 4DQKUWSG6C; DevelopmentTeam = 4DQKUWSG6C;
LastSwiftMigration = 1100; LastSwiftMigration = 1100;
ProvisioningStyle = Automatic;
}; };
}; };
}; };
@ -451,7 +452,9 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 63; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 78;
DEVELOPMENT_TEAM = 4DQKUWSG6C; DEVELOPMENT_TEAM = 4DQKUWSG6C;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
@ -505,10 +508,11 @@
"$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**", "$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**",
"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs", "$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs",
); );
MARKETING_VERSION = 1.4.48; MARKETING_VERSION = 1.5.8;
ONLY_ACTIVE_ARCH = NO; ONLY_ACTIVE_ARCH = NO;
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet; PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
STRIP_INSTALLED_PRODUCT = NO; STRIP_INSTALLED_PRODUCT = NO;
STRIP_STYLE = "non-global"; STRIP_STYLE = "non-global";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@ -635,7 +639,9 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 63; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 78;
DEVELOPMENT_TEAM = 4DQKUWSG6C; DEVELOPMENT_TEAM = 4DQKUWSG6C;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
@ -689,10 +695,11 @@
"$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**", "$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**",
"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs", "$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs",
); );
MARKETING_VERSION = 1.4.48; MARKETING_VERSION = 1.5.8;
ONLY_ACTIVE_ARCH = NO; ONLY_ACTIVE_ARCH = NO;
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet; PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
STRIP_INSTALLED_PRODUCT = NO; STRIP_INSTALLED_PRODUCT = NO;
STRIP_STYLE = "non-global"; STRIP_STYLE = "non-global";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@ -711,7 +718,9 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 63; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 78;
DEVELOPMENT_TEAM = 4DQKUWSG6C; DEVELOPMENT_TEAM = 4DQKUWSG6C;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
@ -765,10 +774,11 @@
"$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**", "$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**",
"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs", "$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs",
); );
MARKETING_VERSION = 1.4.48; MARKETING_VERSION = 1.5.8;
ONLY_ACTIVE_ARCH = NO; ONLY_ACTIVE_ARCH = NO;
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet; PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
STRIP_INSTALLED_PRODUCT = NO; STRIP_INSTALLED_PRODUCT = NO;
STRIP_STYLE = "non-global"; STRIP_STYLE = "non-global";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";

View file

@ -207,6 +207,7 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
didLoad = true; didLoad = true;
await DB.instance.init(); await DB.instance.init();
await _prefs.init();
_notificationsService = ref.read(notificationsProvider); _notificationsService = ref.read(notificationsProvider);
_nodeService = ref.read(nodeServiceChangeNotifierProvider); _nodeService = ref.read(nodeServiceChangeNotifierProvider);
@ -223,7 +224,6 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
tradesService: _tradesService, tradesService: _tradesService,
prefs: _prefs, prefs: _prefs,
); );
await _prefs.init();
ref.read(priceAnd24hChangeNotifierProvider).start(true); ref.read(priceAnd24hChangeNotifierProvider).start(true);
await _wallets.load(_prefs); await _wallets.load(_prefs);
loadingCompleter.complete(); loadingCompleter.complete();
@ -231,7 +231,7 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
// unawaited(_nodeService.updateCommunityNodes()); // unawaited(_nodeService.updateCommunityNodes());
// run without awaiting // run without awaiting
if (Constants.enableExchange) { if (Constants.enableExchange && _prefs.externalCalls) {
unawaited(ExchangeDataLoadingService().loadAll(ref)); unawaited(ExchangeDataLoadingService().loadAll(ref));
} }

View file

@ -5,6 +5,7 @@ import 'package:stackwallet/models/exchange/response_objects/currency.dart';
import 'package:stackwallet/models/exchange/response_objects/estimate.dart'; import 'package:stackwallet/models/exchange/response_objects/estimate.dart';
import 'package:stackwallet/models/exchange/response_objects/fixed_rate_market.dart'; import 'package:stackwallet/models/exchange/response_objects/fixed_rate_market.dart';
import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart'; import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart';
import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dart';
import 'package:stackwallet/services/exchange/exchange.dart'; import 'package:stackwallet/services/exchange/exchange.dart';
import 'package:stackwallet/services/exchange/simpleswap/simpleswap_exchange.dart'; import 'package:stackwallet/services/exchange/simpleswap/simpleswap_exchange.dart';
import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/utilities/logger.dart';
@ -41,12 +42,24 @@ class ExchangeFormState extends ChangeNotifier {
Currency? _from; Currency? _from;
Currency? _to; Currency? _to;
@override
String toString() {
return 'ExchangeFormState: {_exchange: $_exchange, _exchangeType: $_exchangeType, reversed: $reversed, fromAmount: $fromAmount, toAmount: $toAmount, minAmount: $minAmount, maxAmount: $maxAmount, rate: $rate, estimate: $estimate, _market: $_market, _from: $_from, _to: $_to, _onError: $_onError}';
}
String? get fromTicker { String? get fromTicker {
switch (exchangeType) { switch (exchangeType) {
case ExchangeRateType.estimated: case ExchangeRateType.estimated:
return _from?.ticker; return _from?.ticker;
case ExchangeRateType.fixed: case ExchangeRateType.fixed:
return _market?.from; switch (exchange?.name) {
case SimpleSwapExchange.exchangeName:
return _from?.ticker;
case ChangeNowExchange.exchangeName:
return market?.from;
default:
return null;
}
} }
} }
@ -55,7 +68,14 @@ class ExchangeFormState extends ChangeNotifier {
case ExchangeRateType.estimated: case ExchangeRateType.estimated:
return _to?.ticker; return _to?.ticker;
case ExchangeRateType.fixed: case ExchangeRateType.fixed:
return _market?.to; switch (exchange?.name) {
case SimpleSwapExchange.exchangeName:
return _to?.ticker;
case ChangeNowExchange.exchangeName:
return market?.to;
default:
return null;
}
} }
} }
@ -72,7 +92,9 @@ class ExchangeFormState extends ChangeNotifier {
String get warning { String get warning {
if (reversed) { if (reversed) {
if (toTicker != null && toAmount != null) { if (toTicker != null && toAmount != null) {
if (minAmount != null && toAmount! < minAmount!) { if (minAmount != null &&
toAmount! < minAmount! &&
toAmount! > Decimal.zero) {
return "Minimum amount ${minAmount!.toString()} ${toTicker!.toUpperCase()}"; return "Minimum amount ${minAmount!.toString()} ${toTicker!.toUpperCase()}";
} else if (maxAmount != null && toAmount! > maxAmount!) { } else if (maxAmount != null && toAmount! > maxAmount!) {
return "Maximum amount ${maxAmount!.toString()} ${toTicker!.toUpperCase()}"; return "Maximum amount ${maxAmount!.toString()} ${toTicker!.toUpperCase()}";
@ -80,7 +102,9 @@ class ExchangeFormState extends ChangeNotifier {
} }
} else { } else {
if (fromTicker != null && fromAmount != null) { if (fromTicker != null && fromAmount != null) {
if (minAmount != null && fromAmount! < minAmount!) { if (minAmount != null &&
fromAmount! < minAmount! &&
fromAmount! > Decimal.zero) {
return "Minimum amount ${minAmount!.toString()} ${fromTicker!.toUpperCase()}"; return "Minimum amount ${minAmount!.toString()} ${fromTicker!.toUpperCase()}";
} else if (maxAmount != null && fromAmount! > maxAmount!) { } else if (maxAmount != null && fromAmount! > maxAmount!) {
return "Maximum amount ${maxAmount!.toString()} ${fromTicker!.toUpperCase()}"; return "Maximum amount ${maxAmount!.toString()} ${fromTicker!.toUpperCase()}";
@ -95,18 +119,18 @@ class ExchangeFormState extends ChangeNotifier {
String get toAmountString => toAmount?.toStringAsFixed(8) ?? ""; String get toAmountString => toAmount?.toStringAsFixed(8) ?? "";
bool get canExchange { bool get canExchange {
switch (exchangeType) { if (exchange?.name == ChangeNowExchange.exchangeName &&
case ExchangeRateType.estimated: exchangeType == ExchangeRateType.fixed) {
return fromAmount != null && return _market != null &&
fromAmount != Decimal.zero && fromAmount != null &&
toAmount != null && toAmount != null &&
rate != null && warning.isEmpty;
warning.isEmpty; } else {
case ExchangeRateType.fixed: return fromAmount != null &&
return _market != null && fromAmount != Decimal.zero &&
fromAmount != null && toAmount != null &&
toAmount != null && rate != null &&
warning.isEmpty; warning.isEmpty;
} }
} }
@ -375,24 +399,22 @@ class ExchangeFormState extends ChangeNotifier {
minAmount = null; minAmount = null;
maxAmount = null; maxAmount = null;
switch (exchangeType) { if (exchangeType == ExchangeRateType.fixed &&
case ExchangeRateType.estimated: exchange?.name == ChangeNowExchange.exchangeName) {
final Currency? newTo = from; await updateMarket(market, false);
final Currency? newFrom = to; } else {
final Currency? newTo = from;
final Currency? newFrom = to;
_to = newTo; _to = newTo;
_from = newFrom; _from = newFrom;
await updateRanges(shouldNotifyListeners: false); await updateRanges(shouldNotifyListeners: false);
await updateEstimate( await updateEstimate(
shouldNotifyListeners: false, shouldNotifyListeners: false,
reversed: reversed, reversed: reversed,
); );
break;
case ExchangeRateType.fixed:
await updateMarket(market, false);
break;
} }
notifyListeners(); notifyListeners();

View file

@ -275,7 +275,7 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
// without using them // without using them
await manager.recoverFromMnemonic( await manager.recoverFromMnemonic(
mnemonic: mnemonic, mnemonic: mnemonic,
maxUnusedAddressGap: 20, maxUnusedAddressGap: widget.coin == Coin.firo ? 50 : 20,
maxNumberOfIndexesToCheck: 1000, maxNumberOfIndexesToCheck: 1000,
height: height, height: height,
); );

View file

@ -24,6 +24,7 @@ import 'package:stackwallet/services/exchange/simpleswap/simpleswap_exchange.dar
import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/flush_bar_type.dart'; import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/custom_loading_overlay.dart'; import 'package:stackwallet/widgets/custom_loading_overlay.dart';
@ -294,33 +295,29 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
); );
if (ref.read(prefsChangeNotifierProvider).exchangeRateType == if (ref.read(prefsChangeNotifierProvider).exchangeRateType ==
ExchangeRateType.estimated) { ExchangeRateType.fixed &&
await ref.read(exchangeFormStateProvider).swap(); ref.read(exchangeFormStateProvider).exchange?.name ==
} else { ChangeNowExchange.exchangeName) {
switch (ref.read(currentExchangeNameStateProvider.state).state) { final from = ref.read(exchangeFormStateProvider).fromTicker;
case ChangeNowExchange.exchangeName: final to = ref.read(exchangeFormStateProvider).toTicker;
final from = ref.read(exchangeFormStateProvider).fromTicker;
final to = ref.read(exchangeFormStateProvider).toTicker;
if (to != null && from != null) { if (to != null && from != null) {
final markets = ref final markets = ref
.read(availableChangeNowCurrenciesProvider) .read(availableChangeNowCurrenciesProvider)
.markets .markets
.where((e) => e.from == to && e.to == from); .where((e) => e.from == to && e.to == from);
if (markets.isNotEmpty) { if (markets.isNotEmpty) {
await ref await ref.read(exchangeFormStateProvider).swap(market: markets.first);
.read(exchangeFormStateProvider) } else {
.swap(market: markets.first); Logging.instance.log(
} "swap to fixed rate market failed",
} level: LogLevel.Warning,
break; );
case SimpleSwapExchange.exchangeName: }
await ref.read(exchangeFormStateProvider).swap();
break;
default:
//
} }
} else {
await ref.read(exchangeFormStateProvider).swap();
} }
if (mounted) { if (mounted) {
Navigator.of(context).pop(); Navigator.of(context).pop();
@ -575,14 +572,16 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
if (mounted) { if (mounted) {
Navigator.of(context).pop(); Navigator.of(context).pop();
} }
unawaited( if (!(fromTicker == "-" || toTicker == "-")) {
showFloatingFlushBar( unawaited(
type: FlushBarType.warning, showFloatingFlushBar(
message: type: FlushBarType.warning,
"Estimated rate trade pair \"$fromTicker-$toTicker\" unavailable. Reverting to last estimated rate pair.", message:
context: context, "Estimated rate trade pair \"$fromTicker-$toTicker\" unavailable. Reverting to last estimated rate pair.",
), context: context,
); ),
);
}
break; break;
case ExchangeRateType.fixed: case ExchangeRateType.fixed:
if (!(toTicker == "-" || fromTicker == "-")) { if (!(toTicker == "-" || fromTicker == "-")) {
@ -1311,20 +1310,24 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
RateTypeToggle( RateTypeToggle(
onChanged: onRateTypeChanged, onChanged: onRateTypeChanged,
), ),
const SizedBox( if (ref.read(exchangeFormStateProvider).fromAmount != null &&
height: 8, ref.read(exchangeFormStateProvider).fromAmount != Decimal.zero)
), const SizedBox(
ExchangeProviderOptions( height: 8,
from: ref.watch(exchangeFormStateProvider).fromTicker, ),
to: ref.watch(exchangeFormStateProvider).toTicker, if (ref.read(exchangeFormStateProvider).fromAmount != null &&
fromAmount: ref.watch(exchangeFormStateProvider).fromAmount, ref.read(exchangeFormStateProvider).fromAmount != Decimal.zero)
toAmount: ref.watch(exchangeFormStateProvider).toAmount, ExchangeProviderOptions(
fixedRate: ref.watch(prefsChangeNotifierProvider from: ref.watch(exchangeFormStateProvider).fromTicker,
.select((value) => value.exchangeRateType)) == to: ref.watch(exchangeFormStateProvider).toTicker,
ExchangeRateType.fixed, fromAmount: ref.watch(exchangeFormStateProvider).fromAmount,
reversed: ref.watch( toAmount: ref.watch(exchangeFormStateProvider).toAmount,
exchangeFormStateProvider.select((value) => value.reversed)), fixedRate: ref.watch(prefsChangeNotifierProvider
), .select((value) => value.exchangeRateType)) ==
ExchangeRateType.fixed,
reversed: ref.watch(
exchangeFormStateProvider.select((value) => value.reversed)),
),
const SizedBox( const SizedBox(
height: 12, height: 12,
), ),

View file

@ -5,6 +5,7 @@ import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/models/exchange/response_objects/estimate.dart'; import 'package:stackwallet/models/exchange/response_objects/estimate.dart';
import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dart'; import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dart';
import 'package:stackwallet/services/exchange/exchange.dart';
import 'package:stackwallet/services/exchange/exchange_response.dart'; import 'package:stackwallet/services/exchange/exchange_response.dart';
import 'package:stackwallet/services/exchange/simpleswap/simpleswap_exchange.dart'; import 'package:stackwallet/services/exchange/simpleswap/simpleswap_exchange.dart';
import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/assets.dart';
@ -46,6 +47,9 @@ class ExchangeProviderOptions extends ConsumerWidget {
ChangeNowExchange.exchangeName) { ChangeNowExchange.exchangeName) {
ref.read(currentExchangeNameStateProvider.state).state = ref.read(currentExchangeNameStateProvider.state).state =
ChangeNowExchange.exchangeName; ChangeNowExchange.exchangeName;
ref.read(exchangeFormStateProvider).exchange =
Exchange.fromName(
ref.read(currentExchangeNameStateProvider.state).state);
} }
}, },
child: Container( child: Container(
@ -69,6 +73,10 @@ class ExchangeProviderOptions extends ConsumerWidget {
ref ref
.read(currentExchangeNameStateProvider.state) .read(currentExchangeNameStateProvider.state)
.state = value; .state = value;
ref.read(exchangeFormStateProvider).exchange =
Exchange.fromName(ref
.read(currentExchangeNameStateProvider.state)
.state);
} }
}, },
), ),
@ -214,6 +222,9 @@ class ExchangeProviderOptions extends ConsumerWidget {
SimpleSwapExchange.exchangeName) { SimpleSwapExchange.exchangeName) {
ref.read(currentExchangeNameStateProvider.state).state = ref.read(currentExchangeNameStateProvider.state).state =
SimpleSwapExchange.exchangeName; SimpleSwapExchange.exchangeName;
ref.read(exchangeFormStateProvider).exchange =
Exchange.fromName(
ref.read(currentExchangeNameStateProvider.state).state);
} }
}, },
child: Container( child: Container(
@ -237,6 +248,10 @@ class ExchangeProviderOptions extends ConsumerWidget {
ref ref
.read(currentExchangeNameStateProvider.state) .read(currentExchangeNameStateProvider.state)
.state = value; .state = value;
ref.read(exchangeFormStateProvider).exchange =
Exchange.fromName(ref
.read(currentExchangeNameStateProvider.state)
.state);
} }
}, },
), ),

View file

@ -79,23 +79,25 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
transactionIfSentFromStack = widget.transactionIfSentFromStack; transactionIfSentFromStack = widget.transactionIfSentFromStack;
walletId = widget.walletId; walletId = widget.walletId;
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { if (ref.read(prefsChangeNotifierProvider).externalCalls) {
final trade = ref WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
.read(tradesServiceProvider) final trade = ref
.trades .read(tradesServiceProvider)
.firstWhere((e) => e.tradeId == tradeId); .trades
.firstWhere((e) => e.tradeId == tradeId);
if (mounted) { if (mounted) {
final exchange = Exchange.fromName(trade.exchangeName); final exchange = Exchange.fromName(trade.exchangeName);
final response = await exchange.updateTrade(trade); final response = await exchange.updateTrade(trade);
if (mounted && response.value != null) { if (mounted && response.value != null) {
await ref await ref
.read(tradesServiceProvider) .read(tradesServiceProvider)
.edit(trade: response.value!, shouldNotifyListeners: true); .edit(trade: response.value!, shouldNotifyListeners: true);
}
} }
} });
}); }
super.initState(); super.initState();
} }

View file

@ -11,20 +11,18 @@ import 'package:stackwallet/pages/settings_views/global_settings_view/global_set
import 'package:stackwallet/pages/settings_views/global_settings_view/hidden_settings.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/hidden_settings.dart';
import 'package:stackwallet/pages/wallets_view/wallets_view.dart'; import 'package:stackwallet/pages/wallets_view/wallets_view.dart';
import 'package:stackwallet/providers/global/notifications_provider.dart'; import 'package:stackwallet/providers/global/notifications_provider.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart';
import 'package:stackwallet/providers/ui/home_view_index_provider.dart'; import 'package:stackwallet/providers/ui/home_view_index_provider.dart';
import 'package:stackwallet/providers/ui/unread_notifications_provider.dart'; import 'package:stackwallet/providers/ui/unread_notifications_provider.dart';
import 'package:stackwallet/services/exchange/exchange_data_loading_service.dart'; import 'package:stackwallet/services/exchange/exchange_data_loading_service.dart';
import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/stack_dialog.dart'; import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
class HomeView extends ConsumerStatefulWidget { class HomeView extends ConsumerStatefulWidget {
const HomeView({Key? key}) : super(key: key); const HomeView({Key? key}) : super(key: key);
@ -45,7 +43,7 @@ class _HomeViewState extends ConsumerState<HomeView> {
bool _exitEnabled = false; bool _exitEnabled = false;
final _cnLoadingService = ExchangeDataLoadingService(); final _exchangeDataLoadingService = ExchangeDataLoadingService();
Future<bool> _onWillPop() async { Future<bool> _onWillPop() async {
// go to home view when tapping back on the main exchange view // go to home view when tapping back on the main exchange view
@ -85,10 +83,8 @@ class _HomeViewState extends ConsumerState<HomeView> {
void _loadCNData() { void _loadCNData() {
// unawaited future // unawaited future
// if (ref.read(prefsChangeNotifierProvider).externalCalls) {
final externalCalls = Prefs.instance.externalCalls; _exchangeDataLoadingService.loadAll(ref);
if (externalCalls) {
_cnLoadingService.loadAll(ref);
} else { } else {
Logging.instance.log("User does not want to use external calls", Logging.instance.log("User does not want to use external calls",
level: LogLevel.Info); level: LogLevel.Info);
@ -290,6 +286,9 @@ class _HomeViewState extends ConsumerState<HomeView> {
_ref.listen(homeViewPageIndexStateProvider, _ref.listen(homeViewPageIndexStateProvider,
(previous, next) { (previous, next) {
if (next is int) { if (next is int) {
if (next == 1) {
_exchangeDataLoadingService.loadAll(ref);
}
if (next >= 0 && next <= 1) { if (next >= 0 && next <= 1) {
_pageController.animateToPage( _pageController.animateToPage(
next, next,

View file

@ -1,5 +1,3 @@
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/providers.dart';
@ -8,8 +6,6 @@ import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/stack_dialog.dart'; import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:stackwallet/utilities/prefs.dart';
class HomeViewButtonBar extends ConsumerStatefulWidget { class HomeViewButtonBar extends ConsumerStatefulWidget {
const HomeViewButtonBar({Key? key}) : super(key: key); const HomeViewButtonBar({Key? key}) : super(key: key);
@ -18,8 +14,8 @@ class HomeViewButtonBar extends ConsumerStatefulWidget {
} }
class _HomeViewButtonBarState extends ConsumerState<HomeViewButtonBar> { class _HomeViewButtonBarState extends ConsumerState<HomeViewButtonBar> {
final DateTime _lastRefreshed = DateTime.now(); // final DateTime _lastRefreshed = DateTime.now();
final Duration _refreshInterval = const Duration(hours: 1); // final Duration _refreshInterval = const Duration(hours: 1);
@override @override
void initState() { void initState() {
@ -104,34 +100,14 @@ class _HomeViewButtonBarState extends ConsumerState<HomeViewButtonBar> {
if (selectedIndex != 1) { if (selectedIndex != 1) {
ref.read(homeViewPageIndexStateProvider.state).state = 1; ref.read(homeViewPageIndexStateProvider.state).state = 1;
} }
DateTime now = DateTime.now(); // DateTime now = DateTime.now();
final _cnLoadingService = ExchangeDataLoadingService(); // if (ref.read(prefsChangeNotifierProvider).externalCalls) {
final externalCalls = Prefs.instance.externalCalls; // print("loading?");
if (!externalCalls) { await ExchangeDataLoadingService().loadAll(ref);
print("loading?"); // }
unawaited(_cnLoadingService.loadAll(ref)); // if (now.difference(_lastRefreshed) > _refreshInterval) {
} // await ExchangeDataLoadingService().loadAll(ref);
if (now.difference(_lastRefreshed) > _refreshInterval) { // }
// bool okPressed = false;
// showDialog<dynamic>(
// context: context,
// barrierDismissible: false,
// builder: (_) => const StackDialog(
// // builder: (_) => StackOkDialog(
// title: "Refreshing ChangeNOW data",
// message: "This may take a while",
// // onOkPressed: (value) {
// // if (value == "OK") {
// // okPressed = true;
// // }
// // },
// ),
// );
await ExchangeDataLoadingService().loadAll(ref);
// if (!okPressed && mounted) {
// Navigator.of(context).pop();
// }
}
}, },
child: Text( child: Text(
"Exchange", "Exchange",

View file

@ -27,6 +27,7 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart'; import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
import 'package:stackwallet/utilities/format.dart'; import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/animated_text.dart'; import 'package:stackwallet/widgets/animated_text.dart';
@ -1107,115 +1108,120 @@ class _SendViewState extends ConsumerState<SendView> {
), ),
), ),
), ),
const SizedBox( if (Prefs.instance.externalCalls)
height: 8, const SizedBox(
), height: 8,
TextField(
style: STextStyles.smallMed14(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
), ),
key: const Key("amountInputFieldFiatTextFieldKey"), if (Prefs.instance.externalCalls)
controller: baseAmountController, TextField(
focusNode: _baseFocus, style: STextStyles.smallMed14(context).copyWith(
keyboardType: const TextInputType.numberWithOptions( color: Theme.of(context)
signed: false, .extension<StackColors>()!
decimal: true, .textDark,
), ),
textAlign: TextAlign.right, key: const Key("amountInputFieldFiatTextFieldKey"),
inputFormatters: [ controller: baseAmountController,
// regex to validate a fiat amount with 2 decimal places focusNode: _baseFocus,
TextInputFormatter.withFunction((oldValue, keyboardType: const TextInputType.numberWithOptions(
newValue) => signed: false,
RegExp(r'^([0-9]*[,.]?[0-9]{0,2}|[,.][0-9]{0,2})$') decimal: true,
.hasMatch(newValue.text) ),
? newValue textAlign: TextAlign.right,
: oldValue), inputFormatters: [
], // regex to validate a fiat amount with 2 decimal places
onChanged: (baseAmountString) { TextInputFormatter.withFunction((oldValue,
if (baseAmountString.isNotEmpty && newValue) =>
baseAmountString != "." && RegExp(r'^([0-9]*[,.]?[0-9]{0,2}|[,.][0-9]{0,2})$')
baseAmountString != ",") { .hasMatch(newValue.text)
final baseAmount = baseAmountString.contains(",") ? newValue
? Decimal.parse( : oldValue),
baseAmountString.replaceFirst(",", ".")) ],
: Decimal.parse(baseAmountString); onChanged: (baseAmountString) {
if (baseAmountString.isNotEmpty &&
baseAmountString != "." &&
baseAmountString != ",") {
final baseAmount = baseAmountString
.contains(",")
? Decimal.parse(
baseAmountString.replaceFirst(",", "."))
: Decimal.parse(baseAmountString);
var _price = ref var _price = ref
.read(priceAnd24hChangeNotifierProvider) .read(priceAnd24hChangeNotifierProvider)
.getPrice(coin) .getPrice(coin)
.item1; .item1;
if (_price == Decimal.zero) { if (_price == Decimal.zero) {
_amountToSend = Decimal.zero; _amountToSend = Decimal.zero;
} else {
_amountToSend = baseAmount <= Decimal.zero
? Decimal.zero
: (baseAmount / _price).toDecimal(
scaleOnInfinitePrecision:
Constants.decimalPlaces);
}
if (_cachedAmountToSend != null &&
_cachedAmountToSend == _amountToSend) {
return;
}
_cachedAmountToSend = _amountToSend;
Logging.instance.log(
"it changed $_amountToSend $_cachedAmountToSend",
level: LogLevel.Info);
final amountString =
Format.localizedStringAsFixed(
value: _amountToSend!,
locale: ref
.read(localeServiceChangeNotifierProvider)
.locale,
decimalPlaces: Constants.decimalPlaces,
);
_cryptoAmountChangeLock = true;
cryptoAmountController.text = amountString;
_cryptoAmountChangeLock = false;
} else { } else {
_amountToSend = baseAmount <= Decimal.zero _amountToSend = Decimal.zero;
? Decimal.zero _cryptoAmountChangeLock = true;
: (baseAmount / _price).toDecimal( cryptoAmountController.text = "";
scaleOnInfinitePrecision: _cryptoAmountChangeLock = false;
Constants.decimalPlaces);
} }
if (_cachedAmountToSend != null && // setState(() {
_cachedAmountToSend == _amountToSend) { // _calculateFeesFuture = calculateFees(
return; // Format.decimalAmountToSatoshis(
} // _amountToSend!));
_cachedAmountToSend = _amountToSend; // });
Logging.instance.log( _updatePreviewButtonState(
"it changed $_amountToSend $_cachedAmountToSend", _address, _amountToSend);
level: LogLevel.Info); },
decoration: InputDecoration(
final amountString = contentPadding: const EdgeInsets.only(
Format.localizedStringAsFixed( top: 12,
value: _amountToSend!, right: 12,
locale: ref ),
.read(localeServiceChangeNotifierProvider) hintText: "0",
.locale, hintStyle:
decimalPlaces: Constants.decimalPlaces, STextStyles.fieldLabel(context).copyWith(
); fontSize: 14,
),
_cryptoAmountChangeLock = true; prefixIcon: FittedBox(
cryptoAmountController.text = amountString; fit: BoxFit.scaleDown,
_cryptoAmountChangeLock = false; child: Padding(
} else { padding: const EdgeInsets.all(12),
_amountToSend = Decimal.zero; child: Text(
_cryptoAmountChangeLock = true; ref.watch(prefsChangeNotifierProvider
cryptoAmountController.text = ""; .select((value) => value.currency)),
_cryptoAmountChangeLock = false; style: STextStyles.smallMed14(context)
} .copyWith(
// setState(() { color: Theme.of(context)
// _calculateFeesFuture = calculateFees( .extension<StackColors>()!
// Format.decimalAmountToSatoshis( .accentColorDark),
// _amountToSend!)); ),
// });
_updatePreviewButtonState(_address, _amountToSend);
},
decoration: InputDecoration(
contentPadding: const EdgeInsets.only(
top: 12,
right: 12,
),
hintText: "0",
hintStyle: STextStyles.fieldLabel(context).copyWith(
fontSize: 14,
),
prefixIcon: FittedBox(
fit: BoxFit.scaleDown,
child: Padding(
padding: const EdgeInsets.all(12),
child: Text(
ref.watch(prefsChangeNotifierProvider
.select((value) => value.currency)),
style: STextStyles.smallMed14(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
), ),
), ),
), ),
), ),
),
const SizedBox( const SizedBox(
height: 12, height: 12,
), ),
@ -1319,6 +1325,12 @@ class _SendViewState extends ConsumerState<SendView> {
cryptoAmountController cryptoAmountController
.text) ?? .text) ??
Decimal.zero, Decimal.zero,
updateChosen: (String fee) {
setState(() {
_calculateFeesFuture =
Future(() => fee);
});
},
), ),
); );
}, },

View file

@ -32,10 +32,12 @@ class TransactionFeeSelectionSheet extends ConsumerStatefulWidget {
Key? key, Key? key,
required this.walletId, required this.walletId,
required this.amount, required this.amount,
required this.updateChosen,
}) : super(key: key); }) : super(key: key);
final String walletId; final String walletId;
final Decimal amount; final Decimal amount;
final Function updateChosen;
@override @override
ConsumerState<TransactionFeeSelectionSheet> createState() => ConsumerState<TransactionFeeSelectionSheet> createState() =>
@ -223,6 +225,10 @@ class _TransactionFeeSelectionSheetState
ref.read(feeRateTypeStateProvider.state).state = ref.read(feeRateTypeStateProvider.state).state =
FeeRateType.fast; FeeRateType.fast;
} }
String? fee = getAmount(FeeRateType.fast);
if (fee != null) {
widget.updateChosen(fee);
}
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
child: Container( child: Container(
@ -352,6 +358,10 @@ class _TransactionFeeSelectionSheetState
ref.read(feeRateTypeStateProvider.state).state = ref.read(feeRateTypeStateProvider.state).state =
FeeRateType.average; FeeRateType.average;
} }
String? fee = getAmount(FeeRateType.average);
if (fee != null) {
widget.updateChosen(fee);
}
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
child: Container( child: Container(
@ -479,6 +489,11 @@ class _TransactionFeeSelectionSheetState
ref.read(feeRateTypeStateProvider.state).state = ref.read(feeRateTypeStateProvider.state).state =
FeeRateType.slow; FeeRateType.slow;
} }
String? fee = getAmount(FeeRateType.slow);
print("fee $fee");
if (fee != null) {
widget.updateChosen(fee);
}
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
child: Container( child: Container(
@ -608,4 +623,45 @@ class _TransactionFeeSelectionSheetState
), ),
); );
} }
String? getAmount(FeeRateType feeRateType) {
try {
print(feeRateType);
var amount = Format.decimalAmountToSatoshis(this.amount);
print(amount);
print(ref.read(feeSheetSessionCacheProvider).fast);
print(ref.read(feeSheetSessionCacheProvider).average);
print(ref.read(feeSheetSessionCacheProvider).slow);
switch (feeRateType) {
case FeeRateType.fast:
if (ref.read(feeSheetSessionCacheProvider).fast[amount] != null) {
return (ref.read(feeSheetSessionCacheProvider).fast[amount]
as Decimal)
.toString();
}
return null;
case FeeRateType.average:
if (ref.read(feeSheetSessionCacheProvider).average[amount] != null) {
return (ref.read(feeSheetSessionCacheProvider).average[amount]
as Decimal)
.toString();
}
return null;
case FeeRateType.slow:
print(ref.read(feeSheetSessionCacheProvider).slow);
print(ref.read(feeSheetSessionCacheProvider).slow[amount]);
if (ref.read(feeSheetSessionCacheProvider).slow[amount] != null) {
return (ref.read(feeSheetSessionCacheProvider).slow[amount]
as Decimal)
.toString();
}
return null;
}
} catch (e, s) {
print("$e $s");
return null;
}
}
} }

View file

@ -402,7 +402,7 @@ abstract class SWB {
// without using them // without using them
await manager.recoverFromMnemonic( await manager.recoverFromMnemonic(
mnemonic: mnemonic, mnemonic: mnemonic,
maxUnusedAddressGap: 20, maxUnusedAddressGap: manager.coin == Coin.firo ? 50 : 20,
maxNumberOfIndexesToCheck: 1000, maxNumberOfIndexesToCheck: 1000,
height: restoreHeight, height: restoreHeight,
); );
@ -997,10 +997,14 @@ abstract class SWB {
// primary nodes // primary nodes
if (primaryNodes != null) { if (primaryNodes != null) {
for (var node in primaryNodes) { for (var node in primaryNodes) {
await nodeService.setPrimaryNodeFor( try {
coin: coinFromPrettyName(node['coinName'] as String), await nodeService.setPrimaryNodeFor(
node: nodeService.getNodeById(id: node['id'] as String)!, coin: coinFromPrettyName(node['coinName'] as String),
); node: nodeService.getNodeById(id: node['id'] as String)!,
);
} catch (e, s) {
Logging.instance.log("$e $s", level: LogLevel.Error);
}
} }
} }
await nodeService.updateDefaults(); await nodeService.updateDefaults();
@ -1175,10 +1179,14 @@ abstract class SWB {
} }
if (primaryNodes != null) { if (primaryNodes != null) {
for (var node in primaryNodes) { for (var node in primaryNodes) {
await nodeService.setPrimaryNodeFor( try {
coin: coinFromPrettyName(node['coinName'] as String), await nodeService.setPrimaryNodeFor(
node: nodeService.getNodeById(id: node['id'] as String)!, coin: coinFromPrettyName(node['coinName'] as String),
); node: nodeService.getNodeById(id: node['id'] as String)!,
);
} catch (e, s) {
Logging.instance.log("$e $s", level: LogLevel.Error);
}
} }
} }
await nodeService.updateDefaults(); await nodeService.updateDefaults();

View file

@ -3,6 +3,7 @@ import 'dart:io';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
class StackFileSystem { class StackFileSystem {
Directory? rootPath; Directory? rootPath;
@ -14,6 +15,9 @@ class StackFileSystem {
final bool isDesktop = !(Platform.isAndroid || Platform.isIOS); final bool isDesktop = !(Platform.isAndroid || Platform.isIOS);
Future<Directory> prepareStorage() async { Future<Directory> prepareStorage() async {
if (Platform.isAndroid) {
await Permission.storage.request();
}
rootPath = (await getApplicationDocumentsDirectory()); rootPath = (await getApplicationDocumentsDirectory());
debugPrint(rootPath!.absolute.toString()); debugPrint(rootPath!.absolute.toString());
if (Platform.isAndroid) { if (Platform.isAndroid) {
@ -22,13 +26,13 @@ class StackFileSystem {
debugPrint(rootPath!.absolute.toString()); debugPrint(rootPath!.absolute.toString());
Directory sampleFolder = Directory sampleFolder =
Directory('${rootPath!.path}/Documents/Stack_backups'); Directory('${rootPath!.path}Documents/Stack_backups');
if (Platform.isIOS) { if (Platform.isIOS) {
sampleFolder = Directory(rootPath!.path); sampleFolder = Directory(rootPath!.path);
} }
try { try {
if (!sampleFolder.existsSync()) { if (!sampleFolder.existsSync()) {
sampleFolder.createSync(); sampleFolder.createSync(recursive: true);
} }
} catch (e, s) { } catch (e, s) {
debugPrint("$e $s"); debugPrint("$e $s");
@ -65,8 +69,7 @@ class StackFileSystem {
result = await FilePicker.platform.pickFiles( result = await FilePicker.platform.pickFiles(
dialogTitle: "Load backup file", dialogTitle: "Load backup file",
initialDirectory: startPath!.path, initialDirectory: startPath!.path,
type: FileType.custom, type: FileType.any,
allowedExtensions: ['bin'],
allowCompression: false, allowCompression: false,
lockParentWindow: true, lockParentWindow: true,
); );

View file

@ -35,7 +35,8 @@ class _RestoringWalletCardState extends ConsumerState<RestoringWalletCard> {
case StackRestoringStatus.waiting: case StackRestoringStatus.waiting:
return SvgPicture.asset( return SvgPicture.asset(
Assets.svg.loader, Assets.svg.loader,
color: Theme.of(context).extension<StackColors>()!.buttonBackSecondary, color:
Theme.of(context).extension<StackColors>()!.buttonBackSecondary,
); );
case StackRestoringStatus.restoring: case StackRestoringStatus.restoring:
return const LoadingIndicator(); return const LoadingIndicator();
@ -95,7 +96,10 @@ class _RestoringWalletCardState extends ConsumerState<RestoringWalletCard> {
try { try {
final mnemonicList = await manager.mnemonic; final mnemonicList = await manager.mnemonic;
const maxUnusedAddressGap = 20; int maxUnusedAddressGap = 20;
if (coin == Coin.firo) {
maxUnusedAddressGap = 50;
}
const maxNumberOfIndexesToCheck = 1000; const maxNumberOfIndexesToCheck = 1000;
if (mnemonicList.isEmpty) { if (mnemonicList.isEmpty) {
@ -155,14 +159,17 @@ class _RestoringWalletCardState extends ConsumerState<RestoringWalletCard> {
? Container( ? Container(
height: 20, height: 20,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).extension<StackColors>()!.buttonBackSecondary, color: Theme.of(context)
.extension<StackColors>()!
.buttonBackSecondary,
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(
1000, 1000,
), ),
), ),
child: RawMaterialButton( child: RawMaterialButton(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
splashColor: Theme.of(context).extension<StackColors>()!.highlight, splashColor:
Theme.of(context).extension<StackColors>()!.highlight,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(
1000, 1000,
@ -187,7 +194,9 @@ class _RestoringWalletCardState extends ConsumerState<RestoringWalletCard> {
child: Text( child: Text(
"Show recovery phrase", "Show recovery phrase",
style: STextStyles.infoSmall(context).copyWith( style: STextStyles.infoSmall(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.accentColorDark), color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
), ),
), ),
), ),

View file

@ -77,7 +77,8 @@ class _WalletNetworkSettingsViewState
Future<void> _attemptRescan() async { Future<void> _attemptRescan() async {
if (!Platform.isLinux) Wakelock.enable(); if (!Platform.isLinux) Wakelock.enable();
const int maxUnusedAddressGap = 20; int maxUnusedAddressGap = 20;
const int maxNumberOfIndexesToCheck = 1000; const int maxNumberOfIndexesToCheck = 1000;
showDialog<dynamic>( showDialog<dynamic>(
@ -88,6 +89,13 @@ class _WalletNetworkSettingsViewState
); );
try { try {
if (ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.coin ==
Coin.firo) {
maxUnusedAddressGap = 50;
}
await ref await ref
.read(walletsChangeNotifierProvider) .read(walletsChangeNotifierProvider)
.getManager(widget.walletId) .getManager(widget.walletId)

View file

@ -1,8 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/hive/db.dart';
import 'package:stackwallet/pages/pinpad_views/create_pin_view.dart'; import 'package:stackwallet/pages/pinpad_views/create_pin_view.dart';
import 'package:stackwallet/pages_desktop_specific/create_password/create_password_view.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart'; import 'package:stackwallet/providers/global/prefs_provider.dart';
import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/constants.dart';
@ -12,9 +12,6 @@ import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart';
import 'package:stackwallet/utilities/prefs.dart';
class StackPrivacyCalls extends ConsumerStatefulWidget { class StackPrivacyCalls extends ConsumerStatefulWidget {
const StackPrivacyCalls({ const StackPrivacyCalls({
Key? key, Key? key,
@ -31,19 +28,19 @@ class StackPrivacyCalls extends ConsumerStatefulWidget {
class _StackPrivacyCalls extends ConsumerState<StackPrivacyCalls> { class _StackPrivacyCalls extends ConsumerState<StackPrivacyCalls> {
late final bool isDesktop; late final bool isDesktop;
bool isEasy = Prefs.instance.externalCalls; late bool isEasy;
final PageController _pageController = late bool infoToggle;
PageController(initialPage: 0, keepPage: true);
@override @override
void initState() { void initState() {
isDesktop = Util.isDesktop; isDesktop = Util.isDesktop;
isEasy = ref.read(prefsChangeNotifierProvider).externalCalls;
infoToggle = isEasy;
super.initState(); super.initState();
} }
@override @override
void dispose() { void dispose() {
_pageController.dispose();
super.dispose(); super.dispose();
} }
@ -59,135 +56,161 @@ class _StackPrivacyCalls extends ConsumerState<StackPrivacyCalls> {
), ),
), ),
body: SafeArea( body: SafeArea(
child: PageView( child: Padding(
controller: _pageController, padding: const EdgeInsets.fromLTRB(0, 40, 0, 0),
physics: const NeverScrollableScrollPhysics(), child: Column(
children: [ mainAxisAlignment: MainAxisAlignment.center,
Padding( children: [
padding: const EdgeInsets.fromLTRB(0, 40, 0, 0), Text(
child: Column( "Choose your Stack experience",
mainAxisAlignment: MainAxisAlignment.center, style: STextStyles.pageTitleH1(context),
children: [ ),
Text( const SizedBox(
"Choose your Stack experience", height: 8,
style: STextStyles.pageTitleH1(context), ),
), Text(
const SizedBox( "You can change it later in Settings",
height: 8, style: STextStyles.subtitle(context),
), ),
Text( const SizedBox(
"You can change it later in Settings", height: 36,
style: STextStyles.subtitle(context), ),
), Padding(
const SizedBox( padding: const EdgeInsets.symmetric(
height: 36, horizontal: 16,
), ),
const Padding( child: PrivacyToggle(
padding: EdgeInsets.symmetric( externalCallsEnabled: isEasy,
horizontal: 16, onChanged: (externalCalls) {
), isEasy = externalCalls;
child: PrivacyToggle(), setState(() {
), infoToggle = isEasy;
const SizedBox( });
height: 36, },
), ),
Padding( ),
padding: const EdgeInsets.all(16.0), const SizedBox(
child: RoundedWhiteContainer( height: 36,
child: Center( ),
child: RichText( Padding(
textAlign: TextAlign.left, padding: const EdgeInsets.all(16.0),
text: TextSpan( child: RoundedWhiteContainer(
style: STextStyles.label(context) child: Center(
.copyWith(fontSize: 12.0), child: RichText(
children: ref.watch( textAlign: TextAlign.left,
prefsChangeNotifierProvider.select( text: TextSpan(
(value) => value.externalCalls, style:
), STextStyles.label(context).copyWith(fontSize: 12.0),
) children: infoToggle
? [ ? [
const TextSpan( const TextSpan(
text: text:
"Exchange data preloaded for a seamless experience."), "Exchange data preloaded for a seamless experience."),
const TextSpan( const TextSpan(
text: text:
"\n\nCoinGecko enabled: (24 hour price change shown in-app, total wallet value shown in USD or other currency)."), "\n\nCoinGecko enabled: (24 hour price change shown in-app, total wallet value shown in USD or other currency)."),
TextSpan( TextSpan(
text: text:
"\n\nRecommended for most crypto users.", "\n\nRecommended for most crypto users.",
style: TextStyle( style: TextStyle(
color: Theme.of(context) color: Theme.of(context)
.extension<StackColors>()! .extension<StackColors>()!
.textDark, .textDark,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
), ),
] ]
: [ : [
const TextSpan( const TextSpan(
text: text:
"Exchange data not preloaded (slower experience)."), "Exchange data not preloaded (slower experience)."),
const TextSpan( const TextSpan(
text: text:
"\n\nCoinGecko disabled (price changes not shown, no wallet value shown in other currencies)."), "\n\nCoinGecko disabled (price changes not shown, no wallet value shown in other currencies)."),
TextSpan( TextSpan(
text: text:
"\n\nRecommended for the privacy conscious.", "\n\nRecommended for the privacy conscious.",
style: TextStyle( style: TextStyle(
color: Theme.of(context) color: Theme.of(context)
.extension<StackColors>()! .extension<StackColors>()!
.textDark, .textDark,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
), ),
], ],
),
),
), ),
), ),
), ),
const Spacer( ),
flex: 4,
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 16,
),
child: Row(
children: [
Expanded(
child: ContinueButton(
isDesktop: isDesktop,
isSettings: widget.isSettings,
isEasy: ref.watch(
prefsChangeNotifierProvider.select(
(value) => value.externalCalls,
),
),
),
),
],
),
),
],
), ),
), const Spacer(
], flex: 4,
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 16,
),
child: Row(
children: [
Expanded(
child: ContinueButton(
isDesktop: isDesktop,
label: !widget.isSettings ? "Continue" : "Save changes",
onPressed: () {
ref.read(prefsChangeNotifierProvider).externalCalls =
isEasy;
if (!widget.isSettings) {
if (isDesktop) {
Navigator.of(context).pushNamed(
CreatePasswordView.routeName,
);
} else {
Navigator.of(context).pushNamed(
CreatePinView.routeName,
);
}
} else {
Navigator.pop(context);
}
},
),
),
],
),
),
],
),
), ),
), ),
); );
} }
} }
class PrivacyToggle extends ConsumerStatefulWidget { class PrivacyToggle extends StatefulWidget {
const PrivacyToggle({Key? key}) : super(key: key); const PrivacyToggle({
Key? key,
required this.externalCallsEnabled,
this.onChanged,
}) : super(key: key);
final bool externalCallsEnabled;
final void Function(bool)? onChanged;
@override @override
ConsumerState<PrivacyToggle> createState() => _PrivacyToggleState(); State<PrivacyToggle> createState() => _PrivacyToggleState();
} }
class _PrivacyToggleState extends ConsumerState<PrivacyToggle> { class _PrivacyToggleState extends State<PrivacyToggle> {
late bool externalCallsEnabled;
@override
void initState() {
// initial toggle state
externalCallsEnabled = widget.externalCallsEnabled;
super.initState();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Row( return Row(
@ -196,11 +219,7 @@ class _PrivacyToggleState extends ConsumerState<PrivacyToggle> {
child: RawMaterialButton( child: RawMaterialButton(
fillColor: Theme.of(context).extension<StackColors>()!.popupBG, fillColor: Theme.of(context).extension<StackColors>()!.popupBG,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
side: !ref.watch( side: !externalCallsEnabled
prefsChangeNotifierProvider.select(
(value) => value.externalCalls,
),
)
? BorderSide.none ? BorderSide.none
: BorderSide( : BorderSide(
color: Theme.of(context) color: Theme.of(context)
@ -213,7 +232,12 @@ class _PrivacyToggleState extends ConsumerState<PrivacyToggle> {
), ),
), ),
onPressed: () { onPressed: () {
ref.read(prefsChangeNotifierProvider).externalCalls = true; setState(() {
// update toggle state
externalCallsEnabled = true;
});
// call callback with newly set value
widget.onChanged?.call(externalCallsEnabled);
}, },
child: Padding( child: Padding(
padding: const EdgeInsets.all( padding: const EdgeInsets.all(
@ -244,11 +268,7 @@ class _PrivacyToggleState extends ConsumerState<PrivacyToggle> {
), ),
], ],
), ),
if (ref.watch( if (externalCallsEnabled)
prefsChangeNotifierProvider.select(
(value) => value.externalCalls,
),
))
Positioned( Positioned(
top: 4, top: 4,
right: 4, right: 4,
@ -261,11 +281,7 @@ class _PrivacyToggleState extends ConsumerState<PrivacyToggle> {
.infoItemIcons, .infoItemIcons,
), ),
), ),
if (!ref.watch( if (!externalCallsEnabled)
prefsChangeNotifierProvider.select(
(value) => value.externalCalls,
),
))
Positioned( Positioned(
top: 4, top: 4,
right: 4, right: 4,
@ -293,11 +309,7 @@ class _PrivacyToggleState extends ConsumerState<PrivacyToggle> {
elevation: 0, elevation: 0,
fillColor: Theme.of(context).extension<StackColors>()!.popupBG, fillColor: Theme.of(context).extension<StackColors>()!.popupBG,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
side: ref.watch( side: externalCallsEnabled
prefsChangeNotifierProvider.select(
(value) => value.externalCalls,
),
)
? BorderSide.none ? BorderSide.none
: BorderSide( : BorderSide(
color: Theme.of(context) color: Theme.of(context)
@ -310,7 +322,12 @@ class _PrivacyToggleState extends ConsumerState<PrivacyToggle> {
), ),
), ),
onPressed: () { onPressed: () {
ref.read(prefsChangeNotifierProvider).externalCalls = false; setState(() {
// update toggle state
externalCallsEnabled = false;
});
// call callback with newly set value
widget.onChanged?.call(externalCallsEnabled);
}, },
child: Padding( child: Padding(
padding: const EdgeInsets.all( padding: const EdgeInsets.all(
@ -342,11 +359,7 @@ class _PrivacyToggleState extends ConsumerState<PrivacyToggle> {
), ),
], ],
), ),
if (!ref.watch( if (!externalCallsEnabled)
prefsChangeNotifierProvider.select(
(value) => value.externalCalls,
),
))
Positioned( Positioned(
top: 4, top: 4,
right: 4, right: 4,
@ -359,11 +372,7 @@ class _PrivacyToggleState extends ConsumerState<PrivacyToggle> {
.infoItemIcons, .infoItemIcons,
), ),
), ),
if (ref.watch( if (externalCallsEnabled)
prefsChangeNotifierProvider.select(
(value) => value.externalCalls,
),
))
Positioned( Positioned(
top: 4, top: 4,
right: 4, right: 4,
@ -388,62 +397,47 @@ class _PrivacyToggleState extends ConsumerState<PrivacyToggle> {
} }
} }
class ContinueButton extends StatelessWidget { class ContinueButton extends ConsumerWidget {
const ContinueButton( const ContinueButton({
{Key? key, Key? key,
required this.isDesktop, required this.isDesktop,
required this.isSettings, required this.onPressed,
required this.isEasy}) required this.label,
: super(key: key); }) : super(key: key);
final String label;
final bool isDesktop; final bool isDesktop;
final bool isSettings; final VoidCallback onPressed;
final bool isEasy;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context, WidgetRef ref) {
return !isDesktop if (isDesktop) {
? TextButton( return SizedBox(
style: Theme.of(context) width: 328,
.extension<StackColors>()! height: 70,
.getPrimaryEnabledButtonColor(context), child: TextButton(
onPressed: () { style: Theme.of(context)
print("Output of isEasy:"); .extension<StackColors>()!
print(isEasy); .getPrimaryEnabledButtonColor(context),
onPressed: onPressed,
Prefs.instance.externalCalls = isEasy; child: Text(
if (!isSettings) { label,
Navigator.of(context).pushNamed(CreatePinView.routeName); style: STextStyles.button(context).copyWith(fontSize: 20),
} ),
}, ),
child: Text( );
!isSettings ? "Continue" : "Save changes", } else {
style: STextStyles.button(context), return TextButton(
), style: Theme.of(context)
) .extension<StackColors>()!
: SizedBox( .getPrimaryEnabledButtonColor(context),
width: 328, onPressed: onPressed,
height: 70, child: Text(
child: TextButton( label,
style: Theme.of(context) style: STextStyles.button(context),
.extension<StackColors>()! ),
.getPrimaryEnabledButtonColor(context), );
onPressed: () { }
print("Output of isEasy:");
print(isEasy);
Prefs.instance.externalCalls = isEasy;
if (!isSettings) {
Navigator.of(context).pushNamed(CreatePinView.routeName);
}
},
child: Text(
!isSettings ? "Continue" : "Save changes",
style: STextStyles.button(context).copyWith(fontSize: 20),
),
),
);
} }
} }

View file

@ -1,3 +1,4 @@
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/providers.dart';
@ -8,6 +9,8 @@ import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
class WalletBalanceToggleSheet extends ConsumerWidget { class WalletBalanceToggleSheet extends ConsumerWidget {
const WalletBalanceToggleSheet({ const WalletBalanceToggleSheet({
Key? key, Key? key,
@ -23,6 +26,22 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
final coin = ref.watch(walletsChangeNotifierProvider final coin = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(walletId).coin)); .select((value) => value.getManager(walletId).coin));
Future<Decimal>? totalBalanceFuture;
Future<Decimal>? availableBalanceFuture;
if (coin == Coin.firo || coin == Coin.firoTestNet) {
final firoWallet = ref
.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(walletId)))
.wallet as FiroWallet;
totalBalanceFuture = firoWallet.availablePublicBalance();
availableBalanceFuture = firoWallet.availablePrivateBalance();
} else {
final wallet = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(walletId)));
totalBalanceFuture = wallet.totalBalance;
availableBalanceFuture = wallet.availableBalance;
}
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).extension<StackColors>()!.popupBG, color: Theme.of(context).extension<StackColors>()!.popupBG,
@ -125,15 +144,35 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
const SizedBox( const SizedBox(
height: 2, height: 2,
), ),
Text( FutureBuilder(
"Current spendable (unlocked) balance", future: availableBalanceFuture,
style: builder: (fbContext,
STextStyles.itemSubtitle12(context).copyWith( AsyncSnapshot<Decimal> snapshot) {
color: Theme.of(context) if (snapshot.connectionState ==
.extension<StackColors>()! ConnectionState.done &&
.textSubtitle1, snapshot.hasData &&
), snapshot.data != null) {
), return Text(
"${snapshot.data!}",
style: STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
);
} else {
return Text(
"",
style: STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
);
}
}),
], ],
), ),
if (coin == Coin.firo || coin == Coin.firoTestNet) if (coin == Coin.firo || coin == Coin.firoTestNet)
@ -147,15 +186,35 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
const SizedBox( const SizedBox(
height: 2, height: 2,
), ),
Text( FutureBuilder(
"Current private spendable (unlocked) balance", future: availableBalanceFuture,
style: builder: (fbContext,
STextStyles.itemSubtitle12(context).copyWith( AsyncSnapshot<Decimal> snapshot) {
color: Theme.of(context) if (snapshot.connectionState ==
.extension<StackColors>()! ConnectionState.done &&
.textSubtitle1, snapshot.hasData &&
), snapshot.data != null) {
), return Text(
"${snapshot.data!}",
style: STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
);
} else {
return Text(
"",
style: STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
);
}
}),
], ],
), ),
], ],
@ -219,15 +278,35 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
const SizedBox( const SizedBox(
height: 2, height: 2,
), ),
Text( FutureBuilder(
"Total wallet balance", future: totalBalanceFuture,
style: builder: (fbContext,
STextStyles.itemSubtitle12(context).copyWith( AsyncSnapshot<Decimal> snapshot) {
color: Theme.of(context) if (snapshot.connectionState ==
.extension<StackColors>()! ConnectionState.done &&
.textSubtitle1, snapshot.hasData &&
), snapshot.data != null) {
), return Text(
"${snapshot.data!}",
style: STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
);
} else {
return Text(
"",
style: STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
);
}
}),
], ],
), ),
if (coin == Coin.firo || coin == Coin.firoTestNet) if (coin == Coin.firo || coin == Coin.firoTestNet)
@ -241,15 +320,35 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
const SizedBox( const SizedBox(
height: 2, height: 2,
), ),
Text( FutureBuilder(
"Current public spendable (unlocked) balance", future: totalBalanceFuture,
style: builder: (fbContext,
STextStyles.itemSubtitle12(context).copyWith( AsyncSnapshot<Decimal> snapshot) {
color: Theme.of(context) if (snapshot.connectionState ==
.extension<StackColors>()! ConnectionState.done &&
.textSubtitle1, snapshot.hasData &&
), snapshot.data != null) {
), return Text(
"${snapshot.data!}",
style: STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
);
} else {
return Text(
"",
style: STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
);
}
}),
], ],
), ),
], ],

View file

@ -70,6 +70,8 @@ class _WalletSummaryInfoState extends State<WalletSummaryInfo> {
builder: (_, ref, __) { builder: (_, ref, __) {
final Coin coin = final Coin coin =
ref.watch(managerProvider.select((value) => value.coin)); ref.watch(managerProvider.select((value) => value.coin));
final externalCalls = ref.watch(prefsChangeNotifierProvider
.select((value) => value.externalCalls));
Future<Decimal>? totalBalanceFuture; Future<Decimal>? totalBalanceFuture;
Future<Decimal>? availableBalanceFuture; Future<Decimal>? availableBalanceFuture;
@ -176,18 +178,19 @@ class _WalletSummaryInfoState extends State<WalletSummaryInfo> {
), ),
), ),
), ),
Text( if (externalCalls)
"${Format.localizedStringAsFixed( Text(
value: priceTuple.item1 * balanceToShow, "${Format.localizedStringAsFixed(
locale: locale, value: priceTuple.item1 * balanceToShow,
decimalPlaces: 2, locale: locale,
)} $baseCurrency", decimalPlaces: 2,
style: STextStyles.subtitle500(context).copyWith( )} $baseCurrency",
color: Theme.of(context) style: STextStyles.subtitle500(context).copyWith(
.extension<StackColors>()! color: Theme.of(context)
.textFavoriteCard, .extension<StackColors>()!
.textFavoriteCard,
),
), ),
),
], ],
); );
} else { } else {

View file

@ -255,17 +255,19 @@ class _TransactionDetailsViewState
const SizedBox( const SizedBox(
height: 2, height: 2,
), ),
SelectableText( if (ref.watch(prefsChangeNotifierProvider
"${Format.localizedStringAsFixed(value: (coin == Coin.monero ? (amount / 10000.toDecimal()).toDecimal() : coin == Coin.wownero ? (amount / 1000.toDecimal()).toDecimal() : amount) * ref.watch(priceAnd24hChangeNotifierProvider.select((value) => value.getPrice(coin).item1)), locale: ref.watch( .select((value) => value.externalCalls)))
localeServiceChangeNotifierProvider SelectableText(
.select((value) => value.locale), "${Format.localizedStringAsFixed(value: (coin == Coin.monero ? (amount / 10000.toDecimal()).toDecimal() : coin == Coin.wownero ? (amount / 1000.toDecimal()).toDecimal() : amount) * ref.watch(priceAnd24hChangeNotifierProvider.select((value) => value.getPrice(coin).item1)), locale: ref.watch(
), decimalPlaces: 2)} ${ref.watch( localeServiceChangeNotifierProvider
prefsChangeNotifierProvider.select( .select((value) => value.locale),
(value) => value.currency, ), decimalPlaces: 2)} ${ref.watch(
), prefsChangeNotifierProvider.select(
)}", (value) => value.currency,
style: STextStyles.itemSubtitle(context), ),
), )}",
style: STextStyles.itemSubtitle(context),
),
], ],
), ),
TxIcon( TxIcon(

View file

@ -37,6 +37,7 @@ import 'package:stackwallet/utilities/enums/backup_frequency_type.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/flush_bar_type.dart'; import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart'; import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -45,12 +46,6 @@ import 'package:stackwallet/widgets/custom_loading_overlay.dart';
import 'package:stackwallet/widgets/stack_dialog.dart'; import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:tuple/tuple.dart'; import 'package:tuple/tuple.dart';
import 'package:stackwallet/hive/db.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
/// [eventBus] should only be set during testing /// [eventBus] should only be set during testing
class WalletView extends ConsumerStatefulWidget { class WalletView extends ConsumerStatefulWidget {
const WalletView({ const WalletView({
@ -235,12 +230,8 @@ class _WalletViewState extends ConsumerState<WalletView> {
} }
void _onExchangePressed(BuildContext context) async { void _onExchangePressed(BuildContext context) async {
final _cnLoadingService = ExchangeDataLoadingService(); unawaited(_cnLoadingService.loadAll(ref));
final externalCalls = Prefs.instance.externalCalls;
if (!externalCalls) {
print("loading?");
unawaited(_cnLoadingService.loadAll(ref));
}
final coin = ref.read(managerProvider).coin; final coin = ref.read(managerProvider).coin;
if (coin == Coin.epicCash) { if (coin == Coin.epicCash) {
@ -371,9 +362,7 @@ class _WalletViewState extends ConsumerState<WalletView> {
void _loadCNData() { void _loadCNData() {
// unawaited future // unawaited future
final externalCalls = DB.instance if (ref.read(prefsChangeNotifierProvider).externalCalls) {
.get<dynamic>(boxName: DB.boxNamePrefs, key: "externalCalls") as bool?;
if (externalCalls ?? false) {
_cnLoadingService.loadAll(ref, coin: ref.read(managerProvider).coin); _cnLoadingService.loadAll(ref, coin: ref.read(managerProvider).coin);
} else { } else {
Logging.instance.log("User does not want to use external calls", Logging.instance.log("User does not want to use external calls",

View file

@ -49,6 +49,8 @@ class _FavoriteCardState extends ConsumerState<FavoriteCard> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final coin = ref.watch(managerProvider.select((value) => value.coin)); final coin = ref.watch(managerProvider.select((value) => value.coin));
final externalCalls = ref.watch(
prefsChangeNotifierProvider.select((value) => value.externalCalls));
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
@ -70,7 +72,9 @@ class _FavoriteCardState extends ConsumerState<FavoriteCard> {
width: widget.width, width: widget.width,
height: widget.height, height: widget.height,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).extension<StackColors>()!.colorForCoin(coin), color: Theme.of(context)
.extension<StackColors>()!
.colorForCoin(coin),
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius, Constants.size.circularBorderRadius,
), ),
@ -138,7 +142,9 @@ class _FavoriteCardState extends ConsumerState<FavoriteCard> {
ref.watch(managerProvider ref.watch(managerProvider
.select((value) => value.walletName)), .select((value) => value.walletName)),
style: STextStyles.itemSubtitle12(context).copyWith( style: STextStyles.itemSubtitle12(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textFavoriteCard, color: Theme.of(context)
.extension<StackColors>()!
.textFavoriteCard,
), ),
overflow: TextOverflow.fade, overflow: TextOverflow.fade,
), ),
@ -159,14 +165,16 @@ class _FavoriteCardState extends ConsumerState<FavoriteCard> {
snapshot.hasData) { snapshot.hasData) {
if (snapshot.data != null) { if (snapshot.data != null) {
_cachedBalance = snapshot.data!; _cachedBalance = snapshot.data!;
_cachedFiatValue = _cachedBalance * if (externalCalls) {
ref _cachedFiatValue = _cachedBalance *
.watch( ref
priceAnd24hChangeNotifierProvider.select( .watch(
(value) => value.getPrice(coin), priceAnd24hChangeNotifierProvider.select(
), (value) => value.getPrice(coin),
) ),
.item1; )
.item1;
}
} }
} }
return Column( return Column(
@ -185,30 +193,36 @@ class _FavoriteCardState extends ConsumerState<FavoriteCard> {
)} ${coin.ticker}", )} ${coin.ticker}",
style: STextStyles.titleBold12(context).copyWith( style: STextStyles.titleBold12(context).copyWith(
fontSize: 16, fontSize: 16,
color: Theme.of(context).extension<StackColors>()!.textFavoriteCard, color: Theme.of(context)
.extension<StackColors>()!
.textFavoriteCard,
), ),
), ),
), ),
const SizedBox( if (externalCalls)
height: 4, const SizedBox(
), height: 4,
Text( ),
"${Format.localizedStringAsFixed( if (externalCalls)
decimalPlaces: 2, Text(
value: _cachedFiatValue, "${Format.localizedStringAsFixed(
locale: ref.watch( decimalPlaces: 2,
localeServiceChangeNotifierProvider value: _cachedFiatValue,
.select((value) => value.locale), locale: ref.watch(
), localeServiceChangeNotifierProvider
)} ${ref.watch( .select((value) => value.locale),
prefsChangeNotifierProvider ),
.select((value) => value.currency), )} ${ref.watch(
)}", prefsChangeNotifierProvider
style: STextStyles.itemSubtitle12(context).copyWith( .select((value) => value.currency),
fontSize: 10, )}",
color: Theme.of(context).extension<StackColors>()!.textFavoriteCard, style: STextStyles.itemSubtitle12(context).copyWith(
fontSize: 10,
color: Theme.of(context)
.extension<StackColors>()!
.textFavoriteCard,
),
), ),
),
], ],
); );
}, },

View file

@ -7,6 +7,7 @@ import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart'; import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart';
@ -67,6 +68,8 @@ class WalletListItem extends ConsumerWidget {
builder: (_, ref, __) { builder: (_, ref, __) {
final tuple = ref.watch(priceAnd24hChangeNotifierProvider final tuple = ref.watch(priceAnd24hChangeNotifierProvider
.select((value) => value.getPrice(coin))); .select((value) => value.getPrice(coin)));
final calls =
ref.watch(prefsChangeNotifierProvider).externalCalls;
final priceString = Format.localizedStringAsFixed( final priceString = Format.localizedStringAsFixed(
value: tuple.item1, value: tuple.item1,
@ -100,10 +103,11 @@ class WalletListItem extends ConsumerWidget {
style: STextStyles.titleBold12(context), style: STextStyles.titleBold12(context),
), ),
const Spacer(), const Spacer(),
Text( if (calls)
"$priceString $currency/${coin.ticker}", Text(
style: STextStyles.itemSubtitle(context), "$priceString $currency/${coin.ticker}",
), style: STextStyles.itemSubtitle(context),
),
], ],
), ),
const SizedBox( const SizedBox(
@ -116,12 +120,13 @@ class WalletListItem extends ConsumerWidget {
style: STextStyles.itemSubtitle(context), style: STextStyles.itemSubtitle(context),
), ),
const Spacer(), const Spacer(),
Text( if (calls)
"${percentChange.toStringAsFixed(2)}%", Text(
style: STextStyles.itemSubtitle(context).copyWith( "${percentChange.toStringAsFixed(2)}%",
color: percentChangedColor, style: STextStyles.itemSubtitle(context).copyWith(
color: percentChangedColor,
),
), ),
),
], ],
), ),
], ],

View file

@ -254,7 +254,7 @@ Future<Map<String, dynamic>> isolateRestore(
} }
final root = getBip32Root(mnemonic, network); final root = getBip32Root(mnemonic, network);
while (currentIndex < lastFoundIndex + 20) { while (currentIndex < lastFoundIndex + 50) {
final mintKeyPair = getBip32NodeFromRoot(MINT_INDEX, currentIndex, root); final mintKeyPair = getBip32NodeFromRoot(MINT_INDEX, currentIndex, root);
final mintTag = CreateTag( final mintTag = CreateTag(
Format.uint8listToString(mintKeyPair.privateKey!), Format.uint8listToString(mintKeyPair.privateKey!),
@ -753,10 +753,11 @@ Future<Map<String, dynamic>?> getInitialAnonymitySetCache(
); );
final response = jsonDecode(anonSetResult.body.toString()); final response = jsonDecode(anonSetResult.body.toString());
Logging.instance.log(response, level: LogLevel.Info);
if (response['status'] == 'success') { if (response['status'] == 'success') {
final anonResponse = jsonDecode(response['result'] as String); final anonResponse = jsonDecode(response['result'] as String);
final setData = Map<String, dynamic>.from(anonResponse["result"] as Map); final setData = Map<String, dynamic>.from(anonResponse as Map);
return setData; return setData;
} else { } else {
return null; return null;
@ -1355,7 +1356,7 @@ class FiroWallet extends CoinServiceAPI {
List<UtxoObject> utxoObjectsToUse = []; List<UtxoObject> utxoObjectsToUse = [];
for (var i = 0; for (var i = 0;
satoshisBeingUsed < satoshiAmountToSend && i < spendableOutputs.length; satoshisBeingUsed <= satoshiAmountToSend && i < spendableOutputs.length;
i++) { i++) {
utxoObjectsToUse.add(spendableOutputs[i]); utxoObjectsToUse.add(spendableOutputs[i]);
satoshisBeingUsed += spendableOutputs[i].value; satoshisBeingUsed += spendableOutputs[i].value;
@ -2040,7 +2041,8 @@ class FiroWallet extends CoinServiceAPI {
case "Sent": case "Sent":
unawaited( unawaited(
NotificationApi.showNotification( NotificationApi.showNotification(
title: "Outgoing transaction", title:
tx.subType == "mint" ? "Anonymizing" : "Outgoing transaction",
body: walletName, body: walletName,
walletId: walletId, walletId: walletId,
iconAssetName: Assets.svg.iconFor(coin: coin), iconAssetName: Assets.svg.iconFor(coin: coin),
@ -2076,7 +2078,9 @@ class FiroWallet extends CoinServiceAPI {
} else if (tx.txType == "Sent" && tx.subType == "join") { } else if (tx.txType == "Sent" && tx.subType == "join") {
unawaited( unawaited(
NotificationApi.showNotification( NotificationApi.showNotification(
title: "Outgoing transaction confirmed", title: tx.subType == "mint"
? "Anonymized"
: "Outgoing transaction confirmed",
body: walletName, body: walletName,
walletId: walletId, walletId: walletId,
iconAssetName: Assets.svg.iconFor(coin: coin), iconAssetName: Assets.svg.iconFor(coin: coin),
@ -4493,6 +4497,7 @@ class FiroWallet extends CoinServiceAPI {
} }
} }
// TODO: investigate the bug here where chosen is null, conditions, given one mint
spendVal += chosen!.amount; spendVal += chosen!.amount;
coinsToSpend.insert(coinsToSpend.length, chosen); coinsToSpend.insert(coinsToSpend.length, chosen);
} }
@ -4514,36 +4519,61 @@ class FiroWallet extends CoinServiceAPI {
Future<int> estimateJoinSplitFee( Future<int> estimateJoinSplitFee(
int spendAmount, int spendAmount,
) async { ) async {
int fee; var lelantusEntry = await _getLelantusEntry();
int size; final balance = await availableBalance;
int spendAmount =
for (fee = 0;;) { (balance * Decimal.fromInt(Constants.satsPerCoin)).toBigInt().toInt();
int currentRequired = spendAmount; if (spendAmount == 0 || lelantusEntry.isEmpty) {
return LelantusFeeData(0, 0, []).fee;
var map = await getCoinsToJoinSplit(currentRequired);
if (map is bool && !map) {
return 0;
}
List<DartLelantusEntry> coinsToBeSpent =
map['coinsToSpend'] as List<DartLelantusEntry>;
// 1054 is constant part, mainly Schnorr and Range proofs, 2560 is for each sigma/aux data
// 179 other parts of tx, assuming 1 utxo and 1 jmint
size = 1054 + 2560 * coinsToBeSpent.length + 180;
// uint64_t feeNeeded = GetMinimumFee(size, DEFAULT_TX_CONFIRM_TARGET);
int feeNeeded =
size; //TODO(Levon) temporary, use real estimation methods here
if (fee >= feeNeeded) {
break;
}
fee = feeNeeded;
} }
ReceivePort receivePort = await getIsolate({
"function": "estimateJoinSplit",
"spendAmount": spendAmount,
"subtractFeeFromAmount": true,
"lelantusEntries": lelantusEntry,
"coin": coin,
});
return fee; final message = await receivePort.first;
if (message is String) {
Logging.instance.log("this is a string", level: LogLevel.Error);
stop(receivePort);
throw Exception("_fetchMaxFee isolate failed");
}
stop(receivePort);
Logging.instance.log('Closing estimateJoinSplit!', level: LogLevel.Info);
return (message as LelantusFeeData).fee;
} }
// int fee;
// int size;
//
// for (fee = 0;;) {
// int currentRequired = spendAmount;
//
// TODO: investigate the bug here
// var map = await getCoinsToJoinSplit(currentRequired);
// if (map is bool && !map) {
// return 0;
// }
//
// List<DartLelantusEntry> coinsToBeSpent =
// map['coinsToSpend'] as List<DartLelantusEntry>;
//
// // 1054 is constant part, mainly Schnorr and Range proofs, 2560 is for each sigma/aux data
// // 179 other parts of tx, assuming 1 utxo and 1 jmint
// size = 1054 + 2560 * coinsToBeSpent.length + 180;
// // uint64_t feeNeeded = GetMinimumFee(size, DEFAULT_TX_CONFIRM_TARGET);
// int feeNeeded =
// size; //TODO(Levon) temporary, use real estimation methods here
//
// if (fee >= feeNeeded) {
// break;
// }
//
// fee = feeNeeded;
// }
//
// return fee;
@override @override
Future<int> estimateFeeFor(int satoshiAmount, int feeRate) async { Future<int> estimateFeeFor(int satoshiAmount, int feeRate) async {

View file

@ -1558,7 +1558,7 @@ class WowneroWallet extends CoinServiceAPI {
"WW3iVcnoAY6K9zNdU4qmdvZELefx6xZz4PMpTwUifRkvMQckyadhSPYMVPJhBdYE8P9c27fg9RPmVaWNFx1cDaj61HnetqBiy", "WW3iVcnoAY6K9zNdU4qmdvZELefx6xZz4PMpTwUifRkvMQckyadhSPYMVPJhBdYE8P9c27fg9RPmVaWNFx1cDaj61HnetqBiy",
satoshiAmount: satoshiAmount, satoshiAmount: satoshiAmount,
args: {"feeRate": feeRateType}))['fee']; args: {"feeRate": feeRateType}))['fee'];
await Future.delayed(const Duration(milliseconds: 100)); await Future.delayed(const Duration(milliseconds: 500));
} catch (e, s) { } catch (e, s) {
aprox = -9999999999999999; aprox = -9999999999999999;
} }

View file

@ -32,17 +32,22 @@ class SimpleSwapAPI {
Future<dynamic> _makeGetRequest(Uri uri) async { Future<dynamic> _makeGetRequest(Uri uri) async {
final client = this.client ?? http.Client(); final client = this.client ?? http.Client();
int code = -1;
try { try {
final response = await client.get( final response = await client.get(
uri, uri,
); );
code = response.statusCode;
final parsed = jsonDecode(response.body); final parsed = jsonDecode(response.body);
return parsed; return parsed;
} catch (e, s) { } catch (e, s) {
Logging.instance Logging.instance.log(
.log("_makeRequest($uri) threw: $e\n$s", level: LogLevel.Error); "_makeRequest($uri) HTTP:$code threw: $e\n$s",
level: LogLevel.Error,
);
rethrow; rethrow;
} }
} }

View file

@ -84,7 +84,9 @@ class NotificationsService extends ChangeNotifier {
_timer = Timer.periodic(notificationRefreshInterval, (_) { _timer = Timer.periodic(notificationRefreshInterval, (_) {
Logging.instance Logging.instance
.log("Periodic notifications update check", level: LogLevel.Info); .log("Periodic notifications update check", level: LogLevel.Info);
_checkTrades(); if (prefs.externalCalls) {
_checkTrades();
}
_checkTransactions(); _checkTransactions();
}); });
} }

View file

@ -7,9 +7,8 @@ import 'package:http/http.dart';
import 'package:stackwallet/hive/db.dart'; import 'package:stackwallet/hive/db.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/utilities/logger.dart';
import 'package:tuple/tuple.dart';
import 'package:stackwallet/utilities/prefs.dart'; import 'package:stackwallet/utilities/prefs.dart';
import 'package:tuple/tuple.dart';
class PriceAPI { class PriceAPI {
static const refreshInterval = 60; static const refreshInterval = 60;

View file

@ -19,9 +19,7 @@ class PriceService extends ChangeNotifier {
Tuple2<Decimal, double> getPrice(Coin coin) => _cachedPrices[coin]!; Tuple2<Decimal, double> getPrice(Coin coin) => _cachedPrices[coin]!;
PriceService(this.baseTicker) { PriceService(this.baseTicker);
start(true);
}
Future<void> updatePrice() async { Future<void> updatePrice() async {
final priceMap = final priceMap =

View file

@ -159,8 +159,8 @@ abstract class DefaultNodes {
); );
static NodeModel get bitcoincashTestnet => NodeModel( static NodeModel get bitcoincashTestnet => NodeModel(
host: "testnet.hsmiths.com", host: "bitcoincash-testnet.stackwallet.com",
port: 53012, port: 60002,
name: defaultName, name: defaultName,
id: _nodeId(Coin.bitcoincashTestnet), id: _nodeId(Coin.bitcoincashTestnet),
useSSL: true, useSSL: true,

View file

@ -12,8 +12,7 @@ import 'package:stackwallet/utilities/enums/log_level_enum.dart';
export 'enums/log_level_enum.dart'; export 'enums/log_level_enum.dart';
class Logging { class Logging {
static const isArmLinux = static const isArmLinux = bool.fromEnvironment("IS_ARM");
bool.fromEnvironment("IS_ARM");
static final isTestEnv = Platform.environment["FLUTTER_TEST"] == "true"; static final isTestEnv = Platform.environment["FLUTTER_TEST"] == "true";
Logging._(); Logging._();
static final Logging _instance = Logging._(); static final Logging _instance = Logging._();
@ -45,41 +44,47 @@ class Logging {
core.bool printToConsole = true, core.bool printToConsole = true,
core.bool printFullLength = false, core.bool printFullLength = false,
}) { }) {
if (isTestEnv || isArmLinux) { try {
Logger.print(object, normalLength: !printFullLength); if (isTestEnv || isArmLinux) {
return; Logger.print(object, normalLength: !printFullLength);
} return;
final now = core.DateTime.now().toUtc(); }
final log = Log() final now = core.DateTime.now().toUtc();
..message = object.toString() final log = Log()
..logLevel = level ..message = object.toString()
..timestampInMillisUTC = now.millisecondsSinceEpoch; ..logLevel = level
if (level == LogLevel.Error || level == LogLevel.Fatal) { ..timestampInMillisUTC = now.millisecondsSinceEpoch;
printFullLength = true; if (level == LogLevel.Error || level == LogLevel.Fatal) {
} printFullLength = true;
}
isar!.writeTxnSync(() => log.id = isar!.logs.putSync(log)); isar!.writeTxnSync(() => log.id = isar!.logs.putSync(log));
if (printToConsole) { if (printToConsole) {
final core.String logStr = "Log: ${log.toString()}"; final core.String logStr = "Log: ${log.toString()}";
final core.int logLength = logStr.length; final core.int logLength = logStr.length;
if (!printFullLength || logLength <= defaultPrintLength) { if (!printFullLength || logLength <= defaultPrintLength) {
debugPrint(logStr); debugPrint(logStr);
} else { } else {
core.int start = 0; core.int start = 0;
core.int endIndex = defaultPrintLength; core.int endIndex = defaultPrintLength;
core.int tmpLogLength = logLength; core.int tmpLogLength = logLength;
while (endIndex < logLength) { while (endIndex < logLength) {
debugPrint(logStr.substring(start, endIndex)); debugPrint(logStr.substring(start, endIndex));
endIndex += defaultPrintLength; endIndex += defaultPrintLength;
start += defaultPrintLength; start += defaultPrintLength;
tmpLogLength -= defaultPrintLength; tmpLogLength -= defaultPrintLength;
} }
if (tmpLogLength > 0) { if (tmpLogLength > 0) {
debugPrint(logStr.substring(start, logLength)); debugPrint(logStr.substring(start, logLength));
}
} }
} }
} catch (e, s) {
print("problem trying to log");
print("$e $s");
Logger.print(object);
} }
} }
} }

View file

@ -546,7 +546,9 @@ class Prefs extends ChangeNotifier {
boxName: DB.boxNamePrefs, key: "startupWalletId") as String?; boxName: DB.boxNamePrefs, key: "startupWalletId") as String?;
} }
bool _externalCalls = false; // incognito mode off by default
// allow external network calls such as exchange data and price info
bool _externalCalls = true;
bool get externalCalls => _externalCalls; bool get externalCalls => _externalCalls;
@ -567,6 +569,6 @@ class Prefs extends ChangeNotifier {
Future<bool> _getHasExternalCalls() async { Future<bool> _getHasExternalCalls() async {
return await DB.instance.get<dynamic>( return await DB.instance.get<dynamic>(
boxName: DB.boxNamePrefs, key: "externalCalls") as bool? ?? boxName: DB.boxNamePrefs, key: "externalCalls") as bool? ??
false; true;
} }
} }

View file

@ -202,35 +202,39 @@ class _TransactionCardState extends ConsumerState<TransactionCard> {
), ),
), ),
), ),
const SizedBox( if (ref.watch(prefsChangeNotifierProvider
width: 10, .select((value) => value.externalCalls)))
), const SizedBox(
Flexible( width: 10,
child: FittedBox( ),
fit: BoxFit.scaleDown, if (ref.watch(prefsChangeNotifierProvider
child: Builder( .select((value) => value.externalCalls)))
builder: (_) { Flexible(
// TODO: modify Format.<functions> to take optional Coin parameter so this type oif check isn't done in ui child: FittedBox(
int value = _transaction.amount; fit: BoxFit.scaleDown,
if (coin == Coin.monero) { child: Builder(
value = (value ~/ 10000); builder: (_) {
} else if (coin == Coin.wownero) { // TODO: modify Format.<functions> to take optional Coin parameter so this type oif check isn't done in ui
value = (value ~/ 1000); int value = _transaction.amount;
} if (coin == Coin.monero) {
value = (value ~/ 10000);
} else if (coin == Coin.wownero) {
value = (value ~/ 1000);
}
return Text( return Text(
"${Format.localizedStringAsFixed( "${Format.localizedStringAsFixed(
value: Format.satoshisToAmount(value) * value: Format.satoshisToAmount(value) *
price, price,
locale: locale, locale: locale,
decimalPlaces: 2, decimalPlaces: 2,
)} $baseCurrency", )} $baseCurrency",
style: STextStyles.label(context), style: STextStyles.label(context),
); );
}, },
),
), ),
), ),
),
], ],
), ),
], ],

101
macos/Podfile.lock Normal file
View file

@ -0,0 +1,101 @@
PODS:
- connectivity_plus_macos (0.0.1):
- FlutterMacOS
- ReachabilitySwift
- flutter_libepiccash (0.0.1):
- FlutterMacOS
- flutter_local_notifications (0.0.1):
- FlutterMacOS
- flutter_secure_storage_macos (3.3.1):
- FlutterMacOS
- FlutterMacOS (1.0.0)
- isar_flutter_libs (1.0.0):
- FlutterMacOS
- package_info_plus_macos (0.0.1):
- FlutterMacOS
- path_provider_macos (0.0.1):
- FlutterMacOS
- ReachabilitySwift (5.0.0)
- share_plus_macos (0.0.1):
- FlutterMacOS
- shared_preferences_macos (0.0.1):
- FlutterMacOS
- stack_wallet_backup (0.0.1):
- FlutterMacOS
- url_launcher_macos (0.0.1):
- FlutterMacOS
- wakelock_macos (0.0.1):
- FlutterMacOS
- window_size (0.0.2):
- FlutterMacOS
DEPENDENCIES:
- connectivity_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus_macos/macos`)
- flutter_libepiccash (from `Flutter/ephemeral/.symlinks/plugins/flutter_libepiccash/macos`)
- flutter_local_notifications (from `Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos`)
- flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`)
- FlutterMacOS (from `Flutter/ephemeral`)
- isar_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/isar_flutter_libs/macos`)
- package_info_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos`)
- path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`)
- share_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos`)
- shared_preferences_macos (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos`)
- stack_wallet_backup (from `Flutter/ephemeral/.symlinks/plugins/stack_wallet_backup/macos`)
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
- wakelock_macos (from `Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos`)
- window_size (from `Flutter/ephemeral/.symlinks/plugins/window_size/macos`)
SPEC REPOS:
trunk:
- ReachabilitySwift
EXTERNAL SOURCES:
connectivity_plus_macos:
:path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus_macos/macos
flutter_libepiccash:
:path: Flutter/ephemeral/.symlinks/plugins/flutter_libepiccash/macos
flutter_local_notifications:
:path: Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos
flutter_secure_storage_macos:
:path: Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos
FlutterMacOS:
:path: Flutter/ephemeral
isar_flutter_libs:
:path: Flutter/ephemeral/.symlinks/plugins/isar_flutter_libs/macos
package_info_plus_macos:
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos
path_provider_macos:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos
share_plus_macos:
:path: Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos
shared_preferences_macos:
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos
stack_wallet_backup:
:path: Flutter/ephemeral/.symlinks/plugins/stack_wallet_backup/macos
url_launcher_macos:
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
wakelock_macos:
:path: Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos
window_size:
:path: Flutter/ephemeral/.symlinks/plugins/window_size/macos
SPEC CHECKSUMS:
connectivity_plus_macos: f6e86fd000e971d361e54b5afcadc8c8fa773308
flutter_libepiccash: b33f7396504712b513b8ff019a3f6f3bdae54cfb
flutter_local_notifications: 3805ca215b2fb7f397d78b66db91f6a747af52e4
flutter_secure_storage_macos: 6ceee8fbc7f484553ad17f79361b556259df89aa
FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811
isar_flutter_libs: 1948109973b6c2e46d6196b1537688a36a6edeac
package_info_plus_macos: f010621b07802a241d96d01876d6705f15e77c1c
path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4
shared_preferences_macos: a64dc611287ed6cbe28fd1297898db1336975727
stack_wallet_backup: 6ebc60b1bdcf11cf1f1cbad9aa78332e1e15778c
url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3
wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9
window_size: 339dafa0b27a95a62a843042038fa6c3c48de195
PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c
COCOAPODS: 1.11.3

View file

@ -26,6 +26,7 @@
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
36299DF6FDF6725B2B9C51D5 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6BB87EF657A3ADFB1CE3E959 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@ -54,7 +55,7 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
33CC10ED2044A3C60003C045 /* firo_wallet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "firo_wallet.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 33CC10ED2044A3C60003C045 /* firo_wallet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = firo_wallet.app; sourceTree = BUILT_PRODUCTS_DIR; };
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; }; 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; }; 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
@ -66,8 +67,12 @@
33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; }; 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; }; 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
6BB87EF657A3ADFB1CE3E959 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
937DF254AD7EDA15AFE96BD9 /* 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 = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
BC4589C48A71C3A1A477DD76 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
EA2D897BC13EBFB1DE697D5C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@ -75,6 +80,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
36299DF6FDF6725B2B9C51D5 /* Pods_Runner.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -99,6 +105,7 @@
33CEB47122A05771004F2AC0 /* Flutter */, 33CEB47122A05771004F2AC0 /* Flutter */,
33CC10EE2044A3C60003C045 /* Products */, 33CC10EE2044A3C60003C045 /* Products */,
D73912EC22F37F3D000D13A0 /* Frameworks */, D73912EC22F37F3D000D13A0 /* Frameworks */,
9000119722579F22067B9BC0 /* Pods */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
@ -145,9 +152,21 @@
path = Runner; path = Runner;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
9000119722579F22067B9BC0 /* Pods */ = {
isa = PBXGroup;
children = (
EA2D897BC13EBFB1DE697D5C /* Pods-Runner.debug.xcconfig */,
937DF254AD7EDA15AFE96BD9 /* Pods-Runner.release.xcconfig */,
BC4589C48A71C3A1A477DD76 /* Pods-Runner.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
D73912EC22F37F3D000D13A0 /* Frameworks */ = { D73912EC22F37F3D000D13A0 /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
6BB87EF657A3ADFB1CE3E959 /* Pods_Runner.framework */,
); );
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
@ -159,11 +178,13 @@
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = ( buildPhases = (
DF80A3E256A63BF2D2008937 /* [CP] Check Pods Manifest.lock */,
33CC10E92044A3C60003C045 /* Sources */, 33CC10E92044A3C60003C045 /* Sources */,
33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EA2044A3C60003C045 /* Frameworks */,
33CC10EB2044A3C60003C045 /* Resources */, 33CC10EB2044A3C60003C045 /* Resources */,
33CC110E2044A8840003C045 /* Bundle Framework */, 33CC110E2044A8840003C045 /* Bundle Framework */,
3399D490228B24CF009A79C7 /* ShellScript */, 3399D490228B24CF009A79C7 /* ShellScript */,
8D7CC24E5AE846869656D4D1 /* [CP] Embed Pods Frameworks */,
); );
buildRules = ( buildRules = (
); );
@ -270,6 +291,45 @@
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
}; };
8D7CC24E5AE846869656D4D1 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
DF80A3E256A63BF2D2008937 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */

View file

@ -4,4 +4,7 @@
<FileRef <FileRef
location = "group:Runner.xcodeproj"> location = "group:Runner.xcodeproj">
</FileRef> </FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace> </Workspace>

View file

@ -42,7 +42,7 @@ packages:
name: archive name: archive
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.1.11" version: "3.3.0"
args: args:
dependency: transitive dependency: transitive
description: description:
@ -56,14 +56,14 @@ packages:
name: asn1lib name: asn1lib
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.1" version: "1.2.0"
async: async:
dependency: transitive dependency: transitive
description: description:
name: async name: async
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.8.2" version: "2.9.0"
barcode_scan2: barcode_scan2:
dependency: "direct main" dependency: "direct main"
description: description:
@ -141,7 +141,7 @@ packages:
name: build_config name: build_config
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.0" version: "1.1.1"
build_daemon: build_daemon:
dependency: transitive dependency: transitive
description: description:
@ -169,7 +169,7 @@ packages:
name: build_runner_core name: build_runner_core
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "7.2.4" version: "7.2.6"
built_collection: built_collection:
dependency: transitive dependency: transitive
description: description:
@ -190,14 +190,7 @@ packages:
name: characters name: characters
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.2.1"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.1"
checked_yaml: checked_yaml:
dependency: transitive dependency: transitive
description: description:
@ -211,7 +204,7 @@ packages:
name: clock name: clock
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.0" version: "1.1.1"
code_builder: code_builder:
dependency: transitive dependency: transitive
description: description:
@ -253,7 +246,7 @@ packages:
name: connectivity_plus_platform_interface name: connectivity_plus_platform_interface
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.1" version: "1.2.2"
connectivity_plus_web: connectivity_plus_web:
dependency: transitive dependency: transitive
description: description:
@ -281,7 +274,7 @@ packages:
name: coverage name: coverage
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.5.0"
cross_file: cross_file:
dependency: transitive dependency: transitive
description: description:
@ -421,7 +414,7 @@ packages:
name: fake_async name: fake_async
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.3.0" version: "1.3.1"
ffi: ffi:
dependency: "direct main" dependency: "direct main"
description: description:
@ -536,7 +529,7 @@ packages:
name: flutter_native_splash name: flutter_native_splash
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.2.9" version: "2.2.11"
flutter_plugin_android_lifecycle: flutter_plugin_android_lifecycle:
dependency: transitive dependency: transitive
description: description:
@ -719,14 +712,14 @@ packages:
name: http_parser name: http_parser
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.0.1" version: "4.0.2"
image: image:
dependency: transitive dependency: transitive
description: description:
name: image name: image
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.2.0" version: "3.2.2"
import_sorter: import_sorter:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -809,13 +802,6 @@ packages:
relative: true relative: true
source: path source: path
version: "0.0.1" version: "0.0.1"
lint:
dependency: transitive
description:
name: lint
url: "https://pub.dartlang.org"
source: hosted
version: "1.10.0"
lints: lints:
dependency: transitive dependency: transitive
description: description:
@ -843,28 +829,28 @@ packages:
name: lottie name: lottie
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.4.2" version: "1.4.3"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.12.11" version: "0.12.12"
material_color_utilities: material_color_utilities:
dependency: transitive dependency: transitive
description: description:
name: material_color_utilities name: material_color_utilities
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.1.4" version: "0.1.5"
meta: meta:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.7.0" version: "1.8.0"
mime: mime:
dependency: transitive dependency: transitive
description: description:
@ -976,7 +962,7 @@ packages:
name: path name: path
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.8.1" version: "1.8.2"
path_drawing: path_drawing:
dependency: transitive dependency: transitive
description: description:
@ -1053,14 +1039,14 @@ packages:
name: permission_handler_android name: permission_handler_android
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "10.1.0" version: "10.2.0"
permission_handler_apple: permission_handler_apple:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_apple name: permission_handler_apple
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "9.0.6" version: "9.0.7"
permission_handler_platform_interface: permission_handler_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -1074,7 +1060,7 @@ packages:
name: permission_handler_windows name: permission_handler_windows
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.1.1" version: "0.1.2"
petitparser: petitparser:
dependency: transitive dependency: transitive
description: description:
@ -1130,7 +1116,7 @@ packages:
name: pub_semver name: pub_semver
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.1" version: "2.1.2"
pubspec_parse: pubspec_parse:
dependency: transitive dependency: transitive
description: description:
@ -1242,7 +1228,7 @@ packages:
name: shared_preferences_android name: shared_preferences_android
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.13" version: "2.0.14"
shared_preferences_ios: shared_preferences_ios:
dependency: transitive dependency: transitive
description: description:
@ -1338,7 +1324,7 @@ packages:
name: source_map_stack_trace name: source_map_stack_trace
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0" version: "2.1.1"
source_maps: source_maps:
dependency: transitive dependency: transitive
description: description:
@ -1352,7 +1338,7 @@ packages:
name: source_span name: source_span
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.8.2" version: "1.9.0"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
@ -1396,49 +1382,49 @@ packages:
name: string_scanner name: string_scanner
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.0" version: "1.1.1"
sync_http: sync_http:
dependency: transitive dependency: transitive
description: description:
name: sync_http name: sync_http
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.3.0" version: "0.3.1"
term_glyph: term_glyph:
dependency: transitive dependency: transitive
description: description:
name: term_glyph name: term_glyph
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.2.1"
test: test:
dependency: transitive dependency: transitive
description: description:
name: test name: test
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.21.1" version: "1.21.4"
test_api: test_api:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.4.9" version: "0.4.12"
test_core: test_core:
dependency: transitive dependency: transitive
description: description:
name: test_core name: test_core
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.4.13" version: "0.4.16"
time: time:
dependency: transitive dependency: transitive
description: description:
name: time name: time
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.2" version: "2.1.3"
timezone: timezone:
dependency: transitive dependency: transitive
description: description:
@ -1480,7 +1466,7 @@ packages:
name: typed_data name: typed_data
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.3.0" version: "1.3.1"
universal_io: universal_io:
dependency: transitive dependency: transitive
description: description:
@ -1564,7 +1550,7 @@ packages:
name: vm_service name: vm_service
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "8.2.2" version: "9.0.0"
wakelock: wakelock:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1606,7 +1592,7 @@ packages:
name: watcher name: watcher
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.1" version: "1.0.2"
web_socket_channel: web_socket_channel:
dependency: transitive dependency: transitive
description: description:
@ -1634,7 +1620,7 @@ packages:
name: win32 name: win32
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.0" version: "3.0.1"
window_size: window_size:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1680,5 +1666,5 @@ packages:
source: hosted source: hosted
version: "1.0.0" version: "1.0.0"
sdks: sdks:
dart: ">=2.17.5 <3.0.0" dart: ">=2.18.0 <3.0.0"
flutter: ">=3.0.1" flutter: ">=3.3.0"

View file

@ -11,7 +11,7 @@ description: Stack Wallet
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.5.1+71 version: 1.5.8+78
environment: environment:
sdk: ">=2.17.0 <3.0.0" sdk: ">=2.17.0 <3.0.0"

2
scripts/prebuild.sh Normal file → Executable file
View file

@ -2,5 +2,5 @@
KEYS=../lib/external_api_keys.dart KEYS=../lib/external_api_keys.dart
if ! test -f "$KEYS"; then if ! test -f "$KEYS"; then
echo 'prebuild.sh: creating template lib/external_api_keys.dart file' echo 'prebuild.sh: creating template lib/external_api_keys.dart file'
echo 'const kChangeNowApiKey = "";' > $KEYS printf 'const kChangeNowApiKey = "";\nconst kSimpleSwapApiKey = "";' > $KEYS
fi fi

51
scripts/setup.sh Normal file
View file

@ -0,0 +1,51 @@
#!/bin/bash
sudo apt update && sudo apt upgrade -y && sudo apt dist-upgrade -y
mkdir "$HOME/development"
mkdir "$HOME/projects"
sudo apt install -y git build-essential curl
export DEVELOPMENT=$HOME/development
export PROJECTS=$HOME/projects
# setup flutter
sudo apt install -y unzip pkg-config clang cmake ninja-build libgtk-3-dev
cd $DEVELOPMENT
git clone https://github.com/flutter/flutter.git
cd flutter
git checkout 3.0.3
export FLUTTER_DIR=$(pwd)/bin
echo 'export PATH="$PATH:'${FLUTTER_DIR}'"' >> ~/.bashrc
source ~/.bashrc
flutter doctor
# setup stack_wallet github
cd $PROJECTS
git clone https://github.com/cypherstack/stack_wallet.git
cd stack_wallet
export STACK_WALLET=$(pwd)
git submodule update --init --recursive
# Create template lib/external_api_keys.dart file if it doesn't already exist
KEYS="$HOME/projects/stack_wallet/lib/external_api_keys.dart"
if ! test -f "$KEYS"; then
echo 'prebuild.sh: creating template lib/external_api_keys.dart file'
printf 'const kChangeNowApiKey = "";\nconst kSimpleSwapApiKey = "";' > $KEYS
fi
#install stack wallet dependencies
sudo apt-get install -y unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake openjdk-8-jre-headless libgit2-dev clang libncurses5-dev libncursesw5-dev zlib1g-dev llvm
sudo apt-get install -y debhelper libclang-dev cargo rustc opencl-headers libssl-dev ocl-icd-opencl-dev
sudo apt-get install -y unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake openjdk-8-jre-headless
sudo apt install -y libc6-dev-i386
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
cargo install cargo-ndk
rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android
# build stack wallet plugins
cd $STACK_WALLET
cd scripts/android
./build_all.sh

View file

@ -3283,9 +3283,9 @@ void main() {
"a5V5r6We6mNZzWJwGwEeRML3mEYLjvK39w", "a5V5r6We6mNZzWJwGwEeRML3mEYLjvK39w",
]); ]);
expect(await firo.maxFee, 1234); // ??? expect(await firo.maxFee, 0); // ???
expect(await firo.balanceMinusMaxFee, Decimal.parse("-0.00001234")); expect(await firo.balanceMinusMaxFee, Decimal.parse("0"));
}); });
test("wallet balance minus maxfee - wallet balance is not zero", () async { test("wallet balance minus maxfee - wallet balance is not zero", () async {

View file

@ -70,6 +70,7 @@ void main() {
when(wallet.coin.ticker).thenAnswer((_) => "FIRO"); when(wallet.coin.ticker).thenAnswer((_) => "FIRO");
when(mockLocaleService.locale).thenAnswer((_) => "en_US"); when(mockLocaleService.locale).thenAnswer((_) => "en_US");
when(mockPrefs.currency).thenAnswer((_) => "USD"); when(mockPrefs.currency).thenAnswer((_) => "USD");
when(mockPrefs.externalCalls).thenAnswer((_) => true);
when(mockPriceService.getPrice(Coin.firo)) when(mockPriceService.getPrice(Coin.firo))
.thenAnswer((realInvocation) => Tuple2(Decimal.ten, 0.00)); .thenAnswer((realInvocation) => Tuple2(Decimal.ten, 0.00));
@ -162,6 +163,7 @@ void main() {
when(wallet.coin.ticker).thenAnswer((_) => "FIRO"); when(wallet.coin.ticker).thenAnswer((_) => "FIRO");
when(mockLocaleService.locale).thenAnswer((_) => "en_US"); when(mockLocaleService.locale).thenAnswer((_) => "en_US");
when(mockPrefs.currency).thenAnswer((_) => "USD"); when(mockPrefs.currency).thenAnswer((_) => "USD");
when(mockPrefs.externalCalls).thenAnswer((_) => true);
when(mockPriceService.getPrice(Coin.firo)) when(mockPriceService.getPrice(Coin.firo))
.thenAnswer((realInvocation) => Tuple2(Decimal.ten, 0.00)); .thenAnswer((realInvocation) => Tuple2(Decimal.ten, 0.00));
@ -245,6 +247,7 @@ void main() {
when(wallet.coin.ticker).thenAnswer((_) => "FIRO"); when(wallet.coin.ticker).thenAnswer((_) => "FIRO");
when(mockLocaleService.locale).thenAnswer((_) => "en_US"); when(mockLocaleService.locale).thenAnswer((_) => "en_US");
when(mockPrefs.currency).thenAnswer((_) => "USD"); when(mockPrefs.currency).thenAnswer((_) => "USD");
when(mockPrefs.externalCalls).thenAnswer((_) => true);
when(mockPriceService.getPrice(Coin.firo)) when(mockPriceService.getPrice(Coin.firo))
.thenAnswer((realInvocation) => Tuple2(Decimal.ten, 0.00)); .thenAnswer((realInvocation) => Tuple2(Decimal.ten, 0.00));