mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-11-16 17:27:39 +00:00
Merge branch 'staging' into widget-tests
This commit is contained in:
commit
ba0eb9636d
36 changed files with 1024 additions and 695 deletions
41
README.md
41
README.md
|
@ -17,15 +17,22 @@ Highlights include:
|
|||
- Custom Nodes.
|
||||
- Open source software.
|
||||
|
||||
## Build and run
|
||||
## Building
|
||||
### Prerequisites
|
||||
- The only OS supported for building is Ubuntu 20.04
|
||||
- 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)
|
||||
- Dart SDK Requirement (>=2.17.0, up until <3.0.0)
|
||||
- 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
|
||||
cd stack_wallet
|
||||
|
@ -57,7 +64,6 @@ cd scripts
|
|||
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
|
||||
```
|
||||
sudo apt list --installed | grep boost
|
||||
|
@ -76,7 +82,7 @@ cd scripts/android/
|
|||
cd ../..
|
||||
```
|
||||
|
||||
Building plugins for testing on Linux
|
||||
Building plugins for Linux
|
||||
|
||||
```
|
||||
cd scripts/linux/
|
||||
|
@ -85,10 +91,33 @@ cd scripts/linux/
|
|||
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 run
|
||||
flutter run android
|
||||
```
|
||||
|
||||
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
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 50;
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
|
@ -451,7 +451,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = 63;
|
||||
CURRENT_PROJECT_VERSION = 75;
|
||||
DEVELOPMENT_TEAM = 4DQKUWSG6C;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
|
@ -505,7 +505,7 @@
|
|||
"$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**",
|
||||
"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs",
|
||||
);
|
||||
MARKETING_VERSION = 1.4.48;
|
||||
MARKETING_VERSION = 1.5.5;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -635,7 +635,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = 63;
|
||||
CURRENT_PROJECT_VERSION = 75;
|
||||
DEVELOPMENT_TEAM = 4DQKUWSG6C;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
|
@ -689,7 +689,7 @@
|
|||
"$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**",
|
||||
"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs",
|
||||
);
|
||||
MARKETING_VERSION = 1.4.48;
|
||||
MARKETING_VERSION = 1.5.5;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -711,7 +711,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = 63;
|
||||
CURRENT_PROJECT_VERSION = 75;
|
||||
DEVELOPMENT_TEAM = 4DQKUWSG6C;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
|
@ -765,7 +765,7 @@
|
|||
"$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**",
|
||||
"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs",
|
||||
);
|
||||
MARKETING_VERSION = 1.4.48;
|
||||
MARKETING_VERSION = 1.5.5;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
|
|
@ -207,6 +207,7 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
didLoad = true;
|
||||
|
||||
await DB.instance.init();
|
||||
await _prefs.init();
|
||||
|
||||
_notificationsService = ref.read(notificationsProvider);
|
||||
_nodeService = ref.read(nodeServiceChangeNotifierProvider);
|
||||
|
@ -223,7 +224,6 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
tradesService: _tradesService,
|
||||
prefs: _prefs,
|
||||
);
|
||||
await _prefs.init();
|
||||
ref.read(priceAnd24hChangeNotifierProvider).start(true);
|
||||
await _wallets.load(_prefs);
|
||||
loadingCompleter.complete();
|
||||
|
@ -231,7 +231,7 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
// unawaited(_nodeService.updateCommunityNodes());
|
||||
|
||||
// run without awaiting
|
||||
if (Constants.enableExchange) {
|
||||
if (Constants.enableExchange && _prefs.externalCalls) {
|
||||
unawaited(ExchangeDataLoadingService().loadAll(ref));
|
||||
}
|
||||
|
||||
|
|
|
@ -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/fixed_rate_market.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/simpleswap/simpleswap_exchange.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
|
@ -41,12 +42,24 @@ class ExchangeFormState extends ChangeNotifier {
|
|||
Currency? _from;
|
||||
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 {
|
||||
switch (exchangeType) {
|
||||
case ExchangeRateType.estimated:
|
||||
return _from?.ticker;
|
||||
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:
|
||||
return _to?.ticker;
|
||||
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 {
|
||||
if (reversed) {
|
||||
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()}";
|
||||
} else if (maxAmount != null && toAmount! > maxAmount!) {
|
||||
return "Maximum amount ${maxAmount!.toString()} ${toTicker!.toUpperCase()}";
|
||||
|
@ -80,7 +102,9 @@ class ExchangeFormState extends ChangeNotifier {
|
|||
}
|
||||
} else {
|
||||
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()}";
|
||||
} else if (maxAmount != null && fromAmount! > maxAmount!) {
|
||||
return "Maximum amount ${maxAmount!.toString()} ${fromTicker!.toUpperCase()}";
|
||||
|
@ -95,18 +119,18 @@ class ExchangeFormState extends ChangeNotifier {
|
|||
String get toAmountString => toAmount?.toStringAsFixed(8) ?? "";
|
||||
|
||||
bool get canExchange {
|
||||
switch (exchangeType) {
|
||||
case ExchangeRateType.estimated:
|
||||
return fromAmount != null &&
|
||||
fromAmount != Decimal.zero &&
|
||||
toAmount != null &&
|
||||
rate != null &&
|
||||
warning.isEmpty;
|
||||
case ExchangeRateType.fixed:
|
||||
return _market != null &&
|
||||
fromAmount != null &&
|
||||
toAmount != null &&
|
||||
warning.isEmpty;
|
||||
if (exchange?.name == ChangeNowExchange.exchangeName &&
|
||||
exchangeType == ExchangeRateType.fixed) {
|
||||
return _market != null &&
|
||||
fromAmount != null &&
|
||||
toAmount != null &&
|
||||
warning.isEmpty;
|
||||
} else {
|
||||
return fromAmount != null &&
|
||||
fromAmount != Decimal.zero &&
|
||||
toAmount != null &&
|
||||
rate != null &&
|
||||
warning.isEmpty;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -375,24 +399,22 @@ class ExchangeFormState extends ChangeNotifier {
|
|||
minAmount = null;
|
||||
maxAmount = null;
|
||||
|
||||
switch (exchangeType) {
|
||||
case ExchangeRateType.estimated:
|
||||
final Currency? newTo = from;
|
||||
final Currency? newFrom = to;
|
||||
if (exchangeType == ExchangeRateType.fixed &&
|
||||
exchange?.name == ChangeNowExchange.exchangeName) {
|
||||
await updateMarket(market, false);
|
||||
} else {
|
||||
final Currency? newTo = from;
|
||||
final Currency? newFrom = to;
|
||||
|
||||
_to = newTo;
|
||||
_from = newFrom;
|
||||
_to = newTo;
|
||||
_from = newFrom;
|
||||
|
||||
await updateRanges(shouldNotifyListeners: false);
|
||||
await updateRanges(shouldNotifyListeners: false);
|
||||
|
||||
await updateEstimate(
|
||||
shouldNotifyListeners: false,
|
||||
reversed: reversed,
|
||||
);
|
||||
break;
|
||||
case ExchangeRateType.fixed:
|
||||
await updateMarket(market, false);
|
||||
break;
|
||||
await updateEstimate(
|
||||
shouldNotifyListeners: false,
|
||||
reversed: reversed,
|
||||
);
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
|
|
|
@ -275,7 +275,7 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
|
|||
// without using them
|
||||
await manager.recoverFromMnemonic(
|
||||
mnemonic: mnemonic,
|
||||
maxUnusedAddressGap: 20,
|
||||
maxUnusedAddressGap: widget.coin == Coin.firo ? 50 : 20,
|
||||
maxNumberOfIndexesToCheck: 1000,
|
||||
height: height,
|
||||
);
|
||||
|
|
|
@ -24,6 +24,7 @@ import 'package:stackwallet/services/exchange/simpleswap/simpleswap_exchange.dar
|
|||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.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/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
|
||||
|
@ -294,33 +295,29 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
|||
);
|
||||
|
||||
if (ref.read(prefsChangeNotifierProvider).exchangeRateType ==
|
||||
ExchangeRateType.estimated) {
|
||||
await ref.read(exchangeFormStateProvider).swap();
|
||||
} else {
|
||||
switch (ref.read(currentExchangeNameStateProvider.state).state) {
|
||||
case ChangeNowExchange.exchangeName:
|
||||
final from = ref.read(exchangeFormStateProvider).fromTicker;
|
||||
final to = ref.read(exchangeFormStateProvider).toTicker;
|
||||
ExchangeRateType.fixed &&
|
||||
ref.read(exchangeFormStateProvider).exchange?.name ==
|
||||
ChangeNowExchange.exchangeName) {
|
||||
final from = ref.read(exchangeFormStateProvider).fromTicker;
|
||||
final to = ref.read(exchangeFormStateProvider).toTicker;
|
||||
|
||||
if (to != null && from != null) {
|
||||
final markets = ref
|
||||
.read(availableChangeNowCurrenciesProvider)
|
||||
.markets
|
||||
.where((e) => e.from == to && e.to == from);
|
||||
if (to != null && from != null) {
|
||||
final markets = ref
|
||||
.read(availableChangeNowCurrenciesProvider)
|
||||
.markets
|
||||
.where((e) => e.from == to && e.to == from);
|
||||
|
||||
if (markets.isNotEmpty) {
|
||||
await ref
|
||||
.read(exchangeFormStateProvider)
|
||||
.swap(market: markets.first);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SimpleSwapExchange.exchangeName:
|
||||
await ref.read(exchangeFormStateProvider).swap();
|
||||
break;
|
||||
default:
|
||||
//
|
||||
if (markets.isNotEmpty) {
|
||||
await ref.read(exchangeFormStateProvider).swap(market: markets.first);
|
||||
} else {
|
||||
Logging.instance.log(
|
||||
"swap to fixed rate market failed",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await ref.read(exchangeFormStateProvider).swap();
|
||||
}
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
|
@ -575,14 +572,16 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
|||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message:
|
||||
"Estimated rate trade pair \"$fromTicker-$toTicker\" unavailable. Reverting to last estimated rate pair.",
|
||||
context: context,
|
||||
),
|
||||
);
|
||||
if (!(fromTicker == "-" || toTicker == "-")) {
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message:
|
||||
"Estimated rate trade pair \"$fromTicker-$toTicker\" unavailable. Reverting to last estimated rate pair.",
|
||||
context: context,
|
||||
),
|
||||
);
|
||||
}
|
||||
break;
|
||||
case ExchangeRateType.fixed:
|
||||
if (!(toTicker == "-" || fromTicker == "-")) {
|
||||
|
@ -1311,20 +1310,24 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
|||
RateTypeToggle(
|
||||
onChanged: onRateTypeChanged,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
ExchangeProviderOptions(
|
||||
from: ref.watch(exchangeFormStateProvider).fromTicker,
|
||||
to: ref.watch(exchangeFormStateProvider).toTicker,
|
||||
fromAmount: ref.watch(exchangeFormStateProvider).fromAmount,
|
||||
toAmount: ref.watch(exchangeFormStateProvider).toAmount,
|
||||
fixedRate: ref.watch(prefsChangeNotifierProvider
|
||||
.select((value) => value.exchangeRateType)) ==
|
||||
ExchangeRateType.fixed,
|
||||
reversed: ref.watch(
|
||||
exchangeFormStateProvider.select((value) => value.reversed)),
|
||||
),
|
||||
if (ref.read(exchangeFormStateProvider).fromAmount != null &&
|
||||
ref.read(exchangeFormStateProvider).fromAmount != Decimal.zero)
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (ref.read(exchangeFormStateProvider).fromAmount != null &&
|
||||
ref.read(exchangeFormStateProvider).fromAmount != Decimal.zero)
|
||||
ExchangeProviderOptions(
|
||||
from: ref.watch(exchangeFormStateProvider).fromTicker,
|
||||
to: ref.watch(exchangeFormStateProvider).toTicker,
|
||||
fromAmount: ref.watch(exchangeFormStateProvider).fromAmount,
|
||||
toAmount: ref.watch(exchangeFormStateProvider).toAmount,
|
||||
fixedRate: ref.watch(prefsChangeNotifierProvider
|
||||
.select((value) => value.exchangeRateType)) ==
|
||||
ExchangeRateType.fixed,
|
||||
reversed: ref.watch(
|
||||
exchangeFormStateProvider.select((value) => value.reversed)),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
|
|
|
@ -79,23 +79,25 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
transactionIfSentFromStack = widget.transactionIfSentFromStack;
|
||||
walletId = widget.walletId;
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||
final trade = ref
|
||||
.read(tradesServiceProvider)
|
||||
.trades
|
||||
.firstWhere((e) => e.tradeId == tradeId);
|
||||
if (ref.read(prefsChangeNotifierProvider).externalCalls) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||
final trade = ref
|
||||
.read(tradesServiceProvider)
|
||||
.trades
|
||||
.firstWhere((e) => e.tradeId == tradeId);
|
||||
|
||||
if (mounted) {
|
||||
final exchange = Exchange.fromName(trade.exchangeName);
|
||||
final response = await exchange.updateTrade(trade);
|
||||
if (mounted) {
|
||||
final exchange = Exchange.fromName(trade.exchangeName);
|
||||
final response = await exchange.updateTrade(trade);
|
||||
|
||||
if (mounted && response.value != null) {
|
||||
await ref
|
||||
.read(tradesServiceProvider)
|
||||
.edit(trade: response.value!, shouldNotifyListeners: true);
|
||||
if (mounted && response.value != null) {
|
||||
await ref
|
||||
.read(tradesServiceProvider)
|
||||
.edit(trade: response.value!, shouldNotifyListeners: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
|
|
@ -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/wallets_view/wallets_view.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/unread_notifications_provider.dart';
|
||||
import 'package:stackwallet/services/exchange/exchange_data_loading_service.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/stack_dialog.dart';
|
||||
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
|
||||
import 'package:stackwallet/utilities/prefs.dart';
|
||||
|
||||
class HomeView extends ConsumerStatefulWidget {
|
||||
const HomeView({Key? key}) : super(key: key);
|
||||
|
||||
|
@ -45,7 +43,7 @@ class _HomeViewState extends ConsumerState<HomeView> {
|
|||
|
||||
bool _exitEnabled = false;
|
||||
|
||||
final _cnLoadingService = ExchangeDataLoadingService();
|
||||
final _exchangeDataLoadingService = ExchangeDataLoadingService();
|
||||
|
||||
Future<bool> _onWillPop() async {
|
||||
// go to home view when tapping back on the main exchange view
|
||||
|
@ -85,10 +83,8 @@ class _HomeViewState extends ConsumerState<HomeView> {
|
|||
|
||||
void _loadCNData() {
|
||||
// unawaited future
|
||||
//
|
||||
final externalCalls = Prefs.instance.externalCalls;
|
||||
if (externalCalls) {
|
||||
_cnLoadingService.loadAll(ref);
|
||||
if (ref.read(prefsChangeNotifierProvider).externalCalls) {
|
||||
_exchangeDataLoadingService.loadAll(ref);
|
||||
} else {
|
||||
Logging.instance.log("User does not want to use external calls",
|
||||
level: LogLevel.Info);
|
||||
|
@ -290,6 +286,9 @@ class _HomeViewState extends ConsumerState<HomeView> {
|
|||
_ref.listen(homeViewPageIndexStateProvider,
|
||||
(previous, next) {
|
||||
if (next is int) {
|
||||
if (next == 1) {
|
||||
_exchangeDataLoadingService.loadAll(ref);
|
||||
}
|
||||
if (next >= 0 && next <= 1) {
|
||||
_pageController.animateToPage(
|
||||
next,
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.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/widgets/stack_dialog.dart';
|
||||
|
||||
import 'package:stackwallet/utilities/prefs.dart';
|
||||
|
||||
class HomeViewButtonBar extends ConsumerStatefulWidget {
|
||||
const HomeViewButtonBar({Key? key}) : super(key: key);
|
||||
|
||||
|
@ -18,8 +14,8 @@ class HomeViewButtonBar extends ConsumerStatefulWidget {
|
|||
}
|
||||
|
||||
class _HomeViewButtonBarState extends ConsumerState<HomeViewButtonBar> {
|
||||
final DateTime _lastRefreshed = DateTime.now();
|
||||
final Duration _refreshInterval = const Duration(hours: 1);
|
||||
// final DateTime _lastRefreshed = DateTime.now();
|
||||
// final Duration _refreshInterval = const Duration(hours: 1);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -104,34 +100,14 @@ class _HomeViewButtonBarState extends ConsumerState<HomeViewButtonBar> {
|
|||
if (selectedIndex != 1) {
|
||||
ref.read(homeViewPageIndexStateProvider.state).state = 1;
|
||||
}
|
||||
DateTime now = DateTime.now();
|
||||
final _cnLoadingService = ExchangeDataLoadingService();
|
||||
final externalCalls = Prefs.instance.externalCalls;
|
||||
if (!externalCalls) {
|
||||
print("loading?");
|
||||
unawaited(_cnLoadingService.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();
|
||||
// }
|
||||
}
|
||||
// DateTime now = DateTime.now();
|
||||
// if (ref.read(prefsChangeNotifierProvider).externalCalls) {
|
||||
// print("loading?");
|
||||
await ExchangeDataLoadingService().loadAll(ref);
|
||||
// }
|
||||
// if (now.difference(_lastRefreshed) > _refreshInterval) {
|
||||
// await ExchangeDataLoadingService().loadAll(ref);
|
||||
// }
|
||||
},
|
||||
child: Text(
|
||||
"Exchange",
|
||||
|
|
|
@ -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/format.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/prefs.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/animated_text.dart';
|
||||
|
@ -1107,115 +1108,120 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
TextField(
|
||||
style: STextStyles.smallMed14(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
if (Prefs.instance.externalCalls)
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
key: const Key("amountInputFieldFiatTextFieldKey"),
|
||||
controller: baseAmountController,
|
||||
focusNode: _baseFocus,
|
||||
keyboardType: const TextInputType.numberWithOptions(
|
||||
signed: false,
|
||||
decimal: true,
|
||||
),
|
||||
textAlign: TextAlign.right,
|
||||
inputFormatters: [
|
||||
// regex to validate a fiat amount with 2 decimal places
|
||||
TextInputFormatter.withFunction((oldValue,
|
||||
newValue) =>
|
||||
RegExp(r'^([0-9]*[,.]?[0-9]{0,2}|[,.][0-9]{0,2})$')
|
||||
.hasMatch(newValue.text)
|
||||
? newValue
|
||||
: oldValue),
|
||||
],
|
||||
onChanged: (baseAmountString) {
|
||||
if (baseAmountString.isNotEmpty &&
|
||||
baseAmountString != "." &&
|
||||
baseAmountString != ",") {
|
||||
final baseAmount = baseAmountString.contains(",")
|
||||
? Decimal.parse(
|
||||
baseAmountString.replaceFirst(",", "."))
|
||||
: Decimal.parse(baseAmountString);
|
||||
if (Prefs.instance.externalCalls)
|
||||
TextField(
|
||||
style: STextStyles.smallMed14(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
),
|
||||
key: const Key("amountInputFieldFiatTextFieldKey"),
|
||||
controller: baseAmountController,
|
||||
focusNode: _baseFocus,
|
||||
keyboardType: const TextInputType.numberWithOptions(
|
||||
signed: false,
|
||||
decimal: true,
|
||||
),
|
||||
textAlign: TextAlign.right,
|
||||
inputFormatters: [
|
||||
// regex to validate a fiat amount with 2 decimal places
|
||||
TextInputFormatter.withFunction((oldValue,
|
||||
newValue) =>
|
||||
RegExp(r'^([0-9]*[,.]?[0-9]{0,2}|[,.][0-9]{0,2})$')
|
||||
.hasMatch(newValue.text)
|
||||
? newValue
|
||||
: oldValue),
|
||||
],
|
||||
onChanged: (baseAmountString) {
|
||||
if (baseAmountString.isNotEmpty &&
|
||||
baseAmountString != "." &&
|
||||
baseAmountString != ",") {
|
||||
final baseAmount = baseAmountString
|
||||
.contains(",")
|
||||
? Decimal.parse(
|
||||
baseAmountString.replaceFirst(",", "."))
|
||||
: Decimal.parse(baseAmountString);
|
||||
|
||||
var _price = ref
|
||||
.read(priceAnd24hChangeNotifierProvider)
|
||||
.getPrice(coin)
|
||||
.item1;
|
||||
var _price = ref
|
||||
.read(priceAnd24hChangeNotifierProvider)
|
||||
.getPrice(coin)
|
||||
.item1;
|
||||
|
||||
if (_price == Decimal.zero) {
|
||||
_amountToSend = Decimal.zero;
|
||||
if (_price == 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 {
|
||||
_amountToSend = baseAmount <= Decimal.zero
|
||||
? Decimal.zero
|
||||
: (baseAmount / _price).toDecimal(
|
||||
scaleOnInfinitePrecision:
|
||||
Constants.decimalPlaces);
|
||||
_amountToSend = Decimal.zero;
|
||||
_cryptoAmountChangeLock = true;
|
||||
cryptoAmountController.text = "";
|
||||
_cryptoAmountChangeLock = false;
|
||||
}
|
||||
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 {
|
||||
_amountToSend = Decimal.zero;
|
||||
_cryptoAmountChangeLock = true;
|
||||
cryptoAmountController.text = "";
|
||||
_cryptoAmountChangeLock = false;
|
||||
}
|
||||
// setState(() {
|
||||
// _calculateFeesFuture = calculateFees(
|
||||
// Format.decimalAmountToSatoshis(
|
||||
// _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),
|
||||
// setState(() {
|
||||
// _calculateFeesFuture = calculateFees(
|
||||
// Format.decimalAmountToSatoshis(
|
||||
// _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(
|
||||
height: 12,
|
||||
),
|
||||
|
@ -1319,6 +1325,12 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
cryptoAmountController
|
||||
.text) ??
|
||||
Decimal.zero,
|
||||
updateChosen: (String fee) {
|
||||
setState(() {
|
||||
_calculateFeesFuture =
|
||||
Future(() => fee);
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
|
@ -32,10 +32,12 @@ class TransactionFeeSelectionSheet extends ConsumerStatefulWidget {
|
|||
Key? key,
|
||||
required this.walletId,
|
||||
required this.amount,
|
||||
required this.updateChosen,
|
||||
}) : super(key: key);
|
||||
|
||||
final String walletId;
|
||||
final Decimal amount;
|
||||
final Function updateChosen;
|
||||
|
||||
@override
|
||||
ConsumerState<TransactionFeeSelectionSheet> createState() =>
|
||||
|
@ -223,6 +225,10 @@ class _TransactionFeeSelectionSheetState
|
|||
ref.read(feeRateTypeStateProvider.state).state =
|
||||
FeeRateType.fast;
|
||||
}
|
||||
String? fee = getAmount(FeeRateType.fast);
|
||||
if (fee != null) {
|
||||
widget.updateChosen(fee);
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Container(
|
||||
|
@ -352,6 +358,10 @@ class _TransactionFeeSelectionSheetState
|
|||
ref.read(feeRateTypeStateProvider.state).state =
|
||||
FeeRateType.average;
|
||||
}
|
||||
String? fee = getAmount(FeeRateType.average);
|
||||
if (fee != null) {
|
||||
widget.updateChosen(fee);
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Container(
|
||||
|
@ -479,6 +489,11 @@ class _TransactionFeeSelectionSheetState
|
|||
ref.read(feeRateTypeStateProvider.state).state =
|
||||
FeeRateType.slow;
|
||||
}
|
||||
String? fee = getAmount(FeeRateType.slow);
|
||||
print("fee $fee");
|
||||
if (fee != null) {
|
||||
widget.updateChosen(fee);
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -402,7 +402,7 @@ abstract class SWB {
|
|||
// without using them
|
||||
await manager.recoverFromMnemonic(
|
||||
mnemonic: mnemonic,
|
||||
maxUnusedAddressGap: 20,
|
||||
maxUnusedAddressGap: manager.coin == Coin.firo ? 50 : 20,
|
||||
maxNumberOfIndexesToCheck: 1000,
|
||||
height: restoreHeight,
|
||||
);
|
||||
|
@ -997,10 +997,14 @@ abstract class SWB {
|
|||
// primary nodes
|
||||
if (primaryNodes != null) {
|
||||
for (var node in primaryNodes) {
|
||||
await nodeService.setPrimaryNodeFor(
|
||||
coin: coinFromPrettyName(node['coinName'] as String),
|
||||
node: nodeService.getNodeById(id: node['id'] as String)!,
|
||||
);
|
||||
try {
|
||||
await nodeService.setPrimaryNodeFor(
|
||||
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();
|
||||
|
@ -1175,10 +1179,14 @@ abstract class SWB {
|
|||
}
|
||||
if (primaryNodes != null) {
|
||||
for (var node in primaryNodes) {
|
||||
await nodeService.setPrimaryNodeFor(
|
||||
coin: coinFromPrettyName(node['coinName'] as String),
|
||||
node: nodeService.getNodeById(id: node['id'] as String)!,
|
||||
);
|
||||
try {
|
||||
await nodeService.setPrimaryNodeFor(
|
||||
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();
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:io';
|
|||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
class StackFileSystem {
|
||||
Directory? rootPath;
|
||||
|
@ -14,6 +15,9 @@ class StackFileSystem {
|
|||
final bool isDesktop = !(Platform.isAndroid || Platform.isIOS);
|
||||
|
||||
Future<Directory> prepareStorage() async {
|
||||
if (Platform.isAndroid) {
|
||||
await Permission.storage.request();
|
||||
}
|
||||
rootPath = (await getApplicationDocumentsDirectory());
|
||||
debugPrint(rootPath!.absolute.toString());
|
||||
if (Platform.isAndroid) {
|
||||
|
@ -22,13 +26,13 @@ class StackFileSystem {
|
|||
debugPrint(rootPath!.absolute.toString());
|
||||
|
||||
Directory sampleFolder =
|
||||
Directory('${rootPath!.path}/Documents/Stack_backups');
|
||||
Directory('${rootPath!.path}Documents/Stack_backups');
|
||||
if (Platform.isIOS) {
|
||||
sampleFolder = Directory(rootPath!.path);
|
||||
}
|
||||
try {
|
||||
if (!sampleFolder.existsSync()) {
|
||||
sampleFolder.createSync();
|
||||
sampleFolder.createSync(recursive: true);
|
||||
}
|
||||
} catch (e, s) {
|
||||
debugPrint("$e $s");
|
||||
|
@ -65,8 +69,7 @@ class StackFileSystem {
|
|||
result = await FilePicker.platform.pickFiles(
|
||||
dialogTitle: "Load backup file",
|
||||
initialDirectory: startPath!.path,
|
||||
type: FileType.custom,
|
||||
allowedExtensions: ['bin'],
|
||||
type: FileType.any,
|
||||
allowCompression: false,
|
||||
lockParentWindow: true,
|
||||
);
|
||||
|
|
|
@ -35,7 +35,8 @@ class _RestoringWalletCardState extends ConsumerState<RestoringWalletCard> {
|
|||
case StackRestoringStatus.waiting:
|
||||
return SvgPicture.asset(
|
||||
Assets.svg.loader,
|
||||
color: Theme.of(context).extension<StackColors>()!.buttonBackSecondary,
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.buttonBackSecondary,
|
||||
);
|
||||
case StackRestoringStatus.restoring:
|
||||
return const LoadingIndicator();
|
||||
|
@ -95,7 +96,10 @@ class _RestoringWalletCardState extends ConsumerState<RestoringWalletCard> {
|
|||
|
||||
try {
|
||||
final mnemonicList = await manager.mnemonic;
|
||||
const maxUnusedAddressGap = 20;
|
||||
int maxUnusedAddressGap = 20;
|
||||
if (coin == Coin.firo) {
|
||||
maxUnusedAddressGap = 50;
|
||||
}
|
||||
const maxNumberOfIndexesToCheck = 1000;
|
||||
|
||||
if (mnemonicList.isEmpty) {
|
||||
|
@ -155,14 +159,17 @@ class _RestoringWalletCardState extends ConsumerState<RestoringWalletCard> {
|
|||
? Container(
|
||||
height: 20,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).extension<StackColors>()!.buttonBackSecondary,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonBackSecondary,
|
||||
borderRadius: BorderRadius.circular(
|
||||
1000,
|
||||
),
|
||||
),
|
||||
child: RawMaterialButton(
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
splashColor: Theme.of(context).extension<StackColors>()!.highlight,
|
||||
splashColor:
|
||||
Theme.of(context).extension<StackColors>()!.highlight,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
1000,
|
||||
|
@ -187,7 +194,9 @@ class _RestoringWalletCardState extends ConsumerState<RestoringWalletCard> {
|
|||
child: Text(
|
||||
"Show recovery phrase",
|
||||
style: STextStyles.infoSmall(context).copyWith(
|
||||
color: Theme.of(context).extension<StackColors>()!.accentColorDark),
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -77,7 +77,8 @@ class _WalletNetworkSettingsViewState
|
|||
Future<void> _attemptRescan() async {
|
||||
if (!Platform.isLinux) Wakelock.enable();
|
||||
|
||||
const int maxUnusedAddressGap = 20;
|
||||
int maxUnusedAddressGap = 20;
|
||||
|
||||
const int maxNumberOfIndexesToCheck = 1000;
|
||||
|
||||
showDialog<dynamic>(
|
||||
|
@ -88,6 +89,13 @@ class _WalletNetworkSettingsViewState
|
|||
);
|
||||
|
||||
try {
|
||||
if (ref
|
||||
.read(walletsChangeNotifierProvider)
|
||||
.getManager(widget.walletId)
|
||||
.coin ==
|
||||
Coin.firo) {
|
||||
maxUnusedAddressGap = 50;
|
||||
}
|
||||
await ref
|
||||
.read(walletsChangeNotifierProvider)
|
||||
.getManager(widget.walletId)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.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_desktop_specific/create_password/create_password_view.dart';
|
||||
import 'package:stackwallet/providers/global/prefs_provider.dart';
|
||||
import 'package:stackwallet/utilities/assets.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/rounded_white_container.dart';
|
||||
|
||||
import 'package:stackwallet/providers/global/prefs_provider.dart';
|
||||
import 'package:stackwallet/utilities/prefs.dart';
|
||||
|
||||
class StackPrivacyCalls extends ConsumerStatefulWidget {
|
||||
const StackPrivacyCalls({
|
||||
Key? key,
|
||||
|
@ -31,19 +28,19 @@ class StackPrivacyCalls extends ConsumerStatefulWidget {
|
|||
|
||||
class _StackPrivacyCalls extends ConsumerState<StackPrivacyCalls> {
|
||||
late final bool isDesktop;
|
||||
bool isEasy = Prefs.instance.externalCalls;
|
||||
final PageController _pageController =
|
||||
PageController(initialPage: 0, keepPage: true);
|
||||
late bool isEasy;
|
||||
late bool infoToggle;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
isDesktop = Util.isDesktop;
|
||||
isEasy = ref.read(prefsChangeNotifierProvider).externalCalls;
|
||||
infoToggle = isEasy;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_pageController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
@ -59,135 +56,161 @@ class _StackPrivacyCalls extends ConsumerState<StackPrivacyCalls> {
|
|||
),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: PageView(
|
||||
controller: _pageController,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0, 40, 0, 0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
"Choose your Stack experience",
|
||||
style: STextStyles.pageTitleH1(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
Text(
|
||||
"You can change it later in Settings",
|
||||
style: STextStyles.subtitle(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 36,
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
),
|
||||
child: PrivacyToggle(),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 36,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: RoundedWhiteContainer(
|
||||
child: Center(
|
||||
child: RichText(
|
||||
textAlign: TextAlign.left,
|
||||
text: TextSpan(
|
||||
style: STextStyles.label(context)
|
||||
.copyWith(fontSize: 12.0),
|
||||
children: ref.watch(
|
||||
prefsChangeNotifierProvider.select(
|
||||
(value) => value.externalCalls,
|
||||
),
|
||||
)
|
||||
? [
|
||||
const TextSpan(
|
||||
text:
|
||||
"Exchange data preloaded for a seamless experience."),
|
||||
const TextSpan(
|
||||
text:
|
||||
"\n\nCoinGecko enabled: (24 hour price change shown in-app, total wallet value shown in USD or other currency)."),
|
||||
TextSpan(
|
||||
text:
|
||||
"\n\nRecommended for most crypto users.",
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
]
|
||||
: [
|
||||
const TextSpan(
|
||||
text:
|
||||
"Exchange data not preloaded (slower experience)."),
|
||||
const TextSpan(
|
||||
text:
|
||||
"\n\nCoinGecko disabled (price changes not shown, no wallet value shown in other currencies)."),
|
||||
TextSpan(
|
||||
text:
|
||||
"\n\nRecommended for the privacy conscious.",
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0, 40, 0, 0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
"Choose your Stack experience",
|
||||
style: STextStyles.pageTitleH1(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
Text(
|
||||
"You can change it later in Settings",
|
||||
style: STextStyles.subtitle(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 36,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
),
|
||||
child: PrivacyToggle(
|
||||
externalCallsEnabled: isEasy,
|
||||
onChanged: (externalCalls) {
|
||||
isEasy = externalCalls;
|
||||
setState(() {
|
||||
infoToggle = isEasy;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 36,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: RoundedWhiteContainer(
|
||||
child: Center(
|
||||
child: RichText(
|
||||
textAlign: TextAlign.left,
|
||||
text: TextSpan(
|
||||
style:
|
||||
STextStyles.label(context).copyWith(fontSize: 12.0),
|
||||
children: infoToggle
|
||||
? [
|
||||
const TextSpan(
|
||||
text:
|
||||
"Exchange data preloaded for a seamless experience."),
|
||||
const TextSpan(
|
||||
text:
|
||||
"\n\nCoinGecko enabled: (24 hour price change shown in-app, total wallet value shown in USD or other currency)."),
|
||||
TextSpan(
|
||||
text:
|
||||
"\n\nRecommended for most crypto users.",
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
]
|
||||
: [
|
||||
const TextSpan(
|
||||
text:
|
||||
"Exchange data not preloaded (slower experience)."),
|
||||
const TextSpan(
|
||||
text:
|
||||
"\n\nCoinGecko disabled (price changes not shown, no wallet value shown in other currencies)."),
|
||||
TextSpan(
|
||||
text:
|
||||
"\n\nRecommended for the privacy conscious.",
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
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 {
|
||||
const PrivacyToggle({Key? key}) : super(key: key);
|
||||
class PrivacyToggle extends StatefulWidget {
|
||||
const PrivacyToggle({
|
||||
Key? key,
|
||||
required this.externalCallsEnabled,
|
||||
this.onChanged,
|
||||
}) : super(key: key);
|
||||
|
||||
final bool externalCallsEnabled;
|
||||
final void Function(bool)? onChanged;
|
||||
|
||||
@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
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
|
@ -196,11 +219,7 @@ class _PrivacyToggleState extends ConsumerState<PrivacyToggle> {
|
|||
child: RawMaterialButton(
|
||||
fillColor: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
shape: RoundedRectangleBorder(
|
||||
side: !ref.watch(
|
||||
prefsChangeNotifierProvider.select(
|
||||
(value) => value.externalCalls,
|
||||
),
|
||||
)
|
||||
side: !externalCallsEnabled
|
||||
? BorderSide.none
|
||||
: BorderSide(
|
||||
color: Theme.of(context)
|
||||
|
@ -213,7 +232,12 @@ class _PrivacyToggleState extends ConsumerState<PrivacyToggle> {
|
|||
),
|
||||
),
|
||||
onPressed: () {
|
||||
ref.read(prefsChangeNotifierProvider).externalCalls = true;
|
||||
setState(() {
|
||||
// update toggle state
|
||||
externalCallsEnabled = true;
|
||||
});
|
||||
// call callback with newly set value
|
||||
widget.onChanged?.call(externalCallsEnabled);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(
|
||||
|
@ -244,11 +268,7 @@ class _PrivacyToggleState extends ConsumerState<PrivacyToggle> {
|
|||
),
|
||||
],
|
||||
),
|
||||
if (ref.watch(
|
||||
prefsChangeNotifierProvider.select(
|
||||
(value) => value.externalCalls,
|
||||
),
|
||||
))
|
||||
if (externalCallsEnabled)
|
||||
Positioned(
|
||||
top: 4,
|
||||
right: 4,
|
||||
|
@ -261,11 +281,7 @@ class _PrivacyToggleState extends ConsumerState<PrivacyToggle> {
|
|||
.infoItemIcons,
|
||||
),
|
||||
),
|
||||
if (!ref.watch(
|
||||
prefsChangeNotifierProvider.select(
|
||||
(value) => value.externalCalls,
|
||||
),
|
||||
))
|
||||
if (!externalCallsEnabled)
|
||||
Positioned(
|
||||
top: 4,
|
||||
right: 4,
|
||||
|
@ -293,11 +309,7 @@ class _PrivacyToggleState extends ConsumerState<PrivacyToggle> {
|
|||
elevation: 0,
|
||||
fillColor: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
shape: RoundedRectangleBorder(
|
||||
side: ref.watch(
|
||||
prefsChangeNotifierProvider.select(
|
||||
(value) => value.externalCalls,
|
||||
),
|
||||
)
|
||||
side: externalCallsEnabled
|
||||
? BorderSide.none
|
||||
: BorderSide(
|
||||
color: Theme.of(context)
|
||||
|
@ -310,7 +322,12 @@ class _PrivacyToggleState extends ConsumerState<PrivacyToggle> {
|
|||
),
|
||||
),
|
||||
onPressed: () {
|
||||
ref.read(prefsChangeNotifierProvider).externalCalls = false;
|
||||
setState(() {
|
||||
// update toggle state
|
||||
externalCallsEnabled = false;
|
||||
});
|
||||
// call callback with newly set value
|
||||
widget.onChanged?.call(externalCallsEnabled);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(
|
||||
|
@ -342,11 +359,7 @@ class _PrivacyToggleState extends ConsumerState<PrivacyToggle> {
|
|||
),
|
||||
],
|
||||
),
|
||||
if (!ref.watch(
|
||||
prefsChangeNotifierProvider.select(
|
||||
(value) => value.externalCalls,
|
||||
),
|
||||
))
|
||||
if (!externalCallsEnabled)
|
||||
Positioned(
|
||||
top: 4,
|
||||
right: 4,
|
||||
|
@ -359,11 +372,7 @@ class _PrivacyToggleState extends ConsumerState<PrivacyToggle> {
|
|||
.infoItemIcons,
|
||||
),
|
||||
),
|
||||
if (ref.watch(
|
||||
prefsChangeNotifierProvider.select(
|
||||
(value) => value.externalCalls,
|
||||
),
|
||||
))
|
||||
if (externalCallsEnabled)
|
||||
Positioned(
|
||||
top: 4,
|
||||
right: 4,
|
||||
|
@ -388,62 +397,47 @@ class _PrivacyToggleState extends ConsumerState<PrivacyToggle> {
|
|||
}
|
||||
}
|
||||
|
||||
class ContinueButton extends StatelessWidget {
|
||||
const ContinueButton(
|
||||
{Key? key,
|
||||
required this.isDesktop,
|
||||
required this.isSettings,
|
||||
required this.isEasy})
|
||||
: super(key: key);
|
||||
class ContinueButton extends ConsumerWidget {
|
||||
const ContinueButton({
|
||||
Key? key,
|
||||
required this.isDesktop,
|
||||
required this.onPressed,
|
||||
required this.label,
|
||||
}) : super(key: key);
|
||||
|
||||
final String label;
|
||||
final bool isDesktop;
|
||||
final bool isSettings;
|
||||
final bool isEasy;
|
||||
final VoidCallback onPressed;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return !isDesktop
|
||||
? TextButton(
|
||||
style: Theme.of(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),
|
||||
),
|
||||
)
|
||||
: SizedBox(
|
||||
width: 328,
|
||||
height: 70,
|
||||
child: TextButton(
|
||||
style: Theme.of(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),
|
||||
),
|
||||
),
|
||||
);
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
if (isDesktop) {
|
||||
return SizedBox(
|
||||
width: 328,
|
||||
height: 70,
|
||||
child: TextButton(
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getPrimaryEnabledButtonColor(context),
|
||||
onPressed: onPressed,
|
||||
child: Text(
|
||||
label,
|
||||
style: STextStyles.button(context).copyWith(fontSize: 20),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return TextButton(
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getPrimaryEnabledButtonColor(context),
|
||||
onPressed: onPressed,
|
||||
child: Text(
|
||||
label,
|
||||
style: STextStyles.button(context),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:decimal/decimal.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.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/theme/stack_colors.dart';
|
||||
|
||||
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
|
||||
|
||||
class WalletBalanceToggleSheet extends ConsumerWidget {
|
||||
const WalletBalanceToggleSheet({
|
||||
Key? key,
|
||||
|
@ -23,6 +26,22 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
|
|||
final coin = ref.watch(walletsChangeNotifierProvider
|
||||
.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(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
|
@ -125,15 +144,35 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
|
|||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
Text(
|
||||
"Current spendable (unlocked) balance",
|
||||
style:
|
||||
STextStyles.itemSubtitle12(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
),
|
||||
FutureBuilder(
|
||||
future: availableBalanceFuture,
|
||||
builder: (fbContext,
|
||||
AsyncSnapshot<Decimal> snapshot) {
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.done &&
|
||||
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)
|
||||
|
@ -147,15 +186,35 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
|
|||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
Text(
|
||||
"Current private spendable (unlocked) balance",
|
||||
style:
|
||||
STextStyles.itemSubtitle12(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
),
|
||||
FutureBuilder(
|
||||
future: availableBalanceFuture,
|
||||
builder: (fbContext,
|
||||
AsyncSnapshot<Decimal> snapshot) {
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.done &&
|
||||
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(
|
||||
height: 2,
|
||||
),
|
||||
Text(
|
||||
"Total wallet balance",
|
||||
style:
|
||||
STextStyles.itemSubtitle12(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
),
|
||||
FutureBuilder(
|
||||
future: totalBalanceFuture,
|
||||
builder: (fbContext,
|
||||
AsyncSnapshot<Decimal> snapshot) {
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.done &&
|
||||
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)
|
||||
|
@ -241,15 +320,35 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
|
|||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
Text(
|
||||
"Current public spendable (unlocked) balance",
|
||||
style:
|
||||
STextStyles.itemSubtitle12(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
),
|
||||
FutureBuilder(
|
||||
future: totalBalanceFuture,
|
||||
builder: (fbContext,
|
||||
AsyncSnapshot<Decimal> snapshot) {
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.done &&
|
||||
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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
|
|
@ -70,6 +70,8 @@ class _WalletSummaryInfoState extends State<WalletSummaryInfo> {
|
|||
builder: (_, ref, __) {
|
||||
final Coin coin =
|
||||
ref.watch(managerProvider.select((value) => value.coin));
|
||||
final externalCalls = ref.watch(prefsChangeNotifierProvider
|
||||
.select((value) => value.externalCalls));
|
||||
|
||||
Future<Decimal>? totalBalanceFuture;
|
||||
Future<Decimal>? availableBalanceFuture;
|
||||
|
@ -176,18 +178,19 @@ class _WalletSummaryInfoState extends State<WalletSummaryInfo> {
|
|||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"${Format.localizedStringAsFixed(
|
||||
value: priceTuple.item1 * balanceToShow,
|
||||
locale: locale,
|
||||
decimalPlaces: 2,
|
||||
)} $baseCurrency",
|
||||
style: STextStyles.subtitle500(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFavoriteCard,
|
||||
if (externalCalls)
|
||||
Text(
|
||||
"${Format.localizedStringAsFixed(
|
||||
value: priceTuple.item1 * balanceToShow,
|
||||
locale: locale,
|
||||
decimalPlaces: 2,
|
||||
)} $baseCurrency",
|
||||
style: STextStyles.subtitle500(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFavoriteCard,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
|
|
|
@ -255,17 +255,19 @@ class _TransactionDetailsViewState
|
|||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
SelectableText(
|
||||
"${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(
|
||||
localeServiceChangeNotifierProvider
|
||||
.select((value) => value.locale),
|
||||
), decimalPlaces: 2)} ${ref.watch(
|
||||
prefsChangeNotifierProvider.select(
|
||||
(value) => value.currency,
|
||||
),
|
||||
)}",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
if (ref.watch(prefsChangeNotifierProvider
|
||||
.select((value) => value.externalCalls)))
|
||||
SelectableText(
|
||||
"${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(
|
||||
localeServiceChangeNotifierProvider
|
||||
.select((value) => value.locale),
|
||||
), decimalPlaces: 2)} ${ref.watch(
|
||||
prefsChangeNotifierProvider.select(
|
||||
(value) => value.currency,
|
||||
),
|
||||
)}",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
TxIcon(
|
||||
|
|
|
@ -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/flush_bar_type.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/theme/stack_colors.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: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
|
||||
class WalletView extends ConsumerStatefulWidget {
|
||||
const WalletView({
|
||||
|
@ -235,12 +230,8 @@ class _WalletViewState extends ConsumerState<WalletView> {
|
|||
}
|
||||
|
||||
void _onExchangePressed(BuildContext context) async {
|
||||
final _cnLoadingService = ExchangeDataLoadingService();
|
||||
final externalCalls = Prefs.instance.externalCalls;
|
||||
if (!externalCalls) {
|
||||
print("loading?");
|
||||
unawaited(_cnLoadingService.loadAll(ref));
|
||||
}
|
||||
unawaited(_cnLoadingService.loadAll(ref));
|
||||
|
||||
final coin = ref.read(managerProvider).coin;
|
||||
|
||||
if (coin == Coin.epicCash) {
|
||||
|
@ -371,9 +362,7 @@ class _WalletViewState extends ConsumerState<WalletView> {
|
|||
|
||||
void _loadCNData() {
|
||||
// unawaited future
|
||||
final externalCalls = DB.instance
|
||||
.get<dynamic>(boxName: DB.boxNamePrefs, key: "externalCalls") as bool?;
|
||||
if (externalCalls ?? false) {
|
||||
if (ref.read(prefsChangeNotifierProvider).externalCalls) {
|
||||
_cnLoadingService.loadAll(ref, coin: ref.read(managerProvider).coin);
|
||||
} else {
|
||||
Logging.instance.log("User does not want to use external calls",
|
||||
|
|
|
@ -49,6 +49,8 @@ class _FavoriteCardState extends ConsumerState<FavoriteCard> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final coin = ref.watch(managerProvider.select((value) => value.coin));
|
||||
final externalCalls = ref.watch(
|
||||
prefsChangeNotifierProvider.select((value) => value.externalCalls));
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
|
@ -70,7 +72,9 @@ class _FavoriteCardState extends ConsumerState<FavoriteCard> {
|
|||
width: widget.width,
|
||||
height: widget.height,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).extension<StackColors>()!.colorForCoin(coin),
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.colorForCoin(coin),
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
|
@ -138,7 +142,9 @@ class _FavoriteCardState extends ConsumerState<FavoriteCard> {
|
|||
ref.watch(managerProvider
|
||||
.select((value) => value.walletName)),
|
||||
style: STextStyles.itemSubtitle12(context).copyWith(
|
||||
color: Theme.of(context).extension<StackColors>()!.textFavoriteCard,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFavoriteCard,
|
||||
),
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
|
@ -159,14 +165,16 @@ class _FavoriteCardState extends ConsumerState<FavoriteCard> {
|
|||
snapshot.hasData) {
|
||||
if (snapshot.data != null) {
|
||||
_cachedBalance = snapshot.data!;
|
||||
_cachedFiatValue = _cachedBalance *
|
||||
ref
|
||||
.watch(
|
||||
priceAnd24hChangeNotifierProvider.select(
|
||||
(value) => value.getPrice(coin),
|
||||
),
|
||||
)
|
||||
.item1;
|
||||
if (externalCalls) {
|
||||
_cachedFiatValue = _cachedBalance *
|
||||
ref
|
||||
.watch(
|
||||
priceAnd24hChangeNotifierProvider.select(
|
||||
(value) => value.getPrice(coin),
|
||||
),
|
||||
)
|
||||
.item1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Column(
|
||||
|
@ -185,30 +193,36 @@ class _FavoriteCardState extends ConsumerState<FavoriteCard> {
|
|||
)} ${coin.ticker}",
|
||||
style: STextStyles.titleBold12(context).copyWith(
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).extension<StackColors>()!.textFavoriteCard,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFavoriteCard,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
Text(
|
||||
"${Format.localizedStringAsFixed(
|
||||
decimalPlaces: 2,
|
||||
value: _cachedFiatValue,
|
||||
locale: ref.watch(
|
||||
localeServiceChangeNotifierProvider
|
||||
.select((value) => value.locale),
|
||||
),
|
||||
)} ${ref.watch(
|
||||
prefsChangeNotifierProvider
|
||||
.select((value) => value.currency),
|
||||
)}",
|
||||
style: STextStyles.itemSubtitle12(context).copyWith(
|
||||
fontSize: 10,
|
||||
color: Theme.of(context).extension<StackColors>()!.textFavoriteCard,
|
||||
if (externalCalls)
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
if (externalCalls)
|
||||
Text(
|
||||
"${Format.localizedStringAsFixed(
|
||||
decimalPlaces: 2,
|
||||
value: _cachedFiatValue,
|
||||
locale: ref.watch(
|
||||
localeServiceChangeNotifierProvider
|
||||
.select((value) => value.locale),
|
||||
),
|
||||
)} ${ref.watch(
|
||||
prefsChangeNotifierProvider
|
||||
.select((value) => value.currency),
|
||||
)}",
|
||||
style: STextStyles.itemSubtitle12(context).copyWith(
|
||||
fontSize: 10,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFavoriteCard,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:stackwallet/utilities/assets.dart';
|
|||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/format.dart';
|
||||
import 'package:stackwallet/utilities/prefs.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
|
@ -67,6 +68,8 @@ class WalletListItem extends ConsumerWidget {
|
|||
builder: (_, ref, __) {
|
||||
final tuple = ref.watch(priceAnd24hChangeNotifierProvider
|
||||
.select((value) => value.getPrice(coin)));
|
||||
final calls =
|
||||
ref.watch(prefsChangeNotifierProvider).externalCalls;
|
||||
|
||||
final priceString = Format.localizedStringAsFixed(
|
||||
value: tuple.item1,
|
||||
|
@ -100,10 +103,11 @@ class WalletListItem extends ConsumerWidget {
|
|||
style: STextStyles.titleBold12(context),
|
||||
),
|
||||
const Spacer(),
|
||||
Text(
|
||||
"$priceString $currency/${coin.ticker}",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
if (calls)
|
||||
Text(
|
||||
"$priceString $currency/${coin.ticker}",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
|
@ -116,12 +120,13 @@ class WalletListItem extends ConsumerWidget {
|
|||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
const Spacer(),
|
||||
Text(
|
||||
"${percentChange.toStringAsFixed(2)}%",
|
||||
style: STextStyles.itemSubtitle(context).copyWith(
|
||||
color: percentChangedColor,
|
||||
if (calls)
|
||||
Text(
|
||||
"${percentChange.toStringAsFixed(2)}%",
|
||||
style: STextStyles.itemSubtitle(context).copyWith(
|
||||
color: percentChangedColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
|
|
@ -254,7 +254,7 @@ Future<Map<String, dynamic>> isolateRestore(
|
|||
}
|
||||
|
||||
final root = getBip32Root(mnemonic, network);
|
||||
while (currentIndex < lastFoundIndex + 20) {
|
||||
while (currentIndex < lastFoundIndex + 50) {
|
||||
final mintKeyPair = getBip32NodeFromRoot(MINT_INDEX, currentIndex, root);
|
||||
final mintTag = CreateTag(
|
||||
Format.uint8listToString(mintKeyPair.privateKey!),
|
||||
|
@ -753,10 +753,11 @@ Future<Map<String, dynamic>?> getInitialAnonymitySetCache(
|
|||
);
|
||||
|
||||
final response = jsonDecode(anonSetResult.body.toString());
|
||||
Logging.instance.log(response, level: LogLevel.Info);
|
||||
if (response['status'] == 'success') {
|
||||
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;
|
||||
} else {
|
||||
return null;
|
||||
|
@ -1355,7 +1356,7 @@ class FiroWallet extends CoinServiceAPI {
|
|||
List<UtxoObject> utxoObjectsToUse = [];
|
||||
|
||||
for (var i = 0;
|
||||
satoshisBeingUsed < satoshiAmountToSend && i < spendableOutputs.length;
|
||||
satoshisBeingUsed <= satoshiAmountToSend && i < spendableOutputs.length;
|
||||
i++) {
|
||||
utxoObjectsToUse.add(spendableOutputs[i]);
|
||||
satoshisBeingUsed += spendableOutputs[i].value;
|
||||
|
@ -2040,7 +2041,8 @@ class FiroWallet extends CoinServiceAPI {
|
|||
case "Sent":
|
||||
unawaited(
|
||||
NotificationApi.showNotification(
|
||||
title: "Outgoing transaction",
|
||||
title:
|
||||
tx.subType == "mint" ? "Anonymizing" : "Outgoing transaction",
|
||||
body: walletName,
|
||||
walletId: walletId,
|
||||
iconAssetName: Assets.svg.iconFor(coin: coin),
|
||||
|
@ -2076,7 +2078,9 @@ class FiroWallet extends CoinServiceAPI {
|
|||
} else if (tx.txType == "Sent" && tx.subType == "join") {
|
||||
unawaited(
|
||||
NotificationApi.showNotification(
|
||||
title: "Outgoing transaction confirmed",
|
||||
title: tx.subType == "mint"
|
||||
? "Anonymized"
|
||||
: "Outgoing transaction confirmed",
|
||||
body: walletName,
|
||||
walletId: walletId,
|
||||
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;
|
||||
coinsToSpend.insert(coinsToSpend.length, chosen);
|
||||
}
|
||||
|
@ -4514,36 +4519,61 @@ class FiroWallet extends CoinServiceAPI {
|
|||
Future<int> estimateJoinSplitFee(
|
||||
int spendAmount,
|
||||
) async {
|
||||
int fee;
|
||||
int size;
|
||||
|
||||
for (fee = 0;;) {
|
||||
int currentRequired = spendAmount;
|
||||
|
||||
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;
|
||||
var lelantusEntry = await _getLelantusEntry();
|
||||
final balance = await availableBalance;
|
||||
int spendAmount =
|
||||
(balance * Decimal.fromInt(Constants.satsPerCoin)).toBigInt().toInt();
|
||||
if (spendAmount == 0 || lelantusEntry.isEmpty) {
|
||||
return LelantusFeeData(0, 0, []).fee;
|
||||
}
|
||||
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
|
||||
Future<int> estimateFeeFor(int satoshiAmount, int feeRate) async {
|
||||
|
|
|
@ -1558,7 +1558,7 @@ class WowneroWallet extends CoinServiceAPI {
|
|||
"WW3iVcnoAY6K9zNdU4qmdvZELefx6xZz4PMpTwUifRkvMQckyadhSPYMVPJhBdYE8P9c27fg9RPmVaWNFx1cDaj61HnetqBiy",
|
||||
satoshiAmount: satoshiAmount,
|
||||
args: {"feeRate": feeRateType}))['fee'];
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
} catch (e, s) {
|
||||
aprox = -9999999999999999;
|
||||
}
|
||||
|
|
|
@ -32,17 +32,22 @@ class SimpleSwapAPI {
|
|||
|
||||
Future<dynamic> _makeGetRequest(Uri uri) async {
|
||||
final client = this.client ?? http.Client();
|
||||
int code = -1;
|
||||
try {
|
||||
final response = await client.get(
|
||||
uri,
|
||||
);
|
||||
|
||||
code = response.statusCode;
|
||||
|
||||
final parsed = jsonDecode(response.body);
|
||||
|
||||
return parsed;
|
||||
} catch (e, s) {
|
||||
Logging.instance
|
||||
.log("_makeRequest($uri) threw: $e\n$s", level: LogLevel.Error);
|
||||
Logging.instance.log(
|
||||
"_makeRequest($uri) HTTP:$code threw: $e\n$s",
|
||||
level: LogLevel.Error,
|
||||
);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,7 +84,9 @@ class NotificationsService extends ChangeNotifier {
|
|||
_timer = Timer.periodic(notificationRefreshInterval, (_) {
|
||||
Logging.instance
|
||||
.log("Periodic notifications update check", level: LogLevel.Info);
|
||||
_checkTrades();
|
||||
if (prefs.externalCalls) {
|
||||
_checkTrades();
|
||||
}
|
||||
_checkTransactions();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -7,9 +7,8 @@ import 'package:http/http.dart';
|
|||
import 'package:stackwallet/hive/db.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import 'package:stackwallet/utilities/prefs.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
class PriceAPI {
|
||||
static const refreshInterval = 60;
|
||||
|
|
|
@ -19,9 +19,7 @@ class PriceService extends ChangeNotifier {
|
|||
|
||||
Tuple2<Decimal, double> getPrice(Coin coin) => _cachedPrices[coin]!;
|
||||
|
||||
PriceService(this.baseTicker) {
|
||||
start(true);
|
||||
}
|
||||
PriceService(this.baseTicker);
|
||||
|
||||
Future<void> updatePrice() async {
|
||||
final priceMap =
|
||||
|
|
|
@ -159,8 +159,8 @@ abstract class DefaultNodes {
|
|||
);
|
||||
|
||||
static NodeModel get bitcoincashTestnet => NodeModel(
|
||||
host: "testnet.hsmiths.com",
|
||||
port: 53012,
|
||||
host: "bitcoincash-testnet.stackwallet.com",
|
||||
port: 60002,
|
||||
name: defaultName,
|
||||
id: _nodeId(Coin.bitcoincashTestnet),
|
||||
useSSL: true,
|
||||
|
|
|
@ -12,8 +12,7 @@ import 'package:stackwallet/utilities/enums/log_level_enum.dart';
|
|||
export 'enums/log_level_enum.dart';
|
||||
|
||||
class Logging {
|
||||
static const isArmLinux =
|
||||
bool.fromEnvironment("IS_ARM");
|
||||
static const isArmLinux = bool.fromEnvironment("IS_ARM");
|
||||
static final isTestEnv = Platform.environment["FLUTTER_TEST"] == "true";
|
||||
Logging._();
|
||||
static final Logging _instance = Logging._();
|
||||
|
@ -45,41 +44,47 @@ class Logging {
|
|||
core.bool printToConsole = true,
|
||||
core.bool printFullLength = false,
|
||||
}) {
|
||||
if (isTestEnv || isArmLinux) {
|
||||
Logger.print(object, normalLength: !printFullLength);
|
||||
return;
|
||||
}
|
||||
final now = core.DateTime.now().toUtc();
|
||||
final log = Log()
|
||||
..message = object.toString()
|
||||
..logLevel = level
|
||||
..timestampInMillisUTC = now.millisecondsSinceEpoch;
|
||||
if (level == LogLevel.Error || level == LogLevel.Fatal) {
|
||||
printFullLength = true;
|
||||
}
|
||||
try {
|
||||
if (isTestEnv || isArmLinux) {
|
||||
Logger.print(object, normalLength: !printFullLength);
|
||||
return;
|
||||
}
|
||||
final now = core.DateTime.now().toUtc();
|
||||
final log = Log()
|
||||
..message = object.toString()
|
||||
..logLevel = level
|
||||
..timestampInMillisUTC = now.millisecondsSinceEpoch;
|
||||
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) {
|
||||
final core.String logStr = "Log: ${log.toString()}";
|
||||
final core.int logLength = logStr.length;
|
||||
if (printToConsole) {
|
||||
final core.String logStr = "Log: ${log.toString()}";
|
||||
final core.int logLength = logStr.length;
|
||||
|
||||
if (!printFullLength || logLength <= defaultPrintLength) {
|
||||
debugPrint(logStr);
|
||||
} else {
|
||||
core.int start = 0;
|
||||
core.int endIndex = defaultPrintLength;
|
||||
core.int tmpLogLength = logLength;
|
||||
while (endIndex < logLength) {
|
||||
debugPrint(logStr.substring(start, endIndex));
|
||||
endIndex += defaultPrintLength;
|
||||
start += defaultPrintLength;
|
||||
tmpLogLength -= defaultPrintLength;
|
||||
}
|
||||
if (tmpLogLength > 0) {
|
||||
debugPrint(logStr.substring(start, logLength));
|
||||
if (!printFullLength || logLength <= defaultPrintLength) {
|
||||
debugPrint(logStr);
|
||||
} else {
|
||||
core.int start = 0;
|
||||
core.int endIndex = defaultPrintLength;
|
||||
core.int tmpLogLength = logLength;
|
||||
while (endIndex < logLength) {
|
||||
debugPrint(logStr.substring(start, endIndex));
|
||||
endIndex += defaultPrintLength;
|
||||
start += defaultPrintLength;
|
||||
tmpLogLength -= defaultPrintLength;
|
||||
}
|
||||
if (tmpLogLength > 0) {
|
||||
debugPrint(logStr.substring(start, logLength));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e, s) {
|
||||
print("problem trying to log");
|
||||
print("$e $s");
|
||||
Logger.print(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -546,7 +546,9 @@ class Prefs extends ChangeNotifier {
|
|||
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;
|
||||
|
||||
|
@ -567,6 +569,6 @@ class Prefs extends ChangeNotifier {
|
|||
Future<bool> _getHasExternalCalls() async {
|
||||
return await DB.instance.get<dynamic>(
|
||||
boxName: DB.boxNamePrefs, key: "externalCalls") as bool? ??
|
||||
false;
|
||||
true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -202,35 +202,39 @@ class _TransactionCardState extends ConsumerState<TransactionCard> {
|
|||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Flexible(
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Builder(
|
||||
builder: (_) {
|
||||
// TODO: modify Format.<functions> to take optional Coin parameter so this type oif check isn't done in ui
|
||||
int value = _transaction.amount;
|
||||
if (coin == Coin.monero) {
|
||||
value = (value ~/ 10000);
|
||||
} else if (coin == Coin.wownero) {
|
||||
value = (value ~/ 1000);
|
||||
}
|
||||
if (ref.watch(prefsChangeNotifierProvider
|
||||
.select((value) => value.externalCalls)))
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
if (ref.watch(prefsChangeNotifierProvider
|
||||
.select((value) => value.externalCalls)))
|
||||
Flexible(
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Builder(
|
||||
builder: (_) {
|
||||
// TODO: modify Format.<functions> to take optional Coin parameter so this type oif check isn't done in ui
|
||||
int value = _transaction.amount;
|
||||
if (coin == Coin.monero) {
|
||||
value = (value ~/ 10000);
|
||||
} else if (coin == Coin.wownero) {
|
||||
value = (value ~/ 1000);
|
||||
}
|
||||
|
||||
return Text(
|
||||
"${Format.localizedStringAsFixed(
|
||||
value: Format.satoshisToAmount(value) *
|
||||
price,
|
||||
locale: locale,
|
||||
decimalPlaces: 2,
|
||||
)} $baseCurrency",
|
||||
style: STextStyles.label(context),
|
||||
);
|
||||
},
|
||||
return Text(
|
||||
"${Format.localizedStringAsFixed(
|
||||
value: Format.satoshisToAmount(value) *
|
||||
price,
|
||||
locale: locale,
|
||||
decimalPlaces: 2,
|
||||
)} $baseCurrency",
|
||||
style: STextStyles.label(context),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
|
|
@ -11,7 +11,7 @@ description: Stack Wallet
|
|||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
version: 1.5.1+71
|
||||
version: 1.5.7+77
|
||||
|
||||
environment:
|
||||
sdk: ">=2.17.0 <3.0.0"
|
||||
|
|
2
scripts/prebuild.sh
Normal file → Executable file
2
scripts/prebuild.sh
Normal file → Executable file
|
@ -2,5 +2,5 @@
|
|||
KEYS=../lib/external_api_keys.dart
|
||||
if ! test -f "$KEYS"; then
|
||||
echo 'prebuild.sh: creating template lib/external_api_keys.dart file'
|
||||
echo 'const kChangeNowApiKey = "";' > $KEYS
|
||||
printf 'const kChangeNowApiKey = "";\nconst kSimpleSwapApiKey = "";' > $KEYS
|
||||
fi
|
||||
|
|
51
scripts/setup.sh
Normal file
51
scripts/setup.sh
Normal 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
|
|
@ -3283,9 +3283,9 @@ void main() {
|
|||
"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 {
|
||||
|
|
Loading…
Reference in a new issue