From d8a9b6acd02fbfc0f02d1bb5a0e65a2386a5ddde Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Mon, 18 Aug 2025 12:32:10 -0400 Subject: [PATCH] feat: add function for fetching l1 representation that accounts for messages that included IT (#3766) --- .../event_wrappers/pangea_message_event.dart | 160 ++++++------------ .../widgets/message_translation_card.dart | 30 +--- .../toolbar/widgets/select_mode_buttons.dart | 14 +- 3 files changed, 56 insertions(+), 148 deletions(-) diff --git a/lib/pangea/events/event_wrappers/pangea_message_event.dart b/lib/pangea/events/event_wrappers/pangea_message_event.dart index 6e3eb16e9..d6464df13 100644 --- a/lib/pangea/events/event_wrappers/pangea_message_event.dart +++ b/lib/pangea/events/event_wrappers/pangea_message_event.dart @@ -13,7 +13,6 @@ import 'package:fluffychat/pangea/choreographer/repo/full_text_translation_repo. import 'package:fluffychat/pangea/choreographer/repo/language_detection_repo.dart'; import 'package:fluffychat/pangea/common/constants/model_keys.dart'; import 'package:fluffychat/pangea/events/event_wrappers/pangea_representation_event.dart'; -import 'package:fluffychat/pangea/events/extensions/pangea_event_extension.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/events/models/representation_content_model.dart'; import 'package:fluffychat/pangea/events/models/stt_translation_model.dart'; @@ -28,7 +27,6 @@ import 'package:fluffychat/pangea/toolbar/event_wrappers/practice_activity_event import 'package:fluffychat/pangea/toolbar/models/speech_to_text_models.dart'; import 'package:fluffychat/pangea/toolbar/widgets/message_audio_card.dart'; import '../../../widgets/matrix.dart'; -import '../../choreographer/enums/use_type.dart'; import '../../common/utils/error_handler.dart'; import '../../learning_settings/constants/language_constants.dart'; import '../constants/pangea_event_types.dart'; @@ -498,19 +496,16 @@ class PangeaMessageEvent { return _representations!; } - RepresentationEvent? representationByLanguage(String langCode) => + RepresentationEvent? representationByLanguage( + String langCode, { + bool Function(RepresentationEvent)? filter, + }) => representations.firstWhereOrNull( - (element) => element.langCode.split("-")[0] == langCode.split("-")[0], + (element) => + element.langCode.split("-")[0] == langCode.split("-")[0] && + (filter?.call(element) ?? true), ); - int translationIndex(String langCode) => representations.indexWhere( - (element) => element.langCode.split("-")[0] == langCode.split("-")[0], - ); - - String translationTextSafe(String langCode) { - return representationByLanguage(langCode)?.text ?? body; - } - Future representationByLanguageGlobal({ required String langCode, }) async { @@ -590,6 +585,48 @@ class PangeaMessageEvent { ); } + Future l1Respresentation() async { + if (l1Code == null || l2Code == null) { + throw Exception("Missing language codes"); + } + + final includedIT = (originalSent?.choreo?.includedIT ?? false) && + !(originalSent?.choreo?.includedIGC ?? true); + + RepresentationEvent? rep; + if (!includedIT) { + // if the message didn't go through translation, get any l1 rep + rep = representationByLanguage(l1Code!); + } else { + // if the message went through translation, get the non-original + // l1 rep since originalWritten could contain some l2 words + // (https://github.com/pangeachat/client/issues/3591) + rep = representationByLanguage( + l1Code!, + filter: (rep) => !rep.content.originalWritten, + ); + } + + if (rep != null) return rep.content; + + final String srcLang = includedIT + ? (originalWritten?.langCode ?? l1Code!) + : (originalSent?.langCode ?? l2Code!); + + // clear representations cache so the new representation event can be added when next requested + _representations = null; + return MatrixState.pangeaController.messageData.getPangeaRepresentation( + req: FullTextTranslationRequestModel( + text: includedIT ? originalWrittenContent : messageDisplayText, + srcLang: srcLang, + tgtLang: l1Code!, + userL2: l2Code!, + userL1: l1Code!, + ), + messageEvent: _event, + ); + } + RepresentationEvent? get originalSent => representations .firstWhereOrNull((element) => element.content.originalSent); @@ -607,82 +644,6 @@ class PangeaMessageEvent { return written ?? body; } - PangeaRepresentation get defaultRepresentation => PangeaRepresentation( - langCode: LanguageKeys.unknownLanguage, - text: body, - originalSent: false, - originalWritten: false, - ); - - UseType get msgUseType { - final ChoreoRecord? choreoRecord = originalSent?.choreo; - if (choreoRecord == null) { - return UseType.un; - } else if (choreoRecord.includedIT) { - return UseType.ta; - } else if (choreoRecord.hasAcceptedMatches) { - return UseType.ga; - } else { - return UseType.wa; - } - } - - bool get showUseType => false; - // *note* turning this feature off but leave code here to bring back (if need) - // !ownMessage && - // _event.room.isSpaceAdmin && - // _event.senderId != BotName.byEnvironment && - // !room.isUserSpaceAdmin(_event.senderId) && - // _event.messageType != PangeaEventTypes.report && - // _event.messageType == MessageTypes.Text; - - // this is just showActivityIcon now but will include - // logic for showing - // NOTE: turning this off for now - bool get showMessageButtons => false; - // bool get showMessageButtons => hasUncompletedActivity; - - /// Returns a boolean value indicating whether to show an activity icon for this message event. - /// - /// The [hasUncompletedActivity] getter checks if the [l2Code] is null, and if so, returns false. - /// Otherwise, it retrieves a list of [PracticeActivityEvent] objects using the [practiceActivities] function - /// with the [l2Code] as an argument. - /// If the list is empty, it returns false. - /// Otherwise, it checks if every activity in the list is complete using the [isComplete] property. - /// If any activity is not complete, it returns true, indicating that the activity icon should be shown. - /// Otherwise, it returns false. - // bool get hasUncompletedActivity { - // if (practiceActivities.isEmpty) return false; - // return practiceActivities.any((activity) => !(activity.isComplete)); - // } - - /// value from 0 to 1 indicating the proportion of activities completed - double get proportionOfActivitiesCompleted { - if (messageDisplayRepresentation == null || - messageDisplayRepresentation?.tokens == null) { - return 1; - } - - final eligibleTokens = messageDisplayRepresentation!.tokens!.where( - (token) => token.isActivityBasicallyEligible( - ActivityTypeEnum.wordMeaning, - ), - ); - - if (eligibleTokens.isEmpty) return 1; - - final alreadyDid = eligibleTokens.where( - (token) => !shouldDoActivity( - token: token, - a: ActivityTypeEnum.wordMeaning, - feature: null, - tag: null, - ), - ); - - return alreadyDid.length / eligibleTokens.length; - } - String? get l2Code => MatrixState.pangeaController.languageController.activeL2Code(); @@ -746,23 +707,6 @@ class PangeaMessageEvent { return practiceEvents; } - /// Returns a boolean value indicating whether there are any - /// activities associated with this message event for the user's active l2 - bool get hasActivities { - try { - return practiceActivities.isNotEmpty; - } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: { - "event": _event.toJson(), - }, - ); - return false; - } - } - /// Returns a list of [PracticeActivityEvent] objects for the given [langCode]. List practiceActivitiesByLangCode( String langCode, { @@ -780,12 +724,6 @@ class PangeaMessageEvent { List get practiceActivities => l2Code == null ? [] : practiceActivitiesByLangCode(l2Code!); - bool get shouldShowToolbar => - !event.isActivityMessage && - !event.redacted && - event.status != EventStatus.sending && - event.status != EventStatus.error; - bool shouldDoActivity({ required PangeaToken? token, required ActivityTypeEnum a, diff --git a/lib/pangea/toolbar/widgets/message_translation_card.dart b/lib/pangea/toolbar/widgets/message_translation_card.dart index 8a5fc7fda..76fce80d6 100644 --- a/lib/pangea/toolbar/widgets/message_translation_card.dart +++ b/lib/pangea/toolbar/widgets/message_translation_card.dart @@ -33,38 +33,20 @@ class MessageTranslationCardState extends State { loadTranslation(); } - Future fetchRepresentationText() async { - if (l1Code == null) return; - - repEvent = widget.messageEvent - .representationByLanguage( - l1Code!, - ) - ?.content; - - if (repEvent == null && mounted) { - repEvent = await widget.messageEvent.representationByLanguageGlobal( - langCode: l1Code!, - ); - } - } - Future loadTranslation() async { if (!mounted) return; - - setState(() => _fetchingTranslation = true); - try { - await fetchRepresentationText(); + setState(() => _fetchingTranslation = true); + repEvent = await widget.messageEvent.l1Respresentation(); } catch (err) { ErrorHandler.logError( e: err, data: {}, ); - } - - if (mounted) { - setState(() => _fetchingTranslation = false); + } finally { + if (mounted) { + setState(() => _fetchingTranslation = false); + } } } diff --git a/lib/pangea/toolbar/widgets/select_mode_buttons.dart b/lib/pangea/toolbar/widgets/select_mode_buttons.dart index 7452bb86a..8226ce3ba 100644 --- a/lib/pangea/toolbar/widgets/select_mode_buttons.dart +++ b/lib/pangea/toolbar/widgets/select_mode_buttons.dart @@ -18,7 +18,6 @@ import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/common/widgets/pressable_button.dart'; import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/events/extensions/pangea_event_extension.dart'; -import 'package:fluffychat/pangea/events/models/representation_content_model.dart'; import 'package:fluffychat/pangea/events/utils/report_message.dart'; import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart'; import 'package:fluffychat/pangea/toolbar/widgets/message_audio_card.dart'; @@ -392,18 +391,7 @@ class SelectModeButtonsState extends State { try { if (mounted) setState(() => _isLoadingTranslation = true); - - PangeaRepresentation? rep = - messageEvent!.representationByLanguage(l1Code!)?.content; - - rep ??= await messageEvent?.representationByLanguageGlobal( - langCode: l1Code!, - ); - - if (rep == null) { - throw Exception('Representation is null'); - } - + final rep = await messageEvent!.l1Respresentation(); widget.overlayController.setTranslation(rep.text); } catch (e, s) { _translationError = e.toString();