mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-18 05:51:34 +02:00
feat: Pay Anything from Any Wallet (#2450)
* feat: Pay Anything from Any Wallet * feat: Trigger payment flow when address is pasted * chore: Remove unneeded changes * feat Expand address checks and fix scanning bch qrs * fix: Add support for dogecoin * feat: More modifications for Pay from Any Wallet This change: - Adds the pay from any wallet flow to externally signed QR codes - Adds fixes for review comments - Fixes failing tests --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
co-authored by
Omar Hatem
parent
16f058e9e2
commit
13a74133f5
@@ -0,0 +1,161 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/utils/payment_request.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/src/widgets/bottom_sheet/base_bottom_sheet_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/view_model/payment/payment_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_switcher_view_model.dart';
|
||||
import 'package:cake_wallet/themes/core/material_base_theme.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
|
||||
class PaymentConfirmationBottomSheet extends BaseBottomSheet {
|
||||
PaymentConfirmationBottomSheet({
|
||||
Key? key,
|
||||
required this.paymentFlowResult,
|
||||
required this.paymentViewModel,
|
||||
required this.walletSwitcherViewModel,
|
||||
required this.currentTheme,
|
||||
required this.paymentRequest,
|
||||
required this.onSelectWallet,
|
||||
required this.onChangeWallet,
|
||||
required this.onSwap,
|
||||
}) : super(
|
||||
titleText: '',
|
||||
footerType: FooterType.none,
|
||||
maxHeight: 900,
|
||||
currentTheme: currentTheme,
|
||||
);
|
||||
|
||||
final PaymentFlowResult paymentFlowResult;
|
||||
final PaymentViewModel paymentViewModel;
|
||||
final WalletSwitcherViewModel walletSwitcherViewModel;
|
||||
final MaterialThemeBase currentTheme;
|
||||
final PaymentRequest paymentRequest;
|
||||
final VoidCallback onSelectWallet;
|
||||
final VoidCallback onChangeWallet;
|
||||
final VoidCallback onSwap;
|
||||
|
||||
@override
|
||||
Widget contentWidget(BuildContext context) {
|
||||
return _PaymentConfirmationContent(
|
||||
paymentFlowResult: paymentFlowResult,
|
||||
paymentViewModel: paymentViewModel,
|
||||
walletSwitcherViewModel: walletSwitcherViewModel,
|
||||
paymentRequest: paymentRequest,
|
||||
onSelectWallet: onSelectWallet,
|
||||
onChangeWallet: onChangeWallet,
|
||||
onSwap: onSwap,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PaymentConfirmationContent extends StatelessWidget {
|
||||
const _PaymentConfirmationContent({
|
||||
required this.paymentFlowResult,
|
||||
required this.paymentViewModel,
|
||||
required this.walletSwitcherViewModel,
|
||||
required this.paymentRequest,
|
||||
required this.onSelectWallet,
|
||||
required this.onChangeWallet,
|
||||
required this.onSwap,
|
||||
});
|
||||
|
||||
final PaymentFlowResult paymentFlowResult;
|
||||
final PaymentViewModel paymentViewModel;
|
||||
final WalletSwitcherViewModel walletSwitcherViewModel;
|
||||
final PaymentRequest paymentRequest;
|
||||
final VoidCallback onSelectWallet;
|
||||
final VoidCallback onChangeWallet;
|
||||
final VoidCallback onSwap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Observer(
|
||||
builder: (_) {
|
||||
final currencyName = walletTypeToString(paymentViewModel.detectedWalletType!);
|
||||
final currentWalletName = walletTypeToString(paymentViewModel.currentWalletType);
|
||||
|
||||
final hasMultipleWallets = paymentFlowResult.type == PaymentFlowType.multipleWallets;
|
||||
final noAvailableWallets = paymentFlowResult.type == PaymentFlowType.noWallets;
|
||||
|
||||
final hasAtLeastOneWallet =
|
||||
paymentFlowResult.type == PaymentFlowType.singleWallet || hasMultipleWallets;
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.fromLTRB(24, 0, 24, 24),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Image.asset(
|
||||
walletTypeToCryptoCurrency(paymentViewModel.detectedWalletType!).iconPath!,
|
||||
width: 118,
|
||||
height: 118,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
'$currencyName ${S.current.address_detected.toLowerCase()}',
|
||||
style: Theme.of(context).textTheme.titleMedium!.copyWith(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w900,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
letterSpacing: 0.0,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
'''Looks like you scanned a $currencyName address.\n\n'''
|
||||
'''Would you like to ${!noAvailableWallets ? 'switch to a $currencyName wallet or' : ''} swap $currentWalletName for $currencyName for this payment?''',
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
letterSpacing: 0.0,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 72),
|
||||
if (hasAtLeastOneWallet) ...[
|
||||
PrimaryButton(
|
||||
onPressed: onSwap,
|
||||
text: '${S.current.swap} $currentWalletName',
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
textColor: Theme.of(context).colorScheme.onSecondaryContainer,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
PrimaryButton(
|
||||
onPressed: hasMultipleWallets ? onSelectWallet : onChangeWallet,
|
||||
text: S.current.switch_wallet,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
] else ...[
|
||||
PrimaryButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
text: S.current.cancel,
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
textColor: Theme.of(context).colorScheme.onSecondaryContainer,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
PrimaryButton(
|
||||
onPressed: onSwap,
|
||||
text: '${S.current.swap} $currentWalletName',
|
||||
color: hasAtLeastOneWallet
|
||||
? Theme.of(context).colorScheme.surfaceContainer
|
||||
: Theme.of(context).colorScheme.primary,
|
||||
textColor: hasAtLeastOneWallet
|
||||
? Theme.of(context).colorScheme.onSecondaryContainer
|
||||
: Theme.of(context).colorScheme.onPrimary,
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/src/widgets/bottom_sheet/base_bottom_sheet_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_switcher_view_model.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/themes/core/material_base_theme.dart';
|
||||
|
||||
class WalletSwitcherBottomSheet extends BaseBottomSheet {
|
||||
WalletSwitcherBottomSheet({
|
||||
Key? key,
|
||||
required this.viewModel,
|
||||
required this.currentTheme,
|
||||
this.filterWalletType,
|
||||
this.subtitle,
|
||||
this.onWalletSelected,
|
||||
}) : super(
|
||||
titleText: S.current.select_a_wallet,
|
||||
footerType: FooterType.none,
|
||||
maxHeight: 900,
|
||||
currentTheme: currentTheme,
|
||||
);
|
||||
|
||||
final WalletSwitcherViewModel viewModel;
|
||||
final MaterialThemeBase currentTheme;
|
||||
final WalletType? filterWalletType;
|
||||
final String? subtitle;
|
||||
final Function(WalletInfo)? onWalletSelected;
|
||||
|
||||
@override
|
||||
Widget contentWidget(BuildContext context) {
|
||||
return _WalletSwitcherContent(
|
||||
viewModel: viewModel,
|
||||
filterWalletType: filterWalletType,
|
||||
subtitle: subtitle,
|
||||
onWalletSelected: onWalletSelected,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _WalletSwitcherContent extends StatelessWidget {
|
||||
const _WalletSwitcherContent({
|
||||
required this.viewModel,
|
||||
this.filterWalletType,
|
||||
this.subtitle,
|
||||
this.onWalletSelected,
|
||||
});
|
||||
|
||||
final WalletSwitcherViewModel viewModel;
|
||||
final WalletType? filterWalletType;
|
||||
final String? subtitle;
|
||||
final Function(WalletInfo)? onWalletSelected;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Observer(
|
||||
builder: (_) {
|
||||
final wallets = viewModel.getWallets(filterWalletType);
|
||||
|
||||
if (viewModel.isProcessing) {
|
||||
return Container(
|
||||
height: 200,
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Container(
|
||||
height: 400,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: StandardList(
|
||||
itemCount: wallets.length,
|
||||
itemBuilder: (context, index) {
|
||||
final wallet = wallets[index];
|
||||
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
viewModel.selectWallet(wallet);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
height: 60,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
Image.asset(
|
||||
walletTypeToCryptoCurrency(wallet.type).iconPath!,
|
||||
width: 32,
|
||||
height: 32,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
wallet.name,
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
letterSpacing: 0.0,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user