From 010f4807fd2844d61d9f4f18685303580c104f57 Mon Sep 17 00:00:00 2001 From: HuyNguyen Date: Mon, 4 Nov 2024 18:57:25 +0700 Subject: [PATCH 1/5] TW-2114: Display phone number as a clickable in message bubble --- lib/pages/chat/events/html_message.dart | 13 +++++++++-- lib/presentation/mixins/linkify_mixin.dart | 23 +++++++++++++++++++ lib/utils/url_launcher.dart | 2 +- .../twake_link_preview.dart | 21 +++++++++++------ pubspec.lock | 19 +++++++++++---- pubspec.yaml | 7 +++++- 6 files changed, 69 insertions(+), 16 deletions(-) create mode 100644 lib/presentation/mixins/linkify_mixin.dart diff --git a/lib/pages/chat/events/html_message.dart b/lib/pages/chat/events/html_message.dart index f7189aa397..50360b7807 100644 --- a/lib/pages/chat/events/html_message.dart +++ b/lib/pages/chat/events/html_message.dart @@ -2,6 +2,7 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/domain/model/extensions/string_extension.dart'; import 'package:fluffychat/pages/image_viewer/image_viewer.dart'; +import 'package:fluffychat/presentation/mixins/linkify_mixin.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/utils/url_launcher.dart'; import 'package:fluffychat/widgets/mentioned_user.dart'; @@ -9,12 +10,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_matrix_html/flutter_html.dart'; +import 'package:linkfy_text/linkfy_text.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/presentation/extensions/send_file_extension.dart'; import 'package:fluffychat/widgets/matrix.dart'; -class HtmlMessage extends StatelessWidget { +class HtmlMessage extends StatelessWidget with LinkifyMixin { final String html; final int? maxLines; final Room room; @@ -70,9 +72,16 @@ class HtmlMessage extends StatelessWidget { decoration: TextDecoration.underline, decorationColor: themeData.colorScheme.secondary, ), + linkTypes: const [ + LinkType.url, + LinkType.phone, + ], shrinkToFit: true, maxLines: maxLines, - onLinkTap: (url) => UrlLauncher(context, url: url.toString()).launchUrl(), + onLinkTap: (link) => handleOnTappedLinkHtml( + context: context, + link: link, + ), onPillTap: !room.isDirectChat ? (url) { UrlLauncher(context, url: url, room: room).launchUrl(); diff --git a/lib/presentation/mixins/linkify_mixin.dart b/lib/presentation/mixins/linkify_mixin.dart new file mode 100644 index 0000000000..23e26f39d3 --- /dev/null +++ b/lib/presentation/mixins/linkify_mixin.dart @@ -0,0 +1,23 @@ +import 'package:fluffychat/utils/url_launcher.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:linkfy_text/linkfy_text.dart'; +import 'package:matrix/matrix.dart'; + +mixin LinkifyMixin { + void handleOnTappedLinkHtml({ + required BuildContext context, + required Link link, + }) { + Logs().i('LinkifyMixin: handleOnTappedLink: link: $link'); + switch (link.type) { + case LinkType.url: + UrlLauncher(context, url: link.value.toString()).launchUrl(); + break; + case LinkType.phone: + break; + default: + Logs().i('LinkifyMixin: handleOnTappedLink: Unhandled link: $link'); + break; + } + } +} diff --git a/lib/utils/url_launcher.dart b/lib/utils/url_launcher.dart index acc1a64f0f..0e67105f9d 100644 --- a/lib/utils/url_launcher.dart +++ b/lib/utils/url_launcher.dart @@ -73,7 +73,7 @@ class UrlLauncher with GoToDraftChatMixin { return; } } - launchUrlString(url!); + launchUrlString('https://$url'); return; } if (uri.host.isEmpty) { diff --git a/lib/widgets/twake_components/twake_preview_link/twake_link_preview.dart b/lib/widgets/twake_components/twake_preview_link/twake_link_preview.dart index fd157dc504..d7385c5444 100644 --- a/lib/widgets/twake_components/twake_preview_link/twake_link_preview.dart +++ b/lib/widgets/twake_components/twake_preview_link/twake_link_preview.dart @@ -1,16 +1,16 @@ import 'package:fluffychat/domain/app_state/preview_url/get_preview_url_success.dart'; import 'package:fluffychat/pages/chat/events/formatted_text_widget.dart'; import 'package:fluffychat/presentation/extensions/media/url_preview_extension.dart'; +import 'package:fluffychat/presentation/mixins/linkify_mixin.dart'; import 'package:fluffychat/utils/string_extension.dart'; -import 'package:fluffychat/utils/url_launcher.dart'; import 'package:fluffychat/widgets/mixins/get_preview_url_mixin.dart'; import 'package:fluffychat/widgets/twake_components/twake_preview_link/twake_link_preview_item.dart'; import 'package:fluffychat/widgets/twake_components/twake_preview_link/twake_link_preview_item_style.dart'; import 'package:fluffychat/widgets/twake_components/twake_preview_link/twake_link_view.dart'; import 'package:flutter/material.dart'; import 'package:linagora_design_flutter/linagora_design_flutter.dart'; +import 'package:linkfy_text/linkfy_text.dart'; import 'package:matrix/matrix.dart' hide Visibility; -import 'package:matrix_link_text/link_text.dart'; import 'package:skeletonizer/skeletonizer.dart'; class TwakeLinkPreview extends StatefulWidget { @@ -38,7 +38,7 @@ class TwakeLinkPreview extends StatefulWidget { } class TwakeLinkPreviewController extends State - with GetPreviewUrlMixin, AutomaticKeepAliveClientMixin { + with GetPreviewUrlMixin, AutomaticKeepAliveClientMixin, LinkifyMixin { String? get firstValidUrl => widget.localizedBody.getFirstValidUrl(); Uri get uri => Uri.parse(firstValidUrl ?? ''); @@ -70,13 +70,20 @@ class TwakeLinkPreviewController extends State linkStyle: widget.linkStyle, fontSize: widget.fontSize, ) - : LinkText( - text: widget.localizedBody, + : LinkifyText( + widget.localizedBody, textStyle: widget.richTextStyle, linkStyle: widget.linkStyle, + softWrap: false, + linkTypes: const [ + LinkType.url, + LinkType.phone, + ], textAlign: TextAlign.start, - onLinkTap: (url) => - UrlLauncher(context, url: url.toString()).launchUrl(), + onLinkTap: (link) => handleOnTappedLinkHtml( + context: context, + link: link, + ), ), previewItemWidget: ValueListenableBuilder( valueListenable: getPreviewUrlStateNotifier, diff --git a/pubspec.lock b/pubspec.lock index afc9ff5fe7..20a0c6f7f7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1168,8 +1168,8 @@ packages: dependency: "direct main" description: path: "." - ref: master - resolved-ref: c4e9f7ab8e093e7e23be3a5f15d901781305ffa7 + ref: implement-linkify-text + resolved-ref: "61b580e093ad2bbcc3a620ac76bd70395c8b6f8f" url: "https://github.com/linagora/flutter_matrix_html.git" source: git version: "1.2.0" @@ -1791,6 +1791,15 @@ packages: url: "git@github.com:linagora/linagora-design-flutter.git" source: git version: "0.0.1" + linkfy_text: + dependency: "direct main" + description: + path: "." + ref: refactor-linkify + resolved-ref: "48d446a8314299d2986078020bb5b55126467bf2" + url: "git@github.com:nqhhdev/linkfy_text.git" + source: git + version: "1.1.6" lints: dependency: transitive description: @@ -2990,10 +2999,10 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: "6ce1e04375be4eed30548f10a315826fd933c1e493206eab82eed01f438c8d2e" + sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603" url: "https://pub.dev" source: hosted - version: "6.2.6" + version: "6.3.1" url_launcher_android: dependency: transitive description: @@ -3385,4 +3394,4 @@ packages: version: "3.1.2" sdks: dart: ">=3.5.0-259.0.dev <4.0.0" - flutter: ">=3.19.0" + flutter: ">=3.19.3" diff --git a/pubspec.yaml b/pubspec.yaml index 772e2f8eae..1087eef90e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -33,7 +33,7 @@ dependencies: flutter_matrix_html: git: url: https://github.com/linagora/flutter_matrix_html.git - ref: master + ref: implement-linkify-text contacts_service: git: @@ -60,6 +60,11 @@ dependencies: url: git@github.com:linagora/future-loading-dialog.git ref: main + linkfy_text: + git: + url: git@github.com:nqhhdev/linkfy_text.git + ref: refactor-linkify + adaptive_dialog: ^1.8.0+1 flutter_adaptive_scaffold: ^0.1.4 animations: ^2.0.7 From af677f6fbb96025daab75f17eaa9e02284553ba7 Mon Sep 17 00:00:00 2001 From: HuyNguyen Date: Wed, 6 Nov 2024 10:54:09 +0700 Subject: [PATCH 2/5] TW-2114: Add context menu when click on phone number on web --- assets/l10n/intl_en.arb | 9 +- lib/pages/chat/chat_context_menu_actions.dart | 6 +- lib/pages/chat/events/html_message.dart | 5 +- lib/presentation/mixins/linkify_mixin.dart | 108 +++++++++++++++++- .../twake_link_preview.dart | 4 +- pubspec.lock | 4 +- 6 files changed, 123 insertions(+), 13 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index b99c4eed3e..e06006c0ad 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -3084,7 +3084,7 @@ "sSwitchCameraLensDirectionLabel": "Switch to the {value} camera", "photo": "Photo", "video": "Video", - "message":"Message", + "message": "Message", "fileTooBig": "The selected file is too large. Please choose a file smaller than {maxSize} MB.", "@fileTooBig": { "placeholders": { @@ -3093,7 +3093,8 @@ } } }, - "enable_notifications":"Enable notifications", - "disable_notifications":"Disable notifications", - "logoutDialogWarning": "You will lose access to encrypted messages. We recommend that you enable chat backups before logging out" + "enable_notifications": "Enable notifications", + "disable_notifications": "Disable notifications", + "logoutDialogWarning": "You will lose access to encrypted messages. We recommend that you enable chat backups before logging out", + "copyNumber": "Copy number" } diff --git a/lib/pages/chat/chat_context_menu_actions.dart b/lib/pages/chat/chat_context_menu_actions.dart index 2fb4c72fcd..8ad4dea4e6 100644 --- a/lib/pages/chat/chat_context_menu_actions.dart +++ b/lib/pages/chat/chat_context_menu_actions.dart @@ -8,7 +8,8 @@ enum ChatContextMenuActions { pinChat, forward, downloadFile, - jumpToMessage; + jumpToMessage, + copyNumber; String getTitle( BuildContext context, { @@ -30,6 +31,8 @@ enum ChatContextMenuActions { return L10n.of(context)!.download; case ChatContextMenuActions.jumpToMessage: return L10n.of(context)!.jumpToMessage; + case ChatContextMenuActions.copyNumber: + return L10n.of(context)!.copyNumber; } } @@ -41,6 +44,7 @@ enum ChatContextMenuActions { case ChatContextMenuActions.select: return isSelected ? Icons.circle_outlined : Icons.check_circle_outline; case ChatContextMenuActions.copyMessage: + case ChatContextMenuActions.copyNumber: return Icons.content_copy; case ChatContextMenuActions.pinChat: return !unpin ? Icons.push_pin_outlined : null; diff --git a/lib/pages/chat/events/html_message.dart b/lib/pages/chat/events/html_message.dart index 50360b7807..12d8407aff 100644 --- a/lib/pages/chat/events/html_message.dart +++ b/lib/pages/chat/events/html_message.dart @@ -25,7 +25,7 @@ class HtmlMessage extends StatelessWidget with LinkifyMixin { final double? emoteSize; final Widget? bottomWidgetSpan; - const HtmlMessage({ + HtmlMessage({ super.key, required this.html, this.maxLines, @@ -78,8 +78,9 @@ class HtmlMessage extends StatelessWidget with LinkifyMixin { ], shrinkToFit: true, maxLines: maxLines, - onLinkTap: (link) => handleOnTappedLinkHtml( + onLinkTap: (tapDownDetails, link) => handleOnTappedLinkHtml( context: context, + details: tapDownDetails, link: link, ), onPillTap: !room.isDirectChat diff --git a/lib/presentation/mixins/linkify_mixin.dart b/lib/presentation/mixins/linkify_mixin.dart index 23e26f39d3..c1a3bf399c 100644 --- a/lib/presentation/mixins/linkify_mixin.dart +++ b/lib/presentation/mixins/linkify_mixin.dart @@ -1,23 +1,127 @@ +import 'package:fluffychat/pages/chat/chat_context_menu_actions.dart'; +import 'package:fluffychat/utils/extension/value_notifier_extension.dart'; import 'package:fluffychat/utils/url_launcher.dart'; -import 'package:flutter/cupertino.dart'; +import 'package:fluffychat/widgets/context_menu/context_menu_action.dart'; +import 'package:fluffychat/widgets/context_menu/twake_context_menu.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:linkfy_text/linkfy_text.dart'; import 'package:matrix/matrix.dart'; mixin LinkifyMixin { + final ValueNotifier openingPopupMenu = ValueNotifier(false); + + List get phoneNumberContextMenu => [ + ChatContextMenuActions.copyNumber, + ]; + + List _mapPopupMenuActionsToContextMenuActions({ + required BuildContext context, + required List actions, + }) { + return actions.map((action) { + return ContextMenuAction( + name: action.getTitle( + context, + ), + icon: action.getIconData(), + ); + }).toList(); + } + + void _handleStateContextMenu() { + openingPopupMenu.toggle(); + } + + void _handleContextMenuAction({ + required BuildContext context, + required TapDownDetails tapDownDetails, + required String number, + }) async { + final offset = tapDownDetails.globalPosition; + final listActions = _mapPopupMenuActionsToContextMenuActions( + context: context, + actions: phoneNumberContextMenu, + ); + final selectedActionIndex = await showTwakeContextMenu( + context: context, + offset: offset, + listActions: listActions, + onClose: _handleStateContextMenu, + ); + if (selectedActionIndex != null && selectedActionIndex is int) { + _handleClickOnContextMenuItem( + action: phoneNumberContextMenu[selectedActionIndex], + number: number, + ); + } + } + + Future showTwakeContextMenu({ + required List listActions, + required Offset offset, + required BuildContext context, + double? verticalPadding, + VoidCallback? onClose, + }) async { + dynamic result; + await showDialog( + context: context, + barrierColor: Colors.transparent, + barrierDismissible: false, + builder: (dialogContext) => TwakeContextMenu( + dialogContext: dialogContext, + listActions: listActions, + position: offset, + verticalPadding: verticalPadding, + ), + ).then((value) { + result = value; + onClose?.call(); + }); + return result; + } + + void _handleClickOnContextMenuItem({ + required ChatContextMenuActions action, + required String number, + }) async { + switch (action) { + case ChatContextMenuActions.copyNumber: + Logs().i('LinkifyMixin: handleContextMenuAction: copyNumber $number'); + await Clipboard.setData(ClipboardData(text: number)); + break; + default: + break; + } + } + void handleOnTappedLinkHtml({ required BuildContext context, + required TapDownDetails details, required Link link, }) { - Logs().i('LinkifyMixin: handleOnTappedLink: link: $link'); + Logs().i( + 'LinkifyMixin: handleOnTappedLink: type: ${link.type} link: ${link.value}', + ); switch (link.type) { case LinkType.url: UrlLauncher(context, url: link.value.toString()).launchUrl(); break; case LinkType.phone: + _handleContextMenuAction( + context: context, + tapDownDetails: details, + number: link.value.toString(), + ); break; default: Logs().i('LinkifyMixin: handleOnTappedLink: Unhandled link: $link'); break; } } + + void dispose() { + openingPopupMenu.dispose(); + } } diff --git a/lib/widgets/twake_components/twake_preview_link/twake_link_preview.dart b/lib/widgets/twake_components/twake_preview_link/twake_link_preview.dart index d7385c5444..09d0408702 100644 --- a/lib/widgets/twake_components/twake_preview_link/twake_link_preview.dart +++ b/lib/widgets/twake_components/twake_preview_link/twake_link_preview.dart @@ -74,14 +74,14 @@ class TwakeLinkPreviewController extends State widget.localizedBody, textStyle: widget.richTextStyle, linkStyle: widget.linkStyle, - softWrap: false, linkTypes: const [ LinkType.url, LinkType.phone, ], textAlign: TextAlign.start, - onLinkTap: (link) => handleOnTappedLinkHtml( + onLinkTap: (tapDownDetails, link) => handleOnTappedLinkHtml( context: context, + details: tapDownDetails, link: link, ), ), diff --git a/pubspec.lock b/pubspec.lock index 20a0c6f7f7..7329312962 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1169,7 +1169,7 @@ packages: description: path: "." ref: implement-linkify-text - resolved-ref: "61b580e093ad2bbcc3a620ac76bd70395c8b6f8f" + resolved-ref: "7884a5fa64cbd40f3997c4071bbbdc67049c17ae" url: "https://github.com/linagora/flutter_matrix_html.git" source: git version: "1.2.0" @@ -1796,7 +1796,7 @@ packages: description: path: "." ref: refactor-linkify - resolved-ref: "48d446a8314299d2986078020bb5b55126467bf2" + resolved-ref: b4a418e454ef48c60829d6840b1778f03302f8bf url: "git@github.com:nqhhdev/linkfy_text.git" source: git version: "1.1.6" From da9ccd97fc9364734b2a76483bab86b5c10a4bb0 Mon Sep 17 00:00:00 2001 From: HuyNguyen Date: Wed, 6 Nov 2024 14:37:59 +0700 Subject: [PATCH 3/5] TW-2114: Add context menu when click on phone number on mobile --- assets/l10n/intl_en.arb | 3 +- lib/presentation/mixins/linkify_mixin.dart | 64 ++++++++++++++++++++-- pubspec.lock | 12 +++- pubspec.yaml | 1 + 4 files changed, 71 insertions(+), 9 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index e06006c0ad..01a399137b 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -3096,5 +3096,6 @@ "enable_notifications": "Enable notifications", "disable_notifications": "Disable notifications", "logoutDialogWarning": "You will lose access to encrypted messages. We recommend that you enable chat backups before logging out", - "copyNumber": "Copy number" + "copyNumber": "Copy number", + "callViaCarrier": "Call via Carrier" } diff --git a/lib/presentation/mixins/linkify_mixin.dart b/lib/presentation/mixins/linkify_mixin.dart index c1a3bf399c..18da2c6544 100644 --- a/lib/presentation/mixins/linkify_mixin.dart +++ b/lib/presentation/mixins/linkify_mixin.dart @@ -1,16 +1,25 @@ import 'package:fluffychat/pages/chat/chat_context_menu_actions.dart'; +import 'package:fluffychat/utils/extension/build_context_extension.dart'; import 'package:fluffychat/utils/extension/value_notifier_extension.dart'; +import 'package:fluffychat/utils/responsive/responsive_utils.dart'; import 'package:fluffychat/utils/url_launcher.dart'; import 'package:fluffychat/widgets/context_menu/context_menu_action.dart'; import 'package:fluffychat/widgets/context_menu/twake_context_menu.dart'; +import 'package:fluffychat/di/global/get_it_initializer.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter/services.dart'; +import 'package:linagora_design_flutter/linagora_design_flutter.dart'; import 'package:linkfy_text/linkfy_text.dart'; import 'package:matrix/matrix.dart'; +import 'package:pull_down_button/pull_down_button.dart'; +import 'package:url_launcher/url_launcher.dart'; mixin LinkifyMixin { final ValueNotifier openingPopupMenu = ValueNotifier(false); + final _responsive = getIt.get(); + List get phoneNumberContextMenu => [ ChatContextMenuActions.copyNumber, ]; @@ -100,7 +109,7 @@ mixin LinkifyMixin { required BuildContext context, required TapDownDetails details, required Link link, - }) { + }) async { Logs().i( 'LinkifyMixin: handleOnTappedLink: type: ${link.type} link: ${link.value}', ); @@ -109,11 +118,54 @@ mixin LinkifyMixin { UrlLauncher(context, url: link.value.toString()).launchUrl(); break; case LinkType.phone: - _handleContextMenuAction( - context: context, - tapDownDetails: details, - number: link.value.toString(), - ); + if (_responsive.isMobile(context)) { + await showPullDownMenu( + context: context, + items: [ + PullDownMenuItem( + onTap: () async { + final phoneUri = Uri( + scheme: "tel", + path: link.value.toString().replaceAll(' ', ''), + ); + if (await canLaunchUrl(phoneUri)) { + launchUrl(phoneUri); + } else { + throw 'Could not launch $phoneUri'; + } + }, + title: L10n.of(context)!.callViaCarrier, + icon: Icons.call_outlined, + ), + PullDownMenuItem( + title: L10n.of(context)!.copyNumber, + onTap: () async { + await Clipboard.setData( + ClipboardData(text: link.value.toString()), + ); + }, + icon: Icons.content_copy_outlined, + ), + const PullDownMenuDivider.large(), + PullDownMenuItem( + title: '${link.value}', + onTap: () {}, + itemTheme: PullDownMenuItemTheme( + textStyle: context.textTheme.bodyLarge!.copyWith( + color: LinagoraRefColors.material().neutral[30], + ), + ), + ), + ], + position: details.globalPosition & Size.zero, + ); + } else { + _handleContextMenuAction( + context: context, + tapDownDetails: details, + number: link.value.toString(), + ); + } break; default: Logs().i('LinkifyMixin: handleOnTappedLink: Unhandled link: $link'); diff --git a/pubspec.lock b/pubspec.lock index 7329312962..999ae032e6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1169,7 +1169,7 @@ packages: description: path: "." ref: implement-linkify-text - resolved-ref: "7884a5fa64cbd40f3997c4071bbbdc67049c17ae" + resolved-ref: ababe4f8a777a51b2aa21819b0e4f524ce4ba0a0 url: "https://github.com/linagora/flutter_matrix_html.git" source: git version: "1.2.0" @@ -1796,7 +1796,7 @@ packages: description: path: "." ref: refactor-linkify - resolved-ref: b4a418e454ef48c60829d6840b1778f03302f8bf + resolved-ref: "9f4b9debbe4da621897ea040f24e66da3c9ba980" url: "git@github.com:nqhhdev/linkfy_text.git" source: git version: "1.1.6" @@ -2372,6 +2372,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.3" + pull_down_button: + dependency: "direct main" + description: + name: pull_down_button + sha256: "12cdd8ff187a3150ebdf075e5074299f085579b158d2b4e655ccbafccf95f25b" + url: "https://pub.dev" + source: hosted + version: "0.10.2" punycode: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 1087eef90e..45729e95db 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -211,6 +211,7 @@ dependencies: auto_size_text: 3.0.0 flutter_avif: 2.4.1 heif_converter: 1.0.0 + pull_down_button: 0.10.2 dev_dependencies: build_runner: 2.4.12 From 6f5378e3411fb04e4bb1b984bf52bd61c109cee6 Mon Sep 17 00:00:00 2001 From: HuyNguyen Date: Thu, 7 Nov 2024 13:47:46 +0700 Subject: [PATCH 4/5] fixup! TW-2114: Add context menu when click on phone number on mobile --- lib/pages/chat/events/html_message.dart | 2 +- lib/presentation/mixins/linkify_mixin.dart | 11 +++++------ .../twake_preview_link/twake_link_preview.dart | 4 ++-- pubspec.lock | 4 ++-- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/lib/pages/chat/events/html_message.dart b/lib/pages/chat/events/html_message.dart index 12d8407aff..54a228b744 100644 --- a/lib/pages/chat/events/html_message.dart +++ b/lib/pages/chat/events/html_message.dart @@ -78,7 +78,7 @@ class HtmlMessage extends StatelessWidget with LinkifyMixin { ], shrinkToFit: true, maxLines: maxLines, - onLinkTap: (tapDownDetails, link) => handleOnTappedLinkHtml( + onTapDownLink: (tapDownDetails, link) => handleOnTappedLinkHtml( context: context, details: tapDownDetails, link: link, diff --git a/lib/presentation/mixins/linkify_mixin.dart b/lib/presentation/mixins/linkify_mixin.dart index 18da2c6544..14ac19dce6 100644 --- a/lib/presentation/mixins/linkify_mixin.dart +++ b/lib/presentation/mixins/linkify_mixin.dart @@ -1,11 +1,10 @@ import 'package:fluffychat/pages/chat/chat_context_menu_actions.dart'; import 'package:fluffychat/utils/extension/build_context_extension.dart'; import 'package:fluffychat/utils/extension/value_notifier_extension.dart'; -import 'package:fluffychat/utils/responsive/responsive_utils.dart'; +import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/url_launcher.dart'; import 'package:fluffychat/widgets/context_menu/context_menu_action.dart'; import 'package:fluffychat/widgets/context_menu/twake_context_menu.dart'; -import 'package:fluffychat/di/global/get_it_initializer.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter/services.dart'; @@ -18,8 +17,6 @@ import 'package:url_launcher/url_launcher.dart'; mixin LinkifyMixin { final ValueNotifier openingPopupMenu = ValueNotifier(false); - final _responsive = getIt.get(); - List get phoneNumberContextMenu => [ ChatContextMenuActions.copyNumber, ]; @@ -118,7 +115,7 @@ mixin LinkifyMixin { UrlLauncher(context, url: link.value.toString()).launchUrl(); break; case LinkType.phone: - if (_responsive.isMobile(context)) { + if (PlatformInfos.isMobile) { await showPullDownMenu( context: context, items: [ @@ -131,7 +128,9 @@ mixin LinkifyMixin { if (await canLaunchUrl(phoneUri)) { launchUrl(phoneUri); } else { - throw 'Could not launch $phoneUri'; + Logs().e( + 'LinkifyMixin: handleOnTappedLink: Cannot launch phoneUri: $phoneUri', + ); } }, title: L10n.of(context)!.callViaCarrier, diff --git a/lib/widgets/twake_components/twake_preview_link/twake_link_preview.dart b/lib/widgets/twake_components/twake_preview_link/twake_link_preview.dart index 09d0408702..9257a14ecf 100644 --- a/lib/widgets/twake_components/twake_preview_link/twake_link_preview.dart +++ b/lib/widgets/twake_components/twake_preview_link/twake_link_preview.dart @@ -70,7 +70,7 @@ class TwakeLinkPreviewController extends State linkStyle: widget.linkStyle, fontSize: widget.fontSize, ) - : LinkifyText( + : MatrixLinkifyText( widget.localizedBody, textStyle: widget.richTextStyle, linkStyle: widget.linkStyle, @@ -79,7 +79,7 @@ class TwakeLinkPreviewController extends State LinkType.phone, ], textAlign: TextAlign.start, - onLinkTap: (tapDownDetails, link) => handleOnTappedLinkHtml( + onTapDownLink: (tapDownDetails, link) => handleOnTappedLinkHtml( context: context, details: tapDownDetails, link: link, diff --git a/pubspec.lock b/pubspec.lock index 999ae032e6..93cb368aee 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1169,7 +1169,7 @@ packages: description: path: "." ref: implement-linkify-text - resolved-ref: ababe4f8a777a51b2aa21819b0e4f524ce4ba0a0 + resolved-ref: c6440c3a148084b5de5e897eb78e5e02b6a4ed90 url: "https://github.com/linagora/flutter_matrix_html.git" source: git version: "1.2.0" @@ -1796,7 +1796,7 @@ packages: description: path: "." ref: refactor-linkify - resolved-ref: "9f4b9debbe4da621897ea040f24e66da3c9ba980" + resolved-ref: d635cdb6ae9d8a9947166c34073cf252ee9cb476 url: "git@github.com:nqhhdev/linkfy_text.git" source: git version: "1.1.6" From c85fbd2ed5ecb5080b3b957c9dff8220ff37920e Mon Sep 17 00:00:00 2001 From: HuyNguyen Date: Fri, 8 Nov 2024 15:00:04 +0700 Subject: [PATCH 5/5] fixup! fixup! TW-2114: Add context menu when click on phone number on mobile --- lib/pages/chat/chat_context_menu_actions.dart | 6 +- .../phone_number_context_menu_actions.dart | 25 ++++ lib/presentation/mixins/linkify_mixin.dart | 108 ++++++++++-------- pubspec.lock | 8 +- pubspec.yaml | 4 +- 5 files changed, 92 insertions(+), 59 deletions(-) create mode 100644 lib/pages/chat/phone_number_context_menu_actions.dart diff --git a/lib/pages/chat/chat_context_menu_actions.dart b/lib/pages/chat/chat_context_menu_actions.dart index 8ad4dea4e6..2fb4c72fcd 100644 --- a/lib/pages/chat/chat_context_menu_actions.dart +++ b/lib/pages/chat/chat_context_menu_actions.dart @@ -8,8 +8,7 @@ enum ChatContextMenuActions { pinChat, forward, downloadFile, - jumpToMessage, - copyNumber; + jumpToMessage; String getTitle( BuildContext context, { @@ -31,8 +30,6 @@ enum ChatContextMenuActions { return L10n.of(context)!.download; case ChatContextMenuActions.jumpToMessage: return L10n.of(context)!.jumpToMessage; - case ChatContextMenuActions.copyNumber: - return L10n.of(context)!.copyNumber; } } @@ -44,7 +41,6 @@ enum ChatContextMenuActions { case ChatContextMenuActions.select: return isSelected ? Icons.circle_outlined : Icons.check_circle_outline; case ChatContextMenuActions.copyMessage: - case ChatContextMenuActions.copyNumber: return Icons.content_copy; case ChatContextMenuActions.pinChat: return !unpin ? Icons.push_pin_outlined : null; diff --git a/lib/pages/chat/phone_number_context_menu_actions.dart b/lib/pages/chat/phone_number_context_menu_actions.dart new file mode 100644 index 0000000000..2124e2e4aa --- /dev/null +++ b/lib/pages/chat/phone_number_context_menu_actions.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; + +enum PhoneNumberContextMenuActions { + call, + copy; + + String getTitle(BuildContext context) { + switch (this) { + case PhoneNumberContextMenuActions.call: + return L10n.of(context)!.callViaCarrier; + case PhoneNumberContextMenuActions.copy: + return L10n.of(context)!.copyNumber; + } + } + + IconData getIconData() { + switch (this) { + case PhoneNumberContextMenuActions.call: + return Icons.call_outlined; + case PhoneNumberContextMenuActions.copy: + return Icons.content_copy_outlined; + } + } +} diff --git a/lib/presentation/mixins/linkify_mixin.dart b/lib/presentation/mixins/linkify_mixin.dart index 14ac19dce6..bdca2ba5d4 100644 --- a/lib/presentation/mixins/linkify_mixin.dart +++ b/lib/presentation/mixins/linkify_mixin.dart @@ -1,4 +1,4 @@ -import 'package:fluffychat/pages/chat/chat_context_menu_actions.dart'; +import 'package:fluffychat/pages/chat/phone_number_context_menu_actions.dart'; import 'package:fluffychat/utils/extension/build_context_extension.dart'; import 'package:fluffychat/utils/extension/value_notifier_extension.dart'; import 'package:fluffychat/utils/platform_infos.dart'; @@ -17,13 +17,13 @@ import 'package:url_launcher/url_launcher.dart'; mixin LinkifyMixin { final ValueNotifier openingPopupMenu = ValueNotifier(false); - List get phoneNumberContextMenu => [ - ChatContextMenuActions.copyNumber, + List get phoneNumberContextMenuOnWeb => [ + PhoneNumberContextMenuActions.copy, ]; List _mapPopupMenuActionsToContextMenuActions({ required BuildContext context, - required List actions, + required List actions, }) { return actions.map((action) { return ContextMenuAction( @@ -47,7 +47,7 @@ mixin LinkifyMixin { final offset = tapDownDetails.globalPosition; final listActions = _mapPopupMenuActionsToContextMenuActions( context: context, - actions: phoneNumberContextMenu, + actions: phoneNumberContextMenuOnWeb, ); final selectedActionIndex = await showTwakeContextMenu( context: context, @@ -57,7 +57,7 @@ mixin LinkifyMixin { ); if (selectedActionIndex != null && selectedActionIndex is int) { _handleClickOnContextMenuItem( - action: phoneNumberContextMenu[selectedActionIndex], + action: phoneNumberContextMenuOnWeb[selectedActionIndex], number: number, ); } @@ -89,11 +89,11 @@ mixin LinkifyMixin { } void _handleClickOnContextMenuItem({ - required ChatContextMenuActions action, + required PhoneNumberContextMenuActions action, required String number, }) async { switch (action) { - case ChatContextMenuActions.copyNumber: + case PhoneNumberContextMenuActions.copy: Logs().i('LinkifyMixin: handleContextMenuAction: copyNumber $number'); await Clipboard.setData(ClipboardData(text: number)); break; @@ -102,6 +102,55 @@ mixin LinkifyMixin { } } + Future _handleShowPullDownMenu({ + required BuildContext context, + required TapDownDetails tapDownDetails, + required String number, + }) { + return showPullDownMenu( + context: context, + items: [ + PullDownMenuItem( + onTap: () async { + final phoneUri = Uri( + scheme: "tel", + path: number.replaceAll(' ', ''), + ); + if (await canLaunchUrl(phoneUri)) { + launchUrl(phoneUri); + } else { + Logs().e( + 'LinkifyMixin: handleOnTappedLink: Cannot launch phoneUri: $phoneUri', + ); + } + }, + title: L10n.of(context)!.callViaCarrier, + icon: Icons.call_outlined, + ), + PullDownMenuItem( + title: L10n.of(context)!.copyNumber, + onTap: () async { + await Clipboard.setData( + ClipboardData(text: number), + ); + }, + icon: Icons.content_copy_outlined, + ), + const PullDownMenuDivider.large(), + PullDownMenuItem( + title: number, + onTap: () {}, + itemTheme: PullDownMenuItemTheme( + textStyle: context.textTheme.bodyLarge!.copyWith( + color: LinagoraRefColors.material().neutral[30], + ), + ), + ), + ], + position: tapDownDetails.globalPosition & Size.zero, + ); + } + void handleOnTappedLinkHtml({ required BuildContext context, required TapDownDetails details, @@ -116,47 +165,10 @@ mixin LinkifyMixin { break; case LinkType.phone: if (PlatformInfos.isMobile) { - await showPullDownMenu( + await _handleShowPullDownMenu( context: context, - items: [ - PullDownMenuItem( - onTap: () async { - final phoneUri = Uri( - scheme: "tel", - path: link.value.toString().replaceAll(' ', ''), - ); - if (await canLaunchUrl(phoneUri)) { - launchUrl(phoneUri); - } else { - Logs().e( - 'LinkifyMixin: handleOnTappedLink: Cannot launch phoneUri: $phoneUri', - ); - } - }, - title: L10n.of(context)!.callViaCarrier, - icon: Icons.call_outlined, - ), - PullDownMenuItem( - title: L10n.of(context)!.copyNumber, - onTap: () async { - await Clipboard.setData( - ClipboardData(text: link.value.toString()), - ); - }, - icon: Icons.content_copy_outlined, - ), - const PullDownMenuDivider.large(), - PullDownMenuItem( - title: '${link.value}', - onTap: () {}, - itemTheme: PullDownMenuItemTheme( - textStyle: context.textTheme.bodyLarge!.copyWith( - color: LinagoraRefColors.material().neutral[30], - ), - ), - ), - ], - position: details.globalPosition & Size.zero, + tapDownDetails: details, + number: link.value.toString(), ); } else { _handleContextMenuAction( diff --git a/pubspec.lock b/pubspec.lock index 93cb368aee..3168bcc1ea 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1169,7 +1169,7 @@ packages: description: path: "." ref: implement-linkify-text - resolved-ref: c6440c3a148084b5de5e897eb78e5e02b6a4ed90 + resolved-ref: c977da1513608e26c44908d6380d84f3e8daf981 url: "https://github.com/linagora/flutter_matrix_html.git" source: git version: "1.2.0" @@ -1795,9 +1795,9 @@ packages: dependency: "direct main" description: path: "." - ref: refactor-linkify - resolved-ref: d635cdb6ae9d8a9947166c34073cf252ee9cb476 - url: "git@github.com:nqhhdev/linkfy_text.git" + ref: main + resolved-ref: "7bb3735134c1e689180a463eaa5a68adf00abfc9" + url: "git@github.com:linagora/linkfy_text.git" source: git version: "1.1.6" lints: diff --git a/pubspec.yaml b/pubspec.yaml index 45729e95db..4004ed207f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -62,8 +62,8 @@ dependencies: linkfy_text: git: - url: git@github.com:nqhhdev/linkfy_text.git - ref: refactor-linkify + url: git@github.com:linagora/linkfy_text.git + ref: main adaptive_dialog: ^1.8.0+1 flutter_adaptive_scaffold: ^0.1.4