From d7f89eb98c51c79969b99f112030308dbe058899 Mon Sep 17 00:00:00 2001 From: Gabby Gurdin Date: Thu, 15 Feb 2024 12:32:15 -0500 Subject: [PATCH 1/2] show translation of selected text --- lib/pangea/widgets/chat/message_toolbar.dart | 5 + .../chat/message_translation_card.dart | 126 +++++++++++++----- 2 files changed, 95 insertions(+), 36 deletions(-) diff --git a/lib/pangea/widgets/chat/message_toolbar.dart b/lib/pangea/widgets/chat/message_toolbar.dart index 18072d468..e410305d9 100644 --- a/lib/pangea/widgets/chat/message_toolbar.dart +++ b/lib/pangea/widgets/chat/message_toolbar.dart @@ -73,6 +73,7 @@ class ToolbarDisplayController { : CrossAxisAlignment.start, children: [ toolbar!, + const SizedBox(height: 6), OverlayMessage( pangeaMessageEvent.event, timeline: pangeaMessageEvent.timeline, @@ -196,6 +197,7 @@ class MessageToolbarState extends State { child = MessageTranslationCard( messageEvent: widget.pangeaMessageEvent, immersionMode: widget.immersionMode, + selection: widget.textSelection, ); } @@ -302,6 +304,9 @@ class MessageToolbarState extends State { children: MessageMode.values.map((mode) { return IconButton( icon: Icon(_getIconData(mode)), + color: _currentMode == mode + ? Theme.of(context).colorScheme.primary + : null, onPressed: _enabledButton(mode) ? () => updateMode(mode) : null, ); diff --git a/lib/pangea/widgets/chat/message_translation_card.dart b/lib/pangea/widgets/chat/message_translation_card.dart index dc3b03f9c..f3d47bbbf 100644 --- a/lib/pangea/widgets/chat/message_translation_card.dart +++ b/lib/pangea/widgets/chat/message_translation_card.dart @@ -1,7 +1,9 @@ import 'package:fluffychat/pangea/models/pangea_message_event.dart'; import 'package:fluffychat/pangea/models/pangea_representation_event.dart'; +import 'package:fluffychat/pangea/repo/full_text_translation_repo.dart'; import 'package:fluffychat/pangea/utils/bot_style.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; +import 'package:fluffychat/pangea/widgets/chat/message_text_selection.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -9,11 +11,13 @@ import 'package:flutter_gen/gen_l10n/l10n.dart'; class MessageTranslationCard extends StatefulWidget { final PangeaMessageEvent messageEvent; final bool immersionMode; + final MessageTextSelection selection; const MessageTranslationCard({ super.key, required this.messageEvent, required this.immersionMode, + required this.selection, }); @override @@ -22,25 +26,20 @@ class MessageTranslationCard extends StatefulWidget { class MessageTranslationCardState extends State { RepresentationEvent? repEvent; + String? selectionTranslation; + String? oldSelectedText; + String? l1Code; + String? l2Code; bool _fetchingRepresentation = false; String? translationLangCode() { - final String? l1Code = - MatrixState.pangeaController.languageController.activeL1Code( - roomID: widget.messageEvent.room.id, - ); if (widget.immersionMode) return l1Code; - - final String? l2Code = - MatrixState.pangeaController.languageController.activeL2Code( - roomID: widget.messageEvent.room.id, - ); final String? originalWrittenCode = widget.messageEvent.originalWritten?.content.langCode; return l1Code == originalWrittenCode ? l2Code : l1Code; } - void fetchRepresentation(BuildContext context) { + Future fetchRepresentation(BuildContext context) async { final String? langCode = translationLangCode(); if (langCode == null) return; @@ -49,33 +48,83 @@ class MessageTranslationCardState extends State { ); if (repEvent == null && mounted) { - setState(() => _fetchingRepresentation = true); - widget.messageEvent - .representationByLanguageGlobal( - context: context, - langCode: langCode, - ) - .onError( - (error, stackTrace) => ErrorHandler.logError( - e: error, - s: stackTrace, - ), - ) - .then((RepresentationEvent? event) => repEvent = event) - .whenComplete( - () { - setState(() => _fetchingRepresentation = false); - }, + repEvent = await widget.messageEvent.representationByLanguageGlobal( + context: context, + langCode: langCode, ); - } else { - if (mounted) setState(() {}); + } + } + + Future translateSelection() async { + final String? targetLang = translationLangCode(); + + if (widget.selection.selectedText == null || + targetLang == null || + l1Code == null || + l2Code == null) { + selectionTranslation = null; + return; + } + + oldSelectedText = widget.selection.selectedText; + final String accessToken = + await MatrixState.pangeaController.userController.accessToken; + + final resp = await FullTextTranslationRepo.translate( + accessToken: accessToken, + request: FullTextTranslationRequestModel( + text: widget.selection.selectedText!, + tgtLang: translationLangCode()!, + userL1: l1Code!, + userL2: l2Code!, + srcLang: widget.messageEvent.messageDisplayLangCode, + ), + ); + + if (mounted) { + selectionTranslation = resp.bestTranslation; + } + } + + Future loadTranslation(Future Function() future) async { + if (!mounted) return; + setState(() => _fetchingRepresentation = true); + try { + await future(); + } catch (err) { + ErrorHandler.logError(e: err); + } + + if (mounted) { + setState(() => _fetchingRepresentation = false); } } @override void initState() { super.initState(); - fetchRepresentation(context); + l1Code = MatrixState.pangeaController.languageController.activeL1Code( + roomID: widget.messageEvent.room.id, + ); + l2Code = MatrixState.pangeaController.languageController.activeL2Code( + roomID: widget.messageEvent.room.id, + ); + setState(() {}); + + loadTranslation(() async { + if (widget.selection.selectedText != null) { + await translateSelection(); + } + fetchRepresentation(context); + }); + } + + @override + void didUpdateWidget(covariant MessageTranslationCard oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldSelectedText != widget.selection.selectedText) { + loadTranslation(translateSelection); + } } @override @@ -91,15 +140,20 @@ class MessageTranslationCardState extends State { color: Theme.of(context).colorScheme.primary, ), ) - : repEvent != null + : selectionTranslation != null ? Text( - repEvent!.text, + selectionTranslation!, style: BotStyle.text(context), ) - : Text( - L10n.of(context)!.oopsSomethingWentWrong, - style: BotStyle.text(context), - ), + : repEvent != null + ? Text( + repEvent!.text, + style: BotStyle.text(context), + ) + : Text( + L10n.of(context)!.oopsSomethingWentWrong, + style: BotStyle.text(context), + ), ); } } From 5a6cc913e87e28bca3571274c0583df8378daf66 Mon Sep 17 00:00:00 2001 From: Gabby Gurdin Date: Thu, 15 Feb 2024 12:38:58 -0500 Subject: [PATCH 2/2] disable selection if message not highlighted --- lib/pages/chat/events/message_content.dart | 2 ++ lib/pangea/widgets/igc/pangea_rich_text.dart | 1 + 2 files changed, 3 insertions(+) diff --git a/lib/pages/chat/events/message_content.dart b/lib/pages/chat/events/message_content.dart index 46c518ca5..daa0a9a54 100644 --- a/lib/pages/chat/events/message_content.dart +++ b/lib/pages/chat/events/message_content.dart @@ -343,6 +343,8 @@ class MessageContent extends StatelessWidget { mode: MessageMode.play, ), ), + enableInteractiveSelection: + toolbarController?.highlighted ?? false, // text: snapshot.data ?? // event.calcLocalizedBodyFallback( // MatrixLocals(L10n.of(context)!), diff --git a/lib/pangea/widgets/igc/pangea_rich_text.dart b/lib/pangea/widgets/igc/pangea_rich_text.dart index 664b496c9..37124175d 100644 --- a/lib/pangea/widgets/igc/pangea_rich_text.dart +++ b/lib/pangea/widgets/igc/pangea_rich_text.dart @@ -124,6 +124,7 @@ class PangeaRichTextState extends State { .onTextSelection(selection); }, onTap: () => widget.toolbarController.showToolbar(context), + enableInteractiveSelection: widget.toolbarController.highlighted, contextMenuBuilder: (context, state) => widget.toolbarController.highlighted ? const SizedBox.shrink()