From 2eb4c04d2ba5fbfb404ab5cbab463c5f341a2b6c Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 24 Jan 2024 15:48:03 -0500 Subject: [PATCH] added selecton to html messages --- lib/config/app_config.dart | 5 +- lib/pages/chat/events/html_message.dart | 191 ++++++++++-------- lib/pages/chat/events/message_content.dart | 32 ++- .../extensions/pangea_room_extension.dart | 29 +-- lib/pangea/models/pangea_message_event.dart | 5 + .../models/pangea_representation_event.dart | 16 +- lib/pangea/utils/show_defintion_util.dart | 55 ++--- lib/pangea/widgets/igc/pangea_rich_text.dart | 154 +++++--------- 8 files changed, 252 insertions(+), 235 deletions(-) diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index 75293a28e..3136bfd56 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -63,10 +63,7 @@ abstract class AppConfig { static const bool enableSentry = true; static const String sentryDns = 'https://8591d0d863b646feb4f3dda7e5dcab38@o256755.ingest.sentry.io/5243143'; - //#Pangea - static bool renderHtml = false; - // static bool renderHtml = true; - //Pangea# + static bool renderHtml = true; static bool hideRedactedEvents = false; static bool hideUnknownEvents = true; static bool hideUnimportantStateEvents = true; diff --git a/lib/pages/chat/events/html_message.dart b/lib/pages/chat/events/html_message.dart index ae67c5813..2ea75ab74 100644 --- a/lib/pages/chat/events/html_message.dart +++ b/lib/pages/chat/events/html_message.dart @@ -1,6 +1,10 @@ -import 'package:flutter/material.dart'; - import 'package:collection/collection.dart'; +import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/pangea/utils/show_defintion_util.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'; @@ -10,21 +14,24 @@ import 'package:html/dom.dart' as dom; import 'package:linkify/linkify.dart'; import 'package:matrix/matrix.dart'; -import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/widgets/avatar.dart'; -import 'package:fluffychat/widgets/mxc_image.dart'; import '../../../utils/url_launcher.dart'; class HtmlMessage extends StatelessWidget { final String html; final Room room; final Color textColor; + // #Pangea + final ShowDefintionUtil? messageToolbar; + // Pangea# const HtmlMessage({ super.key, required this.html, required this.room, this.textColor = Colors.black, + // #Pangea + this.messageToolbar, + // Pangea# }); dom.Node _linkifyHtml(dom.Node element) { @@ -92,84 +99,108 @@ class HtmlMessage extends StatelessWidget { final element = _linkifyHtml(HtmlParser.parseHTML(renderHtml)); // there is no need to pre-validate the html, as we validate it while rendering - return Html.fromElement( - documentElement: element as dom.Element, - style: { - '*': Style( - color: textColor, - margin: Margins.all(0), - fontSize: FontSize(fontSize), + // #Pangea + return MouseRegion( + onHover: messageToolbar?.onMouseRegionUpdate, + child: SelectionArea( + onSelectionChanged: (SelectedContent? selection) => + messageToolbar?.onTextSelection( + selectedContent: selection, + context: context, ), - 'a': Style(color: linkColor, textDecorationColor: linkColor), - 'h1': Style( - fontSize: FontSize(fontSize * 2), - lineHeight: LineHeight.number(1.5), - fontWeight: FontWeight.w600, + focusNode: messageToolbar?.focusNode, + contextMenuBuilder: (context, state) => + messageToolbar?.contextMenuOverride( + context: context, + contentSelection: state, + ) ?? + const SizedBox(), + // Pangea# + child: Html.fromElement( + documentElement: element as dom.Element, + style: { + '*': Style( + color: textColor, + margin: Margins.all(0), + fontSize: FontSize(fontSize), + ), + 'a': Style(color: linkColor, textDecorationColor: linkColor), + 'h1': Style( + fontSize: FontSize(fontSize * 2), + lineHeight: LineHeight.number(1.5), + fontWeight: FontWeight.w600, + ), + 'h2': Style( + fontSize: FontSize(fontSize * 1.75), + lineHeight: LineHeight.number(1.5), + fontWeight: FontWeight.w500, + ), + 'h3': Style( + fontSize: FontSize(fontSize * 1.5), + lineHeight: LineHeight.number(1.5), + ), + 'h4': Style( + fontSize: FontSize(fontSize * 1.25), + lineHeight: LineHeight.number(1.5), + ), + 'h5': Style( + fontSize: FontSize(fontSize * 1.25), + lineHeight: LineHeight.number(1.5), + ), + 'h6': Style( + fontSize: FontSize(fontSize), + lineHeight: LineHeight.number(1.5), + ), + 'blockquote': blockquoteStyle, + 'tg-forward': blockquoteStyle, + 'hr': Style( + border: Border.all(color: textColor, width: 0.5), + ), + 'table': Style( + border: Border.all(color: textColor, width: 0.5), + ), + 'tr': Style( + border: Border.all(color: textColor, width: 0.5), + ), + 'td': Style( + border: Border.all(color: textColor, width: 0.5), + padding: HtmlPaddings.all(2), + ), + 'th': Style( + border: Border.all(color: textColor, width: 0.5), + ), + }, + extensions: [ + RoomPillExtension(context, room), + CodeExtension(fontSize: fontSize), + MatrixMathExtension( + style: TextStyle(fontSize: fontSize, color: textColor), + ), + const TableHtmlExtension(), + SpoilerExtension(textColor: textColor), + const ImageExtension(), + FontColorExtension(), + ], + onLinkTap: (url, _, element) => UrlLauncher( + context, + url, + element?.text, + ).launchUrl(), + onlyRenderTheseTags: const { + ...allowedHtmlTags, + // Needed to make it work properly + 'body', + 'html', + }, + shrinkWrap: true, ), - 'h2': Style( - fontSize: FontSize(fontSize * 1.75), - lineHeight: LineHeight.number(1.5), - fontWeight: FontWeight.w500, - ), - 'h3': Style( - fontSize: FontSize(fontSize * 1.5), - lineHeight: LineHeight.number(1.5), - ), - 'h4': Style( - fontSize: FontSize(fontSize * 1.25), - lineHeight: LineHeight.number(1.5), - ), - 'h5': Style( - fontSize: FontSize(fontSize * 1.25), - lineHeight: LineHeight.number(1.5), - ), - 'h6': Style( - fontSize: FontSize(fontSize), - lineHeight: LineHeight.number(1.5), - ), - 'blockquote': blockquoteStyle, - 'tg-forward': blockquoteStyle, - 'hr': Style( - border: Border.all(color: textColor, width: 0.5), - ), - 'table': Style( - border: Border.all(color: textColor, width: 0.5), - ), - 'tr': Style( - border: Border.all(color: textColor, width: 0.5), - ), - 'td': Style( - border: Border.all(color: textColor, width: 0.5), - padding: HtmlPaddings.all(2), - ), - 'th': Style( - border: Border.all(color: textColor, width: 0.5), - ), - }, - extensions: [ - RoomPillExtension(context, room), - CodeExtension(fontSize: fontSize), - MatrixMathExtension( - style: TextStyle(fontSize: fontSize, color: textColor), - ), - const TableHtmlExtension(), - SpoilerExtension(textColor: textColor), - const ImageExtension(), - FontColorExtension(), - ], - onLinkTap: (url, _, element) => UrlLauncher( - context, - url, - element?.text, - ).launchUrl(), - onlyRenderTheseTags: const { - ...allowedHtmlTags, - // Needed to make it work properly - 'body', - 'html', - }, - shrinkWrap: true, + ), ); + // ), + // ], + // ), + // ), + // ); } /// Keep in sync with: https://spec.matrix.org/v1.6/client-server-api/#mroommessage-msgtypes diff --git a/lib/pages/chat/events/message_content.dart b/lib/pages/chat/events/message_content.dart index c8d5b886d..0f6765df2 100644 --- a/lib/pages/chat/events/message_content.dart +++ b/lib/pages/chat/events/message_content.dart @@ -1,3 +1,4 @@ +import 'package:fluffychat/pages/chat/events/html_message.dart'; import 'package:fluffychat/pages/chat/events/video_player.dart'; import 'package:fluffychat/pangea/models/language_model.dart'; import 'package:fluffychat/pangea/models/pangea_message_event.dart'; @@ -18,7 +19,6 @@ import '../../../utils/platform_infos.dart'; import '../../../utils/url_launcher.dart'; import 'audio_player.dart'; import 'cute_events.dart'; -import 'html_message.dart'; import 'image_bubble.dart'; import 'map_bubble.dart'; import 'message_download_content.dart'; @@ -182,16 +182,25 @@ class MessageContent extends StatelessWidget { case MessageTypes.Notice: case MessageTypes.Emote: if (AppConfig.renderHtml && - !event.redacted && - event.isRichMessage) { + !event.redacted && + event.isRichMessage + // #Pangea + && + !pangeaMessageEvent.showRichText + // Pangea# + ) { var html = event.formattedText; if (event.messageType == MessageTypes.Emote) { html = '* $html'; } + // #Pangea + messageToolbar?.messageText = html; + // Pangea# return HtmlMessage( html: html, textColor: textColor, room: event.room, + messageToolbar: messageToolbar, ); } // else we fall through to the normal message rendering @@ -281,7 +290,7 @@ class MessageContent extends StatelessWidget { return MouseRegion( onHover: messageToolbar?.onMouseRegionUpdate, child: PangeaRichText( - existingStyle: messageTextStyle, + style: messageTextStyle, selected: selected, pangeaMessageEvent: pangeaMessageEvent, immersionMode: immersionMode, @@ -321,7 +330,12 @@ class MessageContent extends StatelessWidget { // Pangea# text: messageText, focusNode: messageToolbar?.focusNode, - contextMenuBuilder: messageToolbar?.contextMenuOverride, + contextMenuBuilder: (context, state) => + messageToolbar?.contextMenuOverride( + context: context, + textSelection: state, + ) ?? + const SizedBox(), // text: snapshot.data ?? // event.calcLocalizedBodyFallback( // MatrixLocals(L10n.of(context)!), @@ -341,8 +355,12 @@ class MessageContent extends StatelessWidget { decorationColor: textColor.withAlpha(150), ), onOpen: (url) => UrlLauncher(context, url.url).launchUrl(), - onSelectionChanged: (selection, cause) => messageToolbar - ?.onTextSelection(selection, cause, context), + onSelectionChanged: (selection, cause) => + messageToolbar?.onTextSelection( + selectedText: selection, + cause: cause, + context: context, + ), ); }, ), diff --git a/lib/pangea/extensions/pangea_room_extension.dart b/lib/pangea/extensions/pangea_room_extension.dart index 27c275484..dd51a06ce 100644 --- a/lib/pangea/extensions/pangea_room_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension.dart @@ -10,7 +10,10 @@ import 'package:fluffychat/pangea/utils/bot_name.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +// import markdown.dart +import 'package:html_unescape/html_unescape.dart'; import 'package:matrix/matrix.dart'; +import 'package:matrix/src/utils/markdown.dart'; import 'package:matrix/src/utils/space_child.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; @@ -858,7 +861,7 @@ extension PangeaRoom on Room { String? txid, Event? inReplyTo, String? editEventId, - bool parseMarkdown = false, + bool parseMarkdown = true, bool parseCommands = false, String msgtype = MessageTypes.Text, String? threadRootEventId, @@ -888,17 +891,19 @@ extension PangeaRoom on Room { ModelKey.tokensWritten: tokensWritten?.toJson(), ModelKey.useType: useType?.string, }; - // if (parseMarkdown) { - // final html = markdown(event['body'], - // getEmotePacks: () => getImagePacksFlat(ImagePackUsage.emoticon), - // getMention: getMention); - // // if the decoded html is the same as the body, there is no need in sending a formatted message - // if (HtmlUnescape().convert(html.replaceAll(RegExp(r'
\n?'), '\n')) != - // event['body']) { - // event['format'] = 'org.matrix.custom.html'; - // event['formatted_body'] = html; - // } - // } + if (parseMarkdown) { + final html = markdown( + event['body'], + getEmotePacks: () => getImagePacksFlat(ImagePackUsage.emoticon), + getMention: getMention, + ); + // if the decoded html is the same as the body, there is no need in sending a formatted message + if (HtmlUnescape().convert(html.replaceAll(RegExp(r'
\n?'), '\n')) != + event['body']) { + event['format'] = 'org.matrix.custom.html'; + event['formatted_body'] = html; + } + } return sendEvent( event, txid: txid, diff --git a/lib/pangea/models/pangea_message_event.dart b/lib/pangea/models/pangea_message_event.dart index d515c046f..e9ed46487 100644 --- a/lib/pangea/models/pangea_message_event.dart +++ b/lib/pangea/models/pangea_message_event.dart @@ -1,4 +1,5 @@ import 'package:collection/collection.dart'; +import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/constants/model_keys.dart'; import 'package:fluffychat/pangea/constants/pangea_message_types.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; @@ -256,6 +257,10 @@ class PangeaMessageEvent { //each match is turned into an activity that other students can access //they're not told the answer but have to find it themselves //the message has a blank piece which they fill in themselves + + // replication of logic from message_content.dart + bool get isHtml => + AppConfig.renderHtml && !_event.redacted && _event.isRichMessage; } class URLFinder { diff --git a/lib/pangea/models/pangea_representation_event.dart b/lib/pangea/models/pangea_representation_event.dart index 49e0f9358..bc1ed921a 100644 --- a/lib/pangea/models/pangea_representation_event.dart +++ b/lib/pangea/models/pangea_representation_event.dart @@ -1,15 +1,15 @@ import 'dart:developer'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -import 'package:matrix/matrix.dart'; -import 'package:sentry_flutter/sentry_flutter.dart'; - import 'package:fluffychat/pangea/extensions/pangea_event_extension.dart'; import 'package:fluffychat/pangea/models/pangea_choreo_event.dart'; import 'package:fluffychat/pangea/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/repo/tokens_repo.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:matrix/matrix.dart'; +import 'package:matrix/src/utils/markdown.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; + import '../../widgets/matrix.dart'; import '../constants/language_keys.dart'; import '../constants/pangea_event_types.dart'; @@ -158,4 +158,8 @@ class RepresentationEvent { return _choreo; } + + String? formatBody() { + return markdown(content.text); + } } diff --git a/lib/pangea/utils/show_defintion_util.dart b/lib/pangea/utils/show_defintion_util.dart index 59a095eed..6aa3ba29f 100644 --- a/lib/pangea/utils/show_defintion_util.dart +++ b/lib/pangea/utils/show_defintion_util.dart @@ -6,6 +6,7 @@ import 'package:fluffychat/pangea/widgets/igc/word_data_card.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:matrix/matrix.dart'; @@ -16,7 +17,7 @@ class ShowDefintionUtil { final String targetId; final FocusNode focusNode = FocusNode(); final Room room; - TextSelection? textSelection; + String? textSelection; bool inCooldown = false; double? dx; double? dy; @@ -28,26 +29,21 @@ class ShowDefintionUtil { required this.messageText, }); - void onTextSelection( - TextSelection selection, + void onTextSelection({ + required BuildContext context, + TextSelection? selectedText, + SelectedContent? selectedContent, SelectionChangedCause? cause, - BuildContext context, - ) { - selection.isCollapsed - ? clearTextSelection() - : setTextSelection( - selection, - cause, - context, - ); - } + }) { + if ((selectedText == null && selectedContent == null) || + selectedText?.isCollapsed == true) { + clearTextSelection(); + return; + } + textSelection = selectedText != null + ? selectedText.textInside(messageText) + : selectedContent!.plainText; - void setTextSelection( - TextSelection selection, - SelectionChangedCause? cause, - BuildContext context, - ) { - textSelection = selection; if (BrowserContextMenu.enabled && kIsWeb) { BrowserContextMenu.disableContextMenu(); } @@ -73,12 +69,11 @@ class ShowDefintionUtil { } void showDefinition(BuildContext context) { - final String? fullText = textSelection?.textInside(messageText); - if (fullText == null) return; + if (textSelection == null) return; OverlayUtil.showPositionedCard( context: context, cardToShow: WordDataCard( - word: fullText, + word: textSelection!, wordLang: langCode, fullText: messageText, fullTextLang: langCode, @@ -136,11 +131,21 @@ class ShowDefintionUtil { dy = event.position.dy; } - Widget contextMenuOverride(BuildContext context, EditableTextState selection) { + Widget contextMenuOverride({ + required BuildContext context, + EditableTextState? textSelection, + SelectableRegionState? contentSelection, + }) { + if (textSelection == null && contentSelection == null) { + return const SizedBox(); + } return AdaptiveTextSelectionToolbar.buttonItems( - anchors: selection.contextMenuAnchors, + anchors: textSelection?.contextMenuAnchors ?? + contentSelection!.contextMenuAnchors, buttonItems: [ - ...selection.contextMenuButtonItems, + if (textSelection != null) ...textSelection.contextMenuButtonItems, + if (contentSelection != null) + ...contentSelection.contextMenuButtonItems, ContextMenuButtonItem( label: L10n.of(context)!.showDefinition, onPressed: () { diff --git a/lib/pangea/widgets/igc/pangea_rich_text.dart b/lib/pangea/widgets/igc/pangea_rich_text.dart index 65810c572..782af3d8b 100644 --- a/lib/pangea/widgets/igc/pangea_rich_text.dart +++ b/lib/pangea/widgets/igc/pangea_rich_text.dart @@ -2,6 +2,7 @@ import 'dart:developer'; import 'dart:ui'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/pages/chat/events/html_message.dart'; import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart'; import 'package:fluffychat/pangea/constants/language_keys.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; @@ -14,16 +15,13 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; -import '../../models/igc_text_data_model.dart'; -import '../../models/language_detection_model.dart'; import '../../models/pangea_match_model.dart'; import '../../models/pangea_representation_event.dart'; -import '../../utils/bot_style.dart'; import '../../utils/instructions.dart'; class PangeaRichText extends StatefulWidget { final PangeaMessageEvent pangeaMessageEvent; - final TextStyle? existingStyle; + final TextStyle? style; final bool selected; final LanguageModel? selectedDisplayLang; final bool immersionMode; @@ -39,7 +37,7 @@ class PangeaRichText extends StatefulWidget { required this.immersionMode, required this.definitions, this.choreographer, - this.existingStyle, + this.style, this.messageToolbar, }); @@ -50,9 +48,8 @@ class PangeaRichText extends StatefulWidget { class PangeaRichTextState extends State { final PangeaController pangeaController = MatrixState.pangeaController; bool _fetchingRepresentation = false; - bool _fetchingTokens = false; double get blur => _fetchingRepresentation && widget.immersionMode ? 5 : 0; - List textSpan = []; + String textSpan = ""; @override void initState() { @@ -69,7 +66,7 @@ class PangeaRichTextState extends State { void updateTextSpan() { setState(() { textSpan = getTextSpan(context); - widget.messageToolbar?.messageText = textSpan.map((e) => e.text).join(); + widget.messageToolbar?.messageText = textSpan; }); } @@ -94,32 +91,48 @@ class PangeaRichTextState extends State { ); } - final Widget richText = SelectableText.rich( - onSelectionChanged: (selection, cause) => - widget.messageToolbar?.onTextSelection(selection, cause, context), - focusNode: widget.messageToolbar?.focusNode, - contextMenuBuilder: widget.messageToolbar?.contextMenuOverride, - TextSpan( - children: [ - ...textSpan, - if (widget.selected && (_fetchingRepresentation || _fetchingTokens)) - // if (widget.selected) - 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 = widget.pangeaMessageEvent.isHtml + ? HtmlMessage( + html: textSpan, + room: widget.pangeaMessageEvent.room, + textColor: widget.style?.color ?? Colors.black, + messageToolbar: widget.messageToolbar, + ) + : SelectableText.rich( + onSelectionChanged: (selection, cause) => + widget.messageToolbar?.onTextSelection( + selectedText: selection, + cause: cause, + context: context, ), - ], - ), - ); + focusNode: widget.messageToolbar?.focusNode, + contextMenuBuilder: (context, state) => + widget.messageToolbar?.contextMenuOverride( + context: context, + textSelection: state, + ) ?? + const SizedBox(), + TextSpan( + text: textSpan, + style: widget.style, + children: [ + if (widget.selected && (_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, + ), + ), + ), + ), + ], + ), + ); return blur > 0 ? ImageFiltered( @@ -129,17 +142,17 @@ class PangeaRichTextState extends State { : richText; } - List getTextSpan(BuildContext context) { + String getTextSpan(BuildContext context) { final String? displayLangCode = widget.selected ? widget.selectedDisplayLang?.langCode : userL2LangCode; if (displayLangCode == null || !widget.immersionMode) { - return simpleText(widget.pangeaMessageEvent.body); + return widget.pangeaMessageEvent.body; } if (widget.pangeaMessageEvent.eventId.contains("webdebug")) { debugger(when: kDebugMode); - return simpleText(widget.pangeaMessageEvent.body); + return widget.pangeaMessageEvent.body; } final RepresentationEvent? repEvent = @@ -158,7 +171,7 @@ class PangeaRichTextState extends State { ) .onError((error, stackTrace) => ErrorHandler.logError()) .whenComplete(() => setState(() => _fetchingRepresentation = false)); - return simpleText(widget.pangeaMessageEvent.body); + return widget.pangeaMessageEvent.body; } if (repEvent.event?.eventId.contains("web") ?? false) { @@ -171,74 +184,13 @@ class PangeaRichTextState extends State { "representationByLanguageGlobal returned RepEvent with event ID containing 'web' - ${repEvent.event?.eventId}", ), ); - // debugger(when: kDebugMode); - return textWithBotStyle(repEvent, context); } - if (!widget.selected || - displayLangCode != userL2LangCode || - !widget.definitions) { - return textWithBotStyle(repEvent, context); - } - - if (repEvent.tokens == null) { - setState(() => _fetchingTokens = true); - repEvent - .tokensGlobal(context) - .onError((error, stackTrace) => ErrorHandler.logError()) - .whenComplete(() => setState(() => _fetchingTokens = false)); - - return textWithBotStyle(repEvent, context); - } - - return IGCTextData( - originalInput: repEvent.text, - fullTextCorrection: repEvent.text, - matches: [], - detections: [LanguageDetection(langCode: displayLangCode)], - tokens: repEvent.tokens!, - enableIT: true, - enableIGC: true, - userL2: userL2LangCode ?? LanguageKeys.unknownLanguage, - userL1: userL1LangCode ?? LanguageKeys.unknownLanguage, - ).constructTokenSpan( - context: context, - defaultStyle: textStyle(repEvent, context), - handleClick: false, - spanCardModel: null, - transformTargetId: widget.pangeaMessageEvent.eventId, - room: widget.pangeaMessageEvent.room, - ); + return widget.pangeaMessageEvent.isHtml + ? repEvent.formatBody() ?? repEvent.text + : repEvent.text; } - List simpleText(String text) => [ - TextSpan( - text: text, - style: widget.existingStyle, - ), - ]; - - List textWithBotStyle( - RepresentationEvent repEvent, - BuildContext context, - ) => - [ - TextSpan( - text: repEvent.text, - style: textStyle(repEvent, context), - ), - ]; - - TextStyle? textStyle(RepresentationEvent repEvent, BuildContext context) => - // !repEvent.botAuthored - true - ? widget.existingStyle - : BotStyle.text( - context, - existingStyle: widget.existingStyle, - setColor: false, - ); - bool get areLanguagesSet => userL2LangCode != null && userL2LangCode != LanguageKeys.unknownLanguage;