CW-425 use the open alias new line UI (#1004)

* CW-425 Display Contact Name in the send view * CW-425 Ignore false positives on OpenAlias

Konstantin Ullrich committed Aug 1, 2023 at 14:00 UTC cfaa89d1657f95f25b8061791c1a3ec1599240e0
7 files changed +89 -94
lib/entities/openalias_record.dart
+1 -1
@@ -37,7 +37,7 @@ class OpenaliasRecord {
37 required String ticker,
38 required List<RRecord> txtRecord,
39 }) {
40 - String address = formattedName;
40 + String address = '';
41 String name = formattedName;
42 String note = '';
43
lib/entities/parsed_address.dart
+20 -8
@@ -1,7 +1,7 @@
1 import 'package:cake_wallet/entities/openalias_record.dart';
2 import 'package:cake_wallet/entities/yat_record.dart';
3
4 -enum ParseFrom { unstoppableDomains, openAlias, yatRecord, fio, notParsed, twitter }
4 +enum ParseFrom { unstoppableDomains, openAlias, yatRecord, fio, notParsed, twitter, contact }
5
6 class ParsedAddress {
7 ParsedAddress({
@@ -40,13 +40,17 @@ class ParsedAddress {
40 );
41 }
42
43 - factory ParsedAddress.fetchOpenAliasAddress({required OpenaliasRecord record, required String name}){
44 - return ParsedAddress(
45 - addresses: [record.address],
46 - name: record.name,
47 - description: record.description,
48 - parseFrom: ParseFrom.openAlias,
49 - );
43 + factory ParsedAddress.fetchOpenAliasAddress(
44 + {required OpenaliasRecord record, required String name}) {
45 + if (record.address.isEmpty) {
46 + return ParsedAddress(addresses: [name]);
47 + }
48 + return ParsedAddress(
49 + addresses: [record.address],
50 + name: record.name,
51 + description: record.description,
52 + parseFrom: ParseFrom.openAlias,
53 + );
54 }
55
56 factory ParsedAddress.fetchFioAddress({required String address, required String name}){
@@ -65,6 +69,14 @@ class ParsedAddress {
69 );
70 }
71
72 + factory ParsedAddress.fetchContactAddress({required String address, required String name}){
73 + return ParsedAddress(
74 + addresses: [address],
75 + name: name,
76 + parseFrom: ParseFrom.contact,
77 + );
78 + }
79 +
80 final List<String> addresses;
81 final String name;
82 final String description;
lib/src/screens/send/widgets/extract_address_from_parsed.dart
+2 -1
@@ -68,6 +68,7 @@ Future<String> extractAddressFromParsed(
68 }
69
70 return address;
71 + case ParseFrom.contact:
72 case ParseFrom.notParsed:
73 address = parsedAddress.addresses.first;
74 return address;
@@ -85,4 +86,4 @@ Future<String> extractAddressFromParsed(
86 });
87
88 return address;
88 -}
\ No newline at end of file
89 +}
lib/src/screens/send/widgets/send_card.dart
+25 -23
@@ -103,7 +103,7 @@ class SendCardState extends State<SendCard>
103 config: KeyboardActionsConfig(
104 keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
105 keyboardBarColor: Theme.of(context)
106 - .accentTextTheme!
106 + .accentTextTheme
107 .bodyLarge!
108 .backgroundColor!,
109 nextFocus: false,
@@ -127,9 +127,9 @@ class SendCardState extends State<SendCard>
127 bottomLeft: Radius.circular(24),
128 bottomRight: Radius.circular(24)),
129 gradient: LinearGradient(colors: [
130 - Theme.of(context).primaryTextTheme!.titleMedium!.color!,
130 + Theme.of(context).primaryTextTheme.titleMedium!.color!,
131 Theme.of(context)
132 - .primaryTextTheme!
132 + .primaryTextTheme
133 .titleMedium!
134 .decorationColor!,
135 ], begin: Alignment.topLeft, end: Alignment.bottomRight),
@@ -165,11 +165,11 @@ class SendCardState extends State<SendCard>
165 AddressTextFieldOption.addressBook
166 ],
167 buttonColor: Theme.of(context)
168 - .primaryTextTheme!
168 + .primaryTextTheme
169 .headlineMedium!
170 .color!,
171 borderColor: Theme.of(context)
172 - .primaryTextTheme!
172 + .primaryTextTheme
173 .headlineSmall!
174 .color!,
175 textStyle: TextStyle(
@@ -180,7 +180,7 @@ class SendCardState extends State<SendCard>
180 fontSize: 14,
181 fontWeight: FontWeight.w500,
182 color: Theme.of(context)
183 - .primaryTextTheme!
183 + .primaryTextTheme
184 .headlineSmall!
185 .decorationColor!),
186 onPushPasteButton: (context) async {
@@ -189,7 +189,9 @@ class SendCardState extends State<SendCard>
189 },
190 onPushAddressBookButton: (context) async {
191 output.resetParsedAddress();
192 - await output.fetchParsedAddress(context);
192 + },
193 + onSelectedContact: (contact) {
194 + output.loadContact(contact);
195 },
196 validator: validator,
197 selectedCurrency: sendViewModel.currency,
@@ -201,7 +203,7 @@ class SendCardState extends State<SendCard>
203 controller: extractedAddressController,
204 readOnly: true,
205 borderColor: Theme.of(context)
204 - .primaryTextTheme!
206 + .primaryTextTheme
207 .headlineSmall!
208 .color!,
209 textStyle: TextStyle(
@@ -233,7 +235,7 @@ class SendCardState extends State<SendCard>
235 height: 32,
236 decoration: BoxDecoration(
237 color: Theme.of(context)
236 - .primaryTextTheme!
238 + .primaryTextTheme
239 .headlineMedium!
240 .color!,
241 borderRadius:
@@ -246,7 +248,7 @@ class SendCardState extends State<SendCard>
248 fontSize: 12,
249 fontWeight: FontWeight.bold,
250 color: Theme.of(context)
249 - .primaryTextTheme!
251 + .primaryTextTheme
252 .headlineMedium!
253 .decorationColor!)),
254 ),
@@ -287,7 +289,7 @@ class SendCardState extends State<SendCard>
289 color: Colors.white),
290 placeholderTextStyle: TextStyle(
291 color: Theme.of(context)
290 - .primaryTextTheme!
292 + .primaryTextTheme
293 .headlineSmall!
294 .decorationColor!,
295 fontWeight: FontWeight.w500,
@@ -308,7 +310,7 @@ class SendCardState extends State<SendCard>
310 child: Container(
311 decoration: BoxDecoration(
312 color: Theme.of(context)
311 - .primaryTextTheme!
313 + .primaryTextTheme
314 .headlineMedium!
315 .color!,
316 borderRadius:
@@ -325,7 +327,7 @@ class SendCardState extends State<SendCard>
327 FontWeight.bold,
328 color:
329 Theme.of(context)
328 - .primaryTextTheme!
330 + .primaryTextTheme
331 .headlineMedium!
332 .decorationColor!))),
333 ))))]),
@@ -334,7 +336,7 @@ class SendCardState extends State<SendCard>
336 )
337 )),
338 Divider(height: 1,color: Theme.of(context)
337 - .primaryTextTheme!
339 + .primaryTextTheme
340 .headlineSmall!
341 .decorationColor!),
342 Observer(
@@ -353,7 +355,7 @@ class SendCardState extends State<SendCard>
355 fontSize: 12,
356 fontWeight: FontWeight.w600,
357 color: Theme.of(context)
356 - .primaryTextTheme!
358 + .primaryTextTheme
359 .headlineSmall!
360 .decorationColor!),
361 )),
@@ -363,7 +365,7 @@ class SendCardState extends State<SendCard>
365 fontSize: 12,
366 fontWeight: FontWeight.w600,
367 color: Theme.of(context)
366 - .primaryTextTheme!
368 + .primaryTextTheme
369 .headlineSmall!
370 .decorationColor!),
371 )
@@ -394,7 +396,7 @@ class SendCardState extends State<SendCard>
396 ),
397 hintText: '0.00',
398 borderColor: Theme.of(context)
397 - .primaryTextTheme!
399 + .primaryTextTheme
400 .headlineSmall!
401 .color!,
402 textStyle: TextStyle(
@@ -403,7 +405,7 @@ class SendCardState extends State<SendCard>
405 color: Colors.white),
406 placeholderTextStyle: TextStyle(
407 color: Theme.of(context)
406 - .primaryTextTheme!.headlineSmall!.decorationColor!,
408 + .primaryTextTheme.headlineSmall!.decorationColor!,
409 fontWeight: FontWeight.w500,
410 fontSize: 14),
411 )),
@@ -414,7 +416,7 @@ class SendCardState extends State<SendCard>
416 keyboardType: TextInputType.multiline,
417 maxLines: null,
418 borderColor: Theme.of(context)
417 - .primaryTextTheme!
419 + .primaryTextTheme
420 .headlineSmall!
421 .color!,
422 textStyle: TextStyle(
@@ -426,7 +428,7 @@ class SendCardState extends State<SendCard>
428 fontSize: 14,
429 fontWeight: FontWeight.w500,
430 color: Theme.of(context)
429 - .primaryTextTheme!
431 + .primaryTextTheme
432 .headlineSmall!
433 .decorationColor!),
434 ),
@@ -490,7 +492,7 @@ class SendCardState extends State<SendCard>
492 FontWeight.w600,
493 color: Theme
494 .of(context)
493 - .primaryTextTheme!
495 + .primaryTextTheme
496 .headlineSmall!
497 .decorationColor!))
498 ),
@@ -586,7 +588,7 @@ class SendCardState extends State<SendCard>
588 });
589
590 noteController.addListener(() {
589 - final note = noteController.text ?? '';
591 + final note = noteController.text;
592
593 if (note != output.note) {
594 output.note = note;
@@ -676,4 +678,4 @@ class SendCardState extends State<SendCard>
678
679 @override
680 bool get wantKeepAlive => true;
679 -}
\ No newline at end of file
681 +}
lib/src/widgets/address_text_field.dart
+30 -58
@@ -1,4 +1,3 @@
1 -
1 import 'package:cake_wallet/utils/device_info.dart';
2 import 'package:cake_wallet/utils/responsive_layout_util.dart';
3 import 'package:flutter/services.dart';
@@ -16,10 +15,7 @@ class AddressTextField extends StatelessWidget {
15 {required this.controller,
16 this.isActive = true,
17 this.placeholder,
19 - this.options = const [
20 - AddressTextFieldOption.qrCode,
21 - AddressTextFieldOption.addressBook
22 - ],
18 + this.options = const [AddressTextFieldOption.qrCode, AddressTextFieldOption.addressBook],
19 this.onURIScanned,
20 this.focusNode,
21 this.isBorderExist = true,
@@ -31,6 +27,7 @@ class AddressTextField extends StatelessWidget {
27 this.validator,
28 this.onPushPasteButton,
29 this.onPushAddressBookButton,
30 + this.onSelectedContact,
31 this.selectedCurrency});
32
33 static const prefixIconWidth = 34.0;
@@ -52,6 +49,7 @@ class AddressTextField extends StatelessWidget {
49 final FocusNode? focusNode;
50 final Function(BuildContext context)? onPushPasteButton;
51 final Function(BuildContext context)? onPushAddressBookButton;
52 + final Function(ContactBase contact)? onSelectedContact;
53 final CryptoCurrency? selectedCurrency;
54
55 @override
@@ -66,34 +64,27 @@ class AddressTextField extends StatelessWidget {
64 controller: controller,
65 focusNode: focusNode,
66 style: textStyle ??
69 - TextStyle(
70 - fontSize: 16,
71 - color: Theme.of(context).primaryTextTheme!.titleLarge!.color!),
67 + TextStyle(fontSize: 16, color: Theme.of(context).primaryTextTheme.titleLarge!.color!),
68 decoration: InputDecoration(
69 suffixIcon: SizedBox(
74 - width: prefixIconWidth * options.length +
75 - (spaceBetweenPrefixIcons * options.length),
70 + width: prefixIconWidth * options.length + (spaceBetweenPrefixIcons * options.length),
71 ),
77 - hintStyle: hintStyle ??
78 - TextStyle(fontSize: 16, color: Theme.of(context).hintColor),
72 + hintStyle: hintStyle ?? TextStyle(fontSize: 16, color: Theme.of(context).hintColor),
73 hintText: placeholder ?? S.current.widgets_address,
74 focusedBorder: isBorderExist
75 ? UnderlineInputBorder(
76 borderSide: BorderSide(
83 - color: borderColor ?? Theme.of(context).dividerColor,
84 - width: 1.0))
77 + color: borderColor ?? Theme.of(context).dividerColor, width: 1.0))
78 : InputBorder.none,
79 disabledBorder: isBorderExist
80 ? UnderlineInputBorder(
81 borderSide: BorderSide(
89 - color: borderColor ?? Theme.of(context).dividerColor,
90 - width: 1.0))
82 + color: borderColor ?? Theme.of(context).dividerColor, width: 1.0))
83 : InputBorder.none,
84 enabledBorder: isBorderExist
85 ? UnderlineInputBorder(
86 borderSide: BorderSide(
95 - color: borderColor ?? Theme.of(context).dividerColor,
96 - width: 1.0))
87 + color: borderColor ?? Theme.of(context).dividerColor, width: 1.0))
88 : InputBorder.none,
89 ),
90 validator: validator,
@@ -102,11 +93,11 @@ class AddressTextField extends StatelessWidget {
93 top: 2,
94 right: 0,
95 child: SizedBox(
105 - width: prefixIconWidth * options.length +
106 - (spaceBetweenPrefixIcons * options.length),
96 + width: prefixIconWidth * options.length + (spaceBetweenPrefixIcons * options.length),
97 child: Row(
108 - mainAxisAlignment: ResponsiveLayoutUtil.instance.isMobile
109 - ? MainAxisAlignment.spaceBetween : MainAxisAlignment.end,
98 + mainAxisAlignment: ResponsiveLayoutUtil.instance.isMobile
99 + ? MainAxisAlignment.spaceBetween
100 + : MainAxisAlignment.end,
101 children: [
102 SizedBox(width: 5),
103 if (this.options.contains(AddressTextFieldOption.paste)) ...[
@@ -122,20 +113,14 @@ class AddressTextField extends StatelessWidget {
113 padding: EdgeInsets.all(8),
114 decoration: BoxDecoration(
115 color: buttonColor ??
125 - Theme.of(context)
126 - .accentTextTheme
127 - !
128 - .titleLarge!
129 - .color!,
130 - borderRadius:
131 - BorderRadius.all(Radius.circular(6))),
116 + Theme.of(context).accentTextTheme.titleLarge!.color!,
117 + borderRadius: BorderRadius.all(Radius.circular(6))),
118 child: Image.asset(
119 'assets/images/paste_ios.png',
120 color: iconColor ??
121 Theme.of(context)
122 .primaryTextTheme
137 - !
138 - .headlineMedium!
123 + .headlineMedium!
124 .decorationColor!,
125 )),
126 ),
@@ -155,28 +140,21 @@ class AddressTextField extends StatelessWidget {
140 padding: EdgeInsets.all(8),
141 decoration: BoxDecoration(
142 color: buttonColor ??
158 - Theme.of(context)
159 - .accentTextTheme
160 -
161 - .titleLarge!
162 - .color!,
163 - borderRadius:
164 - BorderRadius.all(Radius.circular(6))),
143 + Theme.of(context).accentTextTheme.titleLarge!.color!,
144 + borderRadius: BorderRadius.all(Radius.circular(6))),
145 child: Image.asset(
146 'assets/images/qr_code_icon.png',
147 color: iconColor ??
148 Theme.of(context)
149 .primaryTextTheme
170 - !.headlineMedium!
150 + .headlineMedium!
151 .decorationColor!,
152 )),
153 ),
154 ))
155 ] else
156 SizedBox(width: 5),
177 - if (this
178 - .options
179 - .contains(AddressTextFieldOption.addressBook)) ...[
157 + if (this.options.contains(AddressTextFieldOption.addressBook)) ...[
158 Container(
159 width: prefixIconWidth,
160 height: prefixIconHeight,
@@ -184,26 +162,19 @@ class AddressTextField extends StatelessWidget {
162 child: Semantics(
163 label: S.of(context).address_book,
164 child: InkWell(
187 - onTap: () async =>
188 - _presetAddressBookPicker(context),
165 + onTap: () async => _presetAddressBookPicker(context),
166 child: Container(
167 padding: EdgeInsets.all(8),
168 decoration: BoxDecoration(
169 color: buttonColor ??
193 - Theme.of(context)
194 - .accentTextTheme
195 - !
196 - .titleLarge!
197 - .color!,
198 - borderRadius:
199 - BorderRadius.all(Radius.circular(6))),
170 + Theme.of(context).accentTextTheme.titleLarge!.color!,
171 + borderRadius: BorderRadius.all(Radius.circular(6))),
172 child: Image.asset(
173 'assets/images/open_book.png',
174 color: iconColor ??
175 Theme.of(context)
176 .primaryTextTheme
205 - !
206 - .headlineMedium!
177 + .headlineMedium!
178 .decorationColor!,
179 )),
180 ),
@@ -221,30 +192,31 @@ class AddressTextField extends StatelessWidget {
192 if (code.isEmpty) {
193 return;
194 }
224 -
195 +
196 try {
197 final uri = Uri.parse(code);
198 controller?.text = uri.path;
199 onURIScanned?.call(uri);
229 - } catch(_){
200 + } catch (_) {
201 controller?.text = code;
202 }
203 }
204
205 Future<void> _presetAddressBookPicker(BuildContext context) async {
206 final contact = await Navigator.of(context)
236 - .pushNamed(Routes.pickerAddressBook,arguments: selectedCurrency);
207 + .pushNamed(Routes.pickerAddressBook, arguments: selectedCurrency);
208
238 - if (contact is ContactBase && contact.address != null) {
209 + if (contact is ContactBase) {
210 controller?.text = contact.address;
211 onPushAddressBookButton?.call(context);
212 + onSelectedContact?.call(contact);
213 }
214 }
215
216 Future<void> _pasteAddress(BuildContext context) async {
217 final clipboard = await Clipboard.getData('text/plain');
218 final address = clipboard?.text ?? '';
247 -
219 +
220 if (address.isNotEmpty) {
221 controller?.text = address;
222 }
lib/view_model/send/output.dart
+10 -1
@@ -17,6 +17,8 @@ import 'package:cake_wallet/store/settings_store.dart';
17 import 'package:cake_wallet/generated/i18n.dart';
18 import 'package:cake_wallet/bitcoin/bitcoin.dart';
19
20 +import 'package:cake_wallet/entities/contact_base.dart';
21 +
22 part 'output.g.dart';
23
24 const String cryptoNumberPattern = '0.0';
@@ -70,7 +72,7 @@ abstract class OutputBase with Store {
72 int amount = 0;
73
74 try {
73 - if (cryptoAmount?.isNotEmpty ?? false) {
75 + if (cryptoAmount.isNotEmpty) {
76 final _cryptoAmount = cryptoAmount.replaceAll(',', '.');
77 int _amount = 0;
78 switch (walletType) {
@@ -240,4 +242,11 @@ abstract class OutputBase with Store {
242 extractedAddress = await extractAddressFromParsed(context, parsedAddress);
243 note = parsedAddress.description;
244 }
245 +
246 + void loadContact(ContactBase contact) {
247 + address = contact.name;
248 + parsedAddress = ParsedAddress.fetchContactAddress(address: contact.address, name: contact.name);
249 + extractedAddress = parsedAddress.addresses.first;
250 + note = parsedAddress.description;
251 + }
252 }
pubspec_base.yaml
+1 -2
@@ -3,7 +3,6 @@ dependencies:
3 sdk: flutter
4 flutter_localizations:
5 sdk: flutter
6 - flutter_cupertino_localizations: ^1.0.1
6 intl: ^0.17.0
7 url_launcher: ^6.1.4
8 qr_flutter:
@@ -128,4 +127,4 @@ flutter:
127 - asset: assets/fonts/Lato-Regular.ttf
128 - asset: assets/fonts/Lato-Medium.ttf
129 - asset: assets/fonts/Lato-Semibold.ttf
131 - - asset: assets/fonts/Lato-Bold.ttf
\ No newline at end of file
130 + - asset: assets/fonts/Lato-Bold.ttf