diff --git a/lib/g1/g1_helper.dart b/lib/g1/g1_helper.dart index 0d5be13127c7063b1bdfca97c86badda1b34e401..6534ccf083fb431042da735874aa816ac31ef252 100644 --- a/lib/g1/g1_helper.dart +++ b/lib/g1/g1_helper.dart @@ -170,7 +170,8 @@ String getQrUri( return uri; } -PaymentState? parseScannedUri(String qr) { +PaymentState? parseScannedUri(String qrOrig) { + final String qr = Uri.decodeFull(qrOrig); final RegExp regexKeyCommentAmount = RegExp( r'(duniter\:key|june\:\/)/(\w+(:\w{3})?)\?(comment=([^&]+))&amount=([\d.,]+)'); final RegExp regexKeyAmountComment = RegExp( @@ -198,7 +199,7 @@ PaymentState? parseScannedUri(String qr) { return PaymentState( contact: Contact(pubKey: publicKey), amount: amount, - comment: comment ?? ''); + comment: cleanComment(comment)); } if (matchKeyAmountComment != null) { @@ -210,14 +211,14 @@ PaymentState? parseScannedUri(String qr) { return PaymentState( contact: Contact(pubKey: publicKey), amount: amount, - comment: comment ?? ''); + comment: cleanComment(comment)); } if (matchKeyComment != null) { final String publicKey = matchKeyComment.group(2)!; final String? comment = matchKeyComment.group(4); return PaymentState( - contact: Contact(pubKey: publicKey), comment: comment ?? ''); + contact: Contact(pubKey: publicKey), comment: cleanComment(comment)); } final RegExpMatch? matchKeyAmount = regexKeyAmount.firstMatch(qr); diff --git a/lib/ui/ui_helpers.dart b/lib/ui/ui_helpers.dart index 07ef7c230c4e443121d4ee2655349a68e28b9164..ae563ba318682a8ca6b63eec9b7269d5832b9f88 100644 --- a/lib/ui/ui_helpers.dart +++ b/lib/ui/ui_helpers.dart @@ -86,9 +86,9 @@ String humanizeFromToPubKey(String publicAddress, String address) { } String humanizeContact(String publicAddress, Contact contact, - [bool addKey = false]) { + [bool addKey = false, String Function(String s) trf = tr]) { if (contact.pubKey == publicAddress) { - return tr('your_wallet'); + return trf('your_wallet'); } else { final String pubKey = humanizePubKey(contact.pubKey); final bool titleNotTheSameAsPubKey = contact.title != pubKey; @@ -232,6 +232,15 @@ Future<Uint8List?> resizeAvatar(Uint8List avatarBase64) async { final RegExp basicEnglishCharsRegExp = RegExp(r'^[ A-Za-z0-9\s.;:!?()\-_;!@&<>%]*$'); +final RegExp basicEnglishCharsRegExpNegative = + RegExp(r'[^ A-Za-z0-9\s.;:!?()\-_;!@&<>%]'); + +String cleanComment(String? comment) { + return comment == null + ? '' + : comment.replaceAllMapped( + basicEnglishCharsRegExpNegative, (Match match) => ' '); +} void fetchTransactions(BuildContext context) { final AppCubit appCubit = context.read<AppCubit>(); @@ -425,7 +434,10 @@ bool isSymbolPlacementBefore(String pattern) { String currentLocale(BuildContext context) => context.locale.languageCode; String? validateDecimal( - {required String sep, required String locale, required String? amount}) { + {required String sep, + required String locale, + required String? amount, + required String Function(String s) tr}) { final NumberFormat format = NumberFormat.decimalPattern(locale); if (amount == null || amount.isEmpty || amount.startsWith(sep)) { return null; @@ -436,6 +448,10 @@ String? validateDecimal( return tr('enter_a_positive_number'); } final String formattedAmount = format.format(n); + if (amount.contains(sep) && amount.endsWith('0')) { + // remove trailing zeros in 0.10 == 0.1 + amount = amount.replaceAll(RegExp(r'0*$'), ''); + } if (formattedAmount != amount) { return tr('enter_a_valid_number'); } diff --git a/test/g1_test.dart b/test/g1_test.dart index b9f25395c97949603a5552089899a6b57975ce33..9a911f3c0b2da582dc72ff20acdc33f04b3d180c 100644 --- a/test/g1_test.dart +++ b/test/g1_test.dart @@ -54,91 +54,118 @@ void main() { equals(true)); }); - test('validate qr uris', () { - const String baseKey = 'FRYyk57Pi456EJRu9vqVfSHLgmUfx4Qc3goS62a7dUSm'; - final String publicKeyWithChecksum = getFullPubKey(baseKey); - - final List<String> keys = <String>[baseKey, publicKeyWithChecksum]; - - for (final String publicKey in keys) { - final String uriA = getQrUri(pubKey: publicKey, amount: '10'); - final PaymentState? payA = parseScannedUri(uriA); - expect(payA!.amount, equals(10), reason: 'amount should be 10 in $uriA'); - expect(payA.contact!.pubKey, equals(publicKey)); - - final String uriB = getQrUri(pubKey: publicKey); - final PaymentState? payB = parseScannedUri(uriB); - expect(payB!.amount, equals(null)); - expect(payB.contact!.pubKey, equals(publicKey)); - - final PaymentState? payC = parseScannedUri(publicKey); - expect(payC!.amount, equals(null)); - expect(payC.contact!.pubKey, equals(publicKey)); - - final String uriD = getQrUri(pubKey: publicKey, amount: '10.10'); - final PaymentState? payD = parseScannedUri(uriD); - expect(payD!.amount, equals(10.10)); - expect(payD.contact!.pubKey, equals(publicKey)); - - final String uriE = - getQrUri(pubKey: publicKey, amount: '10,10', locale: 'es'); - final PaymentState? payE = parseScannedUri(uriE); - expect(payE!.amount, equals(10.10)); - expect(payE.contact!.pubKey, equals(publicKey)); - - final String uriF = 'june://$publicKey?amount=100'; - final PaymentState? payF = parseScannedUri(uriF); - expect(payF!.amount, equals(100)); - expect(payF.contact!.pubKey, equals(publicKey)); - - final String uriJ = - 'june://$publicKey?comment=GCHANGE:AYDI9JPOVIL9ZVG-PNCU&amount=100'; - final PaymentState? payJ = parseScannedUri(uriJ); - expect(payJ!.comment, equals('GCHANGE:AYDI9JPOVIL9ZVG-PNCU')); - expect(payJ.amount, equals(100)); - expect(payJ.contact!.pubKey, equals(publicKey)); - - const String uriK = - 'june://DsEx1pS33vzYZg4MroyBV9hCw98j1gtHEhwiZ5tK7ech?amount=10&comment=This Is my comment'; - final PaymentState? payK = parseScannedUri(uriK); - expect(payK!.comment, equals('This Is my comment')); - expect(payK.amount, equals(10)); - expect(payK.contact!.pubKey, - equals('DsEx1pS33vzYZg4MroyBV9hCw98j1gtHEhwiZ5tK7ech')); - - const String uriL = - 'june://DsEx1pS33vzYZg4MroyBV9hCw98j1gtHEhwiZ5tK7ech?comment=This Is my comment&amount=10'; - final PaymentState? payL = parseScannedUri(uriL); - expect(payL!.comment, equals('This Is my comment')); - expect(payL.amount, equals(10)); - expect(payL.contact!.pubKey, - equals('DsEx1pS33vzYZg4MroyBV9hCw98j1gtHEhwiZ5tK7ech')); - - const String uriM = - 'june://DsEx1pS33vzYZg4MroyBV9hCw98j1gtHEhwiZ5tK7ech?comment=Mi comentario&amount=10,0'; - final PaymentState? payM = parseScannedUri(uriM); - expect(payM!.comment, equals('Mi comentario')); - expect(payM.amount, equals(10)); - expect(payM.contact!.pubKey, - equals('DsEx1pS33vzYZg4MroyBV9hCw98j1gtHEhwiZ5tK7ech')); - - const String uriN = - 'june://DsEx1pS33vzYZg4MroyBV9hCw98j1gtHEhwiZ5tK7ech?comment=This Is my comment'; - final PaymentState? payN = parseScannedUri(uriN); - expect(payN!.amount == null, equals(true)); - expect(payN.comment, equals('This Is my comment')); - expect(payN.contact!.pubKey, - equals('DsEx1pS33vzYZg4MroyBV9hCw98j1gtHEhwiZ5tK7ech')); - - const String uriO = - 'june://DsEx1pS33vzYZg4MroyBV9hCw98j1gtHEhwiZ5tK7ech:XXX?comment=This Is my comment'; - final PaymentState? payO = parseScannedUri(uriO); - expect(payO!.amount == null, equals(true)); - expect(payO.comment, equals('This Is my comment')); - expect(payO.contact!.pubKey, - equals('DsEx1pS33vzYZg4MroyBV9hCw98j1gtHEhwiZ5tK7ech:XXX')); - } - }); + const String baseKey = 'FRYyk57Pi456EJRu9vqVfSHLgmUfx4Qc3goS62a7dUSm'; + final String publicKeyWithChecksum = getFullPubKey(baseKey); + final List<String> keys = <String>[baseKey, publicKeyWithChecksum]; + + for (final String publicKey in keys) { + group('Tests for publicKey: $publicKey', () { + test('validate qr uri with amount', () { + final String uriA = getQrUri(pubKey: publicKey, amount: '10'); + final PaymentState? payA = parseScannedUri(uriA); + expect(payA!.amount, equals(10), + reason: 'amount should be 10 in $uriA'); + expect(payA.contact!.pubKey, equals(publicKey)); + }); + + test('validate qr uri without amount', () { + final String uriB = getQrUri(pubKey: publicKey); + final PaymentState? payB = parseScannedUri(uriB); + expect(payB!.amount, equals(null)); + expect(payB.contact!.pubKey, equals(publicKey)); + }); + + test('validate qr scanned', () { + final PaymentState? payC = parseScannedUri(publicKey); + expect(payC!.amount, equals(null)); + expect(payC.contact!.pubKey, equals(publicKey)); + }); + + test('validate qr uri with decimal amount', () { + final String uriD = getQrUri(pubKey: publicKey, amount: '10.10'); + final PaymentState? payD = parseScannedUri(uriD); + expect(payD!.amount, equals(10.10)); + expect(payD.contact!.pubKey, equals(publicKey)); + }); + + test('validate qr uri with localized decimal amount', () { + final String uriE = + getQrUri(pubKey: publicKey, amount: '10,10', locale: 'es'); + final PaymentState? payE = parseScannedUri(uriE); + expect(payE!.amount, equals(10.10)); + expect(payE.contact!.pubKey, equals(publicKey)); + }); + + test('validate custom june uri with amount', () { + final String uriF = 'june://$publicKey?amount=100'; + final PaymentState? payF = parseScannedUri(uriF); + expect(payF!.amount, equals(100)); + expect(payF.contact!.pubKey, equals(publicKey)); + }); + + test('validate june uri with comment and amount', () { + final String uriJ = + 'june://$publicKey?comment=GCHANGE:AYDI9JPOVIL9ZVG-PNCU&amount=100'; + final PaymentState? payJ = parseScannedUri(uriJ); + expect(payJ!.comment, equals('GCHANGE:AYDI9JPOVIL9ZVG-PNCU')); + expect(payJ.amount, equals(100)); + expect(payJ.contact!.pubKey, equals(publicKey)); + }); + + test('validate june uri with amount and comment', () { + final String uriK = + 'june://$publicKey?amount=10&comment=This Is my comment'; + final PaymentState? payK = parseScannedUri(uriK); + expect(payK!.comment, equals('This Is my comment')); + expect(payK.amount, equals(10)); + expect(payK.contact!.pubKey, equals(publicKey)); + }); + + test('validate june uri with reordered comment and amount', () { + final String uriL = + 'june://$publicKey?comment=This Is my comment&amount=10'; + final PaymentState? payL = parseScannedUri(uriL); + expect(payL!.comment, equals('This Is my comment')); + expect(payL.amount, equals(10)); + expect(payL.contact!.pubKey, equals(publicKey)); + }); + + test('validate june uri with localized amount and comment', () { + final String uriM = + 'june://$publicKey?comment=Mi comentario&amount=10,0'; + final PaymentState? payM = parseScannedUri(uriM); + expect(payM!.comment, equals('Mi comentario')); + expect(payM.amount, equals(10)); + expect(payM.contact!.pubKey, equals(publicKey)); + }); + + test('validate june uri with comment only', () { + final String uriN = 'june://$publicKey?comment=This Is my comment'; + final PaymentState? payN = parseScannedUri(uriN); + expect(payN!.amount == null, equals(true)); + expect(payN.comment, equals('This Is my comment')); + expect(payN.contact!.pubKey, equals(publicKey)); + }); + + test('validate june uri with encoded uri', () { + final String uriN = + Uri.encodeFull('june://$publicKey?comment=This Is my comment'); + final PaymentState? payN = parseScannedUri(uriN); + expect(payN!.amount == null, equals(true)); + expect(payN.comment, equals('This Is my comment')); + expect(payN.contact!.pubKey, equals(publicKey)); + }); + + test('Replace incorrect comment characters', () { + final String uriN = Uri.encodeFull( + 'june://$publicKey?comment=This Is my comment,!%áéÃóú'); + final PaymentState? payN = parseScannedUri(uriN); + expect(payN!.amount == null, equals(true)); + expect(payN.comment, equals('This Is my comment !% ')); + expect(payN.contact!.pubKey, equals(publicKey)); + }); + }); + } test('encrypt/decrypt of keys', () { const String pass = '1234';