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
This commit is contained in:
Konstantin Ullrich 2023-08-01 14:00:00 +02:00 committed by GitHub
parent 962074c093
commit cfaa89d165
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 89 additions and 94 deletions

View file

@ -37,7 +37,7 @@ class OpenaliasRecord {
required String ticker, required String ticker,
required List<RRecord> txtRecord, required List<RRecord> txtRecord,
}) { }) {
String address = formattedName; String address = '';
String name = formattedName; String name = formattedName;
String note = ''; String note = '';

View file

@ -1,7 +1,7 @@
import 'package:cake_wallet/entities/openalias_record.dart'; import 'package:cake_wallet/entities/openalias_record.dart';
import 'package:cake_wallet/entities/yat_record.dart'; import 'package:cake_wallet/entities/yat_record.dart';
enum ParseFrom { unstoppableDomains, openAlias, yatRecord, fio, notParsed, twitter } enum ParseFrom { unstoppableDomains, openAlias, yatRecord, fio, notParsed, twitter, contact }
class ParsedAddress { class ParsedAddress {
ParsedAddress({ ParsedAddress({
@ -40,13 +40,17 @@ class ParsedAddress {
); );
} }
factory ParsedAddress.fetchOpenAliasAddress({required OpenaliasRecord record, required String name}){ factory ParsedAddress.fetchOpenAliasAddress(
return ParsedAddress( {required OpenaliasRecord record, required String name}) {
addresses: [record.address], if (record.address.isEmpty) {
name: record.name, return ParsedAddress(addresses: [name]);
description: record.description, }
parseFrom: ParseFrom.openAlias, return ParsedAddress(
); addresses: [record.address],
name: record.name,
description: record.description,
parseFrom: ParseFrom.openAlias,
);
} }
factory ParsedAddress.fetchFioAddress({required String address, required String name}){ factory ParsedAddress.fetchFioAddress({required String address, required String name}){
@ -65,6 +69,14 @@ class ParsedAddress {
); );
} }
factory ParsedAddress.fetchContactAddress({required String address, required String name}){
return ParsedAddress(
addresses: [address],
name: name,
parseFrom: ParseFrom.contact,
);
}
final List<String> addresses; final List<String> addresses;
final String name; final String name;
final String description; final String description;

View file

@ -68,6 +68,7 @@ Future<String> extractAddressFromParsed(
} }
return address; return address;
case ParseFrom.contact:
case ParseFrom.notParsed: case ParseFrom.notParsed:
address = parsedAddress.addresses.first; address = parsedAddress.addresses.first;
return address; return address;
@ -85,4 +86,4 @@ Future<String> extractAddressFromParsed(
}); });
return address; return address;
} }

View file

@ -103,7 +103,7 @@ class SendCardState extends State<SendCard>
config: KeyboardActionsConfig( config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS, keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
keyboardBarColor: Theme.of(context) keyboardBarColor: Theme.of(context)
.accentTextTheme! .accentTextTheme
.bodyLarge! .bodyLarge!
.backgroundColor!, .backgroundColor!,
nextFocus: false, nextFocus: false,
@ -127,9 +127,9 @@ class SendCardState extends State<SendCard>
bottomLeft: Radius.circular(24), bottomLeft: Radius.circular(24),
bottomRight: Radius.circular(24)), bottomRight: Radius.circular(24)),
gradient: LinearGradient(colors: [ gradient: LinearGradient(colors: [
Theme.of(context).primaryTextTheme!.titleMedium!.color!, Theme.of(context).primaryTextTheme.titleMedium!.color!,
Theme.of(context) Theme.of(context)
.primaryTextTheme! .primaryTextTheme
.titleMedium! .titleMedium!
.decorationColor!, .decorationColor!,
], begin: Alignment.topLeft, end: Alignment.bottomRight), ], begin: Alignment.topLeft, end: Alignment.bottomRight),
@ -165,11 +165,11 @@ class SendCardState extends State<SendCard>
AddressTextFieldOption.addressBook AddressTextFieldOption.addressBook
], ],
buttonColor: Theme.of(context) buttonColor: Theme.of(context)
.primaryTextTheme! .primaryTextTheme
.headlineMedium! .headlineMedium!
.color!, .color!,
borderColor: Theme.of(context) borderColor: Theme.of(context)
.primaryTextTheme! .primaryTextTheme
.headlineSmall! .headlineSmall!
.color!, .color!,
textStyle: TextStyle( textStyle: TextStyle(
@ -180,7 +180,7 @@ class SendCardState extends State<SendCard>
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Theme.of(context) color: Theme.of(context)
.primaryTextTheme! .primaryTextTheme
.headlineSmall! .headlineSmall!
.decorationColor!), .decorationColor!),
onPushPasteButton: (context) async { onPushPasteButton: (context) async {
@ -189,7 +189,9 @@ class SendCardState extends State<SendCard>
}, },
onPushAddressBookButton: (context) async { onPushAddressBookButton: (context) async {
output.resetParsedAddress(); output.resetParsedAddress();
await output.fetchParsedAddress(context); },
onSelectedContact: (contact) {
output.loadContact(contact);
}, },
validator: validator, validator: validator,
selectedCurrency: sendViewModel.currency, selectedCurrency: sendViewModel.currency,
@ -201,7 +203,7 @@ class SendCardState extends State<SendCard>
controller: extractedAddressController, controller: extractedAddressController,
readOnly: true, readOnly: true,
borderColor: Theme.of(context) borderColor: Theme.of(context)
.primaryTextTheme! .primaryTextTheme
.headlineSmall! .headlineSmall!
.color!, .color!,
textStyle: TextStyle( textStyle: TextStyle(
@ -233,7 +235,7 @@ class SendCardState extends State<SendCard>
height: 32, height: 32,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context) color: Theme.of(context)
.primaryTextTheme! .primaryTextTheme
.headlineMedium! .headlineMedium!
.color!, .color!,
borderRadius: borderRadius:
@ -246,7 +248,7 @@ class SendCardState extends State<SendCard>
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Theme.of(context) color: Theme.of(context)
.primaryTextTheme! .primaryTextTheme
.headlineMedium! .headlineMedium!
.decorationColor!)), .decorationColor!)),
), ),
@ -287,7 +289,7 @@ class SendCardState extends State<SendCard>
color: Colors.white), color: Colors.white),
placeholderTextStyle: TextStyle( placeholderTextStyle: TextStyle(
color: Theme.of(context) color: Theme.of(context)
.primaryTextTheme! .primaryTextTheme
.headlineSmall! .headlineSmall!
.decorationColor!, .decorationColor!,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -308,7 +310,7 @@ class SendCardState extends State<SendCard>
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context) color: Theme.of(context)
.primaryTextTheme! .primaryTextTheme
.headlineMedium! .headlineMedium!
.color!, .color!,
borderRadius: borderRadius:
@ -325,7 +327,7 @@ class SendCardState extends State<SendCard>
FontWeight.bold, FontWeight.bold,
color: color:
Theme.of(context) Theme.of(context)
.primaryTextTheme! .primaryTextTheme
.headlineMedium! .headlineMedium!
.decorationColor!))), .decorationColor!))),
))))]), ))))]),
@ -334,7 +336,7 @@ class SendCardState extends State<SendCard>
) )
)), )),
Divider(height: 1,color: Theme.of(context) Divider(height: 1,color: Theme.of(context)
.primaryTextTheme! .primaryTextTheme
.headlineSmall! .headlineSmall!
.decorationColor!), .decorationColor!),
Observer( Observer(
@ -353,7 +355,7 @@ class SendCardState extends State<SendCard>
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Theme.of(context) color: Theme.of(context)
.primaryTextTheme! .primaryTextTheme
.headlineSmall! .headlineSmall!
.decorationColor!), .decorationColor!),
)), )),
@ -363,7 +365,7 @@ class SendCardState extends State<SendCard>
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Theme.of(context) color: Theme.of(context)
.primaryTextTheme! .primaryTextTheme
.headlineSmall! .headlineSmall!
.decorationColor!), .decorationColor!),
) )
@ -394,7 +396,7 @@ class SendCardState extends State<SendCard>
), ),
hintText: '0.00', hintText: '0.00',
borderColor: Theme.of(context) borderColor: Theme.of(context)
.primaryTextTheme! .primaryTextTheme
.headlineSmall! .headlineSmall!
.color!, .color!,
textStyle: TextStyle( textStyle: TextStyle(
@ -403,7 +405,7 @@ class SendCardState extends State<SendCard>
color: Colors.white), color: Colors.white),
placeholderTextStyle: TextStyle( placeholderTextStyle: TextStyle(
color: Theme.of(context) color: Theme.of(context)
.primaryTextTheme!.headlineSmall!.decorationColor!, .primaryTextTheme.headlineSmall!.decorationColor!,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
fontSize: 14), fontSize: 14),
)), )),
@ -414,7 +416,7 @@ class SendCardState extends State<SendCard>
keyboardType: TextInputType.multiline, keyboardType: TextInputType.multiline,
maxLines: null, maxLines: null,
borderColor: Theme.of(context) borderColor: Theme.of(context)
.primaryTextTheme! .primaryTextTheme
.headlineSmall! .headlineSmall!
.color!, .color!,
textStyle: TextStyle( textStyle: TextStyle(
@ -426,7 +428,7 @@ class SendCardState extends State<SendCard>
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Theme.of(context) color: Theme.of(context)
.primaryTextTheme! .primaryTextTheme
.headlineSmall! .headlineSmall!
.decorationColor!), .decorationColor!),
), ),
@ -490,7 +492,7 @@ class SendCardState extends State<SendCard>
FontWeight.w600, FontWeight.w600,
color: Theme color: Theme
.of(context) .of(context)
.primaryTextTheme! .primaryTextTheme
.headlineSmall! .headlineSmall!
.decorationColor!)) .decorationColor!))
), ),
@ -586,7 +588,7 @@ class SendCardState extends State<SendCard>
}); });
noteController.addListener(() { noteController.addListener(() {
final note = noteController.text ?? ''; final note = noteController.text;
if (note != output.note) { if (note != output.note) {
output.note = note; output.note = note;
@ -676,4 +678,4 @@ class SendCardState extends State<SendCard>
@override @override
bool get wantKeepAlive => true; bool get wantKeepAlive => true;
} }

View file

@ -1,4 +1,3 @@
import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -16,10 +15,7 @@ class AddressTextField extends StatelessWidget {
{required this.controller, {required this.controller,
this.isActive = true, this.isActive = true,
this.placeholder, this.placeholder,
this.options = const [ this.options = const [AddressTextFieldOption.qrCode, AddressTextFieldOption.addressBook],
AddressTextFieldOption.qrCode,
AddressTextFieldOption.addressBook
],
this.onURIScanned, this.onURIScanned,
this.focusNode, this.focusNode,
this.isBorderExist = true, this.isBorderExist = true,
@ -31,6 +27,7 @@ class AddressTextField extends StatelessWidget {
this.validator, this.validator,
this.onPushPasteButton, this.onPushPasteButton,
this.onPushAddressBookButton, this.onPushAddressBookButton,
this.onSelectedContact,
this.selectedCurrency}); this.selectedCurrency});
static const prefixIconWidth = 34.0; static const prefixIconWidth = 34.0;
@ -52,6 +49,7 @@ class AddressTextField extends StatelessWidget {
final FocusNode? focusNode; final FocusNode? focusNode;
final Function(BuildContext context)? onPushPasteButton; final Function(BuildContext context)? onPushPasteButton;
final Function(BuildContext context)? onPushAddressBookButton; final Function(BuildContext context)? onPushAddressBookButton;
final Function(ContactBase contact)? onSelectedContact;
final CryptoCurrency? selectedCurrency; final CryptoCurrency? selectedCurrency;
@override @override
@ -66,34 +64,27 @@ class AddressTextField extends StatelessWidget {
controller: controller, controller: controller,
focusNode: focusNode, focusNode: focusNode,
style: textStyle ?? style: textStyle ??
TextStyle( TextStyle(fontSize: 16, color: Theme.of(context).primaryTextTheme.titleLarge!.color!),
fontSize: 16,
color: Theme.of(context).primaryTextTheme!.titleLarge!.color!),
decoration: InputDecoration( decoration: InputDecoration(
suffixIcon: SizedBox( suffixIcon: SizedBox(
width: prefixIconWidth * options.length + width: prefixIconWidth * options.length + (spaceBetweenPrefixIcons * options.length),
(spaceBetweenPrefixIcons * options.length),
), ),
hintStyle: hintStyle ?? hintStyle: hintStyle ?? TextStyle(fontSize: 16, color: Theme.of(context).hintColor),
TextStyle(fontSize: 16, color: Theme.of(context).hintColor),
hintText: placeholder ?? S.current.widgets_address, hintText: placeholder ?? S.current.widgets_address,
focusedBorder: isBorderExist focusedBorder: isBorderExist
? UnderlineInputBorder( ? UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: borderColor ?? Theme.of(context).dividerColor, color: borderColor ?? Theme.of(context).dividerColor, width: 1.0))
width: 1.0))
: InputBorder.none, : InputBorder.none,
disabledBorder: isBorderExist disabledBorder: isBorderExist
? UnderlineInputBorder( ? UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: borderColor ?? Theme.of(context).dividerColor, color: borderColor ?? Theme.of(context).dividerColor, width: 1.0))
width: 1.0))
: InputBorder.none, : InputBorder.none,
enabledBorder: isBorderExist enabledBorder: isBorderExist
? UnderlineInputBorder( ? UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: borderColor ?? Theme.of(context).dividerColor, color: borderColor ?? Theme.of(context).dividerColor, width: 1.0))
width: 1.0))
: InputBorder.none, : InputBorder.none,
), ),
validator: validator, validator: validator,
@ -102,11 +93,11 @@ class AddressTextField extends StatelessWidget {
top: 2, top: 2,
right: 0, right: 0,
child: SizedBox( child: SizedBox(
width: prefixIconWidth * options.length + width: prefixIconWidth * options.length + (spaceBetweenPrefixIcons * options.length),
(spaceBetweenPrefixIcons * options.length),
child: Row( child: Row(
mainAxisAlignment: ResponsiveLayoutUtil.instance.isMobile mainAxisAlignment: ResponsiveLayoutUtil.instance.isMobile
? MainAxisAlignment.spaceBetween : MainAxisAlignment.end, ? MainAxisAlignment.spaceBetween
: MainAxisAlignment.end,
children: [ children: [
SizedBox(width: 5), SizedBox(width: 5),
if (this.options.contains(AddressTextFieldOption.paste)) ...[ if (this.options.contains(AddressTextFieldOption.paste)) ...[
@ -122,20 +113,14 @@ class AddressTextField extends StatelessWidget {
padding: EdgeInsets.all(8), padding: EdgeInsets.all(8),
decoration: BoxDecoration( decoration: BoxDecoration(
color: buttonColor ?? color: buttonColor ??
Theme.of(context) Theme.of(context).accentTextTheme.titleLarge!.color!,
.accentTextTheme borderRadius: BorderRadius.all(Radius.circular(6))),
!
.titleLarge!
.color!,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset( child: Image.asset(
'assets/images/paste_ios.png', 'assets/images/paste_ios.png',
color: iconColor ?? color: iconColor ??
Theme.of(context) Theme.of(context)
.primaryTextTheme .primaryTextTheme
! .headlineMedium!
.headlineMedium!
.decorationColor!, .decorationColor!,
)), )),
), ),
@ -155,28 +140,21 @@ class AddressTextField extends StatelessWidget {
padding: EdgeInsets.all(8), padding: EdgeInsets.all(8),
decoration: BoxDecoration( decoration: BoxDecoration(
color: buttonColor ?? color: buttonColor ??
Theme.of(context) Theme.of(context).accentTextTheme.titleLarge!.color!,
.accentTextTheme borderRadius: BorderRadius.all(Radius.circular(6))),
.titleLarge!
.color!,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset( child: Image.asset(
'assets/images/qr_code_icon.png', 'assets/images/qr_code_icon.png',
color: iconColor ?? color: iconColor ??
Theme.of(context) Theme.of(context)
.primaryTextTheme .primaryTextTheme
!.headlineMedium! .headlineMedium!
.decorationColor!, .decorationColor!,
)), )),
), ),
)) ))
] else ] else
SizedBox(width: 5), SizedBox(width: 5),
if (this if (this.options.contains(AddressTextFieldOption.addressBook)) ...[
.options
.contains(AddressTextFieldOption.addressBook)) ...[
Container( Container(
width: prefixIconWidth, width: prefixIconWidth,
height: prefixIconHeight, height: prefixIconHeight,
@ -184,26 +162,19 @@ class AddressTextField extends StatelessWidget {
child: Semantics( child: Semantics(
label: S.of(context).address_book, label: S.of(context).address_book,
child: InkWell( child: InkWell(
onTap: () async => onTap: () async => _presetAddressBookPicker(context),
_presetAddressBookPicker(context),
child: Container( child: Container(
padding: EdgeInsets.all(8), padding: EdgeInsets.all(8),
decoration: BoxDecoration( decoration: BoxDecoration(
color: buttonColor ?? color: buttonColor ??
Theme.of(context) Theme.of(context).accentTextTheme.titleLarge!.color!,
.accentTextTheme borderRadius: BorderRadius.all(Radius.circular(6))),
!
.titleLarge!
.color!,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset( child: Image.asset(
'assets/images/open_book.png', 'assets/images/open_book.png',
color: iconColor ?? color: iconColor ??
Theme.of(context) Theme.of(context)
.primaryTextTheme .primaryTextTheme
! .headlineMedium!
.headlineMedium!
.decorationColor!, .decorationColor!,
)), )),
), ),
@ -221,30 +192,31 @@ class AddressTextField extends StatelessWidget {
if (code.isEmpty) { if (code.isEmpty) {
return; return;
} }
try { try {
final uri = Uri.parse(code); final uri = Uri.parse(code);
controller?.text = uri.path; controller?.text = uri.path;
onURIScanned?.call(uri); onURIScanned?.call(uri);
} catch(_){ } catch (_) {
controller?.text = code; controller?.text = code;
} }
} }
Future<void> _presetAddressBookPicker(BuildContext context) async { Future<void> _presetAddressBookPicker(BuildContext context) async {
final contact = await Navigator.of(context) final contact = await Navigator.of(context)
.pushNamed(Routes.pickerAddressBook,arguments: selectedCurrency); .pushNamed(Routes.pickerAddressBook, arguments: selectedCurrency);
if (contact is ContactBase && contact.address != null) { if (contact is ContactBase) {
controller?.text = contact.address; controller?.text = contact.address;
onPushAddressBookButton?.call(context); onPushAddressBookButton?.call(context);
onSelectedContact?.call(contact);
} }
} }
Future<void> _pasteAddress(BuildContext context) async { Future<void> _pasteAddress(BuildContext context) async {
final clipboard = await Clipboard.getData('text/plain'); final clipboard = await Clipboard.getData('text/plain');
final address = clipboard?.text ?? ''; final address = clipboard?.text ?? '';
if (address.isNotEmpty) { if (address.isNotEmpty) {
controller?.text = address; controller?.text = address;
} }

View file

@ -17,6 +17,8 @@ import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/entities/contact_base.dart';
part 'output.g.dart'; part 'output.g.dart';
const String cryptoNumberPattern = '0.0'; const String cryptoNumberPattern = '0.0';
@ -70,7 +72,7 @@ abstract class OutputBase with Store {
int amount = 0; int amount = 0;
try { try {
if (cryptoAmount?.isNotEmpty ?? false) { if (cryptoAmount.isNotEmpty) {
final _cryptoAmount = cryptoAmount.replaceAll(',', '.'); final _cryptoAmount = cryptoAmount.replaceAll(',', '.');
int _amount = 0; int _amount = 0;
switch (walletType) { switch (walletType) {
@ -240,4 +242,11 @@ abstract class OutputBase with Store {
extractedAddress = await extractAddressFromParsed(context, parsedAddress); extractedAddress = await extractAddressFromParsed(context, parsedAddress);
note = parsedAddress.description; note = parsedAddress.description;
} }
void loadContact(ContactBase contact) {
address = contact.name;
parsedAddress = ParsedAddress.fetchContactAddress(address: contact.address, name: contact.name);
extractedAddress = parsedAddress.addresses.first;
note = parsedAddress.description;
}
} }

View file

@ -3,7 +3,6 @@ dependencies:
sdk: flutter sdk: flutter
flutter_localizations: flutter_localizations:
sdk: flutter sdk: flutter
flutter_cupertino_localizations: ^1.0.1
intl: ^0.17.0 intl: ^0.17.0
url_launcher: ^6.1.4 url_launcher: ^6.1.4
qr_flutter: qr_flutter:
@ -128,4 +127,4 @@ flutter:
- asset: assets/fonts/Lato-Regular.ttf - asset: assets/fonts/Lato-Regular.ttf
- asset: assets/fonts/Lato-Medium.ttf - asset: assets/fonts/Lato-Medium.ttf
- asset: assets/fonts/Lato-Semibold.ttf - asset: assets/fonts/Lato-Semibold.ttf
- asset: assets/fonts/Lato-Bold.ttf - asset: assets/fonts/Lato-Bold.ttf