mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-06-28 20:39:51 +00:00
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:
parent
1e4dbb5bc9
commit
1c8af1afae
35 changed files with 885 additions and 581 deletions
|
@ -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];
|
||||||
|
|
|
@ -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),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
],
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
202
lib/src/widgets/adaptable_page_view.dart
Normal file
202
lib/src/widgets/adaptable_page_view.dart
Normal 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),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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": "رفض",
|
||||||
|
|
|
@ -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": "Уволнение",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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": "नकार देना",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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": "Հեռացնել",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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": "却下する",
|
||||||
|
|
|
@ -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": "해고하다",
|
||||||
|
|
|
@ -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": "ထုတ်ပစ်",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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ć",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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": "Увольнять",
|
||||||
|
|
|
@ -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": "อนุญาตให้ออกไป",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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": "Звільнити",
|
||||||
|
|
|
@ -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": "خارج",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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": "解雇",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue