Merge branch 'dev' into cw-1497-transaction-history-improved-layout

This commit is contained in:
Robert Malikowski
2026-07-13 15:35:49 +02:00
137 changed files with 3860 additions and 2415 deletions
+52 -7
View File
@@ -50,6 +50,7 @@ import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/payment/payment_view_model.dart';
import 'package:cake_wallet/view_model/send/output.dart';
import 'package:cake_wallet/core/address_resolver/parsed_address.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:cake_wallet/view_model/wallet_switcher_view_model.dart';
@@ -263,15 +264,34 @@ class _NewSendPageState extends State<NewSendPage> {
_addressFocusNode.addListener(() async {
if (!_addressFocusNode.hasFocus && _addressControllers[_selectedOutput].text.isNotEmpty) {
final output = widget.sendViewModel.outputs[_selectedOutput];
output.fetchParsedAddress(context).then((val) {
if (_addressControllers[_selectedOutput].text != output.extractedAddress) {
_addressControllers[_selectedOutput].text = output.extractedAddress;
}
});
await _resolveAddressForOutput(output);
}
});
}
Future<void> _resolveAddressForOutput(Output output) async {
final result = await widget.sendViewModel.resolveAddressForOutput(output);
if (result == null) return;
final confirmed = await showParsedAddressConfirmationAlert(context, result);
if (confirmed) {
output.applyAddressLookupResult(result);
} else {
output.resetParsedAddress();
return;
}
final outputIndex = widget.sendViewModel.outputs.indexOf(output);
if (outputIndex >= 0 &&
outputIndex < _addressControllers.length &&
output.extractedAddress.isNotEmpty &&
_addressControllers[outputIndex].text != output.extractedAddress) {
_addressControllers[outputIndex].text = output.extractedAddress;
}
}
@override
Widget build(BuildContext context) {
return Observer(builder: (_) {
@@ -377,7 +397,7 @@ class _NewSendPageState extends State<NewSendPage> {
focusNode: _addressFocusNode,
onURIScanned: (uri) async {
output.resetParsedAddress();
await output.fetchParsedAddress(context);
await _resolveAddressForOutput(output);
// Process the payment through the new flow
await _handlePaymentFlow(
@@ -404,7 +424,7 @@ class _NewSendPageState extends State<NewSendPage> {
_justHandledPasteButton = true;
try {
output.resetParsedAddress();
await output.fetchParsedAddress(context);
await _resolveAddressForOutput(output);
final address = output.isParsedAddress
? output.extractedAddress
@@ -1429,3 +1449,28 @@ class SendHelpPage extends StatelessWidget {
);
}
}
Future<bool> showParsedAddressConfirmationAlert(
BuildContext context,
ParsedAddress parsedAddress,
) async {
final confirmed = await showPopUp<bool>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.of(context).address_detected,
headerTitleText: parsedAddress.profileName.isEmpty ? null : parsedAddress.profileName,
headerImageProfileUrl: parsedAddress.profileImageUrl.isEmpty
? parsedAddress.addressSource.iconPath
: parsedAddress.profileImageUrl,
alertContent: S.of(context).extracted_address_content(
'${parsedAddress.handle} (${parsedAddress.addressSource.label})',
),
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop(true),
);
},
);
return confirmed ?? false;
}
+8 -5
View File
@@ -1,8 +1,8 @@
import 'package:cake_wallet/core/address_resolver/address_resolver_service.dart';
import 'package:cake_wallet/core/address_validator.dart';
import 'package:cake_wallet/core/amount_validator.dart';
import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
import 'package:cake_wallet/entities/qr_scanner.dart';
import 'package:cake_wallet/exchange/exchange_trade_state.dart';
import 'package:cake_wallet/exchange/provider/chainflip_exchange_provider.dart';
@@ -23,7 +23,6 @@ import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_args.
import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_sheet.dart';
import 'package:cake_wallet/new-ui/widgets/swap_page/swap_options_page.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/present_provider_picker.dart';
import 'package:cake_wallet/src/screens/send/widgets/extract_address_from_parsed.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/cake_image_widget.dart';
@@ -52,7 +51,7 @@ import 'package:mobx/mobx.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
class NewSwapPage extends StatefulWidget {
NewSwapPage(this.exchangeViewModel, this.authService, this.initialPaymentRequest,
NewSwapPage(this.exchangeViewModel, this.authService,this.adrResService, this.initialPaymentRequest,
{required this.walletSwitcherViewModel, CryptoCurrency? initialCurrency}) {
depositWalletName = exchangeViewModel.depositCurrency == CryptoCurrency.xmr
? exchangeViewModel.wallet.name
@@ -68,6 +67,7 @@ class NewSwapPage extends StatefulWidget {
final ExchangeViewModel exchangeViewModel;
final WalletSwitcherViewModel walletSwitcherViewModel;
final AuthService authService;
final AddressResolverService adrResService;
final PaymentRequest? initialPaymentRequest;
late final String? depositWalletName;
late final String? receiveWalletName;
@@ -423,8 +423,11 @@ class _NewSwapPageState extends State<NewSwapPage> {
Future<String> fetchParsedAddress(
BuildContext context, String domain, CryptoCurrency currency) async {
printV("$domain");
final parsedAddress = await getIt.get<AddressResolver>().resolve(context, domain, currency);
return extractAddressFromParsed(context, parsedAddress);
final parsedAddress = await widget.adrResService.resolve(
query: domain,
wallet: widget.exchangeViewModel.wallet,
currency: currency);
return parsedAddress.isNotEmpty ? parsedAddress.first.parsedAddressByCurrencyMap[currency] ?? '' : '';
}
void _showFeeAlert(BuildContext context) async {
@@ -1,7 +1,7 @@
import 'package:bloc/bloc.dart';
import 'package:bloc_concurrency/bloc_concurrency.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/entities/lnurlpay_record.dart';
import 'package:cake_wallet/core/address_resolver/lnurl_pay/lnurlpay_record.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/wallet_base.dart';
@@ -5,13 +5,12 @@ import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:cake_wallet/utils/address_formatter.dart';
import 'package:cake_wallet/utils/clipboard_util.dart';
import 'package:cake_wallet/view_model/send/send_view_model.dart';
import 'package:cw_core/payment_uris.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import "package:cw_core/wallet_type.dart";
import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
class L2SendExternalModal extends StatefulWidget {
const L2SendExternalModal({super.key, required this.sendViewModel});
@@ -180,11 +179,7 @@ class _L2SendExternalModalState extends State<L2SendExternalModal> {
children: [
Flexible(
child: NewPrimaryButton(
onPressed:(){
Clipboard.setData(
ClipboardData(text: output.address),
);
},
onPressed: () => ClipboardUtil.copyToClipboard(context, output.address),
text: S.of(context).copy,
color: Theme.of(context).colorScheme.primary,
textColor: Theme.of(context).colorScheme.onPrimary),
@@ -227,13 +227,14 @@ class SendTransactionDetails extends StatelessWidget {
decimals: 2)
: sendViewModel.pendingTransactionFeeFiatAmountFormatted;
final showAddress = !sendViewModel.outputs.any((e) =>
RegExp(AddressValidator.bolt11InvoiceMatcher).hasMatch(e.address.toLowerCase()) ||
RegExp(AddressValidator.lnurlMatcher).hasMatch(e.address.toLowerCase()) ||
(e.isParsedAddress &&
e.parsedAddress.addresses.isNotEmpty &&
RegExp(AddressValidator.lnurlMatcher)
.hasMatch(e.parsedAddress.addresses.firstOrNull?.toLowerCase() ?? "")));
final showAddress = !sendViewModel.outputs.any((e) =>
RegExp(AddressValidator.bolt11InvoiceMatcher).hasMatch(e.address.toLowerCase()) ||
RegExp(AddressValidator.lnurlMatcher).hasMatch(e.address.toLowerCase()) ||
(e.isParsedAddress &&
e.parsedAddress.parsedAddressByCurrencyMap[sendViewModel.selectedCryptoCurrency] != null &&
e.parsedAddress.parsedAddressByCurrencyMap[sendViewModel.selectedCryptoCurrency]!.isNotEmpty &&
RegExp(AddressValidator.lnurlMatcher)
.hasMatch(e.parsedAddress.parsedAddressByCurrencyMap[sendViewModel.selectedCryptoCurrency]!.toLowerCase())));
final outputs = sendViewModel.outputs;
@@ -4,11 +4,11 @@ import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
import 'package:cake_wallet/new-ui/widgets/swap_page/swap_modal_header.dart';
import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
import 'package:cake_wallet/utils/address_formatter.dart';
import 'package:cake_wallet/utils/clipboard_util.dart';
import 'package:cake_wallet/view_model/exchange/exchange_trade_view_model.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/payment_uris.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class SwapSendExternalModal extends StatefulWidget {
const SwapSendExternalModal(
@@ -45,7 +45,7 @@ class _SwapSendExternalModalState extends State<SwapSendExternalModal> {
@override
Widget build(BuildContext context) {
if (uri == null) return SizedBox.shrink();
if (widget.address.isEmpty) return SizedBox.shrink();
final resolvedSize = MediaQuery.of(context).size.width * (largeQrMode ? 0.8 : 0.54);
return PopScope(
@@ -139,7 +139,7 @@ class _SwapSendExternalModalState extends State<SwapSendExternalModal> {
child: QrImage(
size: animatedSize,
embeddedImagePath: widget.from.iconPath,
data: uri.toString(),
data: uri?.toString() ?? widget.address,
),
),
);
@@ -175,7 +175,7 @@ class _SwapSendExternalModalState extends State<SwapSendExternalModal> {
children: [
Flexible(
child: NewPrimaryButton(
onPressed: ()=> Clipboard.setData(ClipboardData(text:widget.address)),
onPressed: () => ClipboardUtil.copyToClipboard(context, widget.address),
text: S.of(context).copy,
color: Theme.of(context).colorScheme.primary,
textColor: Theme.of(context).colorScheme.onPrimary),