added selecton to html messages
This commit is contained in:
parent
ad16c6dfef
commit
2eb4c04d2b
8 changed files with 252 additions and 235 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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'<br />\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'<br />\n?'), '\n')) !=
|
||||
event['body']) {
|
||||
event['format'] = 'org.matrix.custom.html';
|
||||
event['formatted_body'] = html;
|
||||
}
|
||||
}
|
||||
return sendEvent(
|
||||
event,
|
||||
txid: txid,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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: () {
|
||||
|
|
|
|||
|
|
@ -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<PangeaRichText> {
|
||||
final PangeaController pangeaController = MatrixState.pangeaController;
|
||||
bool _fetchingRepresentation = false;
|
||||
bool _fetchingTokens = false;
|
||||
double get blur => _fetchingRepresentation && widget.immersionMode ? 5 : 0;
|
||||
List<TextSpan> textSpan = [];
|
||||
String textSpan = "";
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
|
@ -69,7 +66,7 @@ class PangeaRichTextState extends State<PangeaRichText> {
|
|||
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<PangeaRichText> {
|
|||
);
|
||||
}
|
||||
|
||||
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<PangeaRichText> {
|
|||
: richText;
|
||||
}
|
||||
|
||||
List<TextSpan> 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<PangeaRichText> {
|
|||
)
|
||||
.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<PangeaRichText> {
|
|||
"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<TextSpan> simpleText(String text) => [
|
||||
TextSpan(
|
||||
text: text,
|
||||
style: widget.existingStyle,
|
||||
),
|
||||
];
|
||||
|
||||
List<TextSpan> 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;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue