mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-01-08 19:59:29 +00:00
refactor send screen address validation to take into account not being able to send from lelantus to spark directly
This commit is contained in:
parent
97ff9ecf8b
commit
02cb79c6a3
4 changed files with 376 additions and 341 deletions
|
@ -121,38 +121,173 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
late final bool isStellar;
|
late final bool isStellar;
|
||||||
late final bool isFiro;
|
late final bool isFiro;
|
||||||
|
|
||||||
Amount? _amountToSend;
|
|
||||||
Amount? _cachedAmountToSend;
|
Amount? _cachedAmountToSend;
|
||||||
String? _address;
|
String? _address;
|
||||||
|
|
||||||
bool _addressToggleFlag = false;
|
bool _addressToggleFlag = false;
|
||||||
bool _isSparkAddress = false;
|
|
||||||
|
|
||||||
bool _cryptoAmountChangeLock = false;
|
bool _cryptoAmountChangeLock = false;
|
||||||
late VoidCallback onCryptoAmountChanged;
|
late VoidCallback onCryptoAmountChanged;
|
||||||
|
|
||||||
Set<UTXO> selectedUTXOs = {};
|
Set<UTXO> selectedUTXOs = {};
|
||||||
|
|
||||||
|
Future<void> _scanQr() async {
|
||||||
|
try {
|
||||||
|
// ref
|
||||||
|
// .read(
|
||||||
|
// shouldShowLockscreenOnResumeStateProvider
|
||||||
|
// .state)
|
||||||
|
// .state = false;
|
||||||
|
if (FocusScope.of(context).hasFocus) {
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
await Future<void>.delayed(const Duration(milliseconds: 75));
|
||||||
|
}
|
||||||
|
|
||||||
|
final qrResult = await scanner.scan();
|
||||||
|
|
||||||
|
// Future<void>.delayed(
|
||||||
|
// const Duration(seconds: 2),
|
||||||
|
// () => ref
|
||||||
|
// .read(
|
||||||
|
// shouldShowLockscreenOnResumeStateProvider
|
||||||
|
// .state)
|
||||||
|
// .state = true,
|
||||||
|
// );
|
||||||
|
|
||||||
|
Logging.instance.log("qrResult content: ${qrResult.rawContent}",
|
||||||
|
level: LogLevel.Info);
|
||||||
|
|
||||||
|
final results = AddressUtils.parseUri(qrResult.rawContent);
|
||||||
|
|
||||||
|
Logging.instance.log("qrResult parsed: $results", level: LogLevel.Info);
|
||||||
|
|
||||||
|
if (results.isNotEmpty && results["scheme"] == coin.uriScheme) {
|
||||||
|
// auto fill address
|
||||||
|
_address = (results["address"] ?? "").trim();
|
||||||
|
sendToController.text = _address!;
|
||||||
|
|
||||||
|
// autofill notes field
|
||||||
|
if (results["message"] != null) {
|
||||||
|
noteController.text = results["message"]!;
|
||||||
|
} else if (results["label"] != null) {
|
||||||
|
noteController.text = results["label"]!;
|
||||||
|
}
|
||||||
|
|
||||||
|
// autofill amount field
|
||||||
|
if (results["amount"] != null) {
|
||||||
|
final Amount amount = Decimal.parse(results["amount"]!).toAmount(
|
||||||
|
fractionDigits: coin.decimals,
|
||||||
|
);
|
||||||
|
cryptoAmountController.text = ref.read(pAmountFormatter(coin)).format(
|
||||||
|
amount,
|
||||||
|
withUnitName: false,
|
||||||
|
);
|
||||||
|
ref.read(pSendAmount.notifier).state = amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
_setValidAddressProviders(_address);
|
||||||
|
setState(() {
|
||||||
|
_addressToggleFlag = sendToController.text.isNotEmpty;
|
||||||
|
});
|
||||||
|
|
||||||
|
// now check for non standard encoded basic address
|
||||||
|
} else if (ref
|
||||||
|
.read(pWallets)
|
||||||
|
.getWallet(walletId)
|
||||||
|
.cryptoCurrency
|
||||||
|
.validateAddress(qrResult.rawContent)) {
|
||||||
|
_address = qrResult.rawContent.trim();
|
||||||
|
sendToController.text = _address ?? "";
|
||||||
|
|
||||||
|
_setValidAddressProviders(_address);
|
||||||
|
setState(() {
|
||||||
|
_addressToggleFlag = sendToController.text.isNotEmpty;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} on PlatformException catch (e, s) {
|
||||||
|
// ref
|
||||||
|
// .read(
|
||||||
|
// shouldShowLockscreenOnResumeStateProvider
|
||||||
|
// .state)
|
||||||
|
// .state = true;
|
||||||
|
// here we ignore the exception caused by not giving permission
|
||||||
|
// to use the camera to scan a qr code
|
||||||
|
Logging.instance.log(
|
||||||
|
"Failed to get camera permissions while trying to scan qr code in SendView: $e\n$s",
|
||||||
|
level: LogLevel.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _fiatFieldChanged(String baseAmountString) {
|
||||||
|
final baseAmount = Amount.tryParseFiatString(
|
||||||
|
baseAmountString,
|
||||||
|
locale: ref.read(localeServiceChangeNotifierProvider).locale,
|
||||||
|
);
|
||||||
|
final Amount? amount;
|
||||||
|
if (baseAmount != null) {
|
||||||
|
final Decimal _price =
|
||||||
|
ref.read(priceAnd24hChangeNotifierProvider).getPrice(coin).item1;
|
||||||
|
|
||||||
|
if (_price == Decimal.zero) {
|
||||||
|
amount = 0.toAmountAsRaw(fractionDigits: coin.decimals);
|
||||||
|
} else {
|
||||||
|
amount = baseAmount <= Amount.zero
|
||||||
|
? 0.toAmountAsRaw(fractionDigits: coin.decimals)
|
||||||
|
: (baseAmount.decimal / _price)
|
||||||
|
.toDecimal(
|
||||||
|
scaleOnInfinitePrecision: coin.decimals,
|
||||||
|
)
|
||||||
|
.toAmount(fractionDigits: coin.decimals);
|
||||||
|
}
|
||||||
|
if (_cachedAmountToSend != null && _cachedAmountToSend == amount) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_cachedAmountToSend = amount;
|
||||||
|
Logging.instance
|
||||||
|
.log("it changed $amount $_cachedAmountToSend", level: LogLevel.Info);
|
||||||
|
|
||||||
|
final amountString = ref.read(pAmountFormatter(coin)).format(
|
||||||
|
amount,
|
||||||
|
withUnitName: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
_cryptoAmountChangeLock = true;
|
||||||
|
cryptoAmountController.text = amountString;
|
||||||
|
_cryptoAmountChangeLock = false;
|
||||||
|
} else {
|
||||||
|
amount = 0.toAmountAsRaw(fractionDigits: coin.decimals);
|
||||||
|
_cryptoAmountChangeLock = true;
|
||||||
|
cryptoAmountController.text = "";
|
||||||
|
_cryptoAmountChangeLock = false;
|
||||||
|
}
|
||||||
|
// setState(() {
|
||||||
|
// _calculateFeesFuture = calculateFees(
|
||||||
|
// Format.decimalAmountToSatoshis(
|
||||||
|
// _amountToSend!));
|
||||||
|
// });
|
||||||
|
ref.read(pSendAmount.notifier).state = amount;
|
||||||
|
}
|
||||||
|
|
||||||
void _cryptoAmountChanged() async {
|
void _cryptoAmountChanged() async {
|
||||||
if (!_cryptoAmountChangeLock) {
|
if (!_cryptoAmountChangeLock) {
|
||||||
final cryptoAmount = ref.read(pAmountFormatter(coin)).tryParse(
|
final cryptoAmount = ref.read(pAmountFormatter(coin)).tryParse(
|
||||||
cryptoAmountController.text,
|
cryptoAmountController.text,
|
||||||
);
|
);
|
||||||
|
final Amount? amount;
|
||||||
if (cryptoAmount != null) {
|
if (cryptoAmount != null) {
|
||||||
_amountToSend = cryptoAmount;
|
amount = cryptoAmount;
|
||||||
if (_cachedAmountToSend != null &&
|
if (_cachedAmountToSend != null && _cachedAmountToSend == amount) {
|
||||||
_cachedAmountToSend == _amountToSend) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_cachedAmountToSend = _amountToSend;
|
_cachedAmountToSend = amount;
|
||||||
Logging.instance.log("it changed $_amountToSend $_cachedAmountToSend",
|
Logging.instance.log("it changed $amount $_cachedAmountToSend",
|
||||||
level: LogLevel.Info);
|
level: LogLevel.Info);
|
||||||
|
|
||||||
final price =
|
final price =
|
||||||
ref.read(priceAnd24hChangeNotifierProvider).getPrice(coin).item1;
|
ref.read(priceAnd24hChangeNotifierProvider).getPrice(coin).item1;
|
||||||
|
|
||||||
if (price > Decimal.zero) {
|
if (price > Decimal.zero) {
|
||||||
baseAmountController.text = (_amountToSend!.decimal * price)
|
baseAmountController.text = (amount!.decimal * price)
|
||||||
.toAmount(
|
.toAmount(
|
||||||
fractionDigits: 2,
|
fractionDigits: 2,
|
||||||
)
|
)
|
||||||
|
@ -161,20 +296,20 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_amountToSend = null;
|
amount = null;
|
||||||
baseAmountController.text = "";
|
baseAmountController.text = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
_updatePreviewButtonState(_address, _amountToSend);
|
ref.read(pSendAmount.notifier).state = amount;
|
||||||
|
|
||||||
_cryptoAmountChangedFeeUpdateTimer?.cancel();
|
_cryptoAmountChangedFeeUpdateTimer?.cancel();
|
||||||
_cryptoAmountChangedFeeUpdateTimer = Timer(updateFeesTimerDuration, () {
|
_cryptoAmountChangedFeeUpdateTimer = Timer(updateFeesTimerDuration, () {
|
||||||
if (coin != Coin.epicCash && !_baseFocus.hasFocus) {
|
if (coin != Coin.epicCash && !_baseFocus.hasFocus) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_calculateFeesFuture = calculateFees(
|
_calculateFeesFuture = calculateFees(
|
||||||
_amountToSend == null
|
amount == null
|
||||||
? 0.toAmountAsRaw(fractionDigits: coin.decimals)
|
? 0.toAmountAsRaw(fractionDigits: coin.decimals)
|
||||||
: _amountToSend!,
|
: amount!,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -193,9 +328,9 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
if (coin != Coin.epicCash && !_cryptoFocus.hasFocus) {
|
if (coin != Coin.epicCash && !_cryptoFocus.hasFocus) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_calculateFeesFuture = calculateFees(
|
_calculateFeesFuture = calculateFees(
|
||||||
_amountToSend == null
|
ref.read(pSendAmount) == null
|
||||||
? 0.toAmountAsRaw(fractionDigits: coin.decimals)
|
? 0.toAmountAsRaw(fractionDigits: coin.decimals)
|
||||||
: _amountToSend!,
|
: ref.read(pSendAmount)!,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -230,6 +365,7 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
if (_data != null && _data!.contactLabel == address) {
|
if (_data != null && _data!.contactLabel == address) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (address.isNotEmpty &&
|
if (address.isNotEmpty &&
|
||||||
!ref
|
!ref
|
||||||
.read(pWallets)
|
.read(pWallets)
|
||||||
|
@ -241,24 +377,22 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updatePreviewButtonState(String? address, Amount? amount) {
|
void _setValidAddressProviders(String? address) {
|
||||||
if (isPaynymSend) {
|
if (isPaynymSend) {
|
||||||
ref.read(previewTxButtonStateProvider.state).state =
|
ref.read(pValidSendToAddress.notifier).state = true;
|
||||||
(amount != null && amount > Amount.zero);
|
|
||||||
} else {
|
} else {
|
||||||
final walletCurrency =
|
final wallet = ref.read(pWallets).getWallet(walletId);
|
||||||
ref.read(pWallets).getWallet(walletId).cryptoCurrency;
|
if (wallet is SparkInterface) {
|
||||||
final isValidAddress = walletCurrency.validateAddress(address ?? "");
|
ref.read(pValidSparkSendToAddress.notifier).state =
|
||||||
|
SparkInterface.validateSparkAddress(
|
||||||
|
address: address ?? "",
|
||||||
|
isTestNet:
|
||||||
|
wallet.cryptoCurrency.network == CryptoCurrencyNetwork.test,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
_isSparkAddress = isValidAddress
|
ref.read(pValidSendToAddress.notifier).state =
|
||||||
? SparkInterface.validateSparkAddress(
|
wallet.cryptoCurrency.validateAddress(address ?? "");
|
||||||
address: address!,
|
|
||||||
isTestNet: walletCurrency.network == CryptoCurrencyNetwork.test,
|
|
||||||
)
|
|
||||||
: false;
|
|
||||||
|
|
||||||
ref.read(previewTxButtonStateProvider.state).state =
|
|
||||||
(isValidAddress && amount != null && amount > Amount.zero);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -392,7 +526,7 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
);
|
);
|
||||||
final wallet = ref.read(pWallets).getWallet(walletId);
|
final wallet = ref.read(pWallets).getWallet(walletId);
|
||||||
|
|
||||||
final Amount amount = _amountToSend!;
|
final Amount amount = ref.read(pSendAmount)!;
|
||||||
final Amount availableBalance;
|
final Amount availableBalance;
|
||||||
if (isFiro) {
|
if (isFiro) {
|
||||||
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
|
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
|
||||||
|
@ -524,7 +658,7 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
} else if (wallet is FiroWallet) {
|
} else if (wallet is FiroWallet) {
|
||||||
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
|
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
|
||||||
case FiroType.public:
|
case FiroType.public:
|
||||||
if (_isSparkAddress) {
|
if (ref.read(pValidSparkSendToAddress)) {
|
||||||
txDataFuture = wallet.prepareSparkMintTransaction(
|
txDataFuture = wallet.prepareSparkMintTransaction(
|
||||||
txData: TxData(
|
txData: TxData(
|
||||||
sparkRecipients: [
|
sparkRecipients: [
|
||||||
|
@ -570,10 +704,10 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
case FiroType.spark:
|
case FiroType.spark:
|
||||||
txDataFuture = wallet.prepareSendSpark(
|
txDataFuture = wallet.prepareSendSpark(
|
||||||
txData: TxData(
|
txData: TxData(
|
||||||
recipients: _isSparkAddress
|
recipients: ref.read(pValidSparkSendToAddress)
|
||||||
? null
|
? null
|
||||||
: [(address: _address!, amount: amount)],
|
: [(address: _address!, amount: amount)],
|
||||||
sparkRecipients: _isSparkAddress
|
sparkRecipients: ref.read(pValidSparkSendToAddress)
|
||||||
? [
|
? [
|
||||||
(
|
(
|
||||||
address: _address!,
|
address: _address!,
|
||||||
|
@ -807,7 +941,7 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
|
|
||||||
if (isFiro) {
|
if (isFiro) {
|
||||||
ref.listen(publicPrivateBalanceStateProvider, (previous, next) {
|
ref.listen(publicPrivateBalanceStateProvider, (previous, next) {
|
||||||
if (_amountToSend == null) {
|
if (ref.read(pSendAmount) == null) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_calculateFeesFuture =
|
_calculateFeesFuture =
|
||||||
calculateFees(0.toAmountAsRaw(fractionDigits: coin.decimals));
|
calculateFees(0.toAmountAsRaw(fractionDigits: coin.decimals));
|
||||||
|
@ -815,7 +949,7 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
} else {
|
} else {
|
||||||
setState(() {
|
setState(() {
|
||||||
_calculateFeesFuture = calculateFees(
|
_calculateFeesFuture = calculateFees(
|
||||||
_amountToSend!,
|
ref.read(pSendAmount)!,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1077,8 +1211,7 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
),
|
),
|
||||||
onChanged: (newValue) {
|
onChanged: (newValue) {
|
||||||
_address = newValue.trim();
|
_address = newValue.trim();
|
||||||
_updatePreviewButtonState(
|
_setValidAddressProviders(_address);
|
||||||
_address, _amountToSend);
|
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_addressToggleFlag = newValue.isNotEmpty;
|
_addressToggleFlag = newValue.isNotEmpty;
|
||||||
|
@ -1115,9 +1248,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
onTap: () {
|
onTap: () {
|
||||||
sendToController.text = "";
|
sendToController.text = "";
|
||||||
_address = "";
|
_address = "";
|
||||||
_updatePreviewButtonState(
|
_setValidAddressProviders(
|
||||||
_address,
|
_address);
|
||||||
_amountToSend);
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_addressToggleFlag =
|
_addressToggleFlag =
|
||||||
false;
|
false;
|
||||||
|
@ -1159,9 +1291,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
content.trim();
|
content.trim();
|
||||||
_address = content.trim();
|
_address = content.trim();
|
||||||
|
|
||||||
_updatePreviewButtonState(
|
_setValidAddressProviders(
|
||||||
_address,
|
_address);
|
||||||
_amountToSend);
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_addressToggleFlag =
|
_addressToggleFlag =
|
||||||
sendToController
|
sendToController
|
||||||
|
@ -1195,139 +1326,9 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
"Scan QR Button. Opens Camera For Scanning QR Code.",
|
"Scan QR Button. Opens Camera For Scanning QR Code.",
|
||||||
key: const Key(
|
key: const Key(
|
||||||
"sendViewScanQrButtonKey"),
|
"sendViewScanQrButtonKey"),
|
||||||
onTap: () async {
|
onTap: _scanQr,
|
||||||
try {
|
|
||||||
// ref
|
|
||||||
// .read(
|
|
||||||
// shouldShowLockscreenOnResumeStateProvider
|
|
||||||
// .state)
|
|
||||||
// .state = false;
|
|
||||||
if (FocusScope.of(context)
|
|
||||||
.hasFocus) {
|
|
||||||
FocusScope.of(context)
|
|
||||||
.unfocus();
|
|
||||||
await Future<void>.delayed(
|
|
||||||
const Duration(
|
|
||||||
milliseconds: 75));
|
|
||||||
}
|
|
||||||
|
|
||||||
final qrResult =
|
|
||||||
await scanner.scan();
|
|
||||||
|
|
||||||
// Future<void>.delayed(
|
|
||||||
// const Duration(seconds: 2),
|
|
||||||
// () => ref
|
|
||||||
// .read(
|
|
||||||
// shouldShowLockscreenOnResumeStateProvider
|
|
||||||
// .state)
|
|
||||||
// .state = true,
|
|
||||||
// );
|
|
||||||
|
|
||||||
Logging.instance.log(
|
|
||||||
"qrResult content: ${qrResult.rawContent}",
|
|
||||||
level: LogLevel.Info);
|
|
||||||
|
|
||||||
final results =
|
|
||||||
AddressUtils.parseUri(
|
|
||||||
qrResult.rawContent);
|
|
||||||
|
|
||||||
Logging.instance.log(
|
|
||||||
"qrResult parsed: $results",
|
|
||||||
level: LogLevel.Info);
|
|
||||||
|
|
||||||
if (results.isNotEmpty &&
|
|
||||||
results["scheme"] ==
|
|
||||||
coin.uriScheme) {
|
|
||||||
// auto fill address
|
|
||||||
_address =
|
|
||||||
(results["address"] ??
|
|
||||||
"")
|
|
||||||
.trim();
|
|
||||||
sendToController.text =
|
|
||||||
_address!;
|
|
||||||
|
|
||||||
// autofill notes field
|
|
||||||
if (results["message"] !=
|
|
||||||
null) {
|
|
||||||
noteController.text =
|
|
||||||
results["message"]!;
|
|
||||||
} else if (results[
|
|
||||||
"label"] !=
|
|
||||||
null) {
|
|
||||||
noteController.text =
|
|
||||||
results["label"]!;
|
|
||||||
}
|
|
||||||
|
|
||||||
// autofill amount field
|
|
||||||
if (results["amount"] !=
|
|
||||||
null) {
|
|
||||||
final Amount amount =
|
|
||||||
Decimal.parse(results[
|
|
||||||
"amount"]!)
|
|
||||||
.toAmount(
|
|
||||||
fractionDigits:
|
|
||||||
coin.decimals,
|
|
||||||
);
|
|
||||||
cryptoAmountController
|
|
||||||
.text =
|
|
||||||
ref
|
|
||||||
.read(
|
|
||||||
pAmountFormatter(
|
|
||||||
coin))
|
|
||||||
.format(
|
|
||||||
amount,
|
|
||||||
withUnitName:
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
_amountToSend = amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
_updatePreviewButtonState(
|
|
||||||
_address,
|
|
||||||
_amountToSend);
|
|
||||||
setState(() {
|
|
||||||
_addressToggleFlag =
|
|
||||||
sendToController
|
|
||||||
.text.isNotEmpty;
|
|
||||||
});
|
|
||||||
|
|
||||||
// now check for non standard encoded basic address
|
|
||||||
} else if (ref
|
|
||||||
.read(pWallets)
|
|
||||||
.getWallet(walletId)
|
|
||||||
.cryptoCurrency
|
|
||||||
.validateAddress(qrResult
|
|
||||||
.rawContent)) {
|
|
||||||
_address = qrResult
|
|
||||||
.rawContent
|
|
||||||
.trim();
|
|
||||||
sendToController.text =
|
|
||||||
_address ?? "";
|
|
||||||
|
|
||||||
_updatePreviewButtonState(
|
|
||||||
_address,
|
|
||||||
_amountToSend);
|
|
||||||
setState(() {
|
|
||||||
_addressToggleFlag =
|
|
||||||
sendToController
|
|
||||||
.text.isNotEmpty;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} on PlatformException catch (e, s) {
|
|
||||||
// ref
|
|
||||||
// .read(
|
|
||||||
// shouldShowLockscreenOnResumeStateProvider
|
|
||||||
// .state)
|
|
||||||
// .state = true;
|
|
||||||
// here we ignore the exception caused by not giving permission
|
|
||||||
// to use the camera to scan a qr code
|
|
||||||
Logging.instance.log(
|
|
||||||
"Failed to get camera permissions while trying to scan qr code in SendView: $e\n$s",
|
|
||||||
level: LogLevel.Warning);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: const QrCodeIcon(),
|
child: const QrCodeIcon(),
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1338,7 +1339,11 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 10,
|
height: 10,
|
||||||
),
|
),
|
||||||
if (isStellar || _isSparkAddress)
|
if (isStellar ||
|
||||||
|
(ref.watch(pValidSparkSendToAddress) &&
|
||||||
|
ref.watch(
|
||||||
|
publicPrivateBalanceStateProvider) !=
|
||||||
|
FiroType.lelantus))
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(
|
borderRadius: BorderRadius.circular(
|
||||||
Constants.size.circularBorderRadius,
|
Constants.size.circularBorderRadius,
|
||||||
|
@ -1419,9 +1424,50 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
),
|
),
|
||||||
Builder(
|
Builder(
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
final error = _updateInvalidAddressText(
|
final String? error;
|
||||||
_address ?? "",
|
|
||||||
);
|
if (_address == null || _address!.isEmpty) {
|
||||||
|
error = null;
|
||||||
|
} else if (isFiro) {
|
||||||
|
if (ref.watch(
|
||||||
|
publicPrivateBalanceStateProvider) ==
|
||||||
|
FiroType.lelantus) {
|
||||||
|
if (_data != null &&
|
||||||
|
_data!.contactLabel == _address) {
|
||||||
|
error = SparkInterface.validateSparkAddress(
|
||||||
|
address: _data!.address,
|
||||||
|
isTestNet: coin.isTestNet)
|
||||||
|
? "Unsupported"
|
||||||
|
: null;
|
||||||
|
} else if (ref
|
||||||
|
.watch(pValidSparkSendToAddress)) {
|
||||||
|
error = "Unsupported";
|
||||||
|
} else {
|
||||||
|
error = ref.watch(pValidSendToAddress)
|
||||||
|
? null
|
||||||
|
: "Invalid address";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_data != null &&
|
||||||
|
_data!.contactLabel == _address) {
|
||||||
|
error = null;
|
||||||
|
} else if (!ref.watch(pValidSendToAddress) &&
|
||||||
|
!ref.watch(pValidSparkSendToAddress)) {
|
||||||
|
error = "Invalid address";
|
||||||
|
} else {
|
||||||
|
error = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_data != null &&
|
||||||
|
_data!.contactLabel == _address) {
|
||||||
|
error = null;
|
||||||
|
} else if (!ref.watch(pValidSendToAddress)) {
|
||||||
|
error = "Invalid address";
|
||||||
|
} else {
|
||||||
|
error = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (error == null || error.isEmpty) {
|
if (error == null || error.isEmpty) {
|
||||||
return Container();
|
return Container();
|
||||||
|
@ -1737,65 +1783,7 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
// ? newValue
|
// ? newValue
|
||||||
// : oldValue),
|
// : oldValue),
|
||||||
],
|
],
|
||||||
onChanged: (baseAmountString) {
|
onChanged: _fiatFieldChanged,
|
||||||
final baseAmount = Amount.tryParseFiatString(
|
|
||||||
baseAmountString,
|
|
||||||
locale: locale,
|
|
||||||
);
|
|
||||||
if (baseAmount != null) {
|
|
||||||
final Decimal _price = ref
|
|
||||||
.read(priceAnd24hChangeNotifierProvider)
|
|
||||||
.getPrice(coin)
|
|
||||||
.item1;
|
|
||||||
|
|
||||||
if (_price == Decimal.zero) {
|
|
||||||
_amountToSend = 0.toAmountAsRaw(
|
|
||||||
fractionDigits: coin.decimals);
|
|
||||||
} else {
|
|
||||||
_amountToSend = baseAmount <= Amount.zero
|
|
||||||
? 0.toAmountAsRaw(
|
|
||||||
fractionDigits: coin.decimals)
|
|
||||||
: (baseAmount.decimal / _price)
|
|
||||||
.toDecimal(
|
|
||||||
scaleOnInfinitePrecision:
|
|
||||||
coin.decimals,
|
|
||||||
)
|
|
||||||
.toAmount(
|
|
||||||
fractionDigits: coin.decimals);
|
|
||||||
}
|
|
||||||
if (_cachedAmountToSend != null &&
|
|
||||||
_cachedAmountToSend == _amountToSend) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_cachedAmountToSend = _amountToSend;
|
|
||||||
Logging.instance.log(
|
|
||||||
"it changed $_amountToSend $_cachedAmountToSend",
|
|
||||||
level: LogLevel.Info);
|
|
||||||
|
|
||||||
final amountString =
|
|
||||||
ref.read(pAmountFormatter(coin)).format(
|
|
||||||
_amountToSend!,
|
|
||||||
withUnitName: false,
|
|
||||||
);
|
|
||||||
|
|
||||||
_cryptoAmountChangeLock = true;
|
|
||||||
cryptoAmountController.text = amountString;
|
|
||||||
_cryptoAmountChangeLock = false;
|
|
||||||
} else {
|
|
||||||
_amountToSend = 0.toAmountAsRaw(
|
|
||||||
fractionDigits: coin.decimals);
|
|
||||||
_cryptoAmountChangeLock = true;
|
|
||||||
cryptoAmountController.text = "";
|
|
||||||
_cryptoAmountChangeLock = false;
|
|
||||||
}
|
|
||||||
// setState(() {
|
|
||||||
// _calculateFeesFuture = calculateFees(
|
|
||||||
// Format.decimalAmountToSatoshis(
|
|
||||||
// _amountToSend!));
|
|
||||||
// });
|
|
||||||
_updatePreviewButtonState(
|
|
||||||
_address, _amountToSend);
|
|
||||||
},
|
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
contentPadding: const EdgeInsets.only(
|
contentPadding: const EdgeInsets.only(
|
||||||
top: 12,
|
top: 12,
|
||||||
|
@ -1860,8 +1848,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
.spendable;
|
.spendable;
|
||||||
|
|
||||||
Amount? amount;
|
Amount? amount;
|
||||||
if (_amountToSend != null) {
|
if (ref.read(pSendAmount) != null) {
|
||||||
amount = _amountToSend!;
|
amount = ref.read(pSendAmount)!;
|
||||||
|
|
||||||
if (spendable == amount) {
|
if (spendable == amount) {
|
||||||
// this is now a send all
|
// this is now a send all
|
||||||
|
@ -2075,7 +2063,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
amount: (Decimal.tryParse(
|
amount: (Decimal.tryParse(
|
||||||
cryptoAmountController
|
cryptoAmountController
|
||||||
.text) ??
|
.text) ??
|
||||||
_amountToSend
|
ref
|
||||||
|
.watch(pSendAmount)
|
||||||
?.decimal ??
|
?.decimal ??
|
||||||
Decimal.zero)
|
Decimal.zero)
|
||||||
.toAmount(
|
.toAmount(
|
||||||
|
@ -2239,14 +2228,10 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
height: 12,
|
height: 12,
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: ref
|
onPressed: ref.watch(pPreviewTxButtonEnabled(coin))
|
||||||
.watch(previewTxButtonStateProvider.state)
|
|
||||||
.state
|
|
||||||
? _previewTransaction
|
? _previewTransaction
|
||||||
: null,
|
: null,
|
||||||
style: ref
|
style: ref.watch(pPreviewTxButtonEnabled(coin))
|
||||||
.watch(previewTxButtonStateProvider.state)
|
|
||||||
.state
|
|
||||||
? Theme.of(context)
|
? Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.getPrimaryEnabledButtonStyle(context)
|
.getPrimaryEnabledButtonStyle(context)
|
||||||
|
|
|
@ -348,7 +348,7 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
|
||||||
.getWallet(walletId)
|
.getWallet(walletId)
|
||||||
.cryptoCurrency
|
.cryptoCurrency
|
||||||
.validateAddress(address ?? "");
|
.validateAddress(address ?? "");
|
||||||
ref.read(previewTxButtonStateProvider.state).state =
|
ref.read(previewTokenTxButtonStateProvider.state).state =
|
||||||
(isValidAddress && amount != null && amount > Amount.zero);
|
(isValidAddress && amount != null && amount > Amount.zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1227,12 +1227,14 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: ref
|
onPressed: ref
|
||||||
.watch(previewTxButtonStateProvider.state)
|
.watch(
|
||||||
|
previewTokenTxButtonStateProvider.state)
|
||||||
.state
|
.state
|
||||||
? _previewTransaction
|
? _previewTransaction
|
||||||
: null,
|
: null,
|
||||||
style: ref
|
style: ref
|
||||||
.watch(previewTxButtonStateProvider.state)
|
.watch(
|
||||||
|
previewTokenTxButtonStateProvider.state)
|
||||||
.state
|
.state
|
||||||
? Theme.of(context)
|
? Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
|
|
|
@ -114,7 +114,6 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
String? _note;
|
String? _note;
|
||||||
String? _onChainNote;
|
String? _onChainNote;
|
||||||
|
|
||||||
Amount? _amountToSend;
|
|
||||||
Amount? _cachedAmountToSend;
|
Amount? _cachedAmountToSend;
|
||||||
String? _address;
|
String? _address;
|
||||||
|
|
||||||
|
@ -125,8 +124,6 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
|
|
||||||
bool get isPaynymSend => widget.accountLite != null;
|
bool get isPaynymSend => widget.accountLite != null;
|
||||||
|
|
||||||
bool _isSparkAddress = false;
|
|
||||||
|
|
||||||
bool isCustomFee = false;
|
bool isCustomFee = false;
|
||||||
int customFeeRate = 1;
|
int customFeeRate = 1;
|
||||||
(FeeRateType, String?, String?)? feeSelectionResult;
|
(FeeRateType, String?, String?)? feeSelectionResult;
|
||||||
|
@ -141,7 +138,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
Future<void> previewSend() async {
|
Future<void> previewSend() async {
|
||||||
final wallet = ref.read(pWallets).getWallet(walletId);
|
final wallet = ref.read(pWallets).getWallet(walletId);
|
||||||
|
|
||||||
final Amount amount = _amountToSend!;
|
final Amount amount = ref.read(pSendAmount)!;
|
||||||
final Amount availableBalance;
|
final Amount availableBalance;
|
||||||
if ((coin == Coin.firo || coin == Coin.firoTestNet)) {
|
if ((coin == Coin.firo || coin == Coin.firoTestNet)) {
|
||||||
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
|
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
|
||||||
|
@ -321,7 +318,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
} else if (wallet is FiroWallet) {
|
} else if (wallet is FiroWallet) {
|
||||||
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
|
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
|
||||||
case FiroType.public:
|
case FiroType.public:
|
||||||
if (_isSparkAddress) {
|
if (ref.read(pValidSparkSendToAddress)) {
|
||||||
txDataFuture = wallet.prepareSparkMintTransaction(
|
txDataFuture = wallet.prepareSparkMintTransaction(
|
||||||
txData: TxData(
|
txData: TxData(
|
||||||
sparkRecipients: [
|
sparkRecipients: [
|
||||||
|
@ -367,10 +364,10 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
case FiroType.spark:
|
case FiroType.spark:
|
||||||
txDataFuture = wallet.prepareSendSpark(
|
txDataFuture = wallet.prepareSendSpark(
|
||||||
txData: TxData(
|
txData: TxData(
|
||||||
recipients: _isSparkAddress
|
recipients: ref.read(pValidSparkSendToAddress)
|
||||||
? null
|
? null
|
||||||
: [(address: _address!, amount: amount)],
|
: [(address: _address!, amount: amount)],
|
||||||
sparkRecipients: _isSparkAddress
|
sparkRecipients: ref.read(pValidSparkSendToAddress)
|
||||||
? [
|
? [
|
||||||
(
|
(
|
||||||
address: _address!,
|
address: _address!,
|
||||||
|
@ -533,21 +530,21 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
final cryptoAmount = ref.read(pAmountFormatter(coin)).tryParse(
|
final cryptoAmount = ref.read(pAmountFormatter(coin)).tryParse(
|
||||||
cryptoAmountController.text,
|
cryptoAmountController.text,
|
||||||
);
|
);
|
||||||
|
final Amount? amount;
|
||||||
if (cryptoAmount != null) {
|
if (cryptoAmount != null) {
|
||||||
_amountToSend = cryptoAmount;
|
amount = cryptoAmount;
|
||||||
if (_cachedAmountToSend != null &&
|
if (_cachedAmountToSend != null && _cachedAmountToSend == amount) {
|
||||||
_cachedAmountToSend == _amountToSend) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Logging.instance.log("it changed $_amountToSend $_cachedAmountToSend",
|
Logging.instance.log("it changed $amount $_cachedAmountToSend",
|
||||||
level: LogLevel.Info);
|
level: LogLevel.Info);
|
||||||
_cachedAmountToSend = _amountToSend;
|
_cachedAmountToSend = amount;
|
||||||
|
|
||||||
final price =
|
final price =
|
||||||
ref.read(priceAnd24hChangeNotifierProvider).getPrice(coin).item1;
|
ref.read(priceAnd24hChangeNotifierProvider).getPrice(coin).item1;
|
||||||
|
|
||||||
if (price > Decimal.zero) {
|
if (price > Decimal.zero) {
|
||||||
final String fiatAmountString = (_amountToSend!.decimal * price)
|
final String fiatAmountString = (amount!.decimal * price)
|
||||||
.toAmount(fractionDigits: 2)
|
.toAmount(fractionDigits: 2)
|
||||||
.fiatString(
|
.fiatString(
|
||||||
locale: ref.read(localeServiceChangeNotifierProvider).locale,
|
locale: ref.read(localeServiceChangeNotifierProvider).locale,
|
||||||
|
@ -556,52 +553,29 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
baseAmountController.text = fiatAmountString;
|
baseAmountController.text = fiatAmountString;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_amountToSend = null;
|
amount = null;
|
||||||
_cachedAmountToSend = null;
|
_cachedAmountToSend = null;
|
||||||
baseAmountController.text = "";
|
baseAmountController.text = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
_updatePreviewButtonState(_address, _amountToSend);
|
ref.read(pSendAmount.notifier).state = amount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String? _updateInvalidAddressText(String address) {
|
// String? _updateInvalidAddressText(String address) {
|
||||||
if (_data != null && _data!.contactLabel == address) {
|
// if (_data != null && _data!.contactLabel == address) {
|
||||||
return null;
|
// return null;
|
||||||
}
|
// }
|
||||||
if (address.isNotEmpty &&
|
// if (address.isNotEmpty &&
|
||||||
!ref
|
// !ref
|
||||||
.read(pWallets)
|
// .read(pWallets)
|
||||||
.getWallet(walletId)
|
// .getWallet(walletId)
|
||||||
.cryptoCurrency
|
// .cryptoCurrency
|
||||||
.validateAddress(address)) {
|
// .validateAddress(address)) {
|
||||||
return "Invalid address";
|
// return "Invalid address";
|
||||||
}
|
// }
|
||||||
return null;
|
// return null;
|
||||||
}
|
// }
|
||||||
|
|
||||||
void _updatePreviewButtonState(String? address, Amount? amount) {
|
|
||||||
if (isPaynymSend) {
|
|
||||||
ref.read(previewTxButtonStateProvider.state).state =
|
|
||||||
(amount != null && amount > Amount.zero);
|
|
||||||
} else {
|
|
||||||
final walletCurrency =
|
|
||||||
ref.read(pWallets).getWallet(walletId).cryptoCurrency;
|
|
||||||
final isValidAddress = walletCurrency.validateAddress(address ?? "");
|
|
||||||
|
|
||||||
_isSparkAddress = isValidAddress &&
|
|
||||||
ref.read(publicPrivateBalanceStateProvider.state).state !=
|
|
||||||
FiroType.lelantus
|
|
||||||
? SparkInterface.validateSparkAddress(
|
|
||||||
address: address!,
|
|
||||||
isTestNet: walletCurrency.network == CryptoCurrencyNetwork.test,
|
|
||||||
)
|
|
||||||
: false;
|
|
||||||
|
|
||||||
ref.read(previewTxButtonStateProvider.state).state =
|
|
||||||
(isValidAddress && amount != null && amount > Amount.zero);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> scanQr() async {
|
Future<void> scanQr() async {
|
||||||
try {
|
try {
|
||||||
|
@ -639,10 +613,9 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
cryptoAmountController.text = ref
|
cryptoAmountController.text = ref
|
||||||
.read(pAmountFormatter(coin))
|
.read(pAmountFormatter(coin))
|
||||||
.format(amount, withUnitName: false);
|
.format(amount, withUnitName: false);
|
||||||
_amountToSend = amount;
|
ref.read(pSendAmount.notifier).state = amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
_updatePreviewButtonState(_address, _amountToSend);
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_addressToggleFlag = sendToController.text.isNotEmpty;
|
_addressToggleFlag = sendToController.text.isNotEmpty;
|
||||||
});
|
});
|
||||||
|
@ -656,7 +629,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
_address = qrResult.rawContent;
|
_address = qrResult.rawContent;
|
||||||
sendToController.text = _address ?? "";
|
sendToController.text = _address ?? "";
|
||||||
|
|
||||||
_updatePreviewButtonState(_address, _amountToSend);
|
_setValidAddressProviders(_address);
|
||||||
setState(() {
|
setState(() {
|
||||||
_addressToggleFlag = sendToController.text.isNotEmpty;
|
_addressToggleFlag = sendToController.text.isNotEmpty;
|
||||||
});
|
});
|
||||||
|
@ -670,6 +643,25 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _setValidAddressProviders(String? address) {
|
||||||
|
if (isPaynymSend) {
|
||||||
|
ref.read(pValidSendToAddress.notifier).state = true;
|
||||||
|
} else {
|
||||||
|
final wallet = ref.read(pWallets).getWallet(walletId);
|
||||||
|
if (wallet is SparkInterface) {
|
||||||
|
ref.read(pValidSparkSendToAddress.notifier).state =
|
||||||
|
SparkInterface.validateSparkAddress(
|
||||||
|
address: address ?? "",
|
||||||
|
isTestNet:
|
||||||
|
wallet.cryptoCurrency.network == CryptoCurrencyNetwork.test,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ref.read(pValidSendToAddress.notifier).state =
|
||||||
|
wallet.cryptoCurrency.validateAddress(address ?? "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> pasteAddress() async {
|
Future<void> pasteAddress() async {
|
||||||
final ClipboardData? data = await clipboard.getData(Clipboard.kTextPlain);
|
final ClipboardData? data = await clipboard.getData(Clipboard.kTextPlain);
|
||||||
if (data?.text != null && data!.text!.isNotEmpty) {
|
if (data?.text != null && data!.text!.isNotEmpty) {
|
||||||
|
@ -686,7 +678,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
sendToController.text = content;
|
sendToController.text = content;
|
||||||
_address = content;
|
_address = content;
|
||||||
|
|
||||||
_updatePreviewButtonState(_address, _amountToSend);
|
_setValidAddressProviders(_address);
|
||||||
setState(() {
|
setState(() {
|
||||||
_addressToggleFlag = sendToController.text.isNotEmpty;
|
_addressToggleFlag = sendToController.text.isNotEmpty;
|
||||||
});
|
});
|
||||||
|
@ -715,28 +707,29 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
baseAmountString,
|
baseAmountString,
|
||||||
locale: ref.read(localeServiceChangeNotifierProvider).locale,
|
locale: ref.read(localeServiceChangeNotifierProvider).locale,
|
||||||
);
|
);
|
||||||
|
final Amount? amount;
|
||||||
if (baseAmount != null) {
|
if (baseAmount != null) {
|
||||||
final _price =
|
final _price =
|
||||||
ref.read(priceAnd24hChangeNotifierProvider).getPrice(coin).item1;
|
ref.read(priceAnd24hChangeNotifierProvider).getPrice(coin).item1;
|
||||||
|
|
||||||
if (_price == Decimal.zero) {
|
if (_price == Decimal.zero) {
|
||||||
_amountToSend = Decimal.zero.toAmount(fractionDigits: coin.decimals);
|
amount = Decimal.zero.toAmount(fractionDigits: coin.decimals);
|
||||||
} else {
|
} else {
|
||||||
_amountToSend = baseAmount <= Amount.zero
|
amount = baseAmount <= Amount.zero
|
||||||
? Decimal.zero.toAmount(fractionDigits: coin.decimals)
|
? Decimal.zero.toAmount(fractionDigits: coin.decimals)
|
||||||
: (baseAmount.decimal / _price)
|
: (baseAmount.decimal / _price)
|
||||||
.toDecimal(scaleOnInfinitePrecision: coin.decimals)
|
.toDecimal(scaleOnInfinitePrecision: coin.decimals)
|
||||||
.toAmount(fractionDigits: coin.decimals);
|
.toAmount(fractionDigits: coin.decimals);
|
||||||
}
|
}
|
||||||
if (_cachedAmountToSend != null && _cachedAmountToSend == _amountToSend) {
|
if (_cachedAmountToSend != null && _cachedAmountToSend == amount) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_cachedAmountToSend = _amountToSend;
|
_cachedAmountToSend = amount;
|
||||||
Logging.instance.log("it changed $_amountToSend $_cachedAmountToSend",
|
Logging.instance
|
||||||
level: LogLevel.Info);
|
.log("it changed $amount $_cachedAmountToSend", level: LogLevel.Info);
|
||||||
|
|
||||||
final amountString = ref.read(pAmountFormatter(coin)).format(
|
final amountString = ref.read(pAmountFormatter(coin)).format(
|
||||||
_amountToSend!,
|
amount!,
|
||||||
withUnitName: false,
|
withUnitName: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -744,7 +737,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
cryptoAmountController.text = amountString;
|
cryptoAmountController.text = amountString;
|
||||||
_cryptoAmountChangeLock = false;
|
_cryptoAmountChangeLock = false;
|
||||||
} else {
|
} else {
|
||||||
_amountToSend = Decimal.zero.toAmount(fractionDigits: coin.decimals);
|
amount = Decimal.zero.toAmount(fractionDigits: coin.decimals);
|
||||||
_cryptoAmountChangeLock = true;
|
_cryptoAmountChangeLock = true;
|
||||||
cryptoAmountController.text = "";
|
cryptoAmountController.text = "";
|
||||||
_cryptoAmountChangeLock = false;
|
_cryptoAmountChangeLock = false;
|
||||||
|
@ -754,7 +747,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
// Format.decimalAmountToSatoshis(
|
// Format.decimalAmountToSatoshis(
|
||||||
// _amountToSend!));
|
// _amountToSend!));
|
||||||
// });
|
// });
|
||||||
_updatePreviewButtonState(_address, _amountToSend);
|
ref.read(pSendAmount.notifier).state = amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> sendAllTapped() async {
|
Future<void> sendAllTapped() async {
|
||||||
|
@ -784,11 +777,12 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showDesktopCoinControl() async {
|
void _showDesktopCoinControl() async {
|
||||||
|
final amount = ref.read(pSendAmount);
|
||||||
await showDialog<void>(
|
await showDialog<void>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => DesktopCoinControlUseDialog(
|
builder: (context) => DesktopCoinControlUseDialog(
|
||||||
walletId: widget.walletId,
|
walletId: widget.walletId,
|
||||||
amountToSend: _amountToSend,
|
amountToSend: amount,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -797,7 +791,8 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
void initState() {
|
void initState() {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
ref.refresh(feeSheetSessionCacheProvider);
|
ref.refresh(feeSheetSessionCacheProvider);
|
||||||
ref.read(previewTxButtonStateProvider.state).state = false;
|
ref.read(pValidSendToAddress.state).state = false;
|
||||||
|
ref.read(pValidSparkSendToAddress.state).state = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// _calculateFeesFuture = calculateFees(0);
|
// _calculateFeesFuture = calculateFees(0);
|
||||||
|
@ -832,20 +827,20 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
|
|
||||||
_cryptoFocus.addListener(() {
|
_cryptoFocus.addListener(() {
|
||||||
if (!_cryptoFocus.hasFocus && !_baseFocus.hasFocus) {
|
if (!_cryptoFocus.hasFocus && !_baseFocus.hasFocus) {
|
||||||
if (_amountToSend == null) {
|
if (ref.read(pSendAmount) == null) {
|
||||||
ref.refresh(sendAmountProvider);
|
ref.refresh(sendAmountProvider);
|
||||||
} else {
|
} else {
|
||||||
ref.read(sendAmountProvider.state).state = _amountToSend!;
|
ref.read(sendAmountProvider.state).state = ref.read(pSendAmount)!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
_baseFocus.addListener(() {
|
_baseFocus.addListener(() {
|
||||||
if (!_cryptoFocus.hasFocus && !_baseFocus.hasFocus) {
|
if (!_cryptoFocus.hasFocus && !_baseFocus.hasFocus) {
|
||||||
if (_amountToSend == null) {
|
if (ref.read(pSendAmount) == null) {
|
||||||
ref.refresh(sendAmountProvider);
|
ref.refresh(sendAmountProvider);
|
||||||
} else {
|
} else {
|
||||||
ref.read(sendAmountProvider.state).state = _amountToSend!;
|
ref.read(sendAmountProvider.state).state = ref.read(pSendAmount)!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1263,7 +1258,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
),
|
),
|
||||||
onChanged: (newValue) {
|
onChanged: (newValue) {
|
||||||
_address = newValue;
|
_address = newValue;
|
||||||
_updatePreviewButtonState(_address, _amountToSend);
|
_setValidAddressProviders(_address);
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_addressToggleFlag = newValue.isNotEmpty;
|
_addressToggleFlag = newValue.isNotEmpty;
|
||||||
|
@ -1303,8 +1298,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
onTap: () {
|
onTap: () {
|
||||||
sendToController.text = "";
|
sendToController.text = "";
|
||||||
_address = "";
|
_address = "";
|
||||||
_updatePreviewButtonState(
|
_setValidAddressProviders(_address);
|
||||||
_address, _amountToSend);
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_addressToggleFlag = false;
|
_addressToggleFlag = false;
|
||||||
});
|
});
|
||||||
|
@ -1365,10 +1359,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
|
|
||||||
_address = entry.address;
|
_address = entry.address;
|
||||||
|
|
||||||
_updatePreviewButtonState(
|
_setValidAddressProviders(_address);
|
||||||
_address,
|
|
||||||
_amountToSend,
|
|
||||||
);
|
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_addressToggleFlag = true;
|
_addressToggleFlag = true;
|
||||||
|
@ -1393,9 +1384,44 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
if (!isPaynymSend)
|
if (!isPaynymSend)
|
||||||
Builder(
|
Builder(
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
final error = _updateInvalidAddressText(
|
final String? error;
|
||||||
_address ?? "",
|
|
||||||
);
|
if (_address == null || _address!.isEmpty) {
|
||||||
|
error = null;
|
||||||
|
} else if (coin == Coin.firo || coin == Coin.firoTestNet) {
|
||||||
|
if (ref.watch(publicPrivateBalanceStateProvider) ==
|
||||||
|
FiroType.lelantus) {
|
||||||
|
if (_data != null && _data!.contactLabel == _address) {
|
||||||
|
error = SparkInterface.validateSparkAddress(
|
||||||
|
address: _data!.address, isTestNet: coin.isTestNet)
|
||||||
|
? "Lelantus to Spark not supported"
|
||||||
|
: null;
|
||||||
|
} else if (ref.watch(pValidSparkSendToAddress)) {
|
||||||
|
error = "Lelantus to Spark not supported";
|
||||||
|
} else {
|
||||||
|
error = ref.watch(pValidSendToAddress)
|
||||||
|
? null
|
||||||
|
: "Invalid address";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_data != null && _data!.contactLabel == _address) {
|
||||||
|
error = null;
|
||||||
|
} else if (!ref.watch(pValidSendToAddress) &&
|
||||||
|
!ref.watch(pValidSparkSendToAddress)) {
|
||||||
|
error = "Invalid address";
|
||||||
|
} else {
|
||||||
|
error = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_data != null && _data!.contactLabel == _address) {
|
||||||
|
error = null;
|
||||||
|
} else if (!ref.watch(pValidSendToAddress)) {
|
||||||
|
error = "Invalid address";
|
||||||
|
} else {
|
||||||
|
error = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (error == null || error.isEmpty) {
|
if (error == null || error.isEmpty) {
|
||||||
return Container();
|
return Container();
|
||||||
|
@ -1422,16 +1448,16 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (isStellar ||
|
if (isStellar ||
|
||||||
(_isSparkAddress &&
|
(ref.watch(pValidSparkSendToAddress) &&
|
||||||
ref.watch(publicPrivateBalanceStateProvider) !=
|
ref.watch(publicPrivateBalanceStateProvider) !=
|
||||||
FiroType.public))
|
FiroType.lelantus))
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 10,
|
height: 10,
|
||||||
),
|
),
|
||||||
if (isStellar ||
|
if (isStellar ||
|
||||||
(_isSparkAddress &&
|
(ref.watch(pValidSparkSendToAddress) &&
|
||||||
ref.watch(publicPrivateBalanceStateProvider) !=
|
ref.watch(publicPrivateBalanceStateProvider) !=
|
||||||
FiroType.public))
|
FiroType.lelantus))
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(
|
borderRadius: BorderRadius.circular(
|
||||||
Constants.size.circularBorderRadius,
|
Constants.size.circularBorderRadius,
|
||||||
|
@ -1727,10 +1753,9 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
PrimaryButton(
|
PrimaryButton(
|
||||||
buttonHeight: ButtonHeight.l,
|
buttonHeight: ButtonHeight.l,
|
||||||
label: "Preview send",
|
label: "Preview send",
|
||||||
enabled: ref.watch(previewTxButtonStateProvider.state).state,
|
enabled: ref.watch(pPreviewTxButtonEnabled(coin)),
|
||||||
onPressed: ref.watch(previewTxButtonStateProvider.state).state
|
onPressed:
|
||||||
? previewSend
|
ref.watch(pPreviewTxButtonEnabled(coin)) ? previewSend : null,
|
||||||
: null,
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
@ -9,9 +9,32 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
|
||||||
|
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||||
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
|
||||||
final previewTxButtonStateProvider = StateProvider.autoDispose<bool>((_) {
|
final pSendAmount = StateProvider.autoDispose<Amount?>((_) => null);
|
||||||
return false;
|
final pValidSendToAddress = StateProvider.autoDispose<bool>((_) => false);
|
||||||
|
final pValidSparkSendToAddress = StateProvider.autoDispose<bool>((_) => false);
|
||||||
|
|
||||||
|
final pPreviewTxButtonEnabled =
|
||||||
|
Provider.autoDispose.family<bool, Coin>((ref, coin) {
|
||||||
|
final amount = ref.watch(pSendAmount) ?? Amount.zero;
|
||||||
|
|
||||||
|
// TODO [prio=low]: move away from Coin
|
||||||
|
if (coin == Coin.firo || coin == Coin.firoTestNet) {
|
||||||
|
if (ref.watch(publicPrivateBalanceStateProvider) == FiroType.lelantus) {
|
||||||
|
return ref.watch(pValidSendToAddress) &&
|
||||||
|
!ref.watch(pValidSparkSendToAddress) &&
|
||||||
|
amount > Amount.zero;
|
||||||
|
} else {
|
||||||
|
return (ref.watch(pValidSendToAddress) ||
|
||||||
|
ref.watch(pValidSparkSendToAddress)) &&
|
||||||
|
amount > Amount.zero;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return ref.watch(pValidSendToAddress) && amount > Amount.zero;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
final previewTokenTxButtonStateProvider = StateProvider.autoDispose<bool>((_) {
|
final previewTokenTxButtonStateProvider = StateProvider.autoDispose<bool>((_) {
|
||||||
|
|
Loading…
Reference in a new issue