Firo balance type toggle ui and send from balance type switching

This commit is contained in:
julian 2023-12-19 18:34:20 -06:00
parent 311b2adfd9
commit acb0157d8a
12 changed files with 674 additions and 280 deletions

View file

@ -13,6 +13,7 @@ import 'package:stackwallet/wallets/wallet/supporting/epiccash_wallet_info_exten
Future<void> migrateWalletsToIsar({
required SecureStorageInterface secureStore,
}) async {
await MainDB.instance.initMainDB();
final allWalletsBox = await Hive.openBox<dynamic>(DB.boxNameAllWalletsData);
final names = DB.instance

View file

@ -15,6 +15,7 @@ import 'package:cw_core/monero_transaction_priority.dart';
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_native_splash/cli_commands.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
@ -49,10 +50,12 @@ import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/wallets/models/tx_data.dart';
import 'package:stackwallet/wallets/wallet/impl/firo_wallet.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart';
import 'package:stackwallet/widgets/animated_text.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -116,12 +119,14 @@ class _SendViewState extends ConsumerState<SendView> {
final _memoFocus = FocusNode();
late final bool isStellar;
late final bool isFiro;
Amount? _amountToSend;
Amount? _cachedAmountToSend;
String? _address;
bool _addressToggleFlag = false;
bool _isSparkAddress = false;
bool _cryptoAmountChangeLock = false;
late VoidCallback onCryptoAmountChanged;
@ -241,11 +246,17 @@ class _SendViewState extends ConsumerState<SendView> {
ref.read(previewTxButtonStateProvider.state).state =
(amount != null && amount > Amount.zero);
} else {
final isValidAddress = ref
.read(pWallets)
.getWallet(walletId)
.cryptoCurrency
.validateAddress(address ?? "");
final walletCurrency =
ref.read(pWallets).getWallet(walletId).cryptoCurrency;
final isValidAddress = walletCurrency.validateAddress(address ?? "");
_isSparkAddress = isValidAddress
? SparkInterface.validateSparkAddress(
address: address!,
isTestNet: walletCurrency.network == CryptoCurrencyNetwork.test,
)
: false;
ref.read(previewTxButtonStateProvider.state).state =
(isValidAddress && amount != null && amount > Amount.zero);
}
@ -254,7 +265,8 @@ class _SendViewState extends ConsumerState<SendView> {
late Future<String> _calculateFeesFuture;
Map<Amount, String> cachedFees = {};
Map<Amount, String> cachedFiroPrivateFees = {};
Map<Amount, String> cachedFiroLelantusFees = {};
Map<Amount, String> cachedFiroSparkFees = {};
Map<Amount, String> cachedFiroPublicFees = {};
Future<String> calculateFees(Amount amount) async {
@ -262,16 +274,23 @@ class _SendViewState extends ConsumerState<SendView> {
return "0";
}
if (coin == Coin.firo || coin == Coin.firoTestNet) {
if (ref.read(publicPrivateBalanceStateProvider.state).state ==
"Private") {
if (cachedFiroPrivateFees[amount] != null) {
return cachedFiroPrivateFees[amount]!;
}
} else {
if (cachedFiroPublicFees[amount] != null) {
return cachedFiroPublicFees[amount]!;
}
if (isFiro) {
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
case FiroType.public:
if (cachedFiroPublicFees[amount] != null) {
return cachedFiroPublicFees[amount]!;
}
break;
case FiroType.lelantus:
if (cachedFiroLelantusFees[amount] != null) {
return cachedFiroLelantusFees[amount]!;
}
break;
case FiroType.spark:
if (cachedFiroSparkFees[amount] != null) {
return cachedFiroSparkFees[amount]!;
}
break;
}
} else if (cachedFees[amount] != null) {
return cachedFees[amount]!;
@ -321,31 +340,37 @@ class _SendViewState extends ConsumerState<SendView> {
);
return cachedFees[amount]!;
} else if (coin == Coin.firo || coin == Coin.firoTestNet) {
if (ref.read(publicPrivateBalanceStateProvider.state).state ==
"Private") {
fee = await wallet.estimateFeeFor(amount, feeRate);
} else if (isFiro) {
final firoWallet = wallet as FiroWallet;
cachedFiroPrivateFees[amount] = ref.read(pAmountFormatter(coin)).format(
fee,
withUnitName: true,
indicatePrecisionLoss: false,
);
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
case FiroType.public:
fee = await firoWallet.estimateFeeFor(amount, feeRate);
cachedFiroPublicFees[amount] =
ref.read(pAmountFormatter(coin)).format(
fee,
withUnitName: true,
indicatePrecisionLoss: false,
);
return cachedFiroPublicFees[amount]!;
return cachedFiroPrivateFees[amount]!;
} else {
// TODO: [prio=high] firo public send fees refactor or something...
throw UnimplementedError("Firo pub fees todo");
// fee = await (manager.wallet as FiroWallet)
// .estimateFeeForPublic(amount, feeRate);
//
// cachedFiroPublicFees[amount] = ref.read(pAmountFormatter(coin)).format(
// fee,
// withUnitName: true,
// indicatePrecisionLoss: false,
// );
//
// return cachedFiroPublicFees[amount]!;
case FiroType.lelantus:
fee = await firoWallet.estimateFeeForLelantus(amount);
cachedFiroLelantusFees[amount] =
ref.read(pAmountFormatter(coin)).format(
fee,
withUnitName: true,
indicatePrecisionLoss: false,
);
return cachedFiroLelantusFees[amount]!;
case FiroType.spark:
fee = await firoWallet.estimateFeeForSpark(amount);
cachedFiroSparkFees[amount] = ref.read(pAmountFormatter(coin)).format(
fee,
withUnitName: true,
indicatePrecisionLoss: false,
);
return cachedFiroSparkFees[amount]!;
}
} else {
fee = await wallet.estimateFeeFor(amount, feeRate);
@ -369,13 +394,17 @@ class _SendViewState extends ConsumerState<SendView> {
final Amount amount = _amountToSend!;
final Amount availableBalance;
if ((coin == Coin.firo || coin == Coin.firoTestNet)) {
if (ref.read(publicPrivateBalanceStateProvider.state).state ==
"Private") {
availableBalance = ref.read(pWalletBalance(walletId)).spendable;
} else {
availableBalance =
ref.read(pWalletBalanceSecondary(walletId)).spendable;
if (isFiro) {
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
case FiroType.public:
availableBalance = wallet.info.cachedBalance.spendable;
break;
case FiroType.lelantus:
availableBalance = wallet.info.cachedBalanceSecondary.spendable;
break;
case FiroType.spark:
availableBalance = wallet.info.cachedBalanceTertiary.spendable;
break;
}
} else {
availableBalance = ref.read(pWalletBalance(walletId)).spendable;
@ -492,14 +521,63 @@ class _SendViewState extends ConsumerState<SendView> {
: null,
),
);
} else if (wallet is FiroWallet &&
ref.read(publicPrivateBalanceStateProvider.state).state ==
"Private") {
txDataFuture = wallet.prepareSendLelantus(
txData: TxData(
recipients: [(address: _address!, amount: amount)],
),
);
} else if (wallet is FiroWallet) {
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
case FiroType.public:
txDataFuture = wallet.prepareSend(
txData: TxData(
recipients: _isSparkAddress
? null
: [(address: _address!, amount: amount)],
sparkRecipients: _isSparkAddress
? [
(
address: _address!,
amount: amount,
memo: memoController.text,
subtractFeeFromAmount: false,
)
]
: null,
feeRateType: ref.read(feeRateTypeStateProvider),
satsPerVByte: isCustomFee ? customFeeRate : null,
utxos: (wallet is CoinControlInterface &&
coinControlEnabled &&
selectedUTXOs.isNotEmpty)
? selectedUTXOs
: null,
),
);
break;
case FiroType.lelantus:
txDataFuture = wallet.prepareSendLelantus(
txData: TxData(
recipients: [(address: _address!, amount: amount)],
),
);
break;
case FiroType.spark:
txDataFuture = wallet.prepareSendSpark(
txData: TxData(
recipients: _isSparkAddress
? null
: [(address: _address!, amount: amount)],
sparkRecipients: _isSparkAddress
? [
(
address: _address!,
amount: amount,
memo: memoController.text,
subtractFeeFromAmount: false,
)
]
: null,
),
);
break;
}
} else {
final memo = coin == Coin.stellar || coin == Coin.stellarTestnet
? memoController.text
@ -610,6 +688,7 @@ class _SendViewState extends ConsumerState<SendView> {
clipboard = widget.clipboard;
scanner = widget.barcodeScanner;
isStellar = coin == Coin.stellar || coin == Coin.stellarTestnet;
isFiro = coin == Coin.firo || coin == Coin.firoTestNet;
sendToController = TextEditingController();
cryptoAmountController = TextEditingController();
@ -718,7 +797,7 @@ class _SendViewState extends ConsumerState<SendView> {
),
);
if (coin == Coin.firo || coin == Coin.firoTestNet) {
if (isFiro) {
ref.listen(publicPrivateBalanceStateProvider, (previous, next) {
if (_amountToSend == null) {
setState(() {
@ -830,10 +909,9 @@ class _SendViewState extends ConsumerState<SendView> {
// const SizedBox(
// height: 2,
// ),
if (coin == Coin.firo ||
coin == Coin.firoTestNet)
if (isFiro)
Text(
"${ref.watch(publicPrivateBalanceStateProvider.state).state} balance",
"${ref.watch(publicPrivateBalanceStateProvider.state).state.name.capitalize()} balance",
style: STextStyles.label(context)
.copyWith(fontSize: 10),
),
@ -849,22 +927,29 @@ class _SendViewState extends ConsumerState<SendView> {
const Spacer(),
Builder(builder: (context) {
final Amount amount;
if (coin != Coin.firo &&
coin != Coin.firoTestNet) {
if (ref
.watch(
publicPrivateBalanceStateProvider
.state)
.state ==
"Private") {
amount = ref
.read(pWalletBalance(walletId))
.spendable;
} else {
amount = ref
.read(pWalletBalanceSecondary(
walletId))
.spendable;
if (isFiro) {
switch (ref
.watch(
publicPrivateBalanceStateProvider
.state)
.state) {
case FiroType.public:
amount = ref
.read(pWalletBalance(walletId))
.spendable;
break;
case FiroType.lelantus:
amount = ref
.read(pWalletBalanceSecondary(
walletId))
.spendable;
break;
case FiroType.spark:
amount = ref
.read(pWalletBalanceTertiary(
walletId))
.spendable;
break;
}
} else {
amount = ref
@ -1245,7 +1330,7 @@ class _SendViewState extends ConsumerState<SendView> {
const SizedBox(
height: 10,
),
if (isStellar)
if (isStellar || _isSparkAddress)
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
@ -1355,21 +1440,21 @@ class _SendViewState extends ConsumerState<SendView> {
}
},
),
if (coin == Coin.firo)
if (isFiro)
const SizedBox(
height: 12,
),
if (coin == Coin.firo)
if (isFiro)
Text(
"Send from",
style: STextStyles.smallMed12(context),
textAlign: TextAlign.left,
),
if (coin == Coin.firo)
if (isFiro)
const SizedBox(
height: 8,
),
if (coin == Coin.firo)
if (isFiro)
Stack(
children: [
TextField(
@ -1414,47 +1499,53 @@ class _SendViewState extends ConsumerState<SendView> {
Row(
children: [
Text(
"${ref.watch(publicPrivateBalanceStateProvider.state).state} balance",
"${ref.watch(publicPrivateBalanceStateProvider.state).state.name.capitalize()} balance",
style: STextStyles.itemSubtitle12(
context),
),
const SizedBox(
width: 10,
),
if (ref
.read(
publicPrivateBalanceStateProvider
.state)
.state ==
"Private")
Text(
ref
.watch(
pAmountFormatter(coin))
.format(
ref
.watch(
pWalletBalanceSecondary(
walletId))
.spendable,
),
style: STextStyles.itemSubtitle(
context),
)
else
Text(
ref
.watch(
pAmountFormatter(coin))
.format(
ref
.watch(pWalletBalance(
Builder(builder: (_) {
final Amount amount;
switch (ref
.read(
publicPrivateBalanceStateProvider
.state)
.state) {
case FiroType.public:
amount = ref
.watch(pWalletBalance(
walletId))
.spendable;
break;
case FiroType.lelantus:
amount = ref
.watch(
pWalletBalanceSecondary(
walletId))
.spendable,
.spendable;
break;
case FiroType.spark:
amount = ref
.watch(
pWalletBalanceTertiary(
walletId))
.spendable;
break;
}
return Text(
ref
.watch(
pAmountFormatter(coin))
.format(
amount,
),
style: STextStyles.itemSubtitle(
context),
),
);
}),
],
),
SvgPicture.asset(
@ -1486,21 +1577,36 @@ class _SendViewState extends ConsumerState<SendView> {
CustomTextButton(
text: "Send all ${coin.ticker}",
onTap: () async {
if ((coin == Coin.firo ||
coin == Coin.firoTestNet) &&
ref
.read(
publicPrivateBalanceStateProvider
.state)
.state ==
"Public") {
if (isFiro) {
final Amount amount;
switch (ref
.read(
publicPrivateBalanceStateProvider
.state)
.state) {
case FiroType.public:
amount = ref
.read(pWalletBalance(walletId))
.spendable;
break;
case FiroType.lelantus:
amount = ref
.read(pWalletBalanceSecondary(
walletId))
.spendable;
break;
case FiroType.spark:
amount = ref
.read(pWalletBalanceTertiary(
walletId))
.spendable;
break;
}
cryptoAmountController.text = ref
.read(pAmountFormatter(coin))
.format(
ref
.read(pWalletBalanceSecondary(
walletId))
.spendable,
amount,
withUnitName: false,
);
} else {
@ -1935,14 +2041,13 @@ class _SendViewState extends ConsumerState<SendView> {
Constants.size.circularBorderRadius,
),
),
onPressed: (coin == Coin.firo ||
coin == Coin.firoTestNet) &&
onPressed: isFiro &&
ref
.watch(
publicPrivateBalanceStateProvider
.state)
.state ==
"Private"
.state !=
FiroType.public
? null
: () {
showModalBottomSheet<dynamic>(
@ -1993,14 +2098,13 @@ class _SendViewState extends ConsumerState<SendView> {
),
);
},
child: ((coin == Coin.firo ||
coin == Coin.firoTestNet) &&
child: (isFiro &&
ref
.watch(
publicPrivateBalanceStateProvider
.state)
.state ==
"Private")
.state !=
FiroType.public)
? Row(
children: [
FutureBuilder(

View file

@ -101,9 +101,9 @@ class _FiroBalanceSelectionSheetState
onTap: () {
final state =
ref.read(publicPrivateBalanceStateProvider.state).state;
if (state != "Private") {
if (state != FiroType.spark) {
ref.read(publicPrivateBalanceStateProvider.state).state =
"Private";
FiroType.spark;
}
Navigator.of(context).pop();
},
@ -122,7 +122,7 @@ class _FiroBalanceSelectionSheetState
activeColor: Theme.of(context)
.extension<StackColors>()!
.radioButtonIconEnabled,
value: "Private",
value: FiroType.spark,
groupValue: ref
.watch(
publicPrivateBalanceStateProvider.state)
@ -131,7 +131,7 @@ class _FiroBalanceSelectionSheetState
ref
.read(publicPrivateBalanceStateProvider
.state)
.state = "Private";
.state = FiroType.spark;
Navigator.of(context).pop();
},
@ -149,7 +149,86 @@ class _FiroBalanceSelectionSheetState
// Row(
// children: [
Text(
"Private balance",
"Spark balance",
style: STextStyles.titleBold12(context),
textAlign: TextAlign.left,
),
const SizedBox(
width: 2,
),
Text(
ref.watch(pAmountFormatter(coin)).format(
firoWallet
.info.cachedBalanceTertiary.spendable,
),
style: STextStyles.itemSubtitle(context),
textAlign: TextAlign.left,
),
],
),
// ],
// ),
)
],
),
),
),
const SizedBox(
height: 16,
),
GestureDetector(
onTap: () {
final state =
ref.read(publicPrivateBalanceStateProvider.state).state;
if (state != FiroType.lelantus) {
ref.read(publicPrivateBalanceStateProvider.state).state =
FiroType.lelantus;
}
Navigator.of(context).pop();
},
child: Container(
color: Colors.transparent,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(
width: 20,
height: 20,
child: Radio(
activeColor: Theme.of(context)
.extension<StackColors>()!
.radioButtonIconEnabled,
value: FiroType.lelantus,
groupValue: ref
.watch(
publicPrivateBalanceStateProvider.state)
.state,
onChanged: (x) {
ref
.read(publicPrivateBalanceStateProvider
.state)
.state = FiroType.lelantus;
Navigator.of(context).pop();
},
),
),
],
),
const SizedBox(
width: 12,
),
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Row(
// children: [
Text(
"Lelantus balance",
style: STextStyles.titleBold12(context),
textAlign: TextAlign.left,
),
@ -180,9 +259,9 @@ class _FiroBalanceSelectionSheetState
onTap: () {
final state =
ref.read(publicPrivateBalanceStateProvider.state).state;
if (state != "Public") {
if (state != FiroType.public) {
ref.read(publicPrivateBalanceStateProvider.state).state =
"Public";
FiroType.public;
}
Navigator.of(context).pop();
},
@ -200,7 +279,7 @@ class _FiroBalanceSelectionSheetState
activeColor: Theme.of(context)
.extension<StackColors>()!
.radioButtonIconEnabled,
value: "Public",
value: FiroType.public,
groupValue: ref
.watch(
publicPrivateBalanceStateProvider.state)
@ -209,7 +288,7 @@ class _FiroBalanceSelectionSheetState
ref
.read(publicPrivateBalanceStateProvider
.state)
.state = "Public";
.state = FiroType.public;
Navigator.of(context).pop();
},
),

View file

@ -25,8 +25,10 @@ import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
enum _BalanceType {
available,
full,
privateAvailable,
privateFull;
lelantusAvailable,
lelantusFull,
sparkAvailable,
sparkFull;
}
class WalletBalanceToggleSheet extends ConsumerWidget {
@ -39,9 +41,10 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final maxHeight = MediaQuery.of(context).size.height * 0.60;
final maxHeight = MediaQuery.of(context).size.height * 0.90;
final coin = ref.watch(pWalletCoin(walletId));
final isFiro = coin == Coin.firo || coin == Coin.firoTestNet;
Balance balance = ref.watch(pWalletBalance(walletId));
@ -52,18 +55,27 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
: _BalanceType.full;
Balance? balanceSecondary;
if (coin == Coin.firo || coin == Coin.firoTestNet) {
Balance? balanceTertiary;
if (isFiro) {
balanceSecondary = ref.watch(pWalletBalanceSecondary(walletId));
balanceTertiary = ref.watch(pWalletBalanceTertiary(walletId));
final temp = balance;
balance = balanceSecondary!;
balanceSecondary = temp;
switch (ref.watch(publicPrivateBalanceStateProvider.state).state) {
case FiroType.spark:
_bal = _bal == _BalanceType.available
? _BalanceType.sparkAvailable
: _BalanceType.sparkFull;
break;
if (ref.watch(publicPrivateBalanceStateProvider.state).state ==
"Private") {
_bal = _bal == _BalanceType.available
? _BalanceType.privateAvailable
: _BalanceType.privateFull;
case FiroType.lelantus:
_bal = _bal == _BalanceType.available
? _BalanceType.lelantusAvailable
: _BalanceType.lelantusFull;
break;
case FiroType.public:
// already set above
break;
}
}
@ -116,22 +128,21 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
height: 24,
),
BalanceSelector(
title:
"Available${balanceSecondary != null ? " public" : ""} balance",
title: "Available${isFiro ? " public" : ""} balance",
coin: coin,
balance: balance.spendable,
onPressed: () {
ref.read(walletBalanceToggleStateProvider.state).state =
WalletBalanceToggleState.available;
ref.read(publicPrivateBalanceStateProvider.state).state =
"Public";
FiroType.public;
Navigator.of(context).pop();
},
onChanged: (_) {
ref.read(walletBalanceToggleStateProvider.state).state =
WalletBalanceToggleState.available;
ref.read(publicPrivateBalanceStateProvider.state).state =
"Public";
FiroType.public;
Navigator.of(context).pop();
},
value: _BalanceType.available,
@ -141,22 +152,21 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
height: 12,
),
BalanceSelector(
title:
"Full${balanceSecondary != null ? " public" : ""} balance",
title: "Full${isFiro ? " public" : ""} balance",
coin: coin,
balance: balance.total,
onPressed: () {
ref.read(walletBalanceToggleStateProvider.state).state =
WalletBalanceToggleState.full;
ref.read(publicPrivateBalanceStateProvider.state).state =
"Public";
FiroType.public;
Navigator.of(context).pop();
},
onChanged: (_) {
ref.read(walletBalanceToggleStateProvider.state).state =
WalletBalanceToggleState.full;
ref.read(publicPrivateBalanceStateProvider.state).state =
"Public";
FiroType.public;
Navigator.of(context).pop();
},
value: _BalanceType.full,
@ -168,24 +178,24 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
),
if (balanceSecondary != null)
BalanceSelector(
title: "Available private balance",
title: "Available lelantus balance",
coin: coin,
balance: balanceSecondary.spendable,
onPressed: () {
ref.read(walletBalanceToggleStateProvider.state).state =
WalletBalanceToggleState.available;
ref.read(publicPrivateBalanceStateProvider.state).state =
"Private";
FiroType.lelantus;
Navigator.of(context).pop();
},
onChanged: (_) {
ref.read(walletBalanceToggleStateProvider.state).state =
WalletBalanceToggleState.available;
ref.read(publicPrivateBalanceStateProvider.state).state =
"Private";
FiroType.lelantus;
Navigator.of(context).pop();
},
value: _BalanceType.privateAvailable,
value: _BalanceType.lelantusAvailable,
groupValue: _bal,
),
if (balanceSecondary != null)
@ -194,24 +204,76 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
),
if (balanceSecondary != null)
BalanceSelector(
title: "Full private balance",
title: "Full lelantus balance",
coin: coin,
balance: balanceSecondary.total,
onPressed: () {
ref.read(walletBalanceToggleStateProvider.state).state =
WalletBalanceToggleState.full;
ref.read(publicPrivateBalanceStateProvider.state).state =
"Private";
FiroType.lelantus;
Navigator.of(context).pop();
},
onChanged: (_) {
ref.read(walletBalanceToggleStateProvider.state).state =
WalletBalanceToggleState.full;
ref.read(publicPrivateBalanceStateProvider.state).state =
"Private";
FiroType.lelantus;
Navigator.of(context).pop();
},
value: _BalanceType.privateFull,
value: _BalanceType.lelantusFull,
groupValue: _bal,
),
if (balanceTertiary != null)
const SizedBox(
height: 12,
),
if (balanceTertiary != null)
BalanceSelector(
title: "Available spark balance",
coin: coin,
balance: balanceTertiary.spendable,
onPressed: () {
ref.read(walletBalanceToggleStateProvider.state).state =
WalletBalanceToggleState.available;
ref.read(publicPrivateBalanceStateProvider.state).state =
FiroType.spark;
Navigator.of(context).pop();
},
onChanged: (_) {
ref.read(walletBalanceToggleStateProvider.state).state =
WalletBalanceToggleState.available;
ref.read(publicPrivateBalanceStateProvider.state).state =
FiroType.spark;
Navigator.of(context).pop();
},
value: _BalanceType.sparkAvailable,
groupValue: _bal,
),
if (balanceTertiary != null)
const SizedBox(
height: 12,
),
if (balanceTertiary != null)
BalanceSelector(
title: "Full spark balance",
coin: coin,
balance: balanceTertiary.total,
onPressed: () {
ref.read(walletBalanceToggleStateProvider.state).state =
WalletBalanceToggleState.full;
ref.read(publicPrivateBalanceStateProvider.state).state =
FiroType.spark;
Navigator.of(context).pop();
},
onChanged: (_) {
ref.read(walletBalanceToggleStateProvider.state).state =
WalletBalanceToggleState.full;
ref.read(publicPrivateBalanceStateProvider.state).state =
FiroType.spark;
Navigator.of(context).pop();
},
value: _BalanceType.sparkFull,
groupValue: _bal,
),
const SizedBox(

View file

@ -12,6 +12,7 @@ import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_native_splash/cli_commands.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart';
@ -29,6 +30,7 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/wallets/wallet/impl/banano_wallet.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
class WalletSummaryInfo extends ConsumerWidget {
@ -45,6 +47,8 @@ class WalletSummaryInfo extends ConsumerWidget {
showModalBottomSheet<dynamic>(
backgroundColor: Colors.transparent,
context: context,
useSafeArea: true,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
@ -58,10 +62,6 @@ class WalletSummaryInfo extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
debugPrint("BUILD: $runtimeType");
bool isMonkey = true;
final receivingAddress = ref.watch(pWalletReceivingAddress(walletId));
final externalCalls = ref.watch(
prefsChangeNotifierProvider.select((value) => value.externalCalls));
final coin = ref.watch(pWalletCoin(walletId));
@ -81,19 +81,28 @@ class WalletSummaryInfo extends ConsumerWidget {
WalletBalanceToggleState.available;
final Amount balanceToShow;
String title;
final String title;
if (coin == Coin.firo || coin == Coin.firoTestNet) {
final _showPrivate =
ref.watch(publicPrivateBalanceStateProvider.state).state == "Private";
final type = ref.watch(publicPrivateBalanceStateProvider.state).state;
title =
"${_showAvailable ? "Available" : "Full"} ${type.name.capitalize()} balance";
switch (type) {
case FiroType.spark:
final balance = ref.watch(pWalletBalanceTertiary(walletId));
balanceToShow = _showAvailable ? balance.spendable : balance.total;
break;
final secondaryBal = ref.watch(pWalletBalanceSecondary(walletId));
case FiroType.lelantus:
final balance = ref.watch(pWalletBalanceSecondary(walletId));
balanceToShow = _showAvailable ? balance.spendable : balance.total;
break;
final bal = _showPrivate ? balance : secondaryBal;
balanceToShow = _showAvailable ? bal.spendable : bal.total;
title = _showAvailable ? "Available" : "Full";
title += _showPrivate ? " private balance" : " public balance";
case FiroType.public:
final balance = ref.watch(pWalletBalance(walletId));
balanceToShow = _showAvailable ? balance.spendable : balance.total;
break;
}
} else {
balanceToShow = _showAvailable ? balance.spendable : balance.total;
title = _showAvailable ? "Available balance" : "Full balance";
@ -102,8 +111,8 @@ class WalletSummaryInfo extends ConsumerWidget {
List<int>? imageBytes;
if (coin == Coin.banano) {
// TODO: [prio=high] fix this and uncomment:
// imageBytes = (manager.wallet as BananoWallet).getMonkeyImageBytes();
imageBytes = (ref.watch(pWallets).getWallet(walletId) as BananoWallet)
.getMonkeyImageBytes();
}
return ConditionalParent(

View file

@ -46,8 +46,6 @@ import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/ui/transaction_filter_provider.dart';
import 'package:stackwallet/providers/ui/unread_notifications_provider.dart';
import 'package:stackwallet/providers/wallet/my_paynym_account_state_provider.dart';
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
import 'package:stackwallet/providers/wallet/wallet_balance_toggle_state_provider.dart';
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
@ -63,7 +61,6 @@ import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/backup_frequency_type.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/show_loading.dart';
import 'package:stackwallet/utilities/text_styles.dart';
@ -951,20 +948,21 @@ class _WalletViewState extends ConsumerState<WalletView> {
label: "Send",
icon: const SendNavIcon(),
onTap: () {
switch (ref
.read(walletBalanceToggleStateProvider.state)
.state) {
case WalletBalanceToggleState.full:
ref
.read(publicPrivateBalanceStateProvider.state)
.state = "Public";
break;
case WalletBalanceToggleState.available:
ref
.read(publicPrivateBalanceStateProvider.state)
.state = "Private";
break;
}
// not sure what this is supposed to accomplish?
// switch (ref
// .read(walletBalanceToggleStateProvider.state)
// .state) {
// case WalletBalanceToggleState.full:
// ref
// .read(publicPrivateBalanceStateProvider.state)
// .state = "Public";
// break;
// case WalletBalanceToggleState.available:
// ref
// .read(publicPrivateBalanceStateProvider.state)
// .state = "Private";
// break;
// }
Navigator.of(context).pushNamed(
SendView.routeName,
arguments: Tuple2(

View file

@ -10,6 +10,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
import 'package:stackwallet/providers/wallet/wallet_balance_toggle_state_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
@ -80,6 +81,8 @@ class DesktopPrivateBalanceToggleButton extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final currentType = ref.watch(publicPrivateBalanceStateProvider);
return SizedBox(
height: 22,
width: 22,
@ -87,13 +90,21 @@ class DesktopPrivateBalanceToggleButton extends ConsumerWidget {
color: Theme.of(context).extension<StackColors>()!.buttonBackSecondary,
splashColor: Theme.of(context).extension<StackColors>()!.highlight,
onPressed: () {
if (ref.read(walletPrivateBalanceToggleStateProvider.state).state ==
WalletBalanceToggleState.available) {
ref.read(walletPrivateBalanceToggleStateProvider.state).state =
WalletBalanceToggleState.full;
} else {
ref.read(walletPrivateBalanceToggleStateProvider.state).state =
WalletBalanceToggleState.available;
switch (currentType) {
case FiroType.public:
ref.read(publicPrivateBalanceStateProvider.state).state =
FiroType.lelantus;
break;
case FiroType.lelantus:
ref.read(publicPrivateBalanceStateProvider.state).state =
FiroType.spark;
break;
case FiroType.spark:
ref.read(publicPrivateBalanceStateProvider.state).state =
FiroType.public;
break;
}
onPressed?.call();
},
@ -110,12 +121,14 @@ class DesktopPrivateBalanceToggleButton extends ConsumerWidget {
child: Center(
child: Image(
image: AssetImage(
ref.watch(walletPrivateBalanceToggleStateProvider.state).state ==
WalletBalanceToggleState.available
? Assets.png.glassesHidden
: Assets.png.glasses,
currentType == FiroType.public
? Assets.png.glasses
: Assets.png.glassesHidden,
),
width: 16,
color: currentType == FiroType.spark
? Theme.of(context).extension<StackColors>()!.accentColorYellow
: null,
),
),
),

View file

@ -48,10 +48,12 @@ import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/wallets/models/tx_data.dart';
import 'package:stackwallet/wallets/wallet/impl/firo_wallet.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart';
import 'package:stackwallet/widgets/animated_text.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
@ -123,6 +125,8 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
bool get isPaynymSend => widget.accountLite != null;
bool _isSparkAddress = false;
bool isCustomFee = false;
int customFeeRate = 1;
(FeeRateType, String?, String?)? feeSelectionResult;
@ -140,13 +144,16 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
final Amount amount = _amountToSend!;
final Amount availableBalance;
if ((coin == Coin.firo || coin == Coin.firoTestNet)) {
if (ref.read(publicPrivateBalanceStateProvider.state).state ==
"Private") {
availableBalance = wallet.info.cachedBalanceSecondary.spendable;
// (manager.wallet as FiroWallet).availablePrivateBalance();
} else {
availableBalance = wallet.info.cachedBalance.spendable;
// (manager.wallet as FiroWallet).availablePublicBalance();
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
case FiroType.public:
availableBalance = wallet.info.cachedBalance.spendable;
break;
case FiroType.lelantus:
availableBalance = wallet.info.cachedBalanceSecondary.spendable;
break;
case FiroType.spark:
availableBalance = wallet.info.cachedBalanceTertiary.spendable;
break;
}
} else {
availableBalance = wallet.info.cachedBalance.spendable;
@ -311,14 +318,63 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
: null,
),
);
} else if (wallet is FiroWallet &&
ref.read(publicPrivateBalanceStateProvider.state).state ==
"Private") {
txDataFuture = wallet.prepareSendLelantus(
txData: TxData(
recipients: [(address: _address!, amount: amount)],
),
);
} else if (wallet is FiroWallet) {
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
case FiroType.public:
txDataFuture = wallet.prepareSend(
txData: TxData(
recipients: _isSparkAddress
? null
: [(address: _address!, amount: amount)],
sparkRecipients: _isSparkAddress
? [
(
address: _address!,
amount: amount,
memo: memoController.text,
subtractFeeFromAmount: false,
)
]
: null,
feeRateType: ref.read(feeRateTypeStateProvider),
satsPerVByte: isCustomFee ? customFeeRate : null,
utxos: (wallet is CoinControlInterface &&
coinControlEnabled &&
ref.read(desktopUseUTXOs).isNotEmpty)
? ref.read(desktopUseUTXOs)
: null,
),
);
break;
case FiroType.lelantus:
txDataFuture = wallet.prepareSendLelantus(
txData: TxData(
recipients: [(address: _address!, amount: amount)],
),
);
break;
case FiroType.spark:
txDataFuture = wallet.prepareSendSpark(
txData: TxData(
recipients: _isSparkAddress
? null
: [(address: _address!, amount: amount)],
sparkRecipients: _isSparkAddress
? [
(
address: _address!,
amount: amount,
memo: memoController.text,
subtractFeeFromAmount: false,
)
]
: null,
),
);
break;
}
} else {
final memo = isStellar ? memoController.text : null;
txDataFuture = wallet.prepareSend(
@ -520,11 +576,17 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
ref.read(previewTxButtonStateProvider.state).state =
(amount != null && amount > Amount.zero);
} else {
final isValidAddress = ref
.read(pWallets)
.getWallet(walletId)
.cryptoCurrency
.validateAddress(address ?? "");
final walletCurrency =
ref.read(pWallets).getWallet(walletId).cryptoCurrency;
final isValidAddress = walletCurrency.validateAddress(address ?? "");
_isSparkAddress = isValidAddress
? SparkInterface.validateSparkAddress(
address: address!,
isTestNet: walletCurrency.network == CryptoCurrencyNetwork.test,
)
: false;
ref.read(previewTxButtonStateProvider.state).state =
(isValidAddress && amount != null && amount > Amount.zero);
}
@ -687,11 +749,23 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
Future<void> sendAllTapped() async {
final info = ref.read(pWalletInfo(walletId));
if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
ref.read(publicPrivateBalanceStateProvider.state).state == "Private") {
cryptoAmountController.text = info
.cachedBalanceSecondary.spendable.decimal
.toStringAsFixed(coin.decimals);
if (coin == Coin.firo || coin == Coin.firoTestNet) {
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
case FiroType.public:
cryptoAmountController.text = info.cachedBalance.spendable.decimal
.toStringAsFixed(coin.decimals);
break;
case FiroType.lelantus:
cryptoAmountController.text = info
.cachedBalanceSecondary.spendable.decimal
.toStringAsFixed(coin.decimals);
break;
case FiroType.spark:
cryptoAmountController.text = info
.cachedBalanceTertiary.spendable.decimal
.toStringAsFixed(coin.decimals);
break;
}
} else {
cryptoAmountController.text =
info.cachedBalance.spendable.decimal.toStringAsFixed(coin.decimals);
@ -841,11 +915,31 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
value: ref.watch(publicPrivateBalanceStateProvider.state).state,
items: [
DropdownMenuItem(
value: "Private",
value: FiroType.spark,
child: Row(
children: [
Text(
"Private balance",
"Spark balance",
style: STextStyles.itemSubtitle12(context),
),
const SizedBox(
width: 10,
),
Text(
ref.watch(pAmountFormatter(coin)).format(ref
.watch(pWalletBalanceTertiary(walletId))
.spendable),
style: STextStyles.itemSubtitle(context),
),
],
),
),
DropdownMenuItem(
value: FiroType.lelantus,
child: Row(
children: [
Text(
"Lelantus balance",
style: STextStyles.itemSubtitle12(context),
),
const SizedBox(
@ -861,7 +955,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
),
),
DropdownMenuItem(
value: "Public",
value: FiroType.public,
child: Row(
children: [
Text(
@ -881,9 +975,9 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
),
],
onChanged: (value) {
if (value is String) {
if (value is FiroType) {
setState(() {
ref.watch(publicPrivateBalanceStateProvider.state).state =
ref.read(publicPrivateBalanceStateProvider.state).state =
value;
});
}
@ -1316,11 +1410,11 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
}
},
),
if (isStellar)
if (isStellar || _isSparkAddress)
const SizedBox(
height: 10,
),
if (isStellar)
if (isStellar || _isSparkAddress)
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
@ -1386,8 +1480,12 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
ConditionalParent(
condition: coin.isElectrumXCoin &&
!(((coin == Coin.firo || coin == Coin.firoTestNet) &&
ref.read(publicPrivateBalanceStateProvider.state).state ==
"Private")),
(ref.watch(publicPrivateBalanceStateProvider.state).state ==
FiroType.lelantus ||
ref
.watch(publicPrivateBalanceStateProvider.state)
.state ==
FiroType.spark))),
builder: (child) => Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@ -1485,15 +1583,33 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
.read(
publicPrivateBalanceStateProvider
.state)
.state ==
"Private") {
throw UnimplementedError("FIXME");
// TODO: [prio=high] firo fee fix
// ref
// .read(feeSheetSessionCacheProvider)
// .average[amount] = await (manager.wallet
// as FiroWallet)
// .estimateFeeForPublic(amount, feeRate);
.state !=
FiroType.public) {
final firoWallet = wallet as FiroWallet;
if (ref
.read(
publicPrivateBalanceStateProvider
.state)
.state ==
FiroType.lelantus) {
ref
.read(feeSheetSessionCacheProvider)
.average[amount] =
await firoWallet
.estimateFeeForLelantus(amount);
} else if (ref
.read(
publicPrivateBalanceStateProvider
.state)
.state ==
FiroType.spark) {
ref
.read(feeSheetSessionCacheProvider)
.average[amount] =
await firoWallet
.estimateFeeForSpark(amount);
}
} else {
ref
.read(feeSheetSessionCacheProvider)
@ -1531,7 +1647,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
.watch(
publicPrivateBalanceStateProvider.state)
.state ==
"Private"
FiroType.lelantus
? Text(
"~${ref.watch(pAmountFormatter(coin)).format(
Amount(

View file

@ -15,6 +15,7 @@ import 'package:stackwallet/pages/token_view/token_view.dart';
import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_refresh_button.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_balance_toggle_button.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
import 'package:stackwallet/providers/wallet/wallet_balance_toggle_state_provider.dart';
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
import 'package:stackwallet/themes/stack_colors.dart';
@ -61,6 +62,7 @@ class _WDesktopWalletSummaryState extends ConsumerState<DesktopWalletSummary> {
),
);
final coin = ref.watch(pWalletCoin(widget.walletId));
final isFiro = coin == Coin.firo || coin == Coin.firoTestNet;
final locale = ref.watch(
localeServiceChangeNotifierProvider.select((value) => value.locale));
@ -82,29 +84,30 @@ class _WDesktopWalletSummaryState extends ConsumerState<DesktopWalletSummary> {
ref.watch(walletBalanceToggleStateProvider.state).state ==
WalletBalanceToggleState.available;
Balance balance = widget.isToken
? ref.watch(tokenServiceProvider.select((value) => value!.balance))
: ref.watch(pWalletBalance(walletId));
final Amount balanceToShow;
if (isFiro) {
switch (ref.watch(publicPrivateBalanceStateProvider.state).state) {
case FiroType.spark:
final balance = ref.watch(pWalletBalanceTertiary(walletId));
balanceToShow = _showAvailable ? balance.spendable : balance.total;
break;
Amount balanceToShow;
if (coin == Coin.firo || coin == Coin.firoTestNet) {
final balanceSecondary = ref.watch(pWalletBalanceSecondary(walletId));
final showPrivate =
ref.watch(walletPrivateBalanceToggleStateProvider.state).state ==
WalletBalanceToggleState.available;
case FiroType.lelantus:
final balance = ref.watch(pWalletBalanceSecondary(walletId));
balanceToShow = _showAvailable ? balance.spendable : balance.total;
break;
if (_showAvailable) {
balanceToShow =
showPrivate ? balanceSecondary.spendable : balance.spendable;
} else {
balanceToShow = showPrivate ? balanceSecondary.total : balance.total;
case FiroType.public:
final balance = ref.watch(pWalletBalance(walletId));
balanceToShow = _showAvailable ? balance.spendable : balance.total;
break;
}
} else {
if (_showAvailable) {
balanceToShow = balance.spendable;
} else {
balanceToShow = balance.total;
}
Balance balance = widget.isToken
? ref.watch(tokenServiceProvider.select((value) => value!.balance))
: ref.watch(pWalletBalance(walletId));
balanceToShow = _showAvailable ? balance.spendable : balance.total;
}
return Consumer(

View file

@ -10,5 +10,11 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
enum FiroType {
public,
lelantus,
spark;
}
final publicPrivateBalanceStateProvider =
StateProvider<String>((_) => "Private");
StateProvider<FiroType>((_) => FiroType.lelantus);

View file

@ -14,7 +14,3 @@ import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart';
final walletBalanceToggleStateProvider =
StateProvider.autoDispose<WalletBalanceToggleState>(
(ref) => WalletBalanceToggleState.full);
final walletPrivateBalanceToggleStateProvider =
StateProvider.autoDispose<WalletBalanceToggleState>(
(ref) => WalletBalanceToggleState.full);

View file

@ -104,7 +104,14 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
}
Future<Amount> estimateFeeForSpark(Amount amount) async {
throw UnimplementedError();
// int spendAmount = amount.raw.toInt();
// if (spendAmount == 0) {
return Amount(
rawValue: BigInt.from(0),
fractionDigits: cryptoCurrency.fractionDigits,
);
// }
// TODO actual fee estimation
}
/// Spark to Spark/Transparent (spend) creation
@ -505,7 +512,7 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
rawValue: unusedCoins
.where((e) =>
e.height != null &&
e.height! + cryptoCurrency.minConfirms >= currentHeight)
e.height! + cryptoCurrency.minConfirms <= currentHeight)
.map((e) => e.value)
.fold(BigInt.zero, (prev, e) => prev + e),
fractionDigits: cryptoCurrency.fractionDigits,