mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-17 05:41:29 +02:00
CW-1132-Cake-Pay-flow-does-not-use-MWEB-funds (#2434)
* add MWEB coin selection for Cake Pay flow * add MWEB checks --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
@@ -3,7 +3,6 @@ import 'dart:io';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/cake_pay/src/models/cake_pay_card.dart';
|
||||
import 'package:cake_wallet/cake_pay/src/models/cake_pay_order.dart';
|
||||
import 'package:cake_wallet/cake_pay/src/services/cake_pay_service.dart';
|
||||
import 'package:cake_wallet/cake_pay/src/widgets/cake_pay_alert_modal.dart';
|
||||
import 'package:cake_wallet/cake_pay/src/widgets/denominations_amount_widget.dart';
|
||||
import 'package:cake_wallet/cake_pay/src/widgets/enter_amount_widget.dart';
|
||||
@@ -24,23 +23,26 @@ import 'package:cake_wallet/src/widgets/bottom_sheet/confirm_sending_bottom_shee
|
||||
import 'package:cake_wallet/src/widgets/bottom_sheet/info_bottom_sheet_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
|
||||
import 'package:cake_wallet/typography.dart';
|
||||
import 'package:cake_wallet/utils/feature_flag.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/cake_pay/cake_pay_buy_card_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/send/output.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
|
||||
import 'package:cw_core/unspent_coin_type.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:keyboard_actions/keyboard_actions.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
class CakePayBuyCardPage extends BasePage {
|
||||
CakePayBuyCardPage(
|
||||
this.cakePayBuyCardViewModel,
|
||||
this.cakePayService,
|
||||
) : _amountFieldFocus = FocusNode(),
|
||||
this.cakePayBuyCardViewModel
|
||||
) : _sendViewModel = cakePayBuyCardViewModel.sendViewModel,
|
||||
_amountFieldFocus = FocusNode(),
|
||||
_amountController = TextEditingController(),
|
||||
_quantityFieldFocus = FocusNode(),
|
||||
_quantityController =
|
||||
@@ -51,7 +53,7 @@ class CakePayBuyCardPage extends BasePage {
|
||||
}
|
||||
|
||||
final CakePayBuyCardViewModel cakePayBuyCardViewModel;
|
||||
final CakePayService cakePayService;
|
||||
final SendViewModel _sendViewModel;
|
||||
|
||||
bool _effectsInstalled = false;
|
||||
late final BuildContext _overlayCtx;
|
||||
@@ -301,6 +303,38 @@ class CakePayBuyCardPage extends BasePage {
|
||||
methods.length <= 1 || selected == null
|
||||
? const SizedBox.shrink()
|
||||
: _buildPaymentMethodWidget(methods, selected),
|
||||
if (_sendViewModel.walletType == WalletType.litecoin && _sendViewModel.isMwebEnabled)
|
||||
Observer(
|
||||
builder: (_) => Padding(
|
||||
padding: EdgeInsets.only(top: 10, bottom: 0, right: 20, left: 20),
|
||||
child: GestureDetector(
|
||||
key: ValueKey('cake_pay_buy_page_unspent_coin_button_key'),
|
||||
onTap: () {
|
||||
bool value = _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: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
borderColor: Theme.of(context).colorScheme.primary,
|
||||
iconColor: Theme.of(context).colorScheme.primary,
|
||||
value:
|
||||
_sendViewModel.coinTypeToSpendFrom == UnspentCoinType.any,
|
||||
onChanged: (bool? value) {
|
||||
_sendViewModel.setAllowMwebCoins(value ?? false);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (FeatureFlag.hasDevOptions)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 10, bottom: 0, right: 20, left: 20),
|
||||
@@ -318,7 +352,7 @@ class CakePayBuyCardPage extends BasePage {
|
||||
isDisabled: !cakePayBuyCardViewModel.isAmountSufficient ||
|
||||
cakePayBuyCardViewModel.isPurchasing,
|
||||
isLoading:
|
||||
cakePayBuyCardViewModel.sendViewModel.state is IsExecutingState ||
|
||||
_sendViewModel.state is IsExecutingState ||
|
||||
cakePayBuyCardViewModel.isPurchasing,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
@@ -338,7 +372,7 @@ class CakePayBuyCardPage extends BasePage {
|
||||
text: S.of(context).purchase_gift_card,
|
||||
isDisabled: !cakePayBuyCardViewModel.isAmountSufficient ||
|
||||
cakePayBuyCardViewModel.isPurchasing,
|
||||
isLoading: cakePayBuyCardViewModel.sendViewModel.state is IsExecutingState ||
|
||||
isLoading: _sendViewModel.state is IsExecutingState ||
|
||||
cakePayBuyCardViewModel.isPurchasing,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
@@ -411,7 +445,7 @@ class CakePayBuyCardPage extends BasePage {
|
||||
}
|
||||
|
||||
Future<void> confirmPurchaseFirst(BuildContext context) async {
|
||||
bool isLogged = await cakePayBuyCardViewModel.cakePayService.isLogged();
|
||||
bool isLogged = await cakePayBuyCardViewModel.isUserLogged;
|
||||
if (!isLogged) {
|
||||
cakePayBuyCardViewModel.isSimulatingFlow = false;
|
||||
Navigator.of(context).pushNamed(Routes.cakePayWelcomePage);
|
||||
@@ -458,7 +492,7 @@ class CakePayBuyCardPage extends BasePage {
|
||||
}
|
||||
|
||||
Future<void> purchaseCard(BuildContext context) async {
|
||||
bool isLogged = await cakePayBuyCardViewModel.cakePayService.isLogged();
|
||||
bool isLogged = await cakePayBuyCardViewModel.isUserLogged;
|
||||
if (!isLogged) {
|
||||
cakePayBuyCardViewModel.isSimulatingFlow = false;
|
||||
Navigator.of(context).pushNamed(Routes.cakePayWelcomePage);
|
||||
@@ -467,7 +501,7 @@ class CakePayBuyCardPage extends BasePage {
|
||||
await cakePayBuyCardViewModel.createOrder();
|
||||
} catch (_) {
|
||||
cakePayBuyCardViewModel.isSimulatingFlow = false;
|
||||
await cakePayBuyCardViewModel.cakePayService.logout();
|
||||
await cakePayBuyCardViewModel.logout();
|
||||
}
|
||||
}
|
||||
cakePayBuyCardViewModel.isPurchasing = false;
|
||||
@@ -484,11 +518,11 @@ class CakePayBuyCardPage extends BasePage {
|
||||
|
||||
_overlayCtx = Navigator.of(context).context;
|
||||
|
||||
if (cakePayBuyCardViewModel.sendViewModel.isElectrumWallet) {
|
||||
bitcoin!.updateFeeRates(cakePayBuyCardViewModel.sendViewModel.wallet);
|
||||
if (_sendViewModel.isElectrumWallet) {
|
||||
bitcoin!.updateFeeRates(_sendViewModel.wallet);
|
||||
}
|
||||
|
||||
reaction((_) => cakePayBuyCardViewModel.sendViewModel.state, (ExecutionState state) async {
|
||||
reaction((_) => _sendViewModel.state, (ExecutionState state) async {
|
||||
if (dialogContext != null && dialogContext!.mounted) Navigator.of(dialogContext!).pop();
|
||||
|
||||
if (confirmBottomSheetContext != null && confirmBottomSheetContext!.mounted) {
|
||||
@@ -521,7 +555,7 @@ class CakePayBuyCardPage extends BasePage {
|
||||
if (state is IsExecutingState) {
|
||||
// wait a bit to avoid showing the loading dialog if transaction is failed
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
final currentState = cakePayBuyCardViewModel.sendViewModel.state;
|
||||
final currentState = _sendViewModel.state;
|
||||
if (currentState is ExecutedSuccessfullyState || currentState is FailureState) {
|
||||
return;
|
||||
}
|
||||
@@ -549,7 +583,7 @@ class CakePayBuyCardPage extends BasePage {
|
||||
|
||||
disposer = reaction((_) => cakePayBuyCardViewModel.isOrderExpired, (bool isExpired) {
|
||||
if (isExpired) {
|
||||
cakePayBuyCardViewModel.sendViewModel.state = FailureState('Order expired');
|
||||
_sendViewModel.state = FailureState('Order expired');
|
||||
disposer?.call();
|
||||
}
|
||||
});
|
||||
@@ -558,7 +592,7 @@ class CakePayBuyCardPage extends BasePage {
|
||||
if (context.mounted) {
|
||||
final order = cakePayBuyCardViewModel.order;
|
||||
|
||||
final displayingOutputs = cakePayBuyCardViewModel.sendViewModel.outputs
|
||||
final displayingOutputs = _sendViewModel.outputs
|
||||
.map((o) => o.OutputCopyWithParsedAddress(
|
||||
parsedAddress: ParsedAddress(
|
||||
addresses: [o.address],
|
||||
@@ -584,20 +618,16 @@ class CakePayBuyCardPage extends BasePage {
|
||||
paymentId: S.of(bottomSheetContext).payment_id,
|
||||
paymentIdValue: cakePayBuyCardViewModel.order?.orderId,
|
||||
expirationTime: cakePayBuyCardViewModel.formattedRemainingTime,
|
||||
walletType: cakePayBuyCardViewModel.sendViewModel.walletType,
|
||||
titleIconPath:
|
||||
cakePayBuyCardViewModel.sendViewModel.selectedCryptoCurrency.iconPath,
|
||||
currency: cakePayBuyCardViewModel.sendViewModel.selectedCryptoCurrency,
|
||||
walletType: _sendViewModel.walletType,
|
||||
titleIconPath: _sendViewModel.selectedCryptoCurrency.iconPath,
|
||||
currency: _sendViewModel.selectedCryptoCurrency,
|
||||
amount: S.of(bottomSheetContext).send_amount,
|
||||
amountValue:
|
||||
cakePayBuyCardViewModel.sendViewModel.pendingTransaction!.amountFormatted,
|
||||
amountValue: _sendViewModel.pendingTransaction!.amountFormatted,
|
||||
quantity: 'QTY: ${cakePayBuyCardViewModel.quantity}',
|
||||
fiatAmountValue:
|
||||
cakePayBuyCardViewModel.sendViewModel.pendingTransactionFiatAmountFormatted,
|
||||
fiatAmountValue: _sendViewModel.pendingTransactionFiatAmountFormatted,
|
||||
fee: S.of(bottomSheetContext).send_fee,
|
||||
feeValue: cakePayBuyCardViewModel.sendViewModel.pendingTransaction!.feeFormatted,
|
||||
feeFiatAmount: cakePayBuyCardViewModel
|
||||
.sendViewModel.pendingTransactionFeeFiatAmountFormatted,
|
||||
feeValue: _sendViewModel.pendingTransaction!.feeFormatted,
|
||||
feeFiatAmount:_sendViewModel.pendingTransactionFeeFiatAmountFormatted,
|
||||
outputs: displayingOutputs,
|
||||
footerType: FooterType.slideActionButton,
|
||||
slideActionButtonText:
|
||||
@@ -608,10 +638,10 @@ class CakePayBuyCardPage extends BasePage {
|
||||
Navigator.of(bottomSheetContext).pop(true);
|
||||
cakePayBuyCardViewModel.isSimulating
|
||||
? cakePayBuyCardViewModel.simulatePayment()
|
||||
: cakePayBuyCardViewModel.sendViewModel.commitTransaction(context);
|
||||
: _sendViewModel.commitTransaction(context);
|
||||
},
|
||||
change: cakePayBuyCardViewModel.sendViewModel.pendingTransaction!.change,
|
||||
isOpenCryptoPay: cakePayBuyCardViewModel.sendViewModel.ocpRequest != null,
|
||||
change: _sendViewModel.pendingTransaction!.change,
|
||||
isOpenCryptoPay: _sendViewModel.ocpRequest != null,
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -619,14 +649,14 @@ class CakePayBuyCardPage extends BasePage {
|
||||
confirmBottomSheetContext = null;
|
||||
cakePayBuyCardViewModel.isSimulatingFlow = false;
|
||||
_handleDispose(disposer);
|
||||
if (result == null) cakePayBuyCardViewModel.sendViewModel.dismissTransaction();
|
||||
if (result == null) _sendViewModel.dismissTransaction();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (state is TransactionCommitted) {
|
||||
final order = cakePayBuyCardViewModel.order;
|
||||
final outputsCopy = List<Output>.from(cakePayBuyCardViewModel.sendViewModel.outputs);
|
||||
final outputsCopy = List<Output>.from(_sendViewModel.outputs);
|
||||
|
||||
final displayingOutputs = outputsCopy
|
||||
.map((o) => o.OutputCopyWithParsedAddress(
|
||||
@@ -640,7 +670,7 @@ class CakePayBuyCardPage extends BasePage {
|
||||
))
|
||||
.toList();
|
||||
|
||||
cakePayBuyCardViewModel.sendViewModel.clearOutputs();
|
||||
_sendViewModel.clearOutputs();
|
||||
|
||||
final bool usePageContextLater = context.mounted;
|
||||
|
||||
@@ -668,17 +698,14 @@ class CakePayBuyCardPage extends BasePage {
|
||||
child: Icon(Icons.check, size: 16, color: Colors.white),
|
||||
),
|
||||
output: displayingOutputs.first,
|
||||
currency: cakePayBuyCardViewModel.sendViewModel.selectedCryptoCurrency,
|
||||
currency: _sendViewModel.selectedCryptoCurrency,
|
||||
amount: S.of(sheetCtx).send_amount,
|
||||
amountValue:
|
||||
cakePayBuyCardViewModel.sendViewModel.pendingTransaction!.amountFormatted,
|
||||
amountValue: _sendViewModel.pendingTransaction!.amountFormatted,
|
||||
quantity: 'QTY: ${cakePayBuyCardViewModel.quantity}',
|
||||
fiatAmountValue:
|
||||
cakePayBuyCardViewModel.sendViewModel.pendingTransactionFiatAmountFormatted,
|
||||
fiatAmountValue: _sendViewModel.pendingTransactionFiatAmountFormatted,
|
||||
fee: S.of(sheetCtx).send_fee,
|
||||
feeValue: cakePayBuyCardViewModel.sendViewModel.pendingTransaction!.feeFormatted,
|
||||
feeFiatAmount:
|
||||
cakePayBuyCardViewModel.sendViewModel.pendingTransactionFeeFiatAmountFormatted,
|
||||
feeValue: _sendViewModel.pendingTransaction!.feeFormatted,
|
||||
feeFiatAmount: _sendViewModel.pendingTransactionFeeFiatAmountFormatted,
|
||||
paymentId: 'Order ID',
|
||||
paymentIdValue: order?.orderId ?? '',
|
||||
onClose: () {
|
||||
@@ -709,7 +736,7 @@ class CakePayBuyCardPage extends BasePage {
|
||||
content: S.of(bottomSheetContext).proceed_on_device_description,
|
||||
singleActionButtonText: S.of(context).cancel,
|
||||
onSingleActionButtonPressed: () {
|
||||
cakePayBuyCardViewModel.sendViewModel.state = InitialExecutionState();
|
||||
_sendViewModel.state = InitialExecutionState();
|
||||
Navigator.of(bottomSheetContext).pop();
|
||||
},
|
||||
),
|
||||
|
||||
@@ -3,10 +3,10 @@ enum CakePayPaymentMethod { BTC, BTC_LN, XMR, LTC, LTC_MWEB }
|
||||
extension CakePayPaymentMethodLabel on CakePayPaymentMethod {
|
||||
String get label => switch (this) {
|
||||
CakePayPaymentMethod.BTC => 'Bitcoin',
|
||||
CakePayPaymentMethod.BTC_LN => 'Bitcoin Lightning',
|
||||
CakePayPaymentMethod.BTC_LN => 'Lightning',
|
||||
CakePayPaymentMethod.XMR => 'Monero',
|
||||
CakePayPaymentMethod.LTC => 'Litecoin',
|
||||
CakePayPaymentMethod.LTC_MWEB => 'Litecoin MWEB',
|
||||
CakePayPaymentMethod.LTC_MWEB => 'MWEB',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -1424,8 +1424,7 @@ Future<void> setup({
|
||||
getIt.registerFactoryParam<CakePayBuyCardPage, List<dynamic>, void>((List<dynamic> args, _) {
|
||||
final vendor = args.first as CakePayVendor;
|
||||
|
||||
return CakePayBuyCardPage(
|
||||
getIt.get<CakePayBuyCardViewModel>(param1: vendor), getIt.get<CakePayService>());
|
||||
return CakePayBuyCardPage(getIt.get<CakePayBuyCardViewModel>(param1: vendor));
|
||||
});
|
||||
|
||||
getIt.registerFactory(() => CakePayCardsPage(getIt.get<CakePayCardsListViewModel>()));
|
||||
|
||||
@@ -443,7 +443,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||
),
|
||||
),
|
||||
),
|
||||
if (sendViewModel.currency == CryptoCurrency.ltc)
|
||||
if (sendViewModel.currency == CryptoCurrency.ltc && sendViewModel.isMwebEnabled)
|
||||
Observer(
|
||||
builder: (_) => Padding(
|
||||
padding: EdgeInsets.only(top: 14),
|
||||
|
||||
@@ -17,8 +17,8 @@ class CakePayBuyCardViewModel = CakePayBuyCardViewModelBase with _$CakePayBuyCar
|
||||
|
||||
abstract class CakePayBuyCardViewModelBase with Store {
|
||||
CakePayBuyCardViewModelBase(
|
||||
{required this.vendor, required this.cakePayService, required this.sendViewModel})
|
||||
: walletType = sendViewModel.walletType,
|
||||
{required this.vendor, required CakePayService cakePayService, required this.sendViewModel})
|
||||
: _cakePayService = cakePayService, walletType = sendViewModel.walletType,
|
||||
amount = vendor.card!.denominations.isNotEmpty
|
||||
? double.parse(vendor.card!.denominations.first)
|
||||
: 0,
|
||||
@@ -31,7 +31,7 @@ abstract class CakePayBuyCardViewModelBase with Store {
|
||||
|
||||
final CakePayVendor vendor;
|
||||
final SendViewModel sendViewModel;
|
||||
final CakePayService cakePayService;
|
||||
final CakePayService _cakePayService;
|
||||
final double max;
|
||||
final double min;
|
||||
final CakePayCard card;
|
||||
@@ -48,6 +48,8 @@ abstract class CakePayBuyCardViewModelBase with Store {
|
||||
String simulatedResponse = '';
|
||||
|
||||
bool get isDenominationSelected => card.denominations.isNotEmpty;
|
||||
|
||||
Future<bool> get isUserLogged async => await _cakePayService.isLogged();
|
||||
|
||||
@observable
|
||||
double amount;
|
||||
@@ -86,7 +88,7 @@ abstract class CakePayBuyCardViewModelBase with Store {
|
||||
case WalletType.bitcoin:
|
||||
return [CakePayPaymentMethod.BTC];
|
||||
case WalletType.litecoin:
|
||||
return [CakePayPaymentMethod.LTC, CakePayPaymentMethod.LTC_MWEB];
|
||||
return [CakePayPaymentMethod.LTC,if (sendViewModel.isMwebEnabled) CakePayPaymentMethod.LTC_MWEB];
|
||||
case WalletType.monero:
|
||||
return [CakePayPaymentMethod.XMR];
|
||||
default:
|
||||
@@ -95,7 +97,12 @@ abstract class CakePayBuyCardViewModelBase with Store {
|
||||
}
|
||||
|
||||
@action
|
||||
void chooseMethod(CakePayPaymentMethod method) => selectedPaymentMethod = method;
|
||||
void chooseMethod(CakePayPaymentMethod method) {
|
||||
selectedPaymentMethod = method;
|
||||
if (walletType == WalletType.litecoin) {
|
||||
sendViewModel.setAllowMwebCoins(method == CakePayPaymentMethod.LTC_MWEB);
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
void onQuantityChanged(int? input) => quantity = input ?? 1;
|
||||
@@ -141,7 +148,7 @@ abstract class CakePayBuyCardViewModelBase with Store {
|
||||
FailureState('Unsupported wallet type, please use Bitcoin, Monero, or Litecoin.');
|
||||
}
|
||||
try {
|
||||
order = await cakePayService.createOrder(
|
||||
order = await _cakePayService.createOrder(
|
||||
cardId: card.id,
|
||||
price: amount.toString(),
|
||||
quantity: quantity,
|
||||
@@ -183,7 +190,7 @@ abstract class CakePayBuyCardViewModelBase with Store {
|
||||
}
|
||||
|
||||
try {
|
||||
simulatedResponse = await cakePayService.simulatePayment(orderId: order!.orderId);
|
||||
simulatedResponse = await _cakePayService.simulatePayment(orderId: order!.orderId);
|
||||
sendViewModel.state = TransactionCommitted();
|
||||
|
||||
} catch (e) {
|
||||
@@ -210,6 +217,8 @@ abstract class CakePayBuyCardViewModelBase with Store {
|
||||
formattedRemainingTime = formatDuration(remainingTime!);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> logout() async => await _cakePayService.logout();
|
||||
|
||||
void _startExpirationTimer() {
|
||||
_timer?.cancel();
|
||||
|
||||
@@ -114,6 +114,8 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
|
||||
bool get showAddressBookPopup => _settingsStore.showAddressBookPopupEnabled;
|
||||
|
||||
bool get isMwebEnabled => balanceViewModel.mwebEnabled;
|
||||
|
||||
@action
|
||||
void setShowAddressBookPopup(bool value) {
|
||||
_settingsStore.showAddressBookPopupEnabled = value;
|
||||
|
||||
Reference in New Issue
Block a user