From 8955a10ca804895b6392c15a3431032255169eda Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Mon, 20 Sep 2021 17:56:27 +0300 Subject: [PATCH] CAKE-359 | fixed yat_record.dart for multiple addresses in yat; fixed ParsedAddress class; applied ParsedAddress in the parse_address_from_domain_alert.dart; added parsedAddress and renamed applyOpenaliasOrUnstoppableDomains() to fetchParsedAddress() in the output.dart; fixed send_page.dart, send_card.dart and exchange_page.dart; added choose_yat_address_alert.dart to the app; added current wallet address to request parameters in the yat_alert.dart --- lib/entities/parse_address_from_domain.dart | 22 +++--- lib/entities/parsed_address.dart | 4 +- lib/src/screens/exchange/exchange_page.dart | 20 +++--- lib/src/screens/send/send_page.dart | 5 +- .../widgets/choose_yat_address_alert.dart | 72 +++++++++++++++++++ .../send/widgets/confirm_sending_alert.dart | 65 +++++++++++++---- .../parse_address_from_domain_alert.dart | 47 +++++++++++- lib/src/screens/send/widgets/send_card.dart | 11 +-- lib/src/screens/yat/yat_alert.dart | 33 ++++++++- lib/src/widgets/address_text_field.dart | 5 +- lib/view_model/send/output.dart | 17 +++-- .../settings/settings_view_model.dart | 2 +- lib/yat/yat_record.dart | 16 +++-- 13 files changed, 253 insertions(+), 66 deletions(-) create mode 100644 lib/src/screens/send/widgets/choose_yat_address_alert.dart diff --git a/lib/entities/parse_address_from_domain.dart b/lib/entities/parse_address_from_domain.dart index f30cb1f5d..3041c4709 100644 --- a/lib/entities/parse_address_from_domain.dart +++ b/lib/entities/parse_address_from_domain.dart @@ -24,18 +24,20 @@ Future parseAddressFromDomain( if (domainParts.length <= 1 || domainParts.first.isEmpty || name.isEmpty) { try { - final address = await fetchYatAddress(domain, ticker); + final addresses = await fetchYatAddress(domain, ticker); - if (address?.isEmpty ?? true) { - return ParsedAddress(address: domain); + if (addresses?.isEmpty ?? true) { + return ParsedAddress( + addresses: [domain], + parseFrom: ParseFrom.yatRecord); } return ParsedAddress( - address: address, + addresses: addresses, name: domain, parseFrom: ParseFrom.yatRecord); } catch (e) { - return ParsedAddress(address: domain); + return ParsedAddress(addresses: [domain]); } } @@ -44,11 +46,11 @@ Future parseAddressFromDomain( await fetchUnstoppableDomainAddress(domain, ticker); if (address?.isEmpty ?? true) { - return ParsedAddress(address: domain); + return ParsedAddress(addresses: [domain]); } return ParsedAddress( - address: address, + addresses: [address], name: domain, parseFrom: ParseFrom.unstoppableDomains); } @@ -56,16 +58,16 @@ Future parseAddressFromDomain( final record = await OpenaliasRecord.fetchAddressAndName(formattedName); if (record == null || record.address.contains(formattedName)) { - return ParsedAddress(address: domain); + return ParsedAddress(addresses: [domain]); } return ParsedAddress( - address: record.address, + addresses: [record.address], name: record.name, parseFrom: ParseFrom.openAlias); } catch (e) { print(e.toString()); } - return ParsedAddress(address: domain); + return ParsedAddress(addresses: [domain]); } \ No newline at end of file diff --git a/lib/entities/parsed_address.dart b/lib/entities/parsed_address.dart index 192d839b5..ae15ce7ea 100644 --- a/lib/entities/parsed_address.dart +++ b/lib/entities/parsed_address.dart @@ -2,11 +2,11 @@ enum ParseFrom {unstoppableDomains, openAlias, yatRecord, notParsed} class ParsedAddress { ParsedAddress({ - this.address = '', + this.addresses, this.name = '', this.parseFrom = ParseFrom.notParsed}); - final String address; + final List addresses; final String name; final ParseFrom parseFrom; } \ No newline at end of file diff --git a/lib/src/screens/exchange/exchange_page.dart b/lib/src/screens/exchange/exchange_page.dart index 3f44f62d7..e5494bf79 100644 --- a/lib/src/screens/exchange/exchange_page.dart +++ b/lib/src/screens/exchange/exchange_page.dart @@ -232,7 +232,7 @@ class ExchangePage extends BasePage { final ticker = exchangeViewModel .depositCurrency.title.toLowerCase(); exchangeViewModel.depositAddress = - await applyOpenaliasOrUnstoppableDomains( + await fetchParsedAddress( context, domain, ticker); }, ), @@ -288,7 +288,7 @@ class ExchangePage extends BasePage { final ticker = exchangeViewModel .receiveCurrency.title.toLowerCase(); exchangeViewModel.receiveAddress = - await applyOpenaliasOrUnstoppableDomains( + await fetchParsedAddress( context, domain, ticker); }, )), @@ -518,12 +518,12 @@ class ExchangePage extends BasePage { var domain = template.depositAddress; var ticker = template.depositCurrency.toLowerCase(); exchangeViewModel.depositAddress = - await applyOpenaliasOrUnstoppableDomains(context, domain, ticker); + await fetchParsedAddress(context, domain, ticker); domain = template.receiveAddress; ticker = template.receiveCurrency.toLowerCase(); exchangeViewModel.receiveAddress = - await applyOpenaliasOrUnstoppableDomains(context, domain, ticker); + await fetchParsedAddress(context, domain, ticker); } void _setReactions( @@ -696,7 +696,7 @@ class ExchangePage extends BasePage { final domain = depositAddressController.text; final ticker = exchangeViewModel.depositCurrency.title.toLowerCase(); exchangeViewModel.depositAddress = - await applyOpenaliasOrUnstoppableDomains(context, domain, ticker); + await fetchParsedAddress(context, domain, ticker); } }); @@ -706,7 +706,7 @@ class ExchangePage extends BasePage { final domain = receiveAddressController.text; final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase(); exchangeViewModel.receiveAddress = - await applyOpenaliasOrUnstoppableDomains(context, domain, ticker); + await fetchParsedAddress(context, domain, ticker); } }); @@ -782,12 +782,10 @@ class ExchangePage extends BasePage { } } - Future applyOpenaliasOrUnstoppableDomains( + Future fetchParsedAddress( BuildContext context, String domain, String ticker) async { final parsedAddress = await parseAddressFromDomain(domain, ticker); - - showAddressAlert(context, parsedAddress); - - return parsedAddress.address; + final address = await defineAddress(context, parsedAddress); + return address; } } diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index c355e099b..21a56fafe 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -215,9 +215,8 @@ class SendPage extends BasePage { output.address = template.address; output.setCryptoAmount(template.amount); - final parsedAddress = await output - .applyOpenaliasOrUnstoppableDomains(); - showAddressAlert(context, parsedAddress); + output.resetParsedAddress(); + await output.fetchParsedAddress(context); }, onRemove: () { showPopUp( diff --git a/lib/src/screens/send/widgets/choose_yat_address_alert.dart b/lib/src/screens/send/widgets/choose_yat_address_alert.dart new file mode 100644 index 000000000..64020a200 --- /dev/null +++ b/lib/src/screens/send/widgets/choose_yat_address_alert.dart @@ -0,0 +1,72 @@ +import 'package:flutter/material.dart'; +import 'package:cake_wallet/src/widgets/base_alert_dialog.dart'; + +class ChooseYatAddressAlert extends BaseAlertDialog { + ChooseYatAddressAlert({ + @required this.alertTitle, + @required this.alertContent, + @required this.addresses, + }); + + final String alertTitle; + final String alertContent; + final List addresses; + + @override + String get titleText => alertTitle; + + @override + String get contentText => alertContent; + + @override + bool get barrierDismissible => false; + + @override + Widget actionButtons(BuildContext context) { + return Container( + width: 300, + height: 105, + color: Theme.of(context).accentTextTheme.body1.backgroundColor, + child: ListView.separated( + padding: EdgeInsets.all(0), + itemCount: addresses.length, + separatorBuilder: (_, __) => Container( + height: 1, + color: Theme.of(context).dividerColor, + ), + itemBuilder: (context, index) { + final address = addresses[index]; + + return GestureDetector( + onTap: () => Navigator.of(context).pop(address), + child: Container( + width: 300, + height: 52, + padding: EdgeInsets.only(left: 24, right: 24), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Text( + address, + textAlign: TextAlign.center, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + fontFamily: 'Lato', + color: Theme.of(context).primaryTextTheme.title.color, + decoration: TextDecoration.none, + ), + ) + ) + ], + ) + ), + ); + }) + ); + } +} \ No newline at end of file diff --git a/lib/src/screens/send/widgets/confirm_sending_alert.dart b/lib/src/screens/send/widgets/confirm_sending_alert.dart index 009340b2e..91220a148 100644 --- a/lib/src/screens/send/widgets/confirm_sending_alert.dart +++ b/lib/src/screens/send/widgets/confirm_sending_alert.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/entities/parsed_address.dart'; import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/view_model/send/output.dart'; import 'package:flutter/material.dart'; @@ -276,15 +277,32 @@ class ConfirmSendingAlertContentState extends State final _address = item.address; final _amount = item.cryptoAmount.replaceAll(',', '.'); + final isParsedAddress = + item.parsedAddress.parseFrom != + ParseFrom.notParsed; return Column( children: [ + if (isParsedAddress) Padding( + padding: EdgeInsets.only(top: 8), + child: Text( + item.parsedAddress.name, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + fontFamily: 'Lato', + color: PaletteDark.pigeonBlue, + decoration: TextDecoration.none, + ), + ) + ), Padding( padding: EdgeInsets.only(top: 8), child: Text( _address, style: TextStyle( - fontSize: 12, + fontSize: 10, fontWeight: FontWeight.w600, fontFamily: 'Lato', color: PaletteDark.pigeonBlue, @@ -301,7 +319,7 @@ class ConfirmSendingAlertContentState extends State Text( _amount, style: TextStyle( - fontSize: 12, + fontSize: 10, fontWeight: FontWeight.w600, fontFamily: 'Lato', color: PaletteDark.pigeonBlue, @@ -314,18 +332,37 @@ class ConfirmSendingAlertContentState extends State ], ); }) - : Padding( - padding: EdgeInsets.only(top: 8), - child: Text( - outputs.first.address, - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: 'Lato', - color: PaletteDark.pigeonBlue, - decoration: TextDecoration.none, - ), - ), + : Column( + children: [ + if (outputs.first.parsedAddress.parseFrom != + ParseFrom.notParsed) Padding( + padding: EdgeInsets.only(top: 8), + child: Text( + outputs.first.parsedAddress.name, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + fontFamily: 'Lato', + color: PaletteDark.pigeonBlue, + decoration: TextDecoration.none, + ), + ) + ), + Padding( + padding: EdgeInsets.only(top: 8), + child: Text( + outputs.first.address, + style: TextStyle( + fontSize: 10, + fontWeight: FontWeight.w600, + fontFamily: 'Lato', + color: PaletteDark.pigeonBlue, + decoration: TextDecoration.none, + ), + ) + ), + ] ) ], ), diff --git a/lib/src/screens/send/widgets/parse_address_from_domain_alert.dart b/lib/src/screens/send/widgets/parse_address_from_domain_alert.dart index 9f16a2754..6fa0c4d0e 100644 --- a/lib/src/screens/send/widgets/parse_address_from_domain_alert.dart +++ b/lib/src/screens/send/widgets/parse_address_from_domain_alert.dart @@ -4,25 +4,64 @@ import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/generated/i18n.dart'; -void showAddressAlert(BuildContext context, ParsedAddress parsedAddress) async { +import 'choose_yat_address_alert.dart'; + +Future defineAddress( + BuildContext context, + ParsedAddress parsedAddress) async { var title = ''; var content = ''; + var address = ''; switch (parsedAddress.parseFrom) { case ParseFrom.unstoppableDomains: title = S.of(context).address_detected; content = S.of(context).address_from_domain(parsedAddress.name); + address = parsedAddress.addresses.first; break; case ParseFrom.openAlias: title = S.of(context).openalias_alert_title; content = S.of(context).openalias_alert_content(parsedAddress.name); + address = parsedAddress.addresses.first; break; case ParseFrom.yatRecord: + if (parsedAddress.name.isEmpty) { + title = 'Yat error'; + content = 'No addresses linked with this Yat. Try another Yat'; + address = parsedAddress.addresses.first; + break; + } + title = S.of(context).address_detected; content = S.of(context).address_from_yat(parsedAddress.name); - break; + + if (parsedAddress.addresses.length == 1) { + address = parsedAddress.addresses.first; + break; + } + + content += '\nPlease choose the address:'; + + address = await showPopUp( + context: context, + builder: (BuildContext context) { + + return WillPopScope( + child: ChooseYatAddressAlert( + alertTitle: title, + alertContent: content, + addresses: parsedAddress.addresses), + onWillPop: () async => false); + }); + + if (address?.isEmpty ?? true) { + return parsedAddress.name; + } + + return address; case ParseFrom.notParsed: - return; + address = parsedAddress.addresses.first; + return address; } await showPopUp( @@ -35,4 +74,6 @@ void showAddressAlert(BuildContext context, ParsedAddress parsedAddress) async { buttonText: S.of(context).ok, buttonAction: () => Navigator.of(context).pop()); }); + + return address; } \ No newline at end of file diff --git a/lib/src/screens/send/widgets/send_card.dart b/lib/src/screens/send/widgets/send_card.dart index b33f29724..ad32b2905 100644 --- a/lib/src/screens/send/widgets/send_card.dart +++ b/lib/src/screens/send/widgets/send_card.dart @@ -147,10 +147,11 @@ class SendCardState extends State .headline .decorationColor), onPushPasteButton: (context) async { - final parsedAddress = - await output.applyOpenaliasOrUnstoppableDomains(); - showAddressAlert(context, parsedAddress); + output.resetParsedAddress(); + await output.fetchParsedAddress(context); }, + onPushAddressBookButton: (context) => + output.resetParsedAddress(), validator: sendViewModel.addressValidator, ), Observer( @@ -531,8 +532,8 @@ class SendCardState extends State addressFocusNode.addListener(() async { if (!addressFocusNode.hasFocus && addressController.text.isNotEmpty) { - final parsedAddress = await output.applyOpenaliasOrUnstoppableDomains(); - showAddressAlert(context, parsedAddress); + output.resetParsedAddress(); + await output.fetchParsedAddress(context); } }); diff --git a/lib/src/screens/yat/yat_alert.dart b/lib/src/screens/yat/yat_alert.dart index 1fece7709..699444db1 100644 --- a/lib/src/screens/yat/yat_alert.dart +++ b/lib/src/screens/yat/yat_alert.dart @@ -1,3 +1,5 @@ +import 'package:cake_wallet/core/wallet_base.dart'; +import 'package:cake_wallet/entities/wallet_type.dart'; import 'package:cake_wallet/src/screens/yat/widgets/yat_bar.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; @@ -8,16 +10,20 @@ import 'package:url_launcher/url_launcher.dart'; import 'package:cake_wallet/generated/i18n.dart'; class YatAlert extends StatelessWidget { - YatAlert({this.isYatDevMode = false}) - : baseUrl = isYatDevMode ? _baseDevUrl : _baseReleaseUrl; + YatAlert({@required this.wallet, this.isYatDevMode = false}) + : baseUrl = isYatDevMode ? _baseDevUrl : _baseReleaseUrl, + address = wallet.walletAddresses.address; + final WalletBase wallet; final bool isYatDevMode; + final String address; final String baseUrl; static const aspectRatioImage = 1.133; static const _baseDevUrl = 'https://yat.fyi'; static const _baseReleaseUrl = 'https://y.at'; static const _signInSuffix = '/partner/CW/link-email'; static const _createSuffix = '/create'; + static const _queryParameter = '?addresses='; final image = Image.asset('assets/images/yat_crypto.png'); @override @@ -107,7 +113,8 @@ class YatAlert extends StatelessWidget { .arrow_up_right_square, mainAxisAlignment: MainAxisAlignment.end, onPressed: () { - final url = baseUrl + _signInSuffix; + final url = baseUrl + _signInSuffix + _queryParameter + + _defineTag() + '%3D' + address; launch(url); }) ) @@ -116,4 +123,24 @@ class YatAlert extends StatelessWidget { ) ); } + + String _defineTag() { + String tag; + switch (wallet.type) { + case WalletType.monero: + tag = address.startsWith('4') + ? '0x1001' + : '0x1002'; + break; + case WalletType.bitcoin: + tag = '0x1003'; + break; + case WalletType.litecoin: + tag = '0x3fff'; + break; + default: + tag = '0x3fff'; + } + return tag; + } } \ No newline at end of file diff --git a/lib/src/widgets/address_text_field.dart b/lib/src/widgets/address_text_field.dart index 130912a2f..fbdc3ed54 100644 --- a/lib/src/widgets/address_text_field.dart +++ b/lib/src/widgets/address_text_field.dart @@ -25,7 +25,8 @@ class AddressTextField extends StatelessWidget { this.textStyle, this.hintStyle, this.validator, - this.onPushPasteButton}); + this.onPushPasteButton, + this.onPushAddressBookButton}); static const prefixIconWidth = 34.0; static const prefixIconHeight = 34.0; @@ -45,6 +46,7 @@ class AddressTextField extends StatelessWidget { final TextStyle hintStyle; final FocusNode focusNode; final Function(BuildContext context) onPushPasteButton; + final Function(BuildContext context) onPushAddressBookButton; @override Widget build(BuildContext context) { @@ -216,6 +218,7 @@ class AddressTextField extends StatelessWidget { if (contact is ContactBase && contact.address != null) { controller.text = contact.address; + onPushAddressBookButton?.call(context); } } diff --git a/lib/view_model/send/output.dart b/lib/view_model/send/output.dart index e47956d7c..85f97b97b 100644 --- a/lib/view_model/send/output.dart +++ b/lib/view_model/send/output.dart @@ -4,6 +4,7 @@ import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart'; import 'package:cake_wallet/entities/parse_address_from_domain.dart'; import 'package:cake_wallet/entities/parsed_address.dart'; import 'package:cake_wallet/monero/monero_amount_format.dart'; +import 'package:cake_wallet/src/screens/send/widgets/parse_address_from_domain_alert.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:mobx/mobx.dart'; @@ -46,6 +47,8 @@ abstract class OutputBase with Store { @observable bool sendAll; + ParsedAddress parsedAddress; + @computed int get formattedCryptoAmount { int amount = 0; @@ -127,6 +130,11 @@ abstract class OutputBase with Store { fiatAmount = ''; address = ''; note = ''; + resetParsedAddress(); + } + + void resetParsedAddress() { + parsedAddress = ParsedAddress(addresses: []); } @action @@ -194,13 +202,10 @@ abstract class OutputBase with Store { _cryptoNumberFormat.maximumFractionDigits = maximumFractionDigits; } - Future applyOpenaliasOrUnstoppableDomains() async { + Future fetchParsedAddress(BuildContext context) async { final domain = address; final ticker = _wallet.currency.title.toLowerCase(); - final parsedAddress = await parseAddressFromDomain(domain, ticker); - - address = parsedAddress.address; - - return parsedAddress; + parsedAddress = await parseAddressFromDomain(domain, ticker); + address = await defineAddress(context, parsedAddress); } } \ No newline at end of file diff --git a/lib/view_model/settings/settings_view_model.dart b/lib/view_model/settings/settings_view_model.dart index 93e0926b6..f517ce644 100644 --- a/lib/view_model/settings/settings_view_model.dart +++ b/lib/view_model/settings/settings_view_model.dart @@ -162,7 +162,7 @@ abstract class SettingsViewModelBase with Store { await showPopUp( context: context, builder: (BuildContext context) { - return YatAlert(isYatDevMode: true); + return YatAlert(wallet: wallet, isYatDevMode: true); }); }, ), diff --git a/lib/yat/yat_record.dart b/lib/yat/yat_record.dart index 8173d99ae..5ea2dae2c 100644 --- a/lib/yat/yat_record.dart +++ b/lib/yat/yat_record.dart @@ -2,9 +2,8 @@ import 'dart:convert'; import 'package:cake_wallet/yat/yat_exception.dart'; import 'package:http/http.dart'; -Future fetchYatAddress(String emojiId, String ticker) async { +Future> fetchYatAddress(String emojiId, String ticker) async { const _requestURL = 'https://a.y.at/emoji_id/'; - final url = _requestURL + emojiId + '/' + ticker.toUpperCase(); final response = await get(url); @@ -16,14 +15,17 @@ Future fetchYatAddress(String emojiId, String ticker) async { final result = responseJSON['result'] as List; if (result?.isEmpty ?? true) { - return ''; + return []; } - final yatAddress = result.first['data'] as String; + final List addresses = []; - if (yatAddress?.isEmpty ?? true) { - return ''; + for (var elem in result) { + final yatAddress = elem['data'] as String; + if (yatAddress?.isNotEmpty ?? false) { + addresses.add(yatAddress); + } } - return yatAddress; + return addresses; } \ No newline at end of file