diff --git a/lib/pages/chat/events/html_message.dart b/lib/pages/chat/events/html_message.dart index 61f9e1065..5bd4ef9bc 100644 --- a/lib/pages/chat/events/html_message.dart +++ b/lib/pages/chat/events/html_message.dart @@ -1,8 +1,11 @@ import 'package:collection/collection.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/pages/chat/chat.dart'; +import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/mxc_image.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; import 'package:flutter_highlighter/flutter_highlighter.dart'; import 'package:flutter_highlighter/themes/shades-of-purple.dart'; import 'package:flutter_html/flutter_html.dart'; @@ -18,12 +21,22 @@ class HtmlMessage extends StatelessWidget { final String html; final Room room; final Color textColor; + // #Pangea + final bool isOverlay; + final PangeaMessageEvent? pangeaMessageEvent; + final ChatController controller; + // Pangea# const HtmlMessage({ super.key, required this.html, required this.room, this.textColor = Colors.black, + // #Pangea + required this.isOverlay, + this.pangeaMessageEvent, + required this.controller, + // Pangea# }); dom.Node _linkifyHtml(dom.Node element) { @@ -76,21 +89,16 @@ class HtmlMessage extends StatelessWidget { // there is no need to pre-validate the html, as we validate it while rendering // #Pangea - return MouseRegion( - // onHover: messageToolbar?.onMouseRegionUpdate, - child: SelectionArea( - // onSelectionChanged: (SelectedContent? selection) => - // messageToolbar?.onTextSelection( - // selectedContent: selection, - // context: context, - // ), - // focusNode: messageToolbar?.focusNode, - // contextMenuBuilder: (context, state) => - // messageToolbar?.contextMenuOverride( - // context: context, - // contentSelection: state, - // ) ?? - // const SizedBox(), + return SelectionArea( + onSelectionChanged: (SelectedContent? selection) { + controller.textSelection.onSelection(selection?.plainText); + }, + child: GestureDetector( + onTap: () { + if (pangeaMessageEvent != null && !isOverlay) { + controller.showToolbar(pangeaMessageEvent!); + } + }, // Pangea# child: Html.fromElement( documentElement: element as dom.Element, @@ -173,11 +181,6 @@ class HtmlMessage extends StatelessWidget { ), ), ); - // ), - // ], - // ), - // ), - // ); } static const Set fallbackTextTags = {'tg-forward'}; @@ -303,7 +306,6 @@ class ImageExtension extends HtmlExtension { uri: mxcUrl, width: width ?? height ?? defaultDimension, height: height ?? width ?? defaultDimension, - cacheKey: mxcUrl.toString(), ), ), ); diff --git a/lib/pages/chat/events/message_content.dart b/lib/pages/chat/events/message_content.dart index ae20d7a20..92bde721b 100644 --- a/lib/pages/chat/events/message_content.dart +++ b/lib/pages/chat/events/message_content.dart @@ -3,13 +3,13 @@ import 'dart:math'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pages/chat/events/video_player.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; +import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart'; import 'package:fluffychat/pangea/widgets/igc/pangea_rich_text.dart'; import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; import 'package:fluffychat/utils/date_time_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_linkify/flutter_linkify.dart'; import 'package:matrix/matrix.dart'; @@ -205,6 +205,11 @@ class MessageContent extends StatelessWidget { html: html, textColor: textColor, room: event.room, + // #Pangea + isOverlay: isOverlay, + controller: controller, + pangeaMessageEvent: pangeaMessageEvent, + // Pangea# ); } // else we fall through to the normal message rendering @@ -285,8 +290,8 @@ class MessageContent extends StatelessWidget { final bigEmotes = event.onlyEmotes && event.numberEmotes > 0 && event.numberEmotes <= 10; + // #Pangea - // return Linkify( final messageTextStyle = TextStyle( overflow: TextOverflow.ellipsis, color: textColor, @@ -314,38 +319,36 @@ class MessageContent extends StatelessWidget { ), ); } + // Pangea# - return SelectableLinkify( - onSelectionChanged: (selection, cause) { - if (isOverlay) { - controller.textSelection.onTextSelection(selection); - } - }, - onTap: () { - if (pangeaMessageEvent != null && !isOverlay) { - HapticFeedback.mediumImpact(); - controller.showToolbar(pangeaMessageEvent!); - } - }, - enableInteractiveSelection: isOverlay, - // Pangea# - text: event.calcLocalizedBodyFallback( - MatrixLocals(L10n.of(context)!), - hideReply: true, + return + // #Pangea + ToolbarSelectionArea( + controller: controller, + pangeaMessageEvent: pangeaMessageEvent, + isOverlay: isOverlay, + child: + // Pangea# + Linkify( + text: event.calcLocalizedBodyFallback( + MatrixLocals(L10n.of(context)!), + hideReply: true, + ), + style: TextStyle( + color: textColor, + fontSize: bigEmotes ? fontSize * 3 : fontSize, + decoration: + event.redacted ? TextDecoration.lineThrough : null, + ), + options: const LinkifyOptions(humanize: false), + linkStyle: TextStyle( + color: textColor.withAlpha(150), + fontSize: bigEmotes ? fontSize * 3 : fontSize, + decoration: TextDecoration.underline, + decorationColor: textColor.withAlpha(150), + ), + onOpen: (url) => UrlLauncher(context, url.url).launchUrl(), ), - style: TextStyle( - color: textColor, - fontSize: bigEmotes ? fontSize * 3 : fontSize, - decoration: event.redacted ? TextDecoration.lineThrough : null, - ), - options: const LinkifyOptions(humanize: false), - linkStyle: TextStyle( - color: textColor.withAlpha(150), - fontSize: bigEmotes ? fontSize * 3 : fontSize, - decoration: TextDecoration.underline, - decorationColor: textColor.withAlpha(150), - ), - onOpen: (url) => UrlLauncher(context, url.url).launchUrl(), ); } case EventTypes.CallInvite: diff --git a/lib/pangea/widgets/chat/message_text_selection.dart b/lib/pangea/widgets/chat/message_text_selection.dart index 6263738f4..0a332a545 100644 --- a/lib/pangea/widgets/chat/message_text_selection.dart +++ b/lib/pangea/widgets/chat/message_text_selection.dart @@ -1,8 +1,5 @@ import 'dart:async'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; - class MessageTextSelection { String? selectedText; String messageText = ""; @@ -13,23 +10,17 @@ class MessageTextSelection { messageText = text; } - void onTextSelection(TextSelection selection) => selection.isCollapsed == true + void onSelection(String? text) => text == null || text.isEmpty ? clearTextSelection() - : setTextSelection(selection); + : setTextSelection(text); - void setTextSelection(TextSelection selection) { - selectedText = selection.textInside(messageText); - if (BrowserContextMenu.enabled && kIsWeb) { - BrowserContextMenu.disableContextMenu(); - } + void setTextSelection(String selection) { + selectedText = selection; selectionStream.add(selectedText); } void clearTextSelection() { selectedText = null; - if (kIsWeb && !BrowserContextMenu.enabled) { - BrowserContextMenu.enableContextMenu(); - } selectionStream.add(selectedText); } diff --git a/lib/pangea/widgets/chat/message_toolbar.dart b/lib/pangea/widgets/chat/message_toolbar.dart index 943269bdb..2bdfe6b7a 100644 --- a/lib/pangea/widgets/chat/message_toolbar.dart +++ b/lib/pangea/widgets/chat/message_toolbar.dart @@ -14,6 +14,7 @@ import 'package:fluffychat/pangea/widgets/igc/word_data_card.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_card.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; class MessageToolbar extends StatefulWidget { final MessageTextSelection textSelection; @@ -276,3 +277,35 @@ class MessageToolbarState extends State { ); } } + +class ToolbarSelectionArea extends StatelessWidget { + final ChatController controller; + final PangeaMessageEvent? pangeaMessageEvent; + final bool isOverlay; + final Widget child; + + const ToolbarSelectionArea({ + required this.controller, + this.pangeaMessageEvent, + this.isOverlay = false, + required this.child, + super.key, + }); + + @override + Widget build(BuildContext context) { + return SelectionArea( + onSelectionChanged: (SelectedContent? selection) { + controller.textSelection.onSelection(selection?.plainText); + }, + child: GestureDetector( + onTap: () { + if (pangeaMessageEvent != null && !isOverlay) { + controller.showToolbar(pangeaMessageEvent!); + } + }, + child: child, + ), + ); + } +} diff --git a/lib/pangea/widgets/igc/pangea_rich_text.dart b/lib/pangea/widgets/igc/pangea_rich_text.dart index 3ff10d4c7..0115f2f6c 100644 --- a/lib/pangea/widgets/igc/pangea_rich_text.dart +++ b/lib/pangea/widgets/igc/pangea_rich_text.dart @@ -8,6 +8,7 @@ import 'package:fluffychat/pangea/enum/instructions_enum.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/models/representation_content_model.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; +import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -134,37 +135,31 @@ class PangeaRichTextState extends State { } //TODO - take out of build function of every message - final Widget richText = SelectableText.rich( - onSelectionChanged: (selection, cause) { - if (widget.isOverlay) { - widget.controller.textSelection.onTextSelection(selection); - } - }, - onTap: () { - if (!widget.isOverlay) { - widget.controller.showToolbar(widget.pangeaMessageEvent); - } - }, - enableInteractiveSelection: widget.isOverlay, - TextSpan( - text: textSpan, - style: widget.style, - children: [ - if (_fetchingRepresentation) - const WidgetSpan( - child: Padding( - padding: EdgeInsets.only(left: 5.0), - child: SizedBox( - height: 14, - width: 14, - child: CircularProgressIndicator( - strokeWidth: 2.0, - color: AppConfig.secondaryColor, + final Widget richText = ToolbarSelectionArea( + isOverlay: widget.isOverlay, + pangeaMessageEvent: widget.pangeaMessageEvent, + controller: widget.controller, + child: RichText( + text: TextSpan( + text: textSpan, + style: widget.style, + children: [ + if (_fetchingRepresentation) + const WidgetSpan( + child: Padding( + padding: EdgeInsets.only(left: 5.0), + child: SizedBox( + height: 14, + width: 14, + child: CircularProgressIndicator( + strokeWidth: 2.0, + color: AppConfig.secondaryColor, + ), ), ), ), - ), - ], + ], + ), ), );