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

This commit is contained in:
OleksandrSobol 2021-09-20 17:56:27 +03:00
parent 7fd4772121
commit 8955a10ca8
13 changed files with 253 additions and 66 deletions

View file

@ -24,18 +24,20 @@ Future<ParsedAddress> parseAddressFromDomain(
if (domainParts.length <= 1 || domainParts.first.isEmpty || name.isEmpty) { if (domainParts.length <= 1 || domainParts.first.isEmpty || name.isEmpty) {
try { try {
final address = await fetchYatAddress(domain, ticker); final addresses = await fetchYatAddress(domain, ticker);
if (address?.isEmpty ?? true) { if (addresses?.isEmpty ?? true) {
return ParsedAddress(address: domain); return ParsedAddress(
addresses: [domain],
parseFrom: ParseFrom.yatRecord);
} }
return ParsedAddress( return ParsedAddress(
address: address, addresses: addresses,
name: domain, name: domain,
parseFrom: ParseFrom.yatRecord); parseFrom: ParseFrom.yatRecord);
} catch (e) { } catch (e) {
return ParsedAddress(address: domain); return ParsedAddress(addresses: [domain]);
} }
} }
@ -44,11 +46,11 @@ Future<ParsedAddress> parseAddressFromDomain(
await fetchUnstoppableDomainAddress(domain, ticker); await fetchUnstoppableDomainAddress(domain, ticker);
if (address?.isEmpty ?? true) { if (address?.isEmpty ?? true) {
return ParsedAddress(address: domain); return ParsedAddress(addresses: [domain]);
} }
return ParsedAddress( return ParsedAddress(
address: address, addresses: [address],
name: domain, name: domain,
parseFrom: ParseFrom.unstoppableDomains); parseFrom: ParseFrom.unstoppableDomains);
} }
@ -56,16 +58,16 @@ Future<ParsedAddress> parseAddressFromDomain(
final record = await OpenaliasRecord.fetchAddressAndName(formattedName); final record = await OpenaliasRecord.fetchAddressAndName(formattedName);
if (record == null || record.address.contains(formattedName)) { if (record == null || record.address.contains(formattedName)) {
return ParsedAddress(address: domain); return ParsedAddress(addresses: [domain]);
} }
return ParsedAddress( return ParsedAddress(
address: record.address, addresses: [record.address],
name: record.name, name: record.name,
parseFrom: ParseFrom.openAlias); parseFrom: ParseFrom.openAlias);
} catch (e) { } catch (e) {
print(e.toString()); print(e.toString());
} }
return ParsedAddress(address: domain); return ParsedAddress(addresses: [domain]);
} }

View file

@ -2,11 +2,11 @@ enum ParseFrom {unstoppableDomains, openAlias, yatRecord, notParsed}
class ParsedAddress { class ParsedAddress {
ParsedAddress({ ParsedAddress({
this.address = '', this.addresses,
this.name = '', this.name = '',
this.parseFrom = ParseFrom.notParsed}); this.parseFrom = ParseFrom.notParsed});
final String address; final List<String> addresses;
final String name; final String name;
final ParseFrom parseFrom; final ParseFrom parseFrom;
} }

View file

@ -232,7 +232,7 @@ class ExchangePage extends BasePage {
final ticker = exchangeViewModel final ticker = exchangeViewModel
.depositCurrency.title.toLowerCase(); .depositCurrency.title.toLowerCase();
exchangeViewModel.depositAddress = exchangeViewModel.depositAddress =
await applyOpenaliasOrUnstoppableDomains( await fetchParsedAddress(
context, domain, ticker); context, domain, ticker);
}, },
), ),
@ -288,7 +288,7 @@ class ExchangePage extends BasePage {
final ticker = exchangeViewModel final ticker = exchangeViewModel
.receiveCurrency.title.toLowerCase(); .receiveCurrency.title.toLowerCase();
exchangeViewModel.receiveAddress = exchangeViewModel.receiveAddress =
await applyOpenaliasOrUnstoppableDomains( await fetchParsedAddress(
context, domain, ticker); context, domain, ticker);
}, },
)), )),
@ -518,12 +518,12 @@ class ExchangePage extends BasePage {
var domain = template.depositAddress; var domain = template.depositAddress;
var ticker = template.depositCurrency.toLowerCase(); var ticker = template.depositCurrency.toLowerCase();
exchangeViewModel.depositAddress = exchangeViewModel.depositAddress =
await applyOpenaliasOrUnstoppableDomains(context, domain, ticker); await fetchParsedAddress(context, domain, ticker);
domain = template.receiveAddress; domain = template.receiveAddress;
ticker = template.receiveCurrency.toLowerCase(); ticker = template.receiveCurrency.toLowerCase();
exchangeViewModel.receiveAddress = exchangeViewModel.receiveAddress =
await applyOpenaliasOrUnstoppableDomains(context, domain, ticker); await fetchParsedAddress(context, domain, ticker);
} }
void _setReactions( void _setReactions(
@ -696,7 +696,7 @@ class ExchangePage extends BasePage {
final domain = depositAddressController.text; final domain = depositAddressController.text;
final ticker = exchangeViewModel.depositCurrency.title.toLowerCase(); final ticker = exchangeViewModel.depositCurrency.title.toLowerCase();
exchangeViewModel.depositAddress = 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 domain = receiveAddressController.text;
final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase(); final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase();
exchangeViewModel.receiveAddress = exchangeViewModel.receiveAddress =
await applyOpenaliasOrUnstoppableDomains(context, domain, ticker); await fetchParsedAddress(context, domain, ticker);
} }
}); });
@ -782,12 +782,10 @@ class ExchangePage extends BasePage {
} }
} }
Future<String> applyOpenaliasOrUnstoppableDomains( Future<String> fetchParsedAddress(
BuildContext context, String domain, String ticker) async { BuildContext context, String domain, String ticker) async {
final parsedAddress = await parseAddressFromDomain(domain, ticker); final parsedAddress = await parseAddressFromDomain(domain, ticker);
final address = await defineAddress(context, parsedAddress);
showAddressAlert(context, parsedAddress); return address;
return parsedAddress.address;
} }
} }

View file

@ -215,9 +215,8 @@ class SendPage extends BasePage {
output.address = output.address =
template.address; template.address;
output.setCryptoAmount(template.amount); output.setCryptoAmount(template.amount);
final parsedAddress = await output output.resetParsedAddress();
.applyOpenaliasOrUnstoppableDomains(); await output.fetchParsedAddress(context);
showAddressAlert(context, parsedAddress);
}, },
onRemove: () { onRemove: () {
showPopUp<void>( showPopUp<void>(

View file

@ -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<String> 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<String>(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,
),
)
)
],
)
),
);
})
);
}
}

View file

@ -1,3 +1,4 @@
import 'package:cake_wallet/entities/parsed_address.dart';
import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/view_model/send/output.dart'; import 'package:cake_wallet/view_model/send/output.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -276,15 +277,32 @@ class ConfirmSendingAlertContentState extends State<ConfirmSendingAlertContent>
final _address = item.address; final _address = item.address;
final _amount = final _amount =
item.cryptoAmount.replaceAll(',', '.'); item.cryptoAmount.replaceAll(',', '.');
final isParsedAddress =
item.parsedAddress.parseFrom !=
ParseFrom.notParsed;
return Column( return Column(
children: [ 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(
padding: EdgeInsets.only(top: 8), padding: EdgeInsets.only(top: 8),
child: Text( child: Text(
_address, _address,
style: TextStyle( style: TextStyle(
fontSize: 12, fontSize: 10,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
fontFamily: 'Lato', fontFamily: 'Lato',
color: PaletteDark.pigeonBlue, color: PaletteDark.pigeonBlue,
@ -301,7 +319,7 @@ class ConfirmSendingAlertContentState extends State<ConfirmSendingAlertContent>
Text( Text(
_amount, _amount,
style: TextStyle( style: TextStyle(
fontSize: 12, fontSize: 10,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
fontFamily: 'Lato', fontFamily: 'Lato',
color: PaletteDark.pigeonBlue, color: PaletteDark.pigeonBlue,
@ -314,18 +332,37 @@ class ConfirmSendingAlertContentState extends State<ConfirmSendingAlertContent>
], ],
); );
}) })
: Padding( : Column(
padding: EdgeInsets.only(top: 8), children: [
child: Text( if (outputs.first.parsedAddress.parseFrom !=
outputs.first.address, ParseFrom.notParsed) Padding(
style: TextStyle( padding: EdgeInsets.only(top: 8),
fontSize: 12, child: Text(
fontWeight: FontWeight.w600, outputs.first.parsedAddress.name,
fontFamily: 'Lato', textAlign: TextAlign.center,
color: PaletteDark.pigeonBlue, style: TextStyle(
decoration: TextDecoration.none, 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,
),
)
),
]
) )
], ],
), ),

View file

@ -4,25 +4,64 @@ import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
void showAddressAlert(BuildContext context, ParsedAddress parsedAddress) async { import 'choose_yat_address_alert.dart';
Future<String> defineAddress(
BuildContext context,
ParsedAddress parsedAddress) async {
var title = ''; var title = '';
var content = ''; var content = '';
var address = '';
switch (parsedAddress.parseFrom) { switch (parsedAddress.parseFrom) {
case ParseFrom.unstoppableDomains: case ParseFrom.unstoppableDomains:
title = S.of(context).address_detected; title = S.of(context).address_detected;
content = S.of(context).address_from_domain(parsedAddress.name); content = S.of(context).address_from_domain(parsedAddress.name);
address = parsedAddress.addresses.first;
break; break;
case ParseFrom.openAlias: case ParseFrom.openAlias:
title = S.of(context).openalias_alert_title; title = S.of(context).openalias_alert_title;
content = S.of(context).openalias_alert_content(parsedAddress.name); content = S.of(context).openalias_alert_content(parsedAddress.name);
address = parsedAddress.addresses.first;
break; break;
case ParseFrom.yatRecord: 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; title = S.of(context).address_detected;
content = S.of(context).address_from_yat(parsedAddress.name); 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<String>(
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: case ParseFrom.notParsed:
return; address = parsedAddress.addresses.first;
return address;
} }
await showPopUp<void>( await showPopUp<void>(
@ -35,4 +74,6 @@ void showAddressAlert(BuildContext context, ParsedAddress parsedAddress) async {
buttonText: S.of(context).ok, buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop()); buttonAction: () => Navigator.of(context).pop());
}); });
return address;
} }

View file

@ -147,10 +147,11 @@ class SendCardState extends State<SendCard>
.headline .headline
.decorationColor), .decorationColor),
onPushPasteButton: (context) async { onPushPasteButton: (context) async {
final parsedAddress = output.resetParsedAddress();
await output.applyOpenaliasOrUnstoppableDomains(); await output.fetchParsedAddress(context);
showAddressAlert(context, parsedAddress);
}, },
onPushAddressBookButton: (context) =>
output.resetParsedAddress(),
validator: sendViewModel.addressValidator, validator: sendViewModel.addressValidator,
), ),
Observer( Observer(
@ -531,8 +532,8 @@ class SendCardState extends State<SendCard>
addressFocusNode.addListener(() async { addressFocusNode.addListener(() async {
if (!addressFocusNode.hasFocus && addressController.text.isNotEmpty) { if (!addressFocusNode.hasFocus && addressController.text.isNotEmpty) {
final parsedAddress = await output.applyOpenaliasOrUnstoppableDomains(); output.resetParsedAddress();
showAddressAlert(context, parsedAddress); await output.fetchParsedAddress(context);
} }
}); });

View file

@ -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/screens/yat/widgets/yat_bar.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';
@ -8,16 +10,20 @@ import 'package:url_launcher/url_launcher.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
class YatAlert extends StatelessWidget { class YatAlert extends StatelessWidget {
YatAlert({this.isYatDevMode = false}) YatAlert({@required this.wallet, this.isYatDevMode = false})
: baseUrl = isYatDevMode ? _baseDevUrl : _baseReleaseUrl; : baseUrl = isYatDevMode ? _baseDevUrl : _baseReleaseUrl,
address = wallet.walletAddresses.address;
final WalletBase wallet;
final bool isYatDevMode; final bool isYatDevMode;
final String address;
final String baseUrl; final String baseUrl;
static const aspectRatioImage = 1.133; static const aspectRatioImage = 1.133;
static const _baseDevUrl = 'https://yat.fyi'; static const _baseDevUrl = 'https://yat.fyi';
static const _baseReleaseUrl = 'https://y.at'; static const _baseReleaseUrl = 'https://y.at';
static const _signInSuffix = '/partner/CW/link-email'; static const _signInSuffix = '/partner/CW/link-email';
static const _createSuffix = '/create'; static const _createSuffix = '/create';
static const _queryParameter = '?addresses=';
final image = Image.asset('assets/images/yat_crypto.png'); final image = Image.asset('assets/images/yat_crypto.png');
@override @override
@ -107,7 +113,8 @@ class YatAlert extends StatelessWidget {
.arrow_up_right_square, .arrow_up_right_square,
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
onPressed: () { onPressed: () {
final url = baseUrl + _signInSuffix; final url = baseUrl + _signInSuffix + _queryParameter +
_defineTag() + '%3D' + address;
launch(url); 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;
}
} }

View file

@ -25,7 +25,8 @@ class AddressTextField extends StatelessWidget {
this.textStyle, this.textStyle,
this.hintStyle, this.hintStyle,
this.validator, this.validator,
this.onPushPasteButton}); this.onPushPasteButton,
this.onPushAddressBookButton});
static const prefixIconWidth = 34.0; static const prefixIconWidth = 34.0;
static const prefixIconHeight = 34.0; static const prefixIconHeight = 34.0;
@ -45,6 +46,7 @@ class AddressTextField extends StatelessWidget {
final TextStyle hintStyle; final TextStyle hintStyle;
final FocusNode focusNode; final FocusNode focusNode;
final Function(BuildContext context) onPushPasteButton; final Function(BuildContext context) onPushPasteButton;
final Function(BuildContext context) onPushAddressBookButton;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -216,6 +218,7 @@ class AddressTextField extends StatelessWidget {
if (contact is ContactBase && contact.address != null) { if (contact is ContactBase && contact.address != null) {
controller.text = contact.address; controller.text = contact.address;
onPushAddressBookButton?.call(context);
} }
} }

View file

@ -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/parse_address_from_domain.dart';
import 'package:cake_wallet/entities/parsed_address.dart'; import 'package:cake_wallet/entities/parsed_address.dart';
import 'package:cake_wallet/monero/monero_amount_format.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:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
@ -46,6 +47,8 @@ abstract class OutputBase with Store {
@observable @observable
bool sendAll; bool sendAll;
ParsedAddress parsedAddress;
@computed @computed
int get formattedCryptoAmount { int get formattedCryptoAmount {
int amount = 0; int amount = 0;
@ -127,6 +130,11 @@ abstract class OutputBase with Store {
fiatAmount = ''; fiatAmount = '';
address = ''; address = '';
note = ''; note = '';
resetParsedAddress();
}
void resetParsedAddress() {
parsedAddress = ParsedAddress(addresses: []);
} }
@action @action
@ -194,13 +202,10 @@ abstract class OutputBase with Store {
_cryptoNumberFormat.maximumFractionDigits = maximumFractionDigits; _cryptoNumberFormat.maximumFractionDigits = maximumFractionDigits;
} }
Future<ParsedAddress> applyOpenaliasOrUnstoppableDomains() async { Future<void> fetchParsedAddress(BuildContext context) async {
final domain = address; final domain = address;
final ticker = _wallet.currency.title.toLowerCase(); final ticker = _wallet.currency.title.toLowerCase();
final parsedAddress = await parseAddressFromDomain(domain, ticker); parsedAddress = await parseAddressFromDomain(domain, ticker);
address = await defineAddress(context, parsedAddress);
address = parsedAddress.address;
return parsedAddress;
} }
} }

View file

@ -162,7 +162,7 @@ abstract class SettingsViewModelBase with Store {
await showPopUp<void>( await showPopUp<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return YatAlert(isYatDevMode: true); return YatAlert(wallet: wallet, isYatDevMode: true);
}); });
}, },
), ),

View file

@ -2,9 +2,8 @@ import 'dart:convert';
import 'package:cake_wallet/yat/yat_exception.dart'; import 'package:cake_wallet/yat/yat_exception.dart';
import 'package:http/http.dart'; import 'package:http/http.dart';
Future<String> fetchYatAddress(String emojiId, String ticker) async { Future<List<String>> fetchYatAddress(String emojiId, String ticker) async {
const _requestURL = 'https://a.y.at/emoji_id/'; const _requestURL = 'https://a.y.at/emoji_id/';
final url = _requestURL + emojiId + '/' + ticker.toUpperCase(); final url = _requestURL + emojiId + '/' + ticker.toUpperCase();
final response = await get(url); final response = await get(url);
@ -16,14 +15,17 @@ Future<String> fetchYatAddress(String emojiId, String ticker) async {
final result = responseJSON['result'] as List<dynamic>; final result = responseJSON['result'] as List<dynamic>;
if (result?.isEmpty ?? true) { if (result?.isEmpty ?? true) {
return ''; return [];
} }
final yatAddress = result.first['data'] as String; final List<String> addresses = [];
if (yatAddress?.isEmpty ?? true) { for (var elem in result) {
return ''; final yatAddress = elem['data'] as String;
if (yatAddress?.isNotEmpty ?? false) {
addresses.add(yatAddress);
}
} }
return yatAddress; return addresses;
} }