Mweb checkbox (#2000)

* [skip-ci] wip

* [skip-ci] styles still need updating

* working but needs style updates

* fix checkbox caption color

* sort mweb coins to be last when selecting inputs

* ui fixes

* [skip-ci] default to mweb-checkbox being off

* adaptable page view builder + workaround for keyboard actions

* Fix checkbox themeing and send card sizing

* Update lib/src/screens/send/widgets/send_card.dart

---------

Co-authored-by: tuxpizza <tuxsudo@tux.pizza>
Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
Matthew Fosse 2025-03-10 16:37:23 -07:00 committed by GitHub
parent 1e4dbb5bc9
commit 1c8af1afae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 885 additions and 581 deletions

View file

@ -632,8 +632,8 @@ abstract class ElectrumWalletBase
}).toList(); }).toList();
final unconfirmedCoins = availableInputs.where((utx) => utx.confirmations == 0).toList(); final unconfirmedCoins = availableInputs.where((utx) => utx.confirmations == 0).toList();
// sort the unconfirmed coins so that mweb coins are first: // sort the unconfirmed coins so that mweb coins are last:
availableInputs.sort((a, b) => a.bitcoinAddressRecord.type == SegwitAddresType.mweb ? -1 : 1); availableInputs.sort((a, b) => a.bitcoinAddressRecord.type == SegwitAddresType.mweb ? 1 : -1);
for (int i = 0; i < availableInputs.length; i++) { for (int i = 0; i < availableInputs.length; i++) {
final utx = availableInputs[i]; final utx = availableInputs[i];

View file

@ -748,7 +748,7 @@ Future<void> setup({
getIt.get<ContactListViewModel>(), getIt.get<ContactListViewModel>(),
_transactionDescriptionBox, _transactionDescriptionBox,
getIt.get<AppStore>().wallet!.isHardwareWallet ? getIt.get<LedgerViewModel>() : null, getIt.get<AppStore>().wallet!.isHardwareWallet ? getIt.get<LedgerViewModel>() : null,
coinTypeToSpendFrom: coinTypeToSpendFrom ?? UnspentCoinType.any, coinTypeToSpendFrom: coinTypeToSpendFrom ?? UnspentCoinType.nonMweb,
getIt.get<UnspentCoinsListViewModel>(param1: coinTypeToSpendFrom), getIt.get<UnspentCoinsListViewModel>(param1: coinTypeToSpendFrom),
), ),
); );

View file

@ -14,14 +14,17 @@ import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart'
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart';
import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart'; import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart';
import 'package:cake_wallet/src/screens/send/widgets/send_card.dart'; import 'package:cake_wallet/src/screens/send/widgets/send_card.dart';
import 'package:cake_wallet/src/widgets/adaptable_page_view.dart';
import 'package:cake_wallet/src/widgets/add_template_button.dart'; import 'package:cake_wallet/src/widgets/add_template_button.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:cake_wallet/src/widgets/picker.dart'; import 'package:cake_wallet/src/widgets/picker.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/src/widgets/template_tile.dart'; import 'package:cake_wallet/src/widgets/template_tile.dart';
import 'package:cake_wallet/src/widgets/trail_button.dart'; import 'package:cake_wallet/src/widgets/trail_button.dart';
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
import 'package:cake_wallet/themes/extensions/seed_widget_theme.dart'; import 'package:cake_wallet/themes/extensions/seed_widget_theme.dart';
import 'package:cake_wallet/themes/extensions/send_page_theme.dart'; import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/themes/theme_base.dart';
@ -38,6 +41,7 @@ import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/crypto_currency.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:keyboard_actions/keyboard_actions.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:smooth_page_indicator/smooth_page_indicator.dart'; import 'package:smooth_page_indicator/smooth_page_indicator.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
@ -93,7 +97,7 @@ class SendPage extends BasePage {
return MergeSemantics( return MergeSemantics(
child: SizedBox( child: SizedBox(
height: isMobileView ? 37 : 45, height: isMobileView ? 37 : 45,
width: isMobileView ? 37 : 45, width: isMobileView ? 47: 45,
child: ButtonTheme( child: ButtonTheme(
minWidth: double.minPositive, minWidth: double.minPositive,
child: Semantics( child: Semantics(
@ -114,18 +118,6 @@ class SendPage extends BasePage {
@override @override
AppBarStyle get appBarStyle => AppBarStyle.transparent; AppBarStyle get appBarStyle => AppBarStyle.transparent;
double _sendCardHeight(BuildContext context) {
double initialHeight = 480;
if (sendViewModel.hasCoinControl) {
initialHeight += 55;
}
if (!responsiveLayoutUtil.shouldRenderMobileUI) {
return initialHeight - 66;
}
return initialHeight;
}
@override @override
void onClose(BuildContext context) { void onClose(BuildContext context) {
sendViewModel.onClose(); sendViewModel.onClose();
@ -174,285 +166,316 @@ class SendPage extends BasePage {
Widget body(BuildContext context) { Widget body(BuildContext context) {
_setEffects(context); _setEffects(context);
return GestureDetector( return Observer(builder: (_) {
onLongPress: () => List<Widget> sendCards = [];
sendViewModel.balanceViewModel.isReversing = !sendViewModel.balanceViewModel.isReversing, List<KeyboardActionsItem> keyboardActions = [];
onLongPressUp: () => for (var output in sendViewModel.outputs) {
sendViewModel.balanceViewModel.isReversing = !sendViewModel.balanceViewModel.isReversing, var cryptoAmountFocus = FocusNode();
child: Form( var fiatAmountFocus = FocusNode();
key: _formKey, sendCards.add(SendCard(
child: ScrollableWithBottomSection( currentTheme: currentTheme,
contentPadding: EdgeInsets.only(bottom: 24), key: output.key,
content: FocusTraversalGroup( output: output,
policy: OrderedTraversalPolicy(), sendViewModel: sendViewModel,
child: Column( initialPaymentRequest: initialPaymentRequest,
children: <Widget>[ cryptoAmountFocus: cryptoAmountFocus,
Container( fiatAmountFocus: fiatAmountFocus,
height: _sendCardHeight(context), ));
child: Observer( keyboardActions.add(KeyboardActionsItem(
builder: (_) { focusNode: cryptoAmountFocus, toolbarButtons: [(_) => KeyboardDoneButton()]));
return PageView.builder( keyboardActions.add(KeyboardActionsItem(
scrollDirection: Axis.horizontal, focusNode: fiatAmountFocus, toolbarButtons: [(_) => KeyboardDoneButton()]));
controller: controller, }
itemCount: sendViewModel.outputs.length, return Stack(
itemBuilder: (context, index) { children: [
final output = sendViewModel.outputs[index]; KeyboardActions(
config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.ALL,
keyboardBarColor: Theme.of(context).extension<KeyboardTheme>()!.keyboardBarColor,
nextFocus: false,
actions: keyboardActions,
),
child: Container(
height: 0,
color: Colors.transparent,
),
),
GestureDetector(
onLongPress: () => sendViewModel.balanceViewModel.isReversing =
!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 SendCard( return count > 1
key: output.key, ? Semantics(
output: output, label: 'Page Indicator',
sendViewModel: sendViewModel, hint: 'Swipe to change receiver',
initialPaymentRequest: initialPaymentRequest, 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)
.extension<SendPageTheme>()!
.indicatorDotColor,
activeDotColor: Theme.of(context)
.extension<SendPageTheme>()!
.templateBackgroundColor),
))
: 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 = IsExecutingState();
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,
);
}
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());
},
);
},
);
},
),
],
); );
}); },
}, ),
)), ),
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)
.extension<SendPageTheme>()!
.indicatorDotColor,
activeDotColor: Theme.of(context)
.extension<SendPageTheme>()!
.templateBackgroundColor),
))
: Offstage();
},
),
), ),
), ),
Container( bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
height: 40, bottomSection: Column(
width: double.infinity, children: [
padding: EdgeInsets.only(left: 24), if (sendViewModel.hasCurrecyChanger)
child: SingleChildScrollView( Observer(
scrollDirection: Axis.horizontal, builder: (_) => Padding(
child: Observer( padding: EdgeInsets.only(bottom: 12),
child: PrimaryButton(
key: ValueKey('send_page_change_asset_button_key'),
onPressed: () => presentCurrencyPicker(context),
text: 'Change your asset (${sendViewModel.selectedCryptoCurrency})',
color: Colors.transparent,
textColor:
Theme.of(context).extension<SeedWidgetTheme>()!.hintTextColor,
),
),
),
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).extension<SeedWidgetTheme>()!.hintTextColor,
isDottedBorder: true,
borderColor: Theme.of(context)
.extension<SendPageTheme>()!
.templateDottedBorderColor,
)),
Observer(
builder: (_) { builder: (_) {
final templates = sendViewModel.templates; return LoadingPrimaryButton(
final itemCount = templates.length; key: ValueKey('send_page_send_button_key'),
onPressed: () async {
if (sendViewModel.state is IsExecutingState) return;
if (_formKey.currentState != null &&
!_formKey.currentState!.validate()) {
if (sendViewModel.outputs.length > 1) {
showErrorValidationAlert(context);
}
return Row( return;
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 = IsExecutingState();
if (template.additionalRecipients?.isNotEmpty ?? false) {
sendViewModel.clearOutputs();
for (int i = 0; final notValidItems = sendViewModel.outputs
i < template.additionalRecipients!.length; .where(
i++) { (item) => item.address.isEmpty || item.cryptoAmount.isEmpty)
Output output; .toList();
try {
output = sendViewModel.outputs[i];
} catch (e) {
sendViewModel.addOutput();
output = sendViewModel.outputs[i];
}
await _setInputsFromTemplate( if (notValidItems.isNotEmpty) {
context, showErrorValidationAlert(context);
output: output, return;
template: template.additionalRecipients![i], }
);
} if (sendViewModel.wallet.isHardwareWallet) {
} else { if (!sendViewModel.ledgerViewModel!.isConnected) {
final output = _defineCurrentOutput(); await Navigator.of(context).pushNamed(Routes.connectDevices,
await _setInputsFromTemplate( arguments: ConnectDevicePageParams(
context, walletType: sendViewModel.walletType,
output: output, onConnectDevice: (BuildContext context, _) {
template: template, sendViewModel.ledgerViewModel!
); .setLedger(sendViewModel.wallet);
} Navigator.of(context).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());
}, },
); ));
}, } else {
); sendViewModel.ledgerViewModel!.setLedger(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: 'export-outputs');
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: S.of(context).send,
color: Theme.of(context).primaryColor,
textColor: Colors.white,
isLoading: sendViewModel.state is IsExecutingState ||
sendViewModel.state is TransactionCommitting ||
sendViewModel.state is IsAwaitingDeviceResponseState,
isDisabled: !sendViewModel.isReadyForSend,
); );
}, },
), )
), ],
), )),
],
),
), ),
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24), ),
bottomSection: Column( ],
children: [ );
if (sendViewModel.hasCurrecyChanger) });
Observer(
builder: (_) => Padding(
padding: EdgeInsets.only(bottom: 12),
child: PrimaryButton(
key: ValueKey('send_page_change_asset_button_key'),
onPressed: () => presentCurrencyPicker(context),
text: 'Change your asset (${sendViewModel.selectedCryptoCurrency})',
color: Colors.transparent,
textColor: Theme.of(context).extension<SeedWidgetTheme>()!.hintTextColor,
),
),
),
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).extension<SeedWidgetTheme>()!.hintTextColor,
isDottedBorder: true,
borderColor:
Theme.of(context).extension<SendPageTheme>()!.templateDottedBorderColor,
)),
Observer(
builder: (_) {
return LoadingPrimaryButton(
key: ValueKey('send_page_send_button_key'),
onPressed: () async {
if (sendViewModel.state is IsExecutingState) return;
if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
if (sendViewModel.outputs.length > 1) {
showErrorValidationAlert(context);
}
return;
}
final notValidItems = sendViewModel.outputs
.where((item) => item.address.isEmpty || item.cryptoAmount.isEmpty)
.toList();
if (notValidItems.isNotEmpty) {
showErrorValidationAlert(context);
return;
}
if (sendViewModel.wallet.isHardwareWallet) {
if (!sendViewModel.ledgerViewModel!.isConnected) {
await Navigator.of(context).pushNamed(
Routes.connectDevices,
arguments: ConnectDevicePageParams(
walletType: sendViewModel.walletType,
onConnectDevice: (BuildContext context, _) {
sendViewModel.ledgerViewModel!
.setLedger(sendViewModel.wallet);
Navigator.of(context).pop();
},
));
} else {
sendViewModel.ledgerViewModel!
.setLedger(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: 'export-outputs');
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: S.of(context).send,
color: Theme.of(context).primaryColor,
textColor: Colors.white,
isLoading: sendViewModel.state is IsExecutingState ||
sendViewModel.state is TransactionCommitting ||
sendViewModel.state is IsAwaitingDeviceResponseState,
isDisabled: !sendViewModel.isReadyForSend,
);
},
)
],
)),
),
);
} }
BuildContext? dialogContext; BuildContext? dialogContext;
@ -525,13 +548,12 @@ class SendPage extends BasePage {
if (state is TransactionCommitted) { if (state is TransactionCommitted) {
WidgetsBinding.instance.addPostFrameCallback((_) async { WidgetsBinding.instance.addPostFrameCallback((_) async {
if (!context.mounted) { if (!context.mounted) {
return; return;
} }
final successMessage = S.of(context).send_success( final successMessage =
sendViewModel.selectedCryptoCurrency.toString()); S.of(context).send_success(sendViewModel.selectedCryptoCurrency.toString());
final waitMessage = sendViewModel.walletType == WalletType.solana final waitMessage = sendViewModel.walletType == WalletType.solana
? '. ${S.of(context).waitFewSecondForTxUpdate}' ? '. ${S.of(context).waitFewSecondForTxUpdate}'
@ -539,10 +561,8 @@ class SendPage extends BasePage {
String alertContent = "$successMessage$waitMessage"; String alertContent = "$successMessage$waitMessage";
await Navigator.of(context).pushNamed( await Navigator.of(context)
Routes.transactionSuccessPage, .pushNamed(Routes.transactionSuccessPage, arguments: alertContent);
arguments: alertContent
);
newContactAddress = newContactAddress ?? sendViewModel.newContactAddress(); newContactAddress = newContactAddress ?? sendViewModel.newContactAddress();
if (newContactAddress?.address != null && isRegularElectrumAddress(newContactAddress!.address)) { if (newContactAddress?.address != null && isRegularElectrumAddress(newContactAddress!.address)) {
@ -562,7 +582,7 @@ class SendPage extends BasePage {
leftButtonText: S.of(_dialogContext).ignor, leftButtonText: S.of(_dialogContext).ignor,
alertLeftActionButtonKey: ValueKey('send_page_sent_dialog_ignore_button_key'), alertLeftActionButtonKey: ValueKey('send_page_sent_dialog_ignore_button_key'),
alertRightActionButtonKey: alertRightActionButtonKey:
ValueKey('send_page_sent_dialog_add_contact_button_key'), ValueKey('send_page_sent_dialog_add_contact_button_key'),
actionRightButton: () { actionRightButton: () {
Navigator.of(_dialogContext).pop(); Navigator.of(_dialogContext).pop();
RequestReviewHandler.requestReview(); RequestReviewHandler.requestReview();

View file

@ -1,6 +1,7 @@
import 'package:cake_wallet/entities/priority_for_wallet_type.dart'; import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
import 'package:cake_wallet/src/screens/receive/widgets/currency_input_field.dart'; import 'package:cake_wallet/src/screens/receive/widgets/currency_input_field.dart';
import 'package:cake_wallet/src/widgets/picker.dart'; import 'package:cake_wallet/src/widgets/picker.dart';
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart'; import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart'; import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
@ -12,6 +13,7 @@ import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:cake_wallet/view_model/send/output.dart'; import 'package:cake_wallet/view_model/send/output.dart';
import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/unspent_coin_type.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart';
@ -24,40 +26,58 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart'; import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/themes/extensions/send_page_theme.dart'; import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
import '../../../../themes/extensions/cake_text_theme.dart';
import '../../../../themes/theme_base.dart';
class SendCard extends StatefulWidget { class SendCard extends StatefulWidget {
SendCard({ SendCard({
Key? key, Key? key,
required this.output, required this.output,
required this.sendViewModel, required this.sendViewModel,
required this.currentTheme,
this.initialPaymentRequest, this.initialPaymentRequest,
this.cryptoAmountFocus,
this.fiatAmountFocus,
}) : super(key: key); }) : super(key: key);
final Output output; final Output output;
final SendViewModel sendViewModel; final SendViewModel sendViewModel;
final PaymentRequest? initialPaymentRequest; final PaymentRequest? initialPaymentRequest;
final FocusNode? cryptoAmountFocus;
final FocusNode? fiatAmountFocus;
final ThemeBase currentTheme;
@override @override
SendCardState createState() => SendCardState( SendCardState createState() => SendCardState(
output: output, output: output,
sendViewModel: sendViewModel, sendViewModel: sendViewModel,
initialPaymentRequest: initialPaymentRequest, initialPaymentRequest: initialPaymentRequest,
currentTheme: currentTheme
// cryptoAmountFocus: cryptoAmountFocus ?? FocusNode(),
// fiatAmountFocus: fiatAmountFocus ?? FocusNode(),
// cryptoAmountFocus: FocusNode(),
// fiatAmountFocus: FocusNode(),
); );
} }
class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<SendCard> { class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<SendCard> {
SendCardState({required this.output, required this.sendViewModel, this.initialPaymentRequest}) SendCardState({
: addressController = TextEditingController(), required this.output,
required this.sendViewModel,
this.initialPaymentRequest,
required this.currentTheme,
}) : addressController = TextEditingController(),
cryptoAmountController = TextEditingController(), cryptoAmountController = TextEditingController(),
fiatAmountController = TextEditingController(), fiatAmountController = TextEditingController(),
noteController = TextEditingController(), noteController = TextEditingController(),
extractedAddressController = TextEditingController(), extractedAddressController = TextEditingController(),
cryptoAmountFocus = FocusNode(),
fiatAmountFocus = FocusNode(),
addressFocusNode = FocusNode(); addressFocusNode = FocusNode();
static const prefixIconWidth = 34.0; static const prefixIconWidth = 34.0;
static const prefixIconHeight = 34.0; static const prefixIconHeight = 34.0;
final ThemeBase currentTheme;
final Output output; final Output output;
final SendViewModel sendViewModel; final SendViewModel sendViewModel;
final PaymentRequest? initialPaymentRequest; final PaymentRequest? initialPaymentRequest;
@ -67,8 +87,6 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
final TextEditingController fiatAmountController; final TextEditingController fiatAmountController;
final TextEditingController noteController; final TextEditingController noteController;
final TextEditingController extractedAddressController; final TextEditingController extractedAddressController;
final FocusNode cryptoAmountFocus;
final FocusNode fiatAmountFocus;
final FocusNode addressFocusNode; final FocusNode addressFocusNode;
bool _effectsInstalled = false; bool _effectsInstalled = false;
@ -101,310 +119,336 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
super.build(context); super.build(context);
_setEffects(context); _setEffects(context);
return Stack( // return Stack(
children: [ // children: [
KeyboardActions( // return KeyboardActions(
config: KeyboardActionsConfig( // config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS, // keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
keyboardBarColor: Theme.of(context).extension<KeyboardTheme>()!.keyboardBarColor, // keyboardBarColor: Theme.of(context).extension<KeyboardTheme>()!.keyboardBarColor,
nextFocus: false, // nextFocus: false,
actions: [ // actions: [
KeyboardActionsItem( // KeyboardActionsItem(
focusNode: cryptoAmountFocus, // focusNode: cryptoAmountFocus,
toolbarButtons: [(_) => KeyboardDoneButton()], // toolbarButtons: [(_) => KeyboardDoneButton()],
// ),
// KeyboardActionsItem(
// focusNode: fiatAmountFocus,
// toolbarButtons: [(_) => KeyboardDoneButton()],
// )
// ],
// ),
// // child: Container(
// // height: 0,
// // color: Colors.transparent,
// // ), child:
// child: SizedBox(
// height: 100,
// width: 100,
// child: Text('Send Card'),
// ),
// );
return Container(
decoration: responsiveLayoutUtil.shouldRenderMobileUI
? BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)),
gradient: LinearGradient(
colors: [
Theme.of(context).extension<SendPageTheme>()!.firstGradientColor,
Theme.of(context).extension<SendPageTheme>()!.secondGradientColor,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
), ),
KeyboardActionsItem( )
focusNode: fiatAmountFocus, : null,
toolbarButtons: [(_) => KeyboardDoneButton()], child: Padding(
) padding: EdgeInsets.fromLTRB(
], 24,
), responsiveLayoutUtil.shouldRenderMobileUI ? 110 : 55,
child: Container( 24,
height: 0, responsiveLayoutUtil.shouldRenderMobileUI ? 32 : 0,
color: Colors.transparent,
),
), ),
Container( child: Observer(
decoration: responsiveLayoutUtil.shouldRenderMobileUI builder: (_) => Column(
? BoxDecoration( mainAxisSize: MainAxisSize.min,
borderRadius: BorderRadius.only( children: <Widget>[
bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)), Observer(builder: (_) {
gradient: LinearGradient( final validator = output.isParsedAddress
colors: [ ? sendViewModel.textValidator
Theme.of(context).extension<SendPageTheme>()!.firstGradientColor, : sendViewModel.addressValidator;
Theme.of(context).extension<SendPageTheme>()!.secondGradientColor,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
)
: null,
child: Padding(
padding: EdgeInsets.fromLTRB(
24,
responsiveLayoutUtil.shouldRenderMobileUI ? 110 : 55,
24,
responsiveLayoutUtil.shouldRenderMobileUI ? 32 : 0,
),
child: SingleChildScrollView(
child: Observer(
builder: (_) => Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Observer(builder: (_) {
final validator = output.isParsedAddress
? sendViewModel.textValidator
: sendViewModel.addressValidator;
return AddressTextField( return AddressTextField(
addressKey: ValueKey('send_page_address_textfield_key'), addressKey: ValueKey('send_page_address_textfield_key'),
focusNode: addressFocusNode, focusNode: addressFocusNode,
controller: addressController, controller: addressController,
onURIScanned: (uri) { onURIScanned: (uri) {
final paymentRequest = PaymentRequest.fromUri(uri); final paymentRequest = PaymentRequest.fromUri(uri);
addressController.text = paymentRequest.address; addressController.text = paymentRequest.address;
cryptoAmountController.text = paymentRequest.amount; cryptoAmountController.text = paymentRequest.amount;
noteController.text = paymentRequest.note; noteController.text = paymentRequest.note;
}, },
options: [ options: [
AddressTextFieldOption.paste, AddressTextFieldOption.paste,
AddressTextFieldOption.qrCode, AddressTextFieldOption.qrCode,
AddressTextFieldOption.addressBook AddressTextFieldOption.addressBook
], ],
buttonColor: buttonColor: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,
Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor, borderColor: Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
textStyle:
TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white),
hintStyle: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor),
onPushPasteButton: (context) async {
output.resetParsedAddress();
await output.fetchParsedAddress(context);
},
onPushAddressBookButton: (context) async {
output.resetParsedAddress();
},
onSelectedContact: (contact) {
output.loadContact(contact);
},
validator: validator,
selectedCurrency: sendViewModel.selectedCryptoCurrency,
);
}),
if (output.isParsedAddress)
Padding(
padding: const EdgeInsets.only(top: 20),
child: BaseTextFormField(
controller: extractedAddressController,
readOnly: true,
borderColor: borderColor:
Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor, Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
textStyle: TextStyle( textStyle: TextStyle(
fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white), fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white),
hintStyle: TextStyle( validator: sendViewModel.addressValidator)),
fontSize: 14, CurrencyAmountTextField(
fontWeight: FontWeight.w500, currencyPickerButtonKey: ValueKey('send_page_currency_picker_button_key'),
color: amountTextfieldKey: ValueKey('send_page_amount_textfield_key'),
Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor), sendAllButtonKey: ValueKey('send_page_send_all_button_key'),
onPushPasteButton: (context) async { currencyAmountTextFieldWidgetKey:
output.resetParsedAddress(); ValueKey('send_page_crypto_currency_amount_textfield_widget_key'),
await output.fetchParsedAddress(context); selectedCurrency: sendViewModel.selectedCryptoCurrency.title,
}, amountFocusNode: widget.cryptoAmountFocus,
onPushAddressBookButton: (context) async { amountController: cryptoAmountController,
output.resetParsedAddress(); isAmountEditable: true,
}, onTapPicker: () => _presentPicker(context),
onSelectedContact: (contact) { isPickerEnable: sendViewModel.hasMultipleTokens,
output.loadContact(contact); tag: sendViewModel.selectedCryptoCurrency.tag,
}, allAmountButton:
validator: validator, !sendViewModel.isBatchSending && sendViewModel.shouldDisplaySendALL,
selectedCurrency: sendViewModel.selectedCryptoCurrency, currencyValueValidator: output.sendAll
); ? sendViewModel.allAmountValidator
}), : sendViewModel.amountValidator,
if (output.isParsedAddress) allAmountCallback: () async => output.setSendAll(sendViewModel.balance)),
Padding( Divider(
padding: const EdgeInsets.only(top: 20), height: 1,
child: BaseTextFormField( color: Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor),
controller: extractedAddressController, Observer(
readOnly: true, builder: (_) => Padding(
borderColor: Theme.of(context) padding: EdgeInsets.only(top: 10),
.extension<SendPageTheme>()! child: Row(
.textFieldBorderColor, mainAxisSize: MainAxisSize.max,
textStyle: TextStyle( mainAxisAlignment: MainAxisAlignment.spaceBetween,
fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white), children: <Widget>[
validator: sendViewModel.addressValidator)), Expanded(
CurrencyAmountTextField( child: Text(
currencyPickerButtonKey: ValueKey('send_page_currency_picker_button_key'), S.of(context).available_balance + ':',
amountTextfieldKey: ValueKey('send_page_amount_textfield_key'), style: TextStyle(
sendAllButtonKey: ValueKey('send_page_send_all_button_key'), fontSize: 12,
currencyAmountTextFieldWidgetKey: fontWeight: FontWeight.w600,
ValueKey('send_page_crypto_currency_amount_textfield_widget_key'), color:
selectedCurrency: sendViewModel.selectedCryptoCurrency.title, Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor),
amountFocusNode: cryptoAmountFocus,
amountController: cryptoAmountController,
isAmountEditable: true,
onTapPicker: () => _presentPicker(context),
isPickerEnable: sendViewModel.hasMultipleTokens,
tag: sendViewModel.selectedCryptoCurrency.tag,
allAmountButton:
!sendViewModel.isBatchSending && sendViewModel.shouldDisplaySendALL,
currencyValueValidator: output.sendAll
? sendViewModel.allAmountValidator
: sendViewModel.amountValidator,
allAmountCallback: () async => output.setSendAll(sendViewModel.balance)),
Divider(
height: 1,
color: Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor),
Observer(
builder: (_) => Padding(
padding: EdgeInsets.only(top: 10),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: Text(
S.of(context).available_balance + ':',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.extension<SendPageTheme>()!
.textFieldHintColor),
),
),
Text(
sendViewModel.balance,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.extension<SendPageTheme>()!
.textFieldHintColor),
)
],
), ),
), ),
), Text(
if (!sendViewModel.isFiatDisabled) sendViewModel.balance,
CurrencyAmountTextField( style: TextStyle(
amountTextfieldKey: ValueKey('send_page_fiat_amount_textfield_key'), fontSize: 12,
currencyAmountTextFieldWidgetKey: fontWeight: FontWeight.w600,
ValueKey('send_page_fiat_currency_amount_textfield_widget_key'),
selectedCurrency: sendViewModel.fiat.title,
amountFocusNode: fiatAmountFocus,
amountController: fiatAmountController,
hintText: '0.00',
isAmountEditable: true,
allAmountButton: false),
Divider(
height: 1,
color: Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor),
Padding(
padding: EdgeInsets.only(top: 20),
child: BaseTextFormField(
key: ValueKey('send_page_note_textfield_key'),
controller: noteController,
keyboardType: TextInputType.multiline,
maxLines: null,
borderColor:
Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
textStyle: TextStyle(
fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white),
hintText: S.of(context).note_optional,
placeholderTextStyle: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: color:
Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor), Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor),
), )
), ],
if (sendViewModel.hasFees) ),
Observer( ),
builder: (_) => GestureDetector( ),
key: ValueKey('send_page_select_fee_priority_button_key'), if (!sendViewModel.isFiatDisabled)
onTap: sendViewModel.hasFeesPriority CurrencyAmountTextField(
? () => pickTransactionPriority(context) amountTextfieldKey: ValueKey('send_page_fiat_amount_textfield_key'),
: () {}, currencyAmountTextFieldWidgetKey:
child: Container( ValueKey('send_page_fiat_currency_amount_textfield_widget_key'),
padding: EdgeInsets.only(top: 24), selectedCurrency: sendViewModel.fiat.title,
amountFocusNode: widget.fiatAmountFocus,
amountController: fiatAmountController,
hintText: '0.00',
isAmountEditable: true,
allAmountButton: false),
Divider(
height: 1,
color: Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor),
Padding(
padding: EdgeInsets.only(top: 20),
child: BaseTextFormField(
key: ValueKey('send_page_note_textfield_key'),
controller: noteController,
keyboardType: TextInputType.multiline,
maxLines: null,
borderColor: Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
textStyle:
TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white),
hintText: S.of(context).note_optional,
placeholderTextStyle: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor),
),
),
if (sendViewModel.hasFees)
Observer(
builder: (_) => GestureDetector(
key: ValueKey('send_page_select_fee_priority_button_key'),
onTap: sendViewModel.hasFeesPriority
? () => pickTransactionPriority(context)
: () {},
child: Container(
padding: EdgeInsets.only(top: 24),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).send_estimated_fee,
style: TextStyle(
fontSize: 12, fontWeight: FontWeight.w500, color: Colors.white),
),
Container(
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Text( Column(
S.of(context).send_estimated_fee, mainAxisAlignment: MainAxisAlignment.start,
style: TextStyle( crossAxisAlignment: CrossAxisAlignment.end,
fontSize: 12, children: [
fontWeight: FontWeight.w500, Text(
color: Colors.white), output.estimatedFee.toString() +
), ' ' +
Container( sendViewModel.currency.toString(),
child: Row( style: TextStyle(
crossAxisAlignment: CrossAxisAlignment.start, fontSize: 12,
children: <Widget>[ fontWeight: FontWeight.w600,
Column( color: Colors.white,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
output.estimatedFee.toString() +
' ' +
sendViewModel.currency.toString(),
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
Padding(
padding: EdgeInsets.only(top: 5),
child: sendViewModel.isFiatDisabled
? const SizedBox(height: 14)
: Text(
output.estimatedFeeFiatAmount +
' ' +
sendViewModel.fiat.title,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.extension<SendPageTheme>()!
.textFieldHintColor,
),
),
),
],
), ),
Padding( ),
padding: EdgeInsets.only(top: 2, left: 5), Padding(
child: Icon( padding: EdgeInsets.only(top: 5),
Icons.arrow_forward_ios, child: sendViewModel.isFiatDisabled
size: 12, ? const SizedBox(height: 14)
color: Colors.white, : Text(
), output.estimatedFeeFiatAmount +
) ' ' +
], sendViewModel.fiat.title,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.extension<SendPageTheme>()!
.textFieldHintColor,
),
),
),
],
),
Padding(
padding: EdgeInsets.only(top: 2, left: 5),
child: Icon(
Icons.arrow_forward_ios,
size: 12,
color: Colors.white,
), ),
) )
], ],
), ),
), )
), ],
), ),
if (sendViewModel.hasCoinControl) ),
Padding( ),
padding: EdgeInsets.only(top: 6),
child: GestureDetector(
key: ValueKey('send_page_unspent_coin_button_key'),
onTap: () => Navigator.of(context).pushNamed(
Routes.unspentCoinsList,
arguments: widget.sendViewModel.coinTypeToSpendFrom,
),
child: Container(
color: Colors.transparent,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
S.of(context).coin_control,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Colors.white),
),
Icon(
Icons.arrow_forward_ios,
size: 12,
color: Colors.white,
),
],
),
),
),
),
],
), ),
), if (sendViewModel.hasCoinControl)
), Padding(
padding: EdgeInsets.only(top: 6),
child: GestureDetector(
key: ValueKey('send_page_unspent_coin_button_key'),
onTap: () => Navigator.of(context).pushNamed(
Routes.unspentCoinsList,
arguments: widget.sendViewModel.coinTypeToSpendFrom,
),
child: Container(
color: Colors.transparent,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
S.of(context).coin_control,
style: TextStyle(
fontSize: 12, fontWeight: FontWeight.w600, color: Colors.white),
),
Icon(
Icons.arrow_forward_ios,
size: 12,
color: Colors.white,
),
],
),
),
),
),
if (sendViewModel.currency == CryptoCurrency.ltc)
Observer(
builder: (_) => Padding(
padding: EdgeInsets.only(top: 14),
child: GestureDetector(
key: ValueKey('send_page_unspent_coin_button_key'),
onTap: () {
bool value =
widget.sendViewModel.coinTypeToSpendFrom == UnspentCoinType.any;
sendViewModel.setAllowMwebCoins(!value);
},
child: Container(
color: Colors.transparent,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
StandardCheckbox(
caption: S.of(context).litecoin_mweb_allow_coins,
captionColor: Colors.white,
borderColor: currentTheme.type == ThemeType.bright
? Colors.white
: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor,
iconColor: currentTheme.type == ThemeType.bright
? Colors.white
: Theme.of(context).primaryColor,
value:
widget.sendViewModel.coinTypeToSpendFrom == UnspentCoinType.any,
onChanged: (bool? value) {
sendViewModel.setAllowMwebCoins(value ?? false);
},
),
],
),
),
),
),
),
],
), ),
) ),
], ),
); );
} }

View file

@ -0,0 +1,202 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
const _firstLayoutMaxHeight = 10000.0;
class PageViewHeightAdaptable extends StatefulWidget {
const PageViewHeightAdaptable({
super.key,
required this.controller,
required this.children,
}) : assert(children.length > 0, 'children must not be empty');
final PageController controller;
final List<Widget> children;
@override
State<PageViewHeightAdaptable> createState() => _PageViewHeightAdaptableState();
}
class _PageViewHeightAdaptableState extends State<PageViewHeightAdaptable> {
final _sizes = <int, Size>{};
@override
void didUpdateWidget(PageViewHeightAdaptable oldWidget) {
super.didUpdateWidget(oldWidget);
_sizes.clear();
}
@override
Widget build(BuildContext context) {
return ListenableBuilder(
listenable: widget.controller,
builder: (context, child) => _SizingContainer(
sizes: _sizes,
page: widget.controller.hasClients ? widget.controller.page ?? 0 : 0,
child: child!,
),
child: LayoutBuilder(
builder: (context, constraints) => PageView(
controller: widget.controller,
children: [
for (final (i, child) in widget.children.indexed)
Stack(
alignment: Alignment.topCenter,
clipBehavior: Clip.hardEdge,
children: [
SizedBox.fromSize(size: _sizes[i]),
Positioned(
left: 0,
top: 0,
right: 0,
child: _SizeAware(
child: child,
// don't setState, we'll use it in the layout phase
onSizeLaidOut: (size) {
_sizes[i] = size;
},
),
),
],
),
],
),
),
);
}
}
typedef _OnSizeLaidOutCallback = void Function(Size);
class _SizingContainer extends SingleChildRenderObjectWidget {
const _SizingContainer({
super.child,
required this.sizes,
required this.page,
});
final Map<int, Size> sizes;
final double page;
@override
_RenderSizingContainer createRenderObject(BuildContext context) {
return _RenderSizingContainer(
sizes: sizes,
page: page,
);
}
@override
void updateRenderObject(
BuildContext context,
_RenderSizingContainer renderObject,
) {
renderObject
..sizes = sizes
..page = page;
}
}
class _RenderSizingContainer extends RenderProxyBox {
_RenderSizingContainer({
RenderBox? child,
required Map<int, Size> sizes,
required double page,
}) : _sizes = sizes,
_page = page,
super(child);
Map<int, Size> _sizes;
Map<int, Size> get sizes => _sizes;
set sizes(Map<int, Size> value) {
if (_sizes == value) return;
_sizes = value;
markNeedsLayout();
}
double _page;
double get page => _page;
set page(double value) {
if (_page == value) return;
_page = value;
markNeedsLayout();
}
@override
void performLayout() {
if (child case final child?) {
child.layout(
constraints.copyWith(
minWidth: constraints.maxWidth,
minHeight: 0,
maxHeight: constraints.hasBoundedHeight ? null : _firstLayoutMaxHeight,
),
parentUsesSize: true,
);
final a = sizes[page.floor()]!;
final b = sizes[page.ceil()]!;
final height = lerpDouble(a.height, b.height, page - page.floor());
child.layout(
constraints.copyWith(minHeight: height, maxHeight: height),
parentUsesSize: true,
);
size = child.size;
} else {
size = computeSizeForNoChild(constraints);
}
}
}
class _SizeAware extends SingleChildRenderObjectWidget {
const _SizeAware({
required Widget child,
required this.onSizeLaidOut,
}) : super(child: child);
final _OnSizeLaidOutCallback onSizeLaidOut;
@override
_RenderSizeAware createRenderObject(BuildContext context) {
return _RenderSizeAware(
onSizeLaidOut: onSizeLaidOut,
);
}
@override
void updateRenderObject(BuildContext context, _RenderSizeAware renderObject) {
renderObject.onSizeLaidOut = onSizeLaidOut;
}
}
class _RenderSizeAware extends RenderProxyBox {
_RenderSizeAware({
RenderBox? child,
required _OnSizeLaidOutCallback onSizeLaidOut,
}) : _onSizeLaidOut = onSizeLaidOut,
super(child);
_OnSizeLaidOutCallback? _onSizeLaidOut;
_OnSizeLaidOutCallback get onSizeLaidOut => _onSizeLaidOut!;
set onSizeLaidOut(_OnSizeLaidOutCallback value) {
if (_onSizeLaidOut == value) return;
_onSizeLaidOut = value;
markNeedsLayout();
}
@override
void performLayout() {
super.performLayout();
onSizeLaidOut(
getDryLayout(
constraints.copyWith(maxHeight: double.infinity),
),
);
}
}

View file

@ -9,6 +9,7 @@ class StandardCheckbox extends StatelessWidget {
this.gradientBackground = false, this.gradientBackground = false,
this.borderColor, this.borderColor,
this.iconColor, this.iconColor,
this.captionColor,
required this.onChanged}); required this.onChanged});
final bool value; final bool value;
@ -16,6 +17,7 @@ class StandardCheckbox extends StatelessWidget {
final bool gradientBackground; final bool gradientBackground;
final Color? borderColor; final Color? borderColor;
final Color? iconColor; final Color? iconColor;
final Color? captionColor;
final Function(bool) onChanged; final Function(bool) onChanged;
@override @override
@ -68,7 +70,7 @@ class StandardCheckbox extends StatelessWidget {
fontSize: 16.0, fontSize: 16.0,
fontFamily: 'Lato', fontFamily: 'Lato',
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor, color: captionColor ?? Theme.of(context).extension<CakeTextTheme>()!.titleColor,
decoration: TextDecoration.none, decoration: TextDecoration.none,
), ),
), ),

View file

@ -77,7 +77,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
this.transactionDescriptionBox, this.transactionDescriptionBox,
this.ledgerViewModel, this.ledgerViewModel,
this.unspentCoinsListViewModel, { this.unspentCoinsListViewModel, {
this.coinTypeToSpendFrom = UnspentCoinType.any, this.coinTypeToSpendFrom = UnspentCoinType.nonMweb,
}) : state = InitialExecutionState(), }) : state = InitialExecutionState(),
currencies = appStore.wallet!.balance.keys.toList(), currencies = appStore.wallet!.balance.keys.toList(),
selectedCryptoCurrency = appStore.wallet!.currency, selectedCryptoCurrency = appStore.wallet!.currency,
@ -112,7 +112,8 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
ObservableList<Output> outputs; ObservableList<Output> outputs;
final UnspentCoinType coinTypeToSpendFrom; @observable
UnspentCoinType coinTypeToSpendFrom;
bool get showAddressBookPopup => _settingsStore.showAddressBookPopupEnabled; bool get showAddressBookPopup => _settingsStore.showAddressBookPopupEnabled;
@ -135,6 +136,13 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
addOutput(); addOutput();
} }
@action
void setAllowMwebCoins(bool allow) {
if (wallet.type == WalletType.litecoin) {
coinTypeToSpendFrom = allow ? UnspentCoinType.any : UnspentCoinType.nonMweb;
}
}
@computed @computed
bool get isBatchSending => outputs.length > 1; bool get isBatchSending => outputs.length > 1;

View file

@ -394,6 +394,7 @@
"light_theme": "فاتح", "light_theme": "فاتح",
"litecoin_enable_mweb_sync": "تمكين MWEB المسح الضوئي", "litecoin_enable_mweb_sync": "تمكين MWEB المسح الضوئي",
"litecoin_mweb": "mweb", "litecoin_mweb": "mweb",
"litecoin_mweb_allow_coins": "السماح للعملات المعدنية MWEB",
"litecoin_mweb_always_scan": "اضبط MWEB دائمًا على المسح الضوئي", "litecoin_mweb_always_scan": "اضبط MWEB دائمًا على المسح الضوئي",
"litecoin_mweb_description": "MWEB هو بروتوكول جديد يجلب معاملات أسرع وأرخص وأكثر خصوصية إلى Litecoin", "litecoin_mweb_description": "MWEB هو بروتوكول جديد يجلب معاملات أسرع وأرخص وأكثر خصوصية إلى Litecoin",
"litecoin_mweb_dismiss": "رفض", "litecoin_mweb_dismiss": "رفض",

View file

@ -394,6 +394,7 @@
"light_theme": "Светло", "light_theme": "Светло",
"litecoin_enable_mweb_sync": "Активирайте сканирането на MWeb", "litecoin_enable_mweb_sync": "Активирайте сканирането на MWeb",
"litecoin_mweb": "Mweb", "litecoin_mweb": "Mweb",
"litecoin_mweb_allow_coins": "Позволете на MWeb монети",
"litecoin_mweb_always_scan": "Задайте MWeb винаги сканиране", "litecoin_mweb_always_scan": "Задайте MWeb винаги сканиране",
"litecoin_mweb_description": "MWeb е нов протокол, който носи по -бърз, по -евтин и повече частни транзакции на Litecoin", "litecoin_mweb_description": "MWeb е нов протокол, който носи по -бърз, по -евтин и повече частни транзакции на Litecoin",
"litecoin_mweb_dismiss": "Уволнение", "litecoin_mweb_dismiss": "Уволнение",

View file

@ -394,6 +394,7 @@
"light_theme": "Světlý", "light_theme": "Světlý",
"litecoin_enable_mweb_sync": "Povolit skenování MWeb", "litecoin_enable_mweb_sync": "Povolit skenování MWeb",
"litecoin_mweb": "MWeb", "litecoin_mweb": "MWeb",
"litecoin_mweb_allow_coins": "Povolte mweb mince",
"litecoin_mweb_always_scan": "Nastavit MWeb vždy skenování", "litecoin_mweb_always_scan": "Nastavit MWeb vždy skenování",
"litecoin_mweb_description": "MWEB je nový protokol, který do Litecoin přináší rychlejší, levnější a více soukromých transakcí", "litecoin_mweb_description": "MWEB je nový protokol, který do Litecoin přináší rychlejší, levnější a více soukromých transakcí",
"litecoin_mweb_dismiss": "Propustit", "litecoin_mweb_dismiss": "Propustit",

View file

@ -394,6 +394,7 @@
"light_theme": "Hell", "light_theme": "Hell",
"litecoin_enable_mweb_sync": "Aktivieren Sie das MWEB-Scannen", "litecoin_enable_mweb_sync": "Aktivieren Sie das MWEB-Scannen",
"litecoin_mweb": "MWeb", "litecoin_mweb": "MWeb",
"litecoin_mweb_allow_coins": "MWEB -Münzen zulassen",
"litecoin_mweb_always_scan": "Setzen Sie MWeb immer scannen", "litecoin_mweb_always_scan": "Setzen Sie MWeb immer scannen",
"litecoin_mweb_description": "MWWB ist ein neues Protokoll, das schnellere, billigere und privatere Transaktionen zu Litecoin bringt", "litecoin_mweb_description": "MWWB ist ein neues Protokoll, das schnellere, billigere und privatere Transaktionen zu Litecoin bringt",
"litecoin_mweb_dismiss": "Zurückweisen", "litecoin_mweb_dismiss": "Zurückweisen",

View file

@ -394,6 +394,7 @@
"light_theme": "Light", "light_theme": "Light",
"litecoin_enable_mweb_sync": "Enable MWEB scanning", "litecoin_enable_mweb_sync": "Enable MWEB scanning",
"litecoin_mweb": "MWEB", "litecoin_mweb": "MWEB",
"litecoin_mweb_allow_coins": "Allow MWEB coins",
"litecoin_mweb_always_scan": "Set MWEB always scanning", "litecoin_mweb_always_scan": "Set MWEB always scanning",
"litecoin_mweb_description": "MWEB is a new protocol that brings faster, cheaper, and more private transactions to Litecoin", "litecoin_mweb_description": "MWEB is a new protocol that brings faster, cheaper, and more private transactions to Litecoin",
"litecoin_mweb_dismiss": "Dismiss", "litecoin_mweb_dismiss": "Dismiss",

View file

@ -394,6 +394,7 @@
"light_theme": "Ligero", "light_theme": "Ligero",
"litecoin_enable_mweb_sync": "Habilitar el escaneo mweb", "litecoin_enable_mweb_sync": "Habilitar el escaneo mweb",
"litecoin_mweb": "Mweb", "litecoin_mweb": "Mweb",
"litecoin_mweb_allow_coins": "Permitir monedas mweb",
"litecoin_mweb_always_scan": "Establecer mweb siempre escaneo", "litecoin_mweb_always_scan": "Establecer mweb siempre escaneo",
"litecoin_mweb_description": "Mweb es un nuevo protocolo que trae transacciones más rápidas, más baratas y más privadas a Litecoin", "litecoin_mweb_description": "Mweb es un nuevo protocolo que trae transacciones más rápidas, más baratas y más privadas a Litecoin",
"litecoin_mweb_dismiss": "Despedir", "litecoin_mweb_dismiss": "Despedir",

View file

@ -394,6 +394,7 @@
"light_theme": "Clair", "light_theme": "Clair",
"litecoin_enable_mweb_sync": "Activer la numérisation MWEB", "litecoin_enable_mweb_sync": "Activer la numérisation MWEB",
"litecoin_mweb": "Mweb", "litecoin_mweb": "Mweb",
"litecoin_mweb_allow_coins": "Autoriser les pièces MWeb",
"litecoin_mweb_always_scan": "Définir MWEB Score Scanning", "litecoin_mweb_always_scan": "Définir MWEB Score Scanning",
"litecoin_mweb_description": "MWEB est un nouveau protocole qui apporte des transactions plus rapides, moins chères et plus privées à Litecoin", "litecoin_mweb_description": "MWEB est un nouveau protocole qui apporte des transactions plus rapides, moins chères et plus privées à Litecoin",
"litecoin_mweb_dismiss": "Rejeter", "litecoin_mweb_dismiss": "Rejeter",

View file

@ -394,6 +394,7 @@
"light_theme": "Haske", "light_theme": "Haske",
"litecoin_enable_mweb_sync": "Kunna binciken Mweb", "litecoin_enable_mweb_sync": "Kunna binciken Mweb",
"litecoin_mweb": "Mweb", "litecoin_mweb": "Mweb",
"litecoin_mweb_allow_coins": "Bada izinin Coins na Mweb",
"litecoin_mweb_always_scan": "Saita Mweb koyaushe", "litecoin_mweb_always_scan": "Saita Mweb koyaushe",
"litecoin_mweb_description": "Mweb shine sabon tsarin yarjejeniya da ya kawo da sauri, mai rahusa, da kuma ma'amaloli masu zaman kansu zuwa Litecoin", "litecoin_mweb_description": "Mweb shine sabon tsarin yarjejeniya da ya kawo da sauri, mai rahusa, da kuma ma'amaloli masu zaman kansu zuwa Litecoin",
"litecoin_mweb_dismiss": "Tuɓe \\ sallama", "litecoin_mweb_dismiss": "Tuɓe \\ sallama",

View file

@ -394,6 +394,7 @@
"light_theme": "रोशनी", "light_theme": "रोशनी",
"litecoin_enable_mweb_sync": "MWEB स्कैनिंग सक्षम करें", "litecoin_enable_mweb_sync": "MWEB स्कैनिंग सक्षम करें",
"litecoin_mweb": "मावली", "litecoin_mweb": "मावली",
"litecoin_mweb_allow_coins": "MWEB सिक्कों की अनुमति दें",
"litecoin_mweb_always_scan": "MWEB हमेशा स्कैनिंग सेट करें", "litecoin_mweb_always_scan": "MWEB हमेशा स्कैनिंग सेट करें",
"litecoin_mweb_description": "MWEB एक नया प्रोटोकॉल है जो लिटकोइन के लिए तेजी से, सस्ता और अधिक निजी लेनदेन लाता है", "litecoin_mweb_description": "MWEB एक नया प्रोटोकॉल है जो लिटकोइन के लिए तेजी से, सस्ता और अधिक निजी लेनदेन लाता है",
"litecoin_mweb_dismiss": "नकार देना", "litecoin_mweb_dismiss": "नकार देना",

View file

@ -394,6 +394,7 @@
"light_theme": "Svijetla", "light_theme": "Svijetla",
"litecoin_enable_mweb_sync": "Omogućite MWEB skeniranje", "litecoin_enable_mweb_sync": "Omogućite MWEB skeniranje",
"litecoin_mweb": "MWeb", "litecoin_mweb": "MWeb",
"litecoin_mweb_allow_coins": "Dopustite MWeb kovanice",
"litecoin_mweb_always_scan": "Postavite MWeb uvijek skeniranje", "litecoin_mweb_always_scan": "Postavite MWeb uvijek skeniranje",
"litecoin_mweb_description": "MWEB je novi protokol koji u Litecoin donosi brže, jeftinije i privatnije transakcije", "litecoin_mweb_description": "MWEB je novi protokol koji u Litecoin donosi brže, jeftinije i privatnije transakcije",
"litecoin_mweb_dismiss": "Odbaciti", "litecoin_mweb_dismiss": "Odbaciti",

View file

@ -394,6 +394,7 @@
"light_theme": "Լուսավոր", "light_theme": "Լուսավոր",
"litecoin_enable_mweb_sync": "Միացնել MWEB սկան", "litecoin_enable_mweb_sync": "Միացնել MWEB սկան",
"litecoin_mweb": "Մուեբ", "litecoin_mweb": "Մուեբ",
"litecoin_mweb_allow_coins": "Թույլ տվեք MWeb մետաղադրամներ",
"litecoin_mweb_always_scan": "Սահմանեք Mweb Միշտ սկանավորում", "litecoin_mweb_always_scan": "Սահմանեք Mweb Միշտ սկանավորում",
"litecoin_mweb_description": "Mweb- ը նոր արձանագրություն է, որը բերում է ավելի արագ, ավելի էժան եւ ավելի մասնավոր գործարքներ դեպի LITECOIN", "litecoin_mweb_description": "Mweb- ը նոր արձանագրություն է, որը բերում է ավելի արագ, ավելի էժան եւ ավելի մասնավոր գործարքներ դեպի LITECOIN",
"litecoin_mweb_dismiss": "Հեռացնել", "litecoin_mweb_dismiss": "Հեռացնել",

View file

@ -394,6 +394,7 @@
"light_theme": "Terang", "light_theme": "Terang",
"litecoin_enable_mweb_sync": "Aktifkan pemindaian MWEB", "litecoin_enable_mweb_sync": "Aktifkan pemindaian MWEB",
"litecoin_mweb": "Mweb", "litecoin_mweb": "Mweb",
"litecoin_mweb_allow_coins": "Izinkan koin mWeb",
"litecoin_mweb_always_scan": "Atur mWeb selalu memindai", "litecoin_mweb_always_scan": "Atur mWeb selalu memindai",
"litecoin_mweb_description": "MWEB adalah protokol baru yang membawa transaksi yang lebih cepat, lebih murah, dan lebih pribadi ke Litecoin", "litecoin_mweb_description": "MWEB adalah protokol baru yang membawa transaksi yang lebih cepat, lebih murah, dan lebih pribadi ke Litecoin",
"litecoin_mweb_dismiss": "Membubarkan", "litecoin_mweb_dismiss": "Membubarkan",

View file

@ -394,6 +394,7 @@
"light_theme": "Chiaro", "light_theme": "Chiaro",
"litecoin_enable_mweb_sync": "Abilita la scansione MWeb", "litecoin_enable_mweb_sync": "Abilita la scansione MWeb",
"litecoin_mweb": "MWeb", "litecoin_mweb": "MWeb",
"litecoin_mweb_allow_coins": "Consenti monete mWeb",
"litecoin_mweb_always_scan": "Imposta MWeb per scansionare sempre", "litecoin_mweb_always_scan": "Imposta MWeb per scansionare sempre",
"litecoin_mweb_description": "MWeb è un nuovo protocollo che porta transazioni più veloci, più economiche e più private a Litecoin", "litecoin_mweb_description": "MWeb è un nuovo protocollo che porta transazioni più veloci, più economiche e più private a Litecoin",
"litecoin_mweb_dismiss": "Chiudi", "litecoin_mweb_dismiss": "Chiudi",

View file

@ -395,6 +395,7 @@
"light_theme": "光", "light_theme": "光",
"litecoin_enable_mweb_sync": "MWEBスキャンを有効にします", "litecoin_enable_mweb_sync": "MWEBスキャンを有効にします",
"litecoin_mweb": "mweb", "litecoin_mweb": "mweb",
"litecoin_mweb_allow_coins": "MWEBコインを許可します",
"litecoin_mweb_always_scan": "MWEBを常にスキャンします", "litecoin_mweb_always_scan": "MWEBを常にスキャンします",
"litecoin_mweb_description": "MWEBは、Litecoinにより速く、より安価で、よりプライベートなトランザクションをもたらす新しいプロトコルです", "litecoin_mweb_description": "MWEBは、Litecoinにより速く、より安価で、よりプライベートなトランザクションをもたらす新しいプロトコルです",
"litecoin_mweb_dismiss": "却下する", "litecoin_mweb_dismiss": "却下する",

View file

@ -394,6 +394,7 @@
"light_theme": "빛", "light_theme": "빛",
"litecoin_enable_mweb_sync": "mweb 스캔을 활성화합니다", "litecoin_enable_mweb_sync": "mweb 스캔을 활성화합니다",
"litecoin_mweb": "mweb", "litecoin_mweb": "mweb",
"litecoin_mweb_allow_coins": "mweb 코인을 허용하십시오",
"litecoin_mweb_always_scan": "mweb는 항상 스캔을 설정합니다", "litecoin_mweb_always_scan": "mweb는 항상 스캔을 설정합니다",
"litecoin_mweb_description": "MWEB는 Litecoin에 더 빠르고 저렴하며 개인 거래를 제공하는 새로운 프로토콜입니다.", "litecoin_mweb_description": "MWEB는 Litecoin에 더 빠르고 저렴하며 개인 거래를 제공하는 새로운 프로토콜입니다.",
"litecoin_mweb_dismiss": "해고하다", "litecoin_mweb_dismiss": "해고하다",

View file

@ -394,6 +394,7 @@
"light_theme": "အလင်း", "light_theme": "အလင်း",
"litecoin_enable_mweb_sync": "mweb scanning ဖွင့်ပါ", "litecoin_enable_mweb_sync": "mweb scanning ဖွင့်ပါ",
"litecoin_mweb": "မင်္ဂလာပါ", "litecoin_mweb": "မင်္ဂလာပါ",
"litecoin_mweb_allow_coins": "mweb ဒင်္ဂါးများကိုခွင့်ပြုပါ",
"litecoin_mweb_always_scan": "Mweb အမြဲစကင်ဖတ်စစ်ဆေးပါ", "litecoin_mweb_always_scan": "Mweb အမြဲစကင်ဖတ်စစ်ဆေးပါ",
"litecoin_mweb_description": "Mweb သည် Protocol အသစ်ဖြစ်ပြီး LitCoin သို့ပိုမိုဈေးချိုသာသော, စျေးသက်သက်သာသာသုံးခြင်းနှင့်ပိုမိုများပြားသောပုဂ္ဂလိကငွေပို့ဆောင်မှုများကိုဖြစ်ပေါ်စေသည်", "litecoin_mweb_description": "Mweb သည် Protocol အသစ်ဖြစ်ပြီး LitCoin သို့ပိုမိုဈေးချိုသာသော, စျေးသက်သက်သာသာသုံးခြင်းနှင့်ပိုမိုများပြားသောပုဂ္ဂလိကငွေပို့ဆောင်မှုများကိုဖြစ်ပေါ်စေသည်",
"litecoin_mweb_dismiss": "ထုတ်ပစ်", "litecoin_mweb_dismiss": "ထုတ်ပစ်",

View file

@ -394,6 +394,7 @@
"light_theme": "Licht", "light_theme": "Licht",
"litecoin_enable_mweb_sync": "MWEB -scanning inschakelen", "litecoin_enable_mweb_sync": "MWEB -scanning inschakelen",
"litecoin_mweb": "Mweb", "litecoin_mweb": "Mweb",
"litecoin_mweb_allow_coins": "Sta mweb munten toe",
"litecoin_mweb_always_scan": "Stel mweb altijd op scannen", "litecoin_mweb_always_scan": "Stel mweb altijd op scannen",
"litecoin_mweb_description": "MWEB is een nieuw protocol dat snellere, goedkopere en meer privé -transacties naar Litecoin brengt", "litecoin_mweb_description": "MWEB is een nieuw protocol dat snellere, goedkopere en meer privé -transacties naar Litecoin brengt",
"litecoin_mweb_dismiss": "Afwijzen", "litecoin_mweb_dismiss": "Afwijzen",

View file

@ -394,6 +394,7 @@
"light_theme": "Jasny", "light_theme": "Jasny",
"litecoin_enable_mweb_sync": "Włącz skanowanie MWEB", "litecoin_enable_mweb_sync": "Włącz skanowanie MWEB",
"litecoin_mweb": "MWEB", "litecoin_mweb": "MWEB",
"litecoin_mweb_allow_coins": "Zezwalaj na monety MWEB",
"litecoin_mweb_always_scan": "Ustaw MWEB zawsze skanowanie", "litecoin_mweb_always_scan": "Ustaw MWEB zawsze skanowanie",
"litecoin_mweb_description": "MWEB to nowy protokół, który przynosi szybciej, tańsze i bardziej prywatne transakcje do Litecoin", "litecoin_mweb_description": "MWEB to nowy protokół, który przynosi szybciej, tańsze i bardziej prywatne transakcje do Litecoin",
"litecoin_mweb_dismiss": "Odrzucać", "litecoin_mweb_dismiss": "Odrzucać",

View file

@ -394,6 +394,7 @@
"light_theme": "Luz", "light_theme": "Luz",
"litecoin_enable_mweb_sync": "Ativar digitalização do MWEB", "litecoin_enable_mweb_sync": "Ativar digitalização do MWEB",
"litecoin_mweb": "Mweb", "litecoin_mweb": "Mweb",
"litecoin_mweb_allow_coins": "Permitir moedas MWEB",
"litecoin_mweb_always_scan": "Definir mweb sempre digitalizando", "litecoin_mweb_always_scan": "Definir mweb sempre digitalizando",
"litecoin_mweb_description": "MWEB é um novo protocolo que traz transações mais rápidas, baratas e mais privadas para o Litecoin", "litecoin_mweb_description": "MWEB é um novo protocolo que traz transações mais rápidas, baratas e mais privadas para o Litecoin",
"litecoin_mweb_dismiss": "Liberar", "litecoin_mweb_dismiss": "Liberar",

View file

@ -394,6 +394,7 @@
"light_theme": "Светлая", "light_theme": "Светлая",
"litecoin_enable_mweb_sync": "Включить MWEB сканирование", "litecoin_enable_mweb_sync": "Включить MWEB сканирование",
"litecoin_mweb": "Мвеб", "litecoin_mweb": "Мвеб",
"litecoin_mweb_allow_coins": "Разрешить монеты MWEB",
"litecoin_mweb_always_scan": "Установить MWEB всегда сканирование", "litecoin_mweb_always_scan": "Установить MWEB всегда сканирование",
"litecoin_mweb_description": "MWEB - это новый протокол, который приносит быстрее, дешевле и более частные транзакции в Litecoin", "litecoin_mweb_description": "MWEB - это новый протокол, который приносит быстрее, дешевле и более частные транзакции в Litecoin",
"litecoin_mweb_dismiss": "Увольнять", "litecoin_mweb_dismiss": "Увольнять",

View file

@ -394,6 +394,7 @@
"light_theme": "สว่าง", "light_theme": "สว่าง",
"litecoin_enable_mweb_sync": "เปิดใช้งานการสแกน MWEB", "litecoin_enable_mweb_sync": "เปิดใช้งานการสแกน MWEB",
"litecoin_mweb": "mweb", "litecoin_mweb": "mweb",
"litecoin_mweb_allow_coins": "อนุญาตให้เหรียญ MWEB",
"litecoin_mweb_always_scan": "ตั้งค่าการสแกน MWEB เสมอ", "litecoin_mweb_always_scan": "ตั้งค่าการสแกน MWEB เสมอ",
"litecoin_mweb_description": "MWEB เป็นโปรโตคอลใหม่ที่นำการทำธุรกรรมที่เร็วกว่าราคาถูกกว่าและเป็นส่วนตัวมากขึ้นไปยัง Litecoin", "litecoin_mweb_description": "MWEB เป็นโปรโตคอลใหม่ที่นำการทำธุรกรรมที่เร็วกว่าราคาถูกกว่าและเป็นส่วนตัวมากขึ้นไปยัง Litecoin",
"litecoin_mweb_dismiss": "อนุญาตให้ออกไป", "litecoin_mweb_dismiss": "อนุญาตให้ออกไป",

View file

@ -394,6 +394,7 @@
"light_theme": "Light", "light_theme": "Light",
"litecoin_enable_mweb_sync": "Paganahin ang pag -scan ng MWeb", "litecoin_enable_mweb_sync": "Paganahin ang pag -scan ng MWeb",
"litecoin_mweb": "Mweb", "litecoin_mweb": "Mweb",
"litecoin_mweb_allow_coins": "Payagan ang mga barya ng MWEB",
"litecoin_mweb_always_scan": "Itakda ang MWeb na laging nag -scan", "litecoin_mweb_always_scan": "Itakda ang MWeb na laging nag -scan",
"litecoin_mweb_description": "Ang MWeb ay isang bagong protocol na nagdadala ng mas mabilis, mas mura, at mas maraming pribadong mga transaksyon sa Litecoin", "litecoin_mweb_description": "Ang MWeb ay isang bagong protocol na nagdadala ng mas mabilis, mas mura, at mas maraming pribadong mga transaksyon sa Litecoin",
"litecoin_mweb_dismiss": "Tanggalin", "litecoin_mweb_dismiss": "Tanggalin",

View file

@ -394,6 +394,7 @@
"light_theme": "Aydınlık", "light_theme": "Aydınlık",
"litecoin_enable_mweb_sync": "MWEB taramasını etkinleştir", "litecoin_enable_mweb_sync": "MWEB taramasını etkinleştir",
"litecoin_mweb": "Mweb", "litecoin_mweb": "Mweb",
"litecoin_mweb_allow_coins": "MWEB Coins'e izin ver",
"litecoin_mweb_always_scan": "MWEB'i her zaman taramayı ayarlayın", "litecoin_mweb_always_scan": "MWEB'i her zaman taramayı ayarlayın",
"litecoin_mweb_description": "MWEB, Litecoin'e daha hızlı, daha ucuz ve daha fazla özel işlem getiren yeni bir protokoldür", "litecoin_mweb_description": "MWEB, Litecoin'e daha hızlı, daha ucuz ve daha fazla özel işlem getiren yeni bir protokoldür",
"litecoin_mweb_dismiss": "Azletmek", "litecoin_mweb_dismiss": "Azletmek",

View file

@ -394,6 +394,7 @@
"light_theme": "Світла", "light_theme": "Світла",
"litecoin_enable_mweb_sync": "Увімкнути сканування MWEB", "litecoin_enable_mweb_sync": "Увімкнути сканування MWEB",
"litecoin_mweb": "Мвеб", "litecoin_mweb": "Мвеб",
"litecoin_mweb_allow_coins": "Дозволити монети MWEB",
"litecoin_mweb_always_scan": "Встановити mweb завжди сканувати", "litecoin_mweb_always_scan": "Встановити mweb завжди сканувати",
"litecoin_mweb_description": "MWEB - це новий протокол, який приносить швидкі, дешевші та більш приватні транзакції Litecoin", "litecoin_mweb_description": "MWEB - це новий протокол, який приносить швидкі, дешевші та більш приватні транзакції Litecoin",
"litecoin_mweb_dismiss": "Звільнити", "litecoin_mweb_dismiss": "Звільнити",

View file

@ -394,6 +394,7 @@
"light_theme": "روشنی", "light_theme": "روشنی",
"litecoin_enable_mweb_sync": "MWEB اسکیننگ کو فعال کریں", "litecoin_enable_mweb_sync": "MWEB اسکیننگ کو فعال کریں",
"litecoin_mweb": "MWEB", "litecoin_mweb": "MWEB",
"litecoin_mweb_allow_coins": "MWEB سکے کی اجازت دیں",
"litecoin_mweb_always_scan": "MWEB ہمیشہ اسکیننگ سیٹ کریں", "litecoin_mweb_always_scan": "MWEB ہمیشہ اسکیننگ سیٹ کریں",
"litecoin_mweb_description": "MWEB ایک نیا پروٹوکول ہے جو لیٹیکوئن میں تیز ، سستا اور زیادہ نجی لین دین لاتا ہے", "litecoin_mweb_description": "MWEB ایک نیا پروٹوکول ہے جو لیٹیکوئن میں تیز ، سستا اور زیادہ نجی لین دین لاتا ہے",
"litecoin_mweb_dismiss": "خارج", "litecoin_mweb_dismiss": "خارج",

View file

@ -393,6 +393,7 @@
"light_theme": "Chủ đề sáng", "light_theme": "Chủ đề sáng",
"litecoin_enable_mweb_sync": "Bật quét MWEB", "litecoin_enable_mweb_sync": "Bật quét MWEB",
"litecoin_mweb": "Mweb", "litecoin_mweb": "Mweb",
"litecoin_mweb_allow_coins": "Cho phép tiền xu MWEB",
"litecoin_mweb_always_scan": "Đặt MWEB luôn quét", "litecoin_mweb_always_scan": "Đặt MWEB luôn quét",
"litecoin_mweb_description": "MWEB là một giao thức mới mang lại các giao dịch nhanh hơn, rẻ hơn và riêng tư hơn cho Litecoin", "litecoin_mweb_description": "MWEB là một giao thức mới mang lại các giao dịch nhanh hơn, rẻ hơn và riêng tư hơn cho Litecoin",
"litecoin_mweb_dismiss": "Miễn nhiệm", "litecoin_mweb_dismiss": "Miễn nhiệm",

View file

@ -395,6 +395,7 @@
"light_theme": "Funfun bí eérú", "light_theme": "Funfun bí eérú",
"litecoin_enable_mweb_sync": "Mu mweb ọlọjẹ", "litecoin_enable_mweb_sync": "Mu mweb ọlọjẹ",
"litecoin_mweb": "Mweb", "litecoin_mweb": "Mweb",
"litecoin_mweb_allow_coins": "Gba awọn owo Mweb gba",
"litecoin_mweb_always_scan": "Ṣeto mweb nigbagbogbo n ṣayẹwo", "litecoin_mweb_always_scan": "Ṣeto mweb nigbagbogbo n ṣayẹwo",
"litecoin_mweb_description": "Mweb jẹ ilana ilana tuntun ti o mu iyara wa yiyara, din owo, ati awọn iṣowo ikọkọ diẹ sii si Livcoin", "litecoin_mweb_description": "Mweb jẹ ilana ilana tuntun ti o mu iyara wa yiyara, din owo, ati awọn iṣowo ikọkọ diẹ sii si Livcoin",
"litecoin_mweb_dismiss": "Tuka", "litecoin_mweb_dismiss": "Tuka",

View file

@ -394,6 +394,7 @@
"light_theme": "艳丽", "light_theme": "艳丽",
"litecoin_enable_mweb_sync": "启用MWEB扫描", "litecoin_enable_mweb_sync": "启用MWEB扫描",
"litecoin_mweb": "MWEB", "litecoin_mweb": "MWEB",
"litecoin_mweb_allow_coins": "允许MWEB硬币",
"litecoin_mweb_always_scan": "设置MWEB总是扫描", "litecoin_mweb_always_scan": "设置MWEB总是扫描",
"litecoin_mweb_description": "MWEB是一项新协议它将更快更便宜和更多的私人交易带给Litecoin", "litecoin_mweb_description": "MWEB是一项新协议它将更快更便宜和更多的私人交易带给Litecoin",
"litecoin_mweb_dismiss": "解雇", "litecoin_mweb_dismiss": "解雇",