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
OleksandrSobol committed
Sep 20, 2021 at 17:56 UTC
8955a10ca804895b6392c15a3431032255169eda
13 files changed
+253
-66
lib/entities/parse_address_from_domain.dart
+12
-10
@@ -24,18 +24,20 @@ Future<ParsedAddress> parseAddressFromDomain(
24
25
if (domainParts.length <= 1 || domainParts.first.isEmpty || name.isEmpty) {
26
try {
27
- final address = await fetchYatAddress(domain, ticker);
27
+ final addresses = await fetchYatAddress(domain, ticker);
28
29
- if (address?.isEmpty ?? true) {
30
- return ParsedAddress(address: domain);
29
+ if (addresses?.isEmpty ?? true) {
30
+ return ParsedAddress(
31
+ addresses: [domain],
32
+ parseFrom: ParseFrom.yatRecord);
33
}
34
35
return ParsedAddress(
34
- address: address,
36
+ addresses: addresses,
37
name: domain,
38
parseFrom: ParseFrom.yatRecord);
39
} catch (e) {
38
- return ParsedAddress(address: domain);
40
+ return ParsedAddress(addresses: [domain]);
41
}
42
}
43
@@ -44,11 +46,11 @@ Future<ParsedAddress> parseAddressFromDomain(
46
await fetchUnstoppableDomainAddress(domain, ticker);
47
48
if (address?.isEmpty ?? true) {
47
- return ParsedAddress(address: domain);
49
+ return ParsedAddress(addresses: [domain]);
50
}
51
52
return ParsedAddress(
51
- address: address,
53
+ addresses: [address],
54
name: domain,
55
parseFrom: ParseFrom.unstoppableDomains);
56
}
@@ -56,16 +58,16 @@ Future<ParsedAddress> parseAddressFromDomain(
58
final record = await OpenaliasRecord.fetchAddressAndName(formattedName);
59
60
if (record == null || record.address.contains(formattedName)) {
59
- return ParsedAddress(address: domain);
61
+ return ParsedAddress(addresses: [domain]);
62
}
63
64
return ParsedAddress(
63
- address: record.address,
65
+ addresses: [record.address],
66
name: record.name,
67
parseFrom: ParseFrom.openAlias);
68
} catch (e) {
69
print(e.toString());
70
}
71
70
- return ParsedAddress(address: domain);
72
+ return ParsedAddress(addresses: [domain]);
73
}
\ No newline at end of file
lib/entities/parsed_address.dart
+2
-2
@@ -2,11 +2,11 @@ enum ParseFrom {unstoppableDomains, openAlias, yatRecord, notParsed}
2
3
class ParsedAddress {
4
ParsedAddress({
5
- this.address = '',
5
+ this.addresses,
6
this.name = '',
7
this.parseFrom = ParseFrom.notParsed});
8
9
- final String address;
9
+ final List<String> addresses;
10
final String name;
11
final ParseFrom parseFrom;
12
}
\ No newline at end of file
lib/src/screens/exchange/exchange_page.dart
+9
-11
@@ -232,7 +232,7 @@ class ExchangePage extends BasePage {
232
final ticker = exchangeViewModel
233
.depositCurrency.title.toLowerCase();
234
exchangeViewModel.depositAddress =
235
- await applyOpenaliasOrUnstoppableDomains(
235
+ await fetchParsedAddress(
236
context, domain, ticker);
237
},
238
),
@@ -288,7 +288,7 @@ class ExchangePage extends BasePage {
288
final ticker = exchangeViewModel
289
.receiveCurrency.title.toLowerCase();
290
exchangeViewModel.receiveAddress =
291
- await applyOpenaliasOrUnstoppableDomains(
291
+ await fetchParsedAddress(
292
context, domain, ticker);
293
},
294
)),
@@ -518,12 +518,12 @@ class ExchangePage extends BasePage {
518
var domain = template.depositAddress;
519
var ticker = template.depositCurrency.toLowerCase();
520
exchangeViewModel.depositAddress =
521
- await applyOpenaliasOrUnstoppableDomains(context, domain, ticker);
521
+ await fetchParsedAddress(context, domain, ticker);
522
523
domain = template.receiveAddress;
524
ticker = template.receiveCurrency.toLowerCase();
525
exchangeViewModel.receiveAddress =
526
- await applyOpenaliasOrUnstoppableDomains(context, domain, ticker);
526
+ await fetchParsedAddress(context, domain, ticker);
527
}
528
529
void _setReactions(
@@ -696,7 +696,7 @@ class ExchangePage extends BasePage {
696
final domain = depositAddressController.text;
697
final ticker = exchangeViewModel.depositCurrency.title.toLowerCase();
698
exchangeViewModel.depositAddress =
699
- await applyOpenaliasOrUnstoppableDomains(context, domain, ticker);
699
+ await fetchParsedAddress(context, domain, ticker);
700
}
701
});
702
@@ -706,7 +706,7 @@ class ExchangePage extends BasePage {
706
final domain = receiveAddressController.text;
707
final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase();
708
exchangeViewModel.receiveAddress =
709
- await applyOpenaliasOrUnstoppableDomains(context, domain, ticker);
709
+ await fetchParsedAddress(context, domain, ticker);
710
}
711
});
712
@@ -782,12 +782,10 @@ class ExchangePage extends BasePage {
782
}
783
}
784
785
- Future<String> applyOpenaliasOrUnstoppableDomains(
785
+ Future<String> fetchParsedAddress(
786
BuildContext context, String domain, String ticker) async {
787
final parsedAddress = await parseAddressFromDomain(domain, ticker);
788
-
789
- showAddressAlert(context, parsedAddress);
790
-
791
- return parsedAddress.address;
788
+ final address = await defineAddress(context, parsedAddress);
789
+ return address;
790
}
791
}
lib/src/screens/send/send_page.dart
+2
-3
@@ -215,9 +215,8 @@ class SendPage extends BasePage {
215
output.address =
216
template.address;
217
output.setCryptoAmount(template.amount);
218
- final parsedAddress = await output
219
- .applyOpenaliasOrUnstoppableDomains();
220
- showAddressAlert(context, parsedAddress);
218
+ output.resetParsedAddress();
219
+ await output.fetchParsedAddress(context);
220
},
221
onRemove: () {
222
showPopUp<void>(
lib/src/screens/send/widgets/choose_yat_address_alert.dart
new
+72
@@ -0,0 +1,72 @@
1
+import 'package:flutter/material.dart';
2
+import 'package:cake_wallet/src/widgets/base_alert_dialog.dart';
3
+
4
+class ChooseYatAddressAlert extends BaseAlertDialog {
5
+ ChooseYatAddressAlert({
6
+ @required this.alertTitle,
7
+ @required this.alertContent,
8
+ @required this.addresses,
9
+ });
10
+
11
+ final String alertTitle;
12
+ final String alertContent;
13
+ final List<String> addresses;
14
+
15
+ @override
16
+ String get titleText => alertTitle;
17
+
18
+ @override
19
+ String get contentText => alertContent;
20
+
21
+ @override
22
+ bool get barrierDismissible => false;
23
+
24
+ @override
25
+ Widget actionButtons(BuildContext context) {
26
+ return Container(
27
+ width: 300,
28
+ height: 105,
29
+ color: Theme.of(context).accentTextTheme.body1.backgroundColor,
30
+ child: ListView.separated(
31
+ padding: EdgeInsets.all(0),
32
+ itemCount: addresses.length,
33
+ separatorBuilder: (_, __) => Container(
34
+ height: 1,
35
+ color: Theme.of(context).dividerColor,
36
+ ),
37
+ itemBuilder: (context, index) {
38
+ final address = addresses[index];
39
+
40
+ return GestureDetector(
41
+ onTap: () => Navigator.of(context).pop<String>(address),
42
+ child: Container(
43
+ width: 300,
44
+ height: 52,
45
+ padding: EdgeInsets.only(left: 24, right: 24),
46
+ child: Row(
47
+ mainAxisAlignment: MainAxisAlignment.center,
48
+ crossAxisAlignment: CrossAxisAlignment.center,
49
+ children: [
50
+ Expanded(
51
+ child: Text(
52
+ address,
53
+ textAlign: TextAlign.center,
54
+ maxLines: 1,
55
+ overflow: TextOverflow.ellipsis,
56
+ style: TextStyle(
57
+ fontSize: 15,
58
+ fontWeight: FontWeight.w600,
59
+ fontFamily: 'Lato',
60
+ color: Theme.of(context).primaryTextTheme.title.color,
61
+ decoration: TextDecoration.none,
62
+ ),
63
+ )
64
+ )
65
+ ],
66
+ )
67
+ ),
68
+ );
69
+ })
70
+ );
71
+ }
72
+}
\ No newline at end of file
lib/src/screens/send/widgets/confirm_sending_alert.dart
+51
-14
@@ -1,3 +1,4 @@
1
+import 'package:cake_wallet/entities/parsed_address.dart';
2
import 'package:cake_wallet/palette.dart';
3
import 'package:cake_wallet/view_model/send/output.dart';
4
import 'package:flutter/material.dart';
@@ -276,15 +277,32 @@ class ConfirmSendingAlertContentState extends State<ConfirmSendingAlertContent>
277
final _address = item.address;
278
final _amount =
279
item.cryptoAmount.replaceAll(',', '.');
280
+ final isParsedAddress =
281
+ item.parsedAddress.parseFrom !=
282
+ ParseFrom.notParsed;
283
284
return Column(
285
children: [
286
+ if (isParsedAddress) Padding(
287
+ padding: EdgeInsets.only(top: 8),
288
+ child: Text(
289
+ item.parsedAddress.name,
290
+ textAlign: TextAlign.center,
291
+ style: TextStyle(
292
+ fontSize: 14,
293
+ fontWeight: FontWeight.w600,
294
+ fontFamily: 'Lato',
295
+ color: PaletteDark.pigeonBlue,
296
+ decoration: TextDecoration.none,
297
+ ),
298
+ )
299
+ ),
300
Padding(
301
padding: EdgeInsets.only(top: 8),
302
child: Text(
303
_address,
304
style: TextStyle(
287
- fontSize: 12,
305
+ fontSize: 10,
306
fontWeight: FontWeight.w600,
307
fontFamily: 'Lato',
308
color: PaletteDark.pigeonBlue,
@@ -301,7 +319,7 @@ class ConfirmSendingAlertContentState extends State<ConfirmSendingAlertContent>
319
Text(
320
_amount,
321
style: TextStyle(
304
- fontSize: 12,
322
+ fontSize: 10,
323
fontWeight: FontWeight.w600,
324
fontFamily: 'Lato',
325
color: PaletteDark.pigeonBlue,
@@ -314,18 +332,37 @@ class ConfirmSendingAlertContentState extends State<ConfirmSendingAlertContent>
332
],
333
);
334
})
317
- : Padding(
318
- padding: EdgeInsets.only(top: 8),
319
- child: Text(
320
- outputs.first.address,
321
- style: TextStyle(
322
- fontSize: 12,
323
- fontWeight: FontWeight.w600,
324
- fontFamily: 'Lato',
325
- color: PaletteDark.pigeonBlue,
326
- decoration: TextDecoration.none,
327
- ),
328
- ),
335
+ : Column(
336
+ children: [
337
+ if (outputs.first.parsedAddress.parseFrom !=
338
+ ParseFrom.notParsed) Padding(
339
+ padding: EdgeInsets.only(top: 8),
340
+ child: Text(
341
+ outputs.first.parsedAddress.name,
342
+ textAlign: TextAlign.center,
343
+ style: TextStyle(
344
+ fontSize: 14,
345
+ fontWeight: FontWeight.w600,
346
+ fontFamily: 'Lato',
347
+ color: PaletteDark.pigeonBlue,
348
+ decoration: TextDecoration.none,
349
+ ),
350
+ )
351
+ ),
352
+ Padding(
353
+ padding: EdgeInsets.only(top: 8),
354
+ child: Text(
355
+ outputs.first.address,
356
+ style: TextStyle(
357
+ fontSize: 10,
358
+ fontWeight: FontWeight.w600,
359
+ fontFamily: 'Lato',
360
+ color: PaletteDark.pigeonBlue,
361
+ decoration: TextDecoration.none,
362
+ ),
363
+ )
364
+ ),
365
+ ]
366
)
367
],
368
),
lib/src/screens/send/widgets/parse_address_from_domain_alert.dart
+44
-3
@@ -4,25 +4,64 @@ import 'package:cake_wallet/utils/show_pop_up.dart';
4
import 'package:flutter/material.dart';
5
import 'package:cake_wallet/generated/i18n.dart';
6
7
-void showAddressAlert(BuildContext context, ParsedAddress parsedAddress) async {
7
+import 'choose_yat_address_alert.dart';
8
+
9
+Future<String> defineAddress(
10
+ BuildContext context,
11
+ ParsedAddress parsedAddress) async {
12
var title = '';
13
var content = '';
14
+ var address = '';
15
16
switch (parsedAddress.parseFrom) {
17
case ParseFrom.unstoppableDomains:
18
title = S.of(context).address_detected;
19
content = S.of(context).address_from_domain(parsedAddress.name);
20
+ address = parsedAddress.addresses.first;
21
break;
22
case ParseFrom.openAlias:
23
title = S.of(context).openalias_alert_title;
24
content = S.of(context).openalias_alert_content(parsedAddress.name);
25
+ address = parsedAddress.addresses.first;
26
break;
27
case ParseFrom.yatRecord:
28
+ if (parsedAddress.name.isEmpty) {
29
+ title = 'Yat error';
30
+ content = 'No addresses linked with this Yat. Try another Yat';
31
+ address = parsedAddress.addresses.first;
32
+ break;
33
+ }
34
+
35
title = S.of(context).address_detected;
36
content = S.of(context).address_from_yat(parsedAddress.name);
23
- break;
37
+
38
+ if (parsedAddress.addresses.length == 1) {
39
+ address = parsedAddress.addresses.first;
40
+ break;
41
+ }
42
+
43
+ content += '\nPlease choose the address:';
44
+
45
+ address = await showPopUp<String>(
46
+ context: context,
47
+ builder: (BuildContext context) {
48
+
49
+ return WillPopScope(
50
+ child: ChooseYatAddressAlert(
51
+ alertTitle: title,
52
+ alertContent: content,
53
+ addresses: parsedAddress.addresses),
54
+ onWillPop: () async => false);
55
+ });
56
+
57
+ if (address?.isEmpty ?? true) {
58
+ return parsedAddress.name;
59
+ }
60
+
61
+ return address;
62
case ParseFrom.notParsed:
25
- return;
63
+ address = parsedAddress.addresses.first;
64
+ return address;
65
}
66
67
await showPopUp<void>(
@@ -35,4 +74,6 @@ void showAddressAlert(BuildContext context, ParsedAddress parsedAddress) async {
74
buttonText: S.of(context).ok,
75
buttonAction: () => Navigator.of(context).pop());
76
});
77
+
78
+ return address;
79
}
\ No newline at end of file
lib/src/screens/send/widgets/send_card.dart
+6
-5
@@ -147,10 +147,11 @@ class SendCardState extends State<SendCard>
147
.headline
148
.decorationColor),
149
onPushPasteButton: (context) async {
150
- final parsedAddress =
151
- await output.applyOpenaliasOrUnstoppableDomains();
152
- showAddressAlert(context, parsedAddress);
150
+ output.resetParsedAddress();
151
+ await output.fetchParsedAddress(context);
152
},
153
+ onPushAddressBookButton: (context) =>
154
+ output.resetParsedAddress(),
155
validator: sendViewModel.addressValidator,
156
),
157
Observer(
@@ -531,8 +532,8 @@ class SendCardState extends State<SendCard>
532
533
addressFocusNode.addListener(() async {
534
if (!addressFocusNode.hasFocus && addressController.text.isNotEmpty) {
534
- final parsedAddress = await output.applyOpenaliasOrUnstoppableDomains();
535
- showAddressAlert(context, parsedAddress);
535
+ output.resetParsedAddress();
536
+ await output.fetchParsedAddress(context);
537
}
538
});
539
lib/src/screens/yat/yat_alert.dart
+30
-3
@@ -1,3 +1,5 @@
1
+import 'package:cake_wallet/core/wallet_base.dart';
2
+import 'package:cake_wallet/entities/wallet_type.dart';
3
import 'package:cake_wallet/src/screens/yat/widgets/yat_bar.dart';
4
import 'package:cake_wallet/src/widgets/primary_button.dart';
5
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
@@ -8,16 +10,20 @@ import 'package:url_launcher/url_launcher.dart';
10
import 'package:cake_wallet/generated/i18n.dart';
11
12
class YatAlert extends StatelessWidget {
11
- YatAlert({this.isYatDevMode = false})
12
- : baseUrl = isYatDevMode ? _baseDevUrl : _baseReleaseUrl;
13
+ YatAlert({@required this.wallet, this.isYatDevMode = false})
14
+ : baseUrl = isYatDevMode ? _baseDevUrl : _baseReleaseUrl,
15
+ address = wallet.walletAddresses.address;
16
17
+ final WalletBase wallet;
18
final bool isYatDevMode;
19
+ final String address;
20
final String baseUrl;
21
static const aspectRatioImage = 1.133;
22
static const _baseDevUrl = 'https://yat.fyi';
23
static const _baseReleaseUrl = 'https://y.at';
24
static const _signInSuffix = '/partner/CW/link-email';
25
static const _createSuffix = '/create';
26
+ static const _queryParameter = '?addresses=';
27
final image = Image.asset('assets/images/yat_crypto.png');
28
29
@override
@@ -107,7 +113,8 @@ class YatAlert extends StatelessWidget {
113
.arrow_up_right_square,
114
mainAxisAlignment: MainAxisAlignment.end,
115
onPressed: () {
110
- final url = baseUrl + _signInSuffix;
116
+ final url = baseUrl + _signInSuffix + _queryParameter +
117
+ _defineTag() + '%3D' + address;
118
launch(url);
119
})
120
)
@@ -116,4 +123,24 @@ class YatAlert extends StatelessWidget {
123
)
124
);
125
}
126
+
127
+ String _defineTag() {
128
+ String tag;
129
+ switch (wallet.type) {
130
+ case WalletType.monero:
131
+ tag = address.startsWith('4')
132
+ ? '0x1001'
133
+ : '0x1002';
134
+ break;
135
+ case WalletType.bitcoin:
136
+ tag = '0x1003';
137
+ break;
138
+ case WalletType.litecoin:
139
+ tag = '0x3fff';
140
+ break;
141
+ default:
142
+ tag = '0x3fff';
143
+ }
144
+ return tag;
145
+ }
146
}
\ No newline at end of file
lib/src/widgets/address_text_field.dart
+4
-1
@@ -25,7 +25,8 @@ class AddressTextField extends StatelessWidget {
25
this.textStyle,
26
this.hintStyle,
27
this.validator,
28
- this.onPushPasteButton});
28
+ this.onPushPasteButton,
29
+ this.onPushAddressBookButton});
30
31
static const prefixIconWidth = 34.0;
32
static const prefixIconHeight = 34.0;
@@ -45,6 +46,7 @@ class AddressTextField extends StatelessWidget {
46
final TextStyle hintStyle;
47
final FocusNode focusNode;
48
final Function(BuildContext context) onPushPasteButton;
49
+ final Function(BuildContext context) onPushAddressBookButton;
50
51
@override
52
Widget build(BuildContext context) {
@@ -216,6 +218,7 @@ class AddressTextField extends StatelessWidget {
218
219
if (contact is ContactBase && contact.address != null) {
220
controller.text = contact.address;
221
+ onPushAddressBookButton?.call(context);
222
}
223
}
224
lib/view_model/send/output.dart
+11
-6
@@ -4,6 +4,7 @@ import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart';
4
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
5
import 'package:cake_wallet/entities/parsed_address.dart';
6
import 'package:cake_wallet/monero/monero_amount_format.dart';
7
+import 'package:cake_wallet/src/screens/send/widgets/parse_address_from_domain_alert.dart';
8
import 'package:flutter/material.dart';
9
import 'package:intl/intl.dart';
10
import 'package:mobx/mobx.dart';
@@ -46,6 +47,8 @@ abstract class OutputBase with Store {
47
@observable
48
bool sendAll;
49
50
+ ParsedAddress parsedAddress;
51
+
52
@computed
53
int get formattedCryptoAmount {
54
int amount = 0;
@@ -127,6 +130,11 @@ abstract class OutputBase with Store {
130
fiatAmount = '';
131
address = '';
132
note = '';
133
+ resetParsedAddress();
134
+ }
135
+
136
+ void resetParsedAddress() {
137
+ parsedAddress = ParsedAddress(addresses: []);
138
}
139
140
@action
@@ -194,13 +202,10 @@ abstract class OutputBase with Store {
202
_cryptoNumberFormat.maximumFractionDigits = maximumFractionDigits;
203
}
204
197
- Future<ParsedAddress> applyOpenaliasOrUnstoppableDomains() async {
205
+ Future<void> fetchParsedAddress(BuildContext context) async {
206
final domain = address;
207
final ticker = _wallet.currency.title.toLowerCase();
200
- final parsedAddress = await parseAddressFromDomain(domain, ticker);
201
-
202
- address = parsedAddress.address;
203
-
204
- return parsedAddress;
208
+ parsedAddress = await parseAddressFromDomain(domain, ticker);
209
+ address = await defineAddress(context, parsedAddress);
210
}
211
}
\ No newline at end of file
lib/view_model/settings/settings_view_model.dart
+1
-1
@@ -162,7 +162,7 @@ abstract class SettingsViewModelBase with Store {
162
await showPopUp<void>(
163
context: context,
164
builder: (BuildContext context) {
165
- return YatAlert(isYatDevMode: true);
165
+ return YatAlert(wallet: wallet, isYatDevMode: true);
166
});
167
},
168
),
lib/yat/yat_record.dart
+9
-7
@@ -2,9 +2,8 @@ import 'dart:convert';
2
import 'package:cake_wallet/yat/yat_exception.dart';
3
import 'package:http/http.dart';
4
5
-Future<String> fetchYatAddress(String emojiId, String ticker) async {
5
+Future<List<String>> fetchYatAddress(String emojiId, String ticker) async {
6
const _requestURL = 'https://a.y.at/emoji_id/';
7
-
7
final url = _requestURL + emojiId + '/' + ticker.toUpperCase();
8
final response = await get(url);
9
@@ -16,14 +15,17 @@ Future<String> fetchYatAddress(String emojiId, String ticker) async {
15
final result = responseJSON['result'] as List<dynamic>;
16
17
if (result?.isEmpty ?? true) {
19
- return '';
18
+ return [];
19
}
20
22
- final yatAddress = result.first['data'] as String;
21
+ final List<String> addresses = [];
22
24
- if (yatAddress?.isEmpty ?? true) {
25
- return '';
23
+ for (var elem in result) {
24
+ final yatAddress = elem['data'] as String;
25
+ if (yatAddress?.isNotEmpty ?? false) {
26
+ addresses.add(yatAddress);
27
+ }
28
}
29
28
- return yatAddress;
30
+ return addresses;
31
}
\ No newline at end of file