From 3f2df41612506033f5027b7f49ea08819b8fe13f Mon Sep 17 00:00:00 2001 From: Daniel Possienke Date: Wed, 30 Mar 2022 15:29:53 +0200 Subject: [PATCH 1/2] add phone number linkifier --- README.md | 2 +- lib/src/phone_number.dart | 72 ++++++++++++++++++++++++++++++++ test/linkify_test.dart | 87 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 158 insertions(+), 3 deletions(-) create mode 100644 lib/src/phone_number.dart diff --git a/README.md b/README.md index d9d28ac..5592865 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # `linkify` [![pub package](https://img.shields.io/pub/v/linkify.svg)](https://pub.dartlang.org/packages/linkify) -Low-level link (text, URLs, emails) parsing library in Dart. +Low-level link (text, URLs, emails, phone numbers) parsing library in Dart. Required Dart >=2.12 (has null-safety support). diff --git a/lib/src/phone_number.dart b/lib/src/phone_number.dart new file mode 100644 index 0000000..5ee5427 --- /dev/null +++ b/lib/src/phone_number.dart @@ -0,0 +1,72 @@ +import 'package:linkify/linkify.dart'; + +final _phoneNumberRegex = RegExp( + // r'(.*?)((\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]\d{3}[\s.-]\d{4})', + r'^(.*?)((tel:)?[+]*[\s/0-9]{8,15})', + caseSensitive: false, + dotAll: true, +); + +class PhoneNumberLinkifier extends Linkifier { + const PhoneNumberLinkifier(); + + @override + List parse(elements, options) { + final list = []; + + elements.forEach((element) { + if (element is TextElement) { + var match = _phoneNumberRegex.firstMatch(element.text); + + if (match == null) { + list.add(element); + } else { + final text = element.text.replaceFirst(match.group(0)!, ''); + + if (match.group(1)?.isNotEmpty == true) { + list.add(TextElement(match.group(1)!)); + } + + if (match.group(2)?.isNotEmpty == true) { + list.add(PhoneNumberElement( + match.group(2)!.replaceFirst(RegExp(r'tel:'), ''), + )); + } + + if (text.isNotEmpty) { + list.addAll(parse([TextElement(text)], options)); + } + } + } else { + list.add(element); + } + }); + + return list; + } +} + +/// Represents an element containing a phone number +class PhoneNumberElement extends LinkableElement { + final String phoneNumber; + + PhoneNumberElement(this.phoneNumber) + : super( + phoneNumber, + 'tel:$phoneNumber', + ); + + @override + String toString() { + return "PhoneNumberElement: '$phoneNumber' ($text)"; + } + + @override + bool operator ==(other) => equals(other); + + @override + bool equals(other) => + other is PhoneNumberElement && + super.equals(other) && + other.phoneNumber == phoneNumber; +} diff --git a/test/linkify_test.dart b/test/linkify_test.dart index f3920d3..166db4a 100644 --- a/test/linkify_test.dart +++ b/test/linkify_test.dart @@ -2,6 +2,8 @@ import 'package:collection/collection.dart'; import 'package:linkify/linkify.dart'; import 'package:test/test.dart'; +import '../lib/src/phone_number.dart'; + final listEqual = const ListEquality().equals; void expectListEqual(List actual, List expected) { @@ -215,14 +217,28 @@ void main() { test('Parses user tag', () { expectListEqual( - linkify("@example"), + linkify( + "@example", + linkifiers: [ + UrlLinkifier(), + EmailLinkifier(), + UserTagLinkifier(), + ], + ), [UserTagElement("@example")], ); }); test('Parses email, link, and user tag', () { expectListEqual( - linkify("person@example.com at https://google.com @example"), + linkify( + "person@example.com at https://google.com @example", + linkifiers: [ + UrlLinkifier(), + EmailLinkifier(), + UserTagLinkifier(), + ], + ), [ EmailElement("person@example.com"), TextElement(" at "), @@ -232,4 +248,71 @@ void main() { ], ); }); + + test('Parses invalid phone number', () { + expectListEqual( + linkify( + "This is an invalid numbers 17.00", + linkifiers: [ + UrlLinkifier(), + EmailLinkifier(), + PhoneNumberLinkifier(), + ], + ), + [ + TextElement("This is an invalid numbers 17.00"), + ], + ); + }); + + test('Parses german phone number', () { + expectListEqual( + linkify( + "This is a german example number +49 30 901820", + linkifiers: [ + UrlLinkifier(), + EmailLinkifier(), + PhoneNumberLinkifier(), + ], + ), + [ + TextElement("This is a german example number "), + PhoneNumberElement("+49 30 901820"), + ], + ); + }); + + test('Parses seattle phone number', () { + expectListEqual( + linkify( + "This is a seattle example number +1 206 555 0100", + linkifiers: [ + UrlLinkifier(), + EmailLinkifier(), + PhoneNumberLinkifier(), + ], + ), + [ + TextElement("This is a seattle example number "), + PhoneNumberElement("+1 206 555 0100"), + ], + ); + }); + + test('Parses uk phone number', () { + expectListEqual( + linkify( + "This is an example number from uk: +44 113 496 0000", + linkifiers: [ + UrlLinkifier(), + EmailLinkifier(), + PhoneNumberLinkifier(), + ], + ), + [ + TextElement("This is an example number from uk: "), + PhoneNumberElement("+44 113 496 0000"), + ], + ); + }); } From 1312a34a30cf8d62feb2721a0c908e2b2f1a98c2 Mon Sep 17 00:00:00 2001 From: Daniel Possienke Date: Wed, 30 Mar 2022 15:34:12 +0200 Subject: [PATCH 2/2] remove old reg exp --- lib/src/phone_number.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/src/phone_number.dart b/lib/src/phone_number.dart index 5ee5427..7f63673 100644 --- a/lib/src/phone_number.dart +++ b/lib/src/phone_number.dart @@ -1,7 +1,6 @@ import 'package:linkify/linkify.dart'; final _phoneNumberRegex = RegExp( - // r'(.*?)((\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]\d{3}[\s.-]\d{4})', r'^(.*?)((tel:)?[+]*[\s/0-9]{8,15})', caseSensitive: false, dotAll: true,