mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-17 05:41:29 +02:00
add RepaintBoundary to send and swap (#3084)
This commit is contained in:
+125
-123
@@ -480,133 +480,135 @@ class _NewSwapPageState extends State<NewSwapPage> {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Form(
|
||||
key: formKey,
|
||||
child: Column(
|
||||
children: [
|
||||
Observer(
|
||||
builder: (_) => SwapAmountBox(
|
||||
isReceiverCard: false,
|
||||
walletSwitcherViewModel: widget.walletSwitcherViewModel,
|
||||
exchangeViewModel: widget.exchangeViewModel,
|
||||
onDispose: disposeBestRateSync,
|
||||
hasAllAmount: widget.exchangeViewModel.hasAllAmount,
|
||||
allAmount: widget.exchangeViewModel.hasAllAmount
|
||||
? () => widget.exchangeViewModel.enableSendAllAmount()
|
||||
: null,
|
||||
key: depositKey,
|
||||
title: S.of(context).send,
|
||||
initialCurrency: widget.exchangeViewModel.depositCurrency,
|
||||
hasRefundAddress: true,
|
||||
currencies: widget.exchangeViewModel.depositCurrencies,
|
||||
onCurrencySelected: (currency) {
|
||||
if (currency is CryptoCurrency) {
|
||||
widget.exchangeViewModel
|
||||
.changeDepositCurrency(currency: currency);
|
||||
}
|
||||
},
|
||||
currencyValueValidator: (value) {
|
||||
return !widget.exchangeViewModel.isFixedRateMode &&
|
||||
value != S.of(context).all
|
||||
? AmountValidator(
|
||||
isAutovalidate: true,
|
||||
currency: widget.exchangeViewModel.isFixedRateMode
|
||||
? widget.exchangeViewModel.receiveCurrency
|
||||
: widget.exchangeViewModel.depositCurrency,
|
||||
minValue: widget.exchangeViewModel.limits.min.toString(),
|
||||
maxValue: widget.exchangeViewModel.limits.max.toString(),
|
||||
amountParsingProxy:
|
||||
widget.exchangeViewModel.amountParsingProxy,
|
||||
).call(value)
|
||||
: null;
|
||||
},
|
||||
addressTextFieldValidator: AddressValidator(
|
||||
type: widget.exchangeViewModel.depositCurrency),
|
||||
onPushPasteButton: (context) async {
|
||||
final clipboard = await Clipboard.getData('text/plain');
|
||||
widget.exchangeViewModel.depositAddress = clipboard?.text ?? '';
|
||||
RepaintBoundary(
|
||||
child: Form(
|
||||
key: formKey,
|
||||
child: Column(
|
||||
children: [
|
||||
Observer(
|
||||
builder: (_) => SwapAmountBox(
|
||||
isReceiverCard: false,
|
||||
walletSwitcherViewModel: widget.walletSwitcherViewModel,
|
||||
exchangeViewModel: widget.exchangeViewModel,
|
||||
onDispose: disposeBestRateSync,
|
||||
hasAllAmount: widget.exchangeViewModel.hasAllAmount,
|
||||
allAmount: widget.exchangeViewModel.hasAllAmount
|
||||
? () => widget.exchangeViewModel.enableSendAllAmount()
|
||||
: null,
|
||||
key: depositKey,
|
||||
title: S.of(context).send,
|
||||
initialCurrency: widget.exchangeViewModel.depositCurrency,
|
||||
hasRefundAddress: true,
|
||||
currencies: widget.exchangeViewModel.depositCurrencies,
|
||||
onCurrencySelected: (currency) {
|
||||
if (currency is CryptoCurrency) {
|
||||
widget.exchangeViewModel
|
||||
.changeDepositCurrency(currency: currency);
|
||||
}
|
||||
},
|
||||
currencyValueValidator: (value) {
|
||||
return !widget.exchangeViewModel.isFixedRateMode &&
|
||||
value != S.of(context).all
|
||||
? AmountValidator(
|
||||
isAutovalidate: true,
|
||||
currency: widget.exchangeViewModel.isFixedRateMode
|
||||
? widget.exchangeViewModel.receiveCurrency
|
||||
: widget.exchangeViewModel.depositCurrency,
|
||||
minValue: widget.exchangeViewModel.limits.min.toString(),
|
||||
maxValue: widget.exchangeViewModel.limits.max.toString(),
|
||||
amountParsingProxy:
|
||||
widget.exchangeViewModel.amountParsingProxy,
|
||||
).call(value)
|
||||
: null;
|
||||
},
|
||||
addressTextFieldValidator: AddressValidator(
|
||||
type: widget.exchangeViewModel.depositCurrency),
|
||||
onPushPasteButton: (context) async {
|
||||
final clipboard = await Clipboard.getData('text/plain');
|
||||
widget.exchangeViewModel.depositAddress = clipboard?.text ?? '';
|
||||
|
||||
final domain = widget.exchangeViewModel.depositAddress;
|
||||
widget.exchangeViewModel.depositAddress =
|
||||
await fetchParsedAddress(context, domain,
|
||||
widget.exchangeViewModel.depositCurrency);
|
||||
},
|
||||
onPushAddressBookButton: (context) async {
|
||||
final domain = widget.exchangeViewModel.depositAddress;
|
||||
widget.exchangeViewModel.depositAddress =
|
||||
await fetchParsedAddress(context, domain,
|
||||
widget.exchangeViewModel.depositCurrency);
|
||||
},
|
||||
final domain = widget.exchangeViewModel.depositAddress;
|
||||
widget.exchangeViewModel.depositAddress =
|
||||
await fetchParsedAddress(context, domain,
|
||||
widget.exchangeViewModel.depositCurrency);
|
||||
},
|
||||
onPushAddressBookButton: (context) async {
|
||||
final domain = widget.exchangeViewModel.depositAddress;
|
||||
widget.exchangeViewModel.depositAddress =
|
||||
await fetchParsedAddress(context, domain,
|
||||
widget.exchangeViewModel.depositCurrency);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
SwapLimitPopup(exchangeViewModel: widget.exchangeViewModel),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12.0),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Container(
|
||||
height: 1,
|
||||
width: double.infinity,
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
),
|
||||
ModernButton.svg(
|
||||
size: 36,
|
||||
iconSize: 24,
|
||||
svgPath: "assets/new-ui/swap_amounts.svg",
|
||||
onPressed: widget.exchangeViewModel.reverseSwapDirection,
|
||||
),
|
||||
],
|
||||
SwapLimitPopup(exchangeViewModel: widget.exchangeViewModel),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12.0),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Container(
|
||||
height: 1,
|
||||
width: double.infinity,
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
),
|
||||
ModernButton.svg(
|
||||
size: 36,
|
||||
iconSize: 24,
|
||||
svgPath: "assets/new-ui/swap_amounts.svg",
|
||||
onPressed: widget.exchangeViewModel.reverseSwapDirection,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Observer(
|
||||
builder: (_) => SwapAmountBox(
|
||||
isReceiverCard: true,
|
||||
walletSwitcherViewModel: widget.walletSwitcherViewModel,
|
||||
exchangeViewModel: widget.exchangeViewModel,
|
||||
onDispose: disposeBestRateSync,
|
||||
key: receiveKey,
|
||||
title: S.of(context).receive,
|
||||
initialCurrency: widget.exchangeViewModel.receiveCurrency,
|
||||
currencies: widget.exchangeViewModel.receiveCurrencies,
|
||||
onCurrencySelected: (currency) {
|
||||
if (currency is CryptoCurrency) {
|
||||
widget.exchangeViewModel
|
||||
.changeReceiveCurrency(currency: currency);
|
||||
}
|
||||
},
|
||||
currencyValueValidator: (value) {
|
||||
return widget.exchangeViewModel.isFixedRateMode
|
||||
? AmountValidator(
|
||||
isAutovalidate: true,
|
||||
currency: widget.exchangeViewModel.receiveCurrency,
|
||||
minValue: widget.exchangeViewModel.limits.min.toString(),
|
||||
maxValue: widget.exchangeViewModel.limits.max.toString(),
|
||||
amountParsingProxy:
|
||||
widget.exchangeViewModel.amountParsingProxy,
|
||||
).call(value)
|
||||
: null;
|
||||
},
|
||||
addressTextFieldValidator: AddressValidator(
|
||||
type: widget.exchangeViewModel.receiveCurrency),
|
||||
onPushPasteButton: (context) async {
|
||||
final clipboard = await Clipboard.getData('text/plain');
|
||||
widget.exchangeViewModel.receiveAddress = clipboard?.text ?? '';
|
||||
Observer(
|
||||
builder: (_) => SwapAmountBox(
|
||||
isReceiverCard: true,
|
||||
walletSwitcherViewModel: widget.walletSwitcherViewModel,
|
||||
exchangeViewModel: widget.exchangeViewModel,
|
||||
onDispose: disposeBestRateSync,
|
||||
key: receiveKey,
|
||||
title: S.of(context).receive,
|
||||
initialCurrency: widget.exchangeViewModel.receiveCurrency,
|
||||
currencies: widget.exchangeViewModel.receiveCurrencies,
|
||||
onCurrencySelected: (currency) {
|
||||
if (currency is CryptoCurrency) {
|
||||
widget.exchangeViewModel
|
||||
.changeReceiveCurrency(currency: currency);
|
||||
}
|
||||
},
|
||||
currencyValueValidator: (value) {
|
||||
return widget.exchangeViewModel.isFixedRateMode
|
||||
? AmountValidator(
|
||||
isAutovalidate: true,
|
||||
currency: widget.exchangeViewModel.receiveCurrency,
|
||||
minValue: widget.exchangeViewModel.limits.min.toString(),
|
||||
maxValue: widget.exchangeViewModel.limits.max.toString(),
|
||||
amountParsingProxy:
|
||||
widget.exchangeViewModel.amountParsingProxy,
|
||||
).call(value)
|
||||
: null;
|
||||
},
|
||||
addressTextFieldValidator: AddressValidator(
|
||||
type: widget.exchangeViewModel.receiveCurrency),
|
||||
onPushPasteButton: (context) async {
|
||||
final clipboard = await Clipboard.getData('text/plain');
|
||||
widget.exchangeViewModel.receiveAddress = clipboard?.text ?? '';
|
||||
|
||||
final domain = widget.exchangeViewModel.receiveAddress;
|
||||
widget.exchangeViewModel.receiveAddress =
|
||||
await fetchParsedAddress(context, domain,
|
||||
widget.exchangeViewModel.receiveCurrency);
|
||||
},
|
||||
onPushAddressBookButton: (context) async {
|
||||
final domain = widget.exchangeViewModel.receiveAddress;
|
||||
widget.exchangeViewModel.receiveAddress =
|
||||
await fetchParsedAddress(context, domain,
|
||||
widget.exchangeViewModel.receiveCurrency);
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
final domain = widget.exchangeViewModel.receiveAddress;
|
||||
widget.exchangeViewModel.receiveAddress =
|
||||
await fetchParsedAddress(context, domain,
|
||||
widget.exchangeViewModel.receiveCurrency);
|
||||
},
|
||||
onPushAddressBookButton: (context) async {
|
||||
final domain = widget.exchangeViewModel.receiveAddress;
|
||||
widget.exchangeViewModel.receiveAddress =
|
||||
await fetchParsedAddress(context, domain,
|
||||
widget.exchangeViewModel.receiveCurrency);
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Observer(
|
||||
|
||||
+242
-240
@@ -227,262 +227,264 @@ class SendPage extends BasePage {
|
||||
!sendViewModel.balanceViewModel.isReversing,
|
||||
onLongPressUp: () => sendViewModel.balanceViewModel.isReversing =
|
||||
!sendViewModel.balanceViewModel.isReversing,
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.only(bottom: 24),
|
||||
content: FocusTraversalGroup(
|
||||
policy: OrderedTraversalPolicy(),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
PageViewHeightAdaptable(
|
||||
controller: controller,
|
||||
children: sendCards,
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 24, right: 24, bottom: 10),
|
||||
child: Container(
|
||||
height: 10,
|
||||
child: Observer(
|
||||
builder: (_) {
|
||||
final count = sendViewModel.outputs.length;
|
||||
|
||||
return count > 1
|
||||
? Semantics(
|
||||
label: 'Page Indicator',
|
||||
hint: 'Swipe to change receiver',
|
||||
excludeSemantics: true,
|
||||
child: SmoothPageIndicator(
|
||||
controller: controller,
|
||||
count: count,
|
||||
effect: ScrollingDotsEffect(
|
||||
spacing: 6.0,
|
||||
radius: 6.0,
|
||||
dotWidth: 6.0,
|
||||
dotHeight: 6.0,
|
||||
dotColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.primary
|
||||
.withAlpha(100),
|
||||
activeDotColor: Theme.of(context).colorScheme.primary,
|
||||
child: RepaintBoundary(
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.only(bottom: 24),
|
||||
content: FocusTraversalGroup(
|
||||
policy: OrderedTraversalPolicy(),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
PageViewHeightAdaptable(
|
||||
controller: controller,
|
||||
children: sendCards,
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 24, right: 24, bottom: 10),
|
||||
child: Container(
|
||||
height: 10,
|
||||
child: Observer(
|
||||
builder: (_) {
|
||||
final count = sendViewModel.outputs.length;
|
||||
|
||||
return count > 1
|
||||
? Semantics(
|
||||
label: 'Page Indicator',
|
||||
hint: 'Swipe to change receiver',
|
||||
excludeSemantics: true,
|
||||
child: SmoothPageIndicator(
|
||||
controller: controller,
|
||||
count: count,
|
||||
effect: ScrollingDotsEffect(
|
||||
spacing: 6.0,
|
||||
radius: 6.0,
|
||||
dotWidth: 6.0,
|
||||
dotHeight: 6.0,
|
||||
dotColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.primary
|
||||
.withAlpha(100),
|
||||
activeDotColor: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: Offstage();
|
||||
},
|
||||
)
|
||||
: Offstage();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 40,
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Observer(
|
||||
builder: (_) {
|
||||
final templates = sendViewModel.templates;
|
||||
final itemCount = templates.length;
|
||||
|
||||
return Row(
|
||||
children: <Widget>[
|
||||
AddTemplateButton(
|
||||
key: ValueKey('send_page_add_template_button_key'),
|
||||
onTap: () =>
|
||||
Navigator.of(context).pushNamed(Routes.sendTemplate),
|
||||
currentTemplatesLength: templates.length,
|
||||
),
|
||||
ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemCount: itemCount,
|
||||
itemBuilder: (context, index) {
|
||||
final template = templates[index];
|
||||
return TemplateTile(
|
||||
key: UniqueKey(),
|
||||
to: template.name,
|
||||
hasMultipleRecipients:
|
||||
template.additionalRecipients != null &&
|
||||
template.additionalRecipients!.length > 1,
|
||||
amount: template.isCurrencySelected
|
||||
? template.amount
|
||||
: template.amountFiat,
|
||||
from: template.isCurrencySelected
|
||||
? template.cryptoCurrency
|
||||
: template.fiatCurrency,
|
||||
onTap: () async {
|
||||
sendViewModel.state = LoadingTemplateExecutingState();
|
||||
if (template.additionalRecipients?.isNotEmpty ??
|
||||
false) {
|
||||
sendViewModel.clearOutputs();
|
||||
|
||||
for (int i = 0;
|
||||
i < template.additionalRecipients!.length;
|
||||
i++) {
|
||||
Output output;
|
||||
try {
|
||||
output = sendViewModel.outputs[i];
|
||||
} catch (e) {
|
||||
sendViewModel.addOutput();
|
||||
output = sendViewModel.outputs[i];
|
||||
Container(
|
||||
height: 40,
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Observer(
|
||||
builder: (_) {
|
||||
final templates = sendViewModel.templates;
|
||||
final itemCount = templates.length;
|
||||
|
||||
return Row(
|
||||
children: <Widget>[
|
||||
AddTemplateButton(
|
||||
key: ValueKey('send_page_add_template_button_key'),
|
||||
onTap: () =>
|
||||
Navigator.of(context).pushNamed(Routes.sendTemplate),
|
||||
currentTemplatesLength: templates.length,
|
||||
),
|
||||
ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemCount: itemCount,
|
||||
itemBuilder: (context, index) {
|
||||
final template = templates[index];
|
||||
return TemplateTile(
|
||||
key: UniqueKey(),
|
||||
to: template.name,
|
||||
hasMultipleRecipients:
|
||||
template.additionalRecipients != null &&
|
||||
template.additionalRecipients!.length > 1,
|
||||
amount: template.isCurrencySelected
|
||||
? template.amount
|
||||
: template.amountFiat,
|
||||
from: template.isCurrencySelected
|
||||
? template.cryptoCurrency
|
||||
: template.fiatCurrency,
|
||||
onTap: () async {
|
||||
sendViewModel.state = LoadingTemplateExecutingState();
|
||||
if (template.additionalRecipients?.isNotEmpty ??
|
||||
false) {
|
||||
sendViewModel.clearOutputs();
|
||||
|
||||
for (int i = 0;
|
||||
i < template.additionalRecipients!.length;
|
||||
i++) {
|
||||
Output output;
|
||||
try {
|
||||
output = sendViewModel.outputs[i];
|
||||
} catch (e) {
|
||||
sendViewModel.addOutput();
|
||||
output = sendViewModel.outputs[i];
|
||||
}
|
||||
|
||||
await _setInputsFromTemplate(
|
||||
context,
|
||||
output: output,
|
||||
template: template.additionalRecipients![i],
|
||||
);
|
||||
}
|
||||
|
||||
} else {
|
||||
final output = _defineCurrentOutput();
|
||||
await _setInputsFromTemplate(
|
||||
context,
|
||||
output: output,
|
||||
template: template.additionalRecipients![i],
|
||||
template: template,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
final output = _defineCurrentOutput();
|
||||
await _setInputsFromTemplate(
|
||||
context,
|
||||
output: output,
|
||||
template: template,
|
||||
sendViewModel.state = InitialExecutionState();
|
||||
},
|
||||
onRemove: () {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (dialogContext) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).template,
|
||||
alertContent:
|
||||
S.of(context).confirm_delete_template,
|
||||
rightButtonText: S.of(context).delete,
|
||||
leftButtonText: S.of(context).cancel,
|
||||
actionRightButton: () {
|
||||
Navigator.of(dialogContext).pop();
|
||||
sendViewModel.sendTemplateViewModel
|
||||
.removeTemplate(template: template);
|
||||
},
|
||||
actionLeftButton: () =>
|
||||
Navigator.of(dialogContext).pop());
|
||||
},
|
||||
);
|
||||
}
|
||||
sendViewModel.state = InitialExecutionState();
|
||||
},
|
||||
onRemove: () {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (dialogContext) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).template,
|
||||
alertContent:
|
||||
S.of(context).confirm_delete_template,
|
||||
rightButtonText: S.of(context).delete,
|
||||
leftButtonText: S.of(context).cancel,
|
||||
actionRightButton: () {
|
||||
Navigator.of(dialogContext).pop();
|
||||
sendViewModel.sendTemplateViewModel
|
||||
.removeTemplate(template: template);
|
||||
},
|
||||
actionLeftButton: () =>
|
||||
Navigator.of(dialogContext).pop());
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||
bottomSection: Column(
|
||||
children: [
|
||||
if (sendViewModel.sendTemplateViewModel.hasMultiRecipient)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(bottom: 12),
|
||||
child: PrimaryButton(
|
||||
key: ValueKey('send_page_add_receiver_button_key'),
|
||||
onPressed: () {
|
||||
sendViewModel.addOutput();
|
||||
Future.delayed(const Duration(milliseconds: 250), () {
|
||||
controller.jumpToPage(sendViewModel.outputs.length - 1);
|
||||
});
|
||||
},
|
||||
text: S.of(context).add_receiver,
|
||||
color: Colors.transparent,
|
||||
textColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
isDottedBorder: true,
|
||||
borderColor: Theme.of(context).colorScheme.outline,
|
||||
)),
|
||||
Observer(
|
||||
builder: (_) {
|
||||
return LoadingPrimaryButton(
|
||||
key: ValueKey('send_page_send_button_key'),
|
||||
onPressed: () async {
|
||||
//Request dummy node to get the focus out of the text fields
|
||||
FocusScope.of(context).requestFocus(FocusNode());
|
||||
|
||||
if (sendViewModel.state is IsExecutingState) return;
|
||||
if (_formKey.currentState != null &&
|
||||
!_formKey.currentState!.validate()) {
|
||||
if (sendViewModel.outputs.length > 1) {
|
||||
showErrorValidationAlert(context);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
final hasInvalidItems = sendViewModel.outputs.any((item) =>
|
||||
item.address.isEmpty ||
|
||||
(item.cryptoAmount.isEmpty && !item.sendAll));
|
||||
|
||||
if (hasInvalidItems) {
|
||||
showErrorValidationAlert(context);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sendViewModel.wallet.isHardwareWallet) {
|
||||
if (!sendViewModel.hardwareWalletViewModel!.isConnected) {
|
||||
await Navigator.of(context).pushNamed(Routes.connectDevices,
|
||||
arguments: ConnectDevicePageParams(
|
||||
walletType: sendViewModel.walletType,
|
||||
hardwareWalletType:
|
||||
sendViewModel.wallet.walletInfo.hardwareWalletType!,
|
||||
onConnectDevice: (BuildContext context, _) {
|
||||
sendViewModel.hardwareWalletViewModel!
|
||||
.initWallet(sendViewModel.wallet);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
));
|
||||
} else {
|
||||
sendViewModel.hardwareWalletViewModel!
|
||||
.initWallet(sendViewModel.wallet);
|
||||
}
|
||||
}
|
||||
|
||||
if (sendViewModel.wallet.type == WalletType.monero) {
|
||||
int amount = 0;
|
||||
for (var item in sendViewModel.outputs) {
|
||||
amount += item.formattedCryptoAmount;
|
||||
}
|
||||
if (monero!.needExportOutputs(sendViewModel.wallet, amount)) {
|
||||
await Navigator.of(context).pushNamed(Routes.urqrAnimatedPage,
|
||||
arguments: monero!.exportOutputsUR(sendViewModel.wallet));
|
||||
await Future.delayed(
|
||||
Duration(seconds: 1)); // wait for monero to refresh the state
|
||||
}
|
||||
if (monero!.needExportOutputs(sendViewModel.wallet, amount)) {
|
||||
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||
bottomSection: Column(
|
||||
children: [
|
||||
if (sendViewModel.sendTemplateViewModel.hasMultiRecipient)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(bottom: 12),
|
||||
child: PrimaryButton(
|
||||
key: ValueKey('send_page_add_receiver_button_key'),
|
||||
onPressed: () {
|
||||
sendViewModel.addOutput();
|
||||
Future.delayed(const Duration(milliseconds: 250), () {
|
||||
controller.jumpToPage(sendViewModel.outputs.length - 1);
|
||||
});
|
||||
},
|
||||
text: S.of(context).add_receiver,
|
||||
color: Colors.transparent,
|
||||
textColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
isDottedBorder: true,
|
||||
borderColor: Theme.of(context).colorScheme.outline,
|
||||
)),
|
||||
Observer(
|
||||
builder: (_) {
|
||||
return LoadingPrimaryButton(
|
||||
key: ValueKey('send_page_send_button_key'),
|
||||
onPressed: () async {
|
||||
//Request dummy node to get the focus out of the text fields
|
||||
FocusScope.of(context).requestFocus(FocusNode());
|
||||
|
||||
if (sendViewModel.state is IsExecutingState) return;
|
||||
if (_formKey.currentState != null &&
|
||||
!_formKey.currentState!.validate()) {
|
||||
if (sendViewModel.outputs.length > 1) {
|
||||
showErrorValidationAlert(context);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final check = sendViewModel.shouldDisplayTotp();
|
||||
authService.authenticateAction(
|
||||
context,
|
||||
conditionToDetermineIfToUse2FA: check,
|
||||
onAuthSuccess: (value) async {
|
||||
if (value) {
|
||||
await sendViewModel.createTransaction();
|
||||
|
||||
final hasInvalidItems = sendViewModel.outputs.any((item) =>
|
||||
item.address.isEmpty ||
|
||||
(item.cryptoAmount.isEmpty && !item.sendAll));
|
||||
|
||||
if (hasInvalidItems) {
|
||||
showErrorValidationAlert(context);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sendViewModel.wallet.isHardwareWallet) {
|
||||
if (!sendViewModel.hardwareWalletViewModel!.isConnected) {
|
||||
await Navigator.of(context).pushNamed(Routes.connectDevices,
|
||||
arguments: ConnectDevicePageParams(
|
||||
walletType: sendViewModel.walletType,
|
||||
hardwareWalletType:
|
||||
sendViewModel.wallet.walletInfo.hardwareWalletType!,
|
||||
onConnectDevice: (BuildContext context, _) {
|
||||
sendViewModel.hardwareWalletViewModel!
|
||||
.initWallet(sendViewModel.wallet);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
));
|
||||
} else {
|
||||
sendViewModel.hardwareWalletViewModel!
|
||||
.initWallet(sendViewModel.wallet);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
text: _sendButtonText(context),
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
isLoading: sendViewModel.state is IsExecutingState ||
|
||||
sendViewModel.state is TransactionCommitting ||
|
||||
sendViewModel.state is IsAwaitingDeviceResponseState ||
|
||||
sendViewModel.state is LoadingTemplateExecutingState,
|
||||
isDisabled: !sendViewModel.isReadyForSend || sendViewModel.state is ExecutedSuccessfullyState,
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
)),
|
||||
}
|
||||
|
||||
if (sendViewModel.wallet.type == WalletType.monero) {
|
||||
int amount = 0;
|
||||
for (var item in sendViewModel.outputs) {
|
||||
amount += item.formattedCryptoAmount;
|
||||
}
|
||||
if (monero!.needExportOutputs(sendViewModel.wallet, amount)) {
|
||||
await Navigator.of(context).pushNamed(Routes.urqrAnimatedPage,
|
||||
arguments: monero!.exportOutputsUR(sendViewModel.wallet));
|
||||
await Future.delayed(
|
||||
Duration(seconds: 1)); // wait for monero to refresh the state
|
||||
}
|
||||
if (monero!.needExportOutputs(sendViewModel.wallet, amount)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final check = sendViewModel.shouldDisplayTotp();
|
||||
authService.authenticateAction(
|
||||
context,
|
||||
conditionToDetermineIfToUse2FA: check,
|
||||
onAuthSuccess: (value) async {
|
||||
if (value) {
|
||||
await sendViewModel.createTransaction();
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
text: _sendButtonText(context),
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
isLoading: sendViewModel.state is IsExecutingState ||
|
||||
sendViewModel.state is TransactionCommitting ||
|
||||
sendViewModel.state is IsAwaitingDeviceResponseState ||
|
||||
sendViewModel.state is LoadingTemplateExecutingState,
|
||||
isDisabled: !sendViewModel.isReadyForSend || sendViewModel.state is ExecutedSuccessfullyState,
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
)),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user