From 26373088912dfcb385f1c1bb5e7b62a8a062b1eb Mon Sep 17 00:00:00 2001 From: ggurdin Date: Fri, 24 Oct 2025 14:27:09 -0400 Subject: [PATCH] refactor: remove unused files in choreo folder, remove request and response models into their own files --- lib/pages/chat/chat.dart | 4 +- lib/pages/chat/input_bar.dart | 2 +- .../constants/choreo_constants.dart | 3 - .../controllers/choreographer.dart | 2 +- .../contextual_definition_controller.dart | 152 ------------- .../controllers/igc_controller.dart | 5 +- .../controllers/it_controller.dart | 19 +- .../controllers/it_feedback_controller.dart | 42 ---- .../controllers/span_data_controller.dart | 5 +- lib/pangea/choreographer/enums/use_type.dart | 92 -------- .../event_wrappers/pangea_choreo_event.dart | 23 -- .../models/it_response_model.dart | 172 --------------- lib/pangea/choreographer/models/it_step.dart | 100 ++++++++- .../choreographer/models/word_data_model.dart | 202 ------------------ .../repo/contextual_definition_repo.dart | 109 ++++++++++ .../contextual_definition_request_model.dart | 44 ++++ .../contextual_definition_response_model.dart | 10 + .../custom_input_request_model.dart} | 2 +- .../repo/full_text_translation_repo.dart | 95 +------- .../full_text_translation_request_model.dart | 63 ++++++ .../full_text_translation_response_model.dart | 31 +++ lib/pangea/choreographer/repo/igc_repo.dart | 132 +----------- .../choreographer/repo/igc_request_model.dart | 108 ++++++++++ .../repo/interactive_translation_repo.dart | 20 +- .../choreographer/repo/it_response_model.dart | 74 +++++++ .../repo/language_detection_repo.dart | 80 ------- .../language_mismatch_repo.dart | 0 .../choreographer/repo/span_data_repo.dart | 49 ----- .../repo/system_choice_translation_model.dart | 45 ---- .../utils/input_paste_listener.dart | 2 +- .../choreographer/utils/normalize_text.dart | 29 --- .../igc => utils}/pangea_text_controller.dart | 6 +- .../utils/text_normalization_util.dart | 31 +++ .../widgets/igc/word_data_card.dart | 195 +++-------------- lib/pangea/choreographer/widgets/it_bar.dart | 64 +----- .../widgets/it_feedback_card.dart | 29 +-- .../widgets/it_feedback_stars.dart | 64 ------ .../common/controllers/pangea_controller.dart | 4 - .../course_settings/course_settings.dart | 2 +- .../controllers/message_data_controller.dart | 4 +- .../event_wrappers/pangea_message_event.dart | 6 +- .../pangea_representation_event.dart | 2 +- .../events/repo/language_detection_repo.dart | 33 +++ .../repo/language_detection_request.dart | 19 ++ .../repo/language_detection_response.dart | 29 +++ .../repo/tokens_repo.dart | 0 46 files changed, 729 insertions(+), 1475 deletions(-) delete mode 100644 lib/pangea/choreographer/controllers/contextual_definition_controller.dart delete mode 100644 lib/pangea/choreographer/controllers/it_feedback_controller.dart delete mode 100644 lib/pangea/choreographer/enums/use_type.dart delete mode 100644 lib/pangea/choreographer/models/it_response_model.dart delete mode 100644 lib/pangea/choreographer/models/word_data_model.dart create mode 100644 lib/pangea/choreographer/repo/contextual_definition_repo.dart create mode 100644 lib/pangea/choreographer/repo/contextual_definition_request_model.dart create mode 100644 lib/pangea/choreographer/repo/contextual_definition_response_model.dart rename lib/pangea/choreographer/{models/custom_input_translation_model.dart => repo/custom_input_request_model.dart} (95%) create mode 100644 lib/pangea/choreographer/repo/full_text_translation_request_model.dart create mode 100644 lib/pangea/choreographer/repo/full_text_translation_response_model.dart create mode 100644 lib/pangea/choreographer/repo/igc_request_model.dart create mode 100644 lib/pangea/choreographer/repo/it_response_model.dart delete mode 100644 lib/pangea/choreographer/repo/language_detection_repo.dart rename lib/pangea/choreographer/{utils => repo}/language_mismatch_repo.dart (100%) delete mode 100644 lib/pangea/choreographer/repo/system_choice_translation_model.dart delete mode 100644 lib/pangea/choreographer/utils/normalize_text.dart rename lib/pangea/choreographer/{widgets/igc => utils}/pangea_text_controller.dart (97%) create mode 100644 lib/pangea/choreographer/utils/text_normalization_util.dart delete mode 100644 lib/pangea/choreographer/widgets/it_feedback_stars.dart create mode 100644 lib/pangea/events/repo/language_detection_repo.dart create mode 100644 lib/pangea/events/repo/language_detection_request.dart create mode 100644 lib/pangea/events/repo/language_detection_response.dart rename lib/pangea/{choreographer => events}/repo/tokens_repo.dart (100%) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 5c1bd759b..4a97fd6fb 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -42,10 +42,10 @@ import 'package:fluffychat/pangea/chat/widgets/event_too_large_dialog.dart'; import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart'; import 'package:fluffychat/pangea/choreographer/enums/edit_type.dart'; import 'package:fluffychat/pangea/choreographer/models/choreo_record.dart'; -import 'package:fluffychat/pangea/choreographer/utils/language_mismatch_repo.dart'; +import 'package:fluffychat/pangea/choreographer/repo/language_mismatch_repo.dart'; +import 'package:fluffychat/pangea/choreographer/utils/pangea_text_controller.dart'; import 'package:fluffychat/pangea/choreographer/widgets/igc/language_mismatch_popup.dart'; import 'package:fluffychat/pangea/choreographer/widgets/igc/message_analytics_feedback.dart'; -import 'package:fluffychat/pangea/choreographer/widgets/igc/pangea_text_controller.dart'; import 'package:fluffychat/pangea/common/constants/model_keys.dart'; import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; diff --git a/lib/pages/chat/input_bar.dart b/lib/pages/chat/input_bar.dart index e6c056776..b1a6281c3 100644 --- a/lib/pages/chat/input_bar.dart +++ b/lib/pages/chat/input_bar.dart @@ -8,7 +8,7 @@ import 'package:matrix/matrix.dart'; import 'package:slugify/slugify.dart'; import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/pangea/choreographer/widgets/igc/pangea_text_controller.dart'; +import 'package:fluffychat/pangea/choreographer/utils/pangea_text_controller.dart'; import 'package:fluffychat/pangea/toolbar/utils/shrinkable_text.dart'; import 'package:fluffychat/utils/markdown_context_builder.dart'; import 'package:fluffychat/widgets/mxc_image.dart'; diff --git a/lib/pangea/choreographer/constants/choreo_constants.dart b/lib/pangea/choreographer/constants/choreo_constants.dart index cbdfdf7d0..7be97ce31 100644 --- a/lib/pangea/choreographer/constants/choreo_constants.dart +++ b/lib/pangea/choreographer/constants/choreo_constants.dart @@ -2,9 +2,6 @@ import 'package:flutter/material.dart'; class ChoreoConstants { static const numberOfITChoices = 4; - // static const millisecondsToDisplayFeedback = 2500; - static const millisecondsToDisplayFeedback = 0; - static const commonalityThreshold = 0.9; static const levelThresholdForGreen = 1; static const levelThresholdForYellow = 2; static const levelThresholdForRed = 3; diff --git a/lib/pangea/choreographer/controllers/choreographer.dart b/lib/pangea/choreographer/controllers/choreographer.dart index b6ae1c447..c4a070823 100644 --- a/lib/pangea/choreographer/controllers/choreographer.dart +++ b/lib/pangea/choreographer/controllers/choreographer.dart @@ -14,7 +14,7 @@ import 'package:fluffychat/pangea/choreographer/models/choreo_record.dart'; import 'package:fluffychat/pangea/choreographer/models/it_step.dart'; import 'package:fluffychat/pangea/choreographer/models/pangea_match_model.dart'; import 'package:fluffychat/pangea/choreographer/utils/input_paste_listener.dart'; -import 'package:fluffychat/pangea/choreographer/widgets/igc/pangea_text_controller.dart'; +import 'package:fluffychat/pangea/choreographer/utils/pangea_text_controller.dart'; import 'package:fluffychat/pangea/choreographer/widgets/igc/paywall_card.dart'; import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/common/utils/any_state_holder.dart'; diff --git a/lib/pangea/choreographer/controllers/contextual_definition_controller.dart b/lib/pangea/choreographer/controllers/contextual_definition_controller.dart deleted file mode 100644 index a0622e957..000000000 --- a/lib/pangea/choreographer/controllers/contextual_definition_controller.dart +++ /dev/null @@ -1,152 +0,0 @@ -import 'dart:convert'; - -import 'package:flutter/foundation.dart'; - -import 'package:collection/collection.dart'; -import 'package:http/http.dart'; - -import 'package:fluffychat/pangea/common/config/environment.dart'; -import 'package:fluffychat/pangea/common/utils/error_handler.dart'; -import '../../common/constants/model_keys.dart'; -import '../../common/controllers/pangea_controller.dart'; -import '../../common/network/requests.dart'; -import '../../common/network/urls.dart'; - -class ContextualDefinitionController { - late PangeaController _pangeaController; - - final List<_ContextualDefinitionCacheItem> _definitions = []; - - ContextualDefinitionController(PangeaController pangeaController) { - _pangeaController = pangeaController; - } - - _ContextualDefinitionCacheItem? _getLocal( - ContextualDefinitionRequestModel req, - ) => - _definitions.firstWhereOrNull( - (e) => e.word == req.word && e.fullText == req.fullText, - ); - - Future get( - ContextualDefinitionRequestModel req, - ) { - final _ContextualDefinitionCacheItem? localItem = _getLocal(req); - - if (localItem != null) return localItem.data; - - _definitions.add( - _ContextualDefinitionCacheItem( - word: req.word, - fullText: req.fullText, - data: _define(req), - ), - ); - - return _definitions.last.data; - } - - Future _define( - ContextualDefinitionRequestModel request, - ) async { - try { - final ContextualDefinitionResponseModel res = - await _ContextualDefinitionRepo.define( - _pangeaController.userController.accessToken, - request, - ); - return res; - } catch (err, stack) { - debugPrint( - "error getting contextual definition for ${request.word} in '${request.fullText}'", - ); - ErrorHandler.logError(e: err, s: stack, data: request.toJson()); - return null; - } - } -} - -class _ContextualDefinitionCacheItem { - String word; - String fullText; - Future data; - - _ContextualDefinitionCacheItem({ - required this.word, - required this.fullText, - required this.data, - }); -} - -class _ContextualDefinitionRepo { - static Future define( - String accessToken, - ContextualDefinitionRequestModel request, - ) async { - final Requests req = Requests( - choreoApiKey: Environment.choreoApiKey, - accessToken: accessToken, - ); - - final Response res = await req.post( - url: PApiUrls.contextualDefinition, - body: request.toJson(), - ); - - final ContextualDefinitionResponseModel response = - ContextualDefinitionResponseModel.fromJson( - jsonDecode( - utf8.decode(res.bodyBytes).toString(), - ), - ); - - if (response.text.isEmpty) { - ErrorHandler.logError( - e: Exception( - "empty text in contextual definition response", - ), - data: { - "request": request.toJson(), - "accessToken": accessToken, - }, - ); - } - - return response; - } -} - -class ContextualDefinitionRequestModel { - final String fullText; - final String word; - final String feedbackLang; - final String fullTextLang; - final String wordLang; - - ContextualDefinitionRequestModel({ - required this.fullText, - required this.word, - required this.feedbackLang, - required this.fullTextLang, - required this.wordLang, - }); - - Map toJson() => { - ModelKey.fullText: fullText, - ModelKey.word: word, - ModelKey.lang: feedbackLang, - ModelKey.fullTextLang: fullTextLang, - ModelKey.wordLang: wordLang, - }; -} - -class ContextualDefinitionResponseModel { - String text; - - ContextualDefinitionResponseModel({required this.text}); - - factory ContextualDefinitionResponseModel.fromJson( - Map json, - ) => - ContextualDefinitionResponseModel(text: json["response"]); -} diff --git a/lib/pangea/choreographer/controllers/igc_controller.dart b/lib/pangea/choreographer/controllers/igc_controller.dart index abefff19b..fe2bcd7e8 100644 --- a/lib/pangea/choreographer/controllers/igc_controller.dart +++ b/lib/pangea/choreographer/controllers/igc_controller.dart @@ -13,6 +13,7 @@ import 'package:fluffychat/pangea/choreographer/controllers/span_data_controller import 'package:fluffychat/pangea/choreographer/models/igc_text_data_model.dart'; import 'package:fluffychat/pangea/choreographer/models/pangea_match_model.dart'; import 'package:fluffychat/pangea/choreographer/repo/igc_repo.dart'; +import 'package:fluffychat/pangea/choreographer/repo/igc_request_model.dart'; import 'package:fluffychat/pangea/choreographer/widgets/igc/span_card.dart'; import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/widgets/matrix.dart'; @@ -48,8 +49,6 @@ class _IgnoredMatchCacheItem { class IgcController { Choreographer choreographer; IGCTextData? igcTextData; - Object? igcError; - Completer igcCompleter = Completer(); late SpanDataController spanDataController; // cache for IGC data and prev message @@ -77,7 +76,7 @@ class IgcController { if (choreographer.currentText.isEmpty) return clear(); debugPrint('getIGCTextData called with ${choreographer.currentText}'); - final IGCRequestBody reqBody = IGCRequestBody( + final IGCRequestModel reqBody = IGCRequestModel( fullText: choreographer.currentText, userId: choreographer.pangeaController.userController.userId!, userL1: choreographer.l1LangCode!, diff --git a/lib/pangea/choreographer/controllers/it_controller.dart b/lib/pangea/choreographer/controllers/it_controller.dart index eb4a56c69..62f5144d5 100644 --- a/lib/pangea/choreographer/controllers/it_controller.dart +++ b/lib/pangea/choreographer/controllers/it_controller.dart @@ -11,10 +11,10 @@ import 'package:fluffychat/pangea/choreographer/controllers/error_service.dart'; import 'package:fluffychat/pangea/choreographer/enums/edit_type.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/widgets/matrix.dart'; -import '../models/custom_input_translation_model.dart'; -import '../models/it_response_model.dart'; import '../models/it_step.dart'; +import '../repo/custom_input_request_model.dart'; import '../repo/interactive_translation_repo.dart'; +import '../repo/it_response_model.dart'; import 'choreographer.dart'; class ITController { @@ -23,7 +23,6 @@ class ITController { bool _isOpen = false; bool _willOpen = false; bool _isEditingSourceText = false; - bool showChoiceFeedback = false; bool dismissed = false; ITStartData? _itStartData; @@ -41,7 +40,6 @@ class ITController { _willOpen = false; MatrixState.pAnyState.closeOverlay("it_feedback_card"); - showChoiceFeedback = false; _isEditingSourceText = false; dismissed = false; @@ -309,19 +307,6 @@ class ITController { final itStep = ITStep(currentITStep!.continuances, chosen: chosenIndex); completedITSteps.add(itStep); - - showChoiceFeedback = true; - - Future.delayed( - const Duration( - milliseconds: ChoreoConstants.millisecondsToDisplayFeedback, - ), - () { - showChoiceFeedback = false; - choreographer.setState(); - }, - ); - choreographer.onITChoiceSelect(itStep); choreographer.setState(); } diff --git a/lib/pangea/choreographer/controllers/it_feedback_controller.dart b/lib/pangea/choreographer/controllers/it_feedback_controller.dart deleted file mode 100644 index 68ad1b9c5..000000000 --- a/lib/pangea/choreographer/controllers/it_feedback_controller.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:fluffychat/pangea/common/constants/model_keys.dart'; - -class ITFeedbackRequestModel { - final String sourceText; - final String currentText; - final String bestContinuance; - final String chosenContinuance; - final String feedbackLang; - final String sourceTextLang; - final String targetLang; - - ITFeedbackRequestModel({ - required this.sourceText, - required this.currentText, - required this.bestContinuance, - required this.chosenContinuance, - required this.feedbackLang, - required this.sourceTextLang, - required this.targetLang, - }); - - Map toJson() => { - ModelKey.sourceText: sourceText, - ModelKey.currentText: currentText, - ModelKey.bestContinuance: bestContinuance, - ModelKey.chosenContinuance: chosenContinuance, - ModelKey.feedbackLang: feedbackLang, - ModelKey.srcLang: sourceTextLang, - ModelKey.tgtLang: targetLang, - }; -} - -class ITFeedbackResponseModel { - String text; - - ITFeedbackResponseModel({required this.text}); - - factory ITFeedbackResponseModel.fromJson( - Map json, - ) => - ITFeedbackResponseModel(text: json[ModelKey.text]); -} diff --git a/lib/pangea/choreographer/controllers/span_data_controller.dart b/lib/pangea/choreographer/controllers/span_data_controller.dart index f93296dc5..91dde7a5a 100644 --- a/lib/pangea/choreographer/controllers/span_data_controller.dart +++ b/lib/pangea/choreographer/controllers/span_data_controller.dart @@ -8,7 +8,7 @@ import 'package:collection/collection.dart'; import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart'; import 'package:fluffychat/pangea/choreographer/models/span_data.dart'; import 'package:fluffychat/pangea/choreographer/repo/span_data_repo.dart'; -import 'package:fluffychat/pangea/choreographer/utils/normalize_text.dart'; +import 'package:fluffychat/pangea/choreographer/utils/text_normalization_util.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; class _SpanDetailsCacheItem { @@ -70,7 +70,8 @@ class SpanDataController { ); return correctChoice != null && - normalizeString(correctChoice) == normalizeString(errorSpan); + TextNormalizationUtil.normalizeString(correctChoice) == + TextNormalizationUtil.normalizeString(errorSpan); } Future getSpanDetails( diff --git a/lib/pangea/choreographer/enums/use_type.dart b/lib/pangea/choreographer/enums/use_type.dart deleted file mode 100644 index 069b3a8a5..000000000 --- a/lib/pangea/choreographer/enums/use_type.dart +++ /dev/null @@ -1,92 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:fluffychat/l10n/l10n.dart'; -import '../../bot/utils/bot_style.dart'; - -enum UseType { wa, ta, ga, un } - -extension UseTypeMethods on UseType { - String get string => toString().split(".").last; - - String tooltipString(BuildContext context) { - final l10n = L10n.of(context); - switch (this) { - case UseType.ga: - return l10n.gaTooltip; - case UseType.ta: - return l10n.taTooltip; - case UseType.wa: - return l10n.waTooltip; - default: - return l10n.unTooltip; - } - } - - IconData get iconData { - switch (this) { - case UseType.ga: - return Icons.spellcheck_outlined; - case UseType.ta: - return Icons.translate; - case UseType.wa: - return Icons.thumb_up; - default: - return Icons.question_mark_outlined; - } - } - - Widget iconView(BuildContext context, Color color, [int size = 14]) => - Tooltip( - message: tooltipString(context), - child: Icon( - iconData, - color: color, - size: size.toDouble(), - ), - ); - - Widget iconButtonView(BuildContext context, Color color, [int size = 14]) => - Tooltip( - message: tooltipString(context), - child: Icon( - iconData, - color: color, - size: size.toDouble(), - ), - ); - - Widget textView(BuildContext context, [TextStyle? existingStyle]) => Tooltip( - message: tooltipString(context), - child: Text( - string, - style: BotStyle.text( - context, - existingStyle: existingStyle, - italics: true, - ), - textAlign: TextAlign.end, - ), - ); - - static bool isDarkMode(BuildContext context) => - Theme.of(context).brightness == Brightness.dark; - - Color color(BuildContext context) { - switch (this) { - case UseType.ga: - return isDarkMode(context) - ? const Color.fromARGB(255, 157, 234, 172) - : const Color.fromARGB(255, 31, 146, 54); - case UseType.ta: - return isDarkMode(context) - ? const Color.fromARGB(255, 169, 183, 237) - : const Color.fromARGB(255, 38, 59, 141); - case UseType.wa: - return isDarkMode(context) - ? const Color.fromARGB(255, 212, 144, 216) - : const Color.fromARGB(255, 163, 39, 169); - default: - return Theme.of(context).textTheme.bodyLarge!.color ?? Colors.blueGrey; - } - } -} diff --git a/lib/pangea/choreographer/event_wrappers/pangea_choreo_event.dart b/lib/pangea/choreographer/event_wrappers/pangea_choreo_event.dart index 49b80e8b4..e92c8fdf2 100644 --- a/lib/pangea/choreographer/event_wrappers/pangea_choreo_event.dart +++ b/lib/pangea/choreographer/event_wrappers/pangea_choreo_event.dart @@ -37,27 +37,4 @@ class ChoreoEvent { return null; } } - - // bool get hasAcceptedMatches => - // content?.steps.any( - // (element) => - // element.acceptedOrIgnoredMatch?.status == - // PangeaMatchStatus.accepted, - // ) ?? - // false; - - // bool get hasIgnoredMatches => - // content?.steps.any( - // (element) => - // element.acceptedOrIgnoredMatch?.status == PangeaMatchStatus.ignored, - // ) ?? - // false; - - // bool get includedIT => - // content?.steps.any((step) { - // return step.acceptedOrIgnoredMatch?.status == - // PangeaMatchStatus.accepted && - // (step.acceptedOrIgnoredMatch?.isITStart ?? false); - // }) ?? - // false; } diff --git a/lib/pangea/choreographer/models/it_response_model.dart b/lib/pangea/choreographer/models/it_response_model.dart deleted file mode 100644 index 2bd26c7f5..000000000 --- a/lib/pangea/choreographer/models/it_response_model.dart +++ /dev/null @@ -1,172 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:collection/collection.dart'; - -import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/pangea/choreographer/constants/choreo_constants.dart'; - -class ITResponseModel { - String fullTextTranslation; - List continuances; - List? goldContinuances; - bool isFinal; - String? translationId; - int payloadId; - - ITResponseModel({ - required this.fullTextTranslation, - required this.continuances, - required this.translationId, - required this.goldContinuances, - required this.isFinal, - required this.payloadId, - }); - - factory ITResponseModel.fromJson(Map json) { - //PTODO - is continuances a variable type? can we change that? - if (json['continuances'].runtimeType == String) { - debugPrint("continuances was string - ${json['continuances']}"); - json['continuances'] = []; - json['finished'] = true; - } - - final List interimCont = (json['continuances'] as List) - .mapIndexed((index, e) { - e["index"] = index; - return Continuance.fromJson(e); - }) - .toList() - .take(ChoreoConstants.numberOfITChoices) - .toList() - .cast() - //can't do this on the backend because step translation can't filter them out - .where((element) => element.inDictionary) - .toList(); - - interimCont.shuffle(); - - return ITResponseModel( - fullTextTranslation: json["full_text_translation"] ?? json["translation"], - continuances: interimCont, - translationId: json['translation_id'], - payloadId: json['payload_id'] ?? 0, - isFinal: json['finished'] ?? false, - goldContinuances: json['gold_continuances'] != null - ? (json['gold_continuances'] as Iterable).map((e) { - e["gold"] = true; - return Continuance.fromJson(e); - }).toList() - : null, - ); - } - - Map toJson() { - final Map data = {}; - data['full_text_translation'] = fullTextTranslation; - data['continuances'] = continuances.map((v) => v.toJson()).toList(); - if (translationId != null) { - data['translation_id'] = translationId; - } - data['payload_id'] = payloadId; - data["finished"] = isFinal; - return data; - } -} - -class Continuance { - /// only saving this top set in a condensed json form - double probability; - int level; - String text; - // List tokens; - - /// saving this in a full json form - String description; - int? indexSavedByServer; - bool wasClicked; - bool inDictionary; - bool hasInfo; - bool gold; - - Continuance({ - required this.probability, - required this.level, - required this.text, - required this.description, - required this.indexSavedByServer, - required this.wasClicked, - required this.inDictionary, - required this.hasInfo, - required this.gold, - // required this.tokens, - }); - - factory Continuance.fromJson(Map json) { - // final List tokensInternal = (json[ModelKey.tokens] != null) - // ? (json[ModelKey.tokens] as Iterable) - // .map( - // (e) => PangeaToken.fromJson(e as Map), - // ) - // .toList() - // .cast() - // : []; - return Continuance( - probability: json['probability'].toDouble(), - level: json['level'], - text: json['text'], - description: json['description'] ?? "", - indexSavedByServer: json["index"], - inDictionary: json['in_dictionary'] ?? true, - wasClicked: json['clkd'] ?? false, - hasInfo: json['has_info'] ?? false, - gold: json['gold'] ?? false, - // tokens: tokensInternal, - ); - } - - Map toJson([bool condensed = false]) { - final Map data = {}; - data['probability'] = probability; - data['level'] = level; - data['text'] = text; - data['clkd'] = wasClicked; - // data[ModelKey.tokens] = tokens.map((e) => e.toJson()).toList(); - - if (!condensed) { - data['description'] = description; - data['in_dictionary'] = inDictionary; - data['has_info'] = hasInfo; - data["index"] = indexSavedByServer; - data['gold'] = gold; - } - return data; - } - - Color? get color { - if (!wasClicked) return null; - switch (level) { - case ChoreoConstants.levelThresholdForGreen: - return ChoreoConstants.green; - case ChoreoConstants.levelThresholdForYellow: - return ChoreoConstants.yellow; - case ChoreoConstants.levelThresholdForRed: - return ChoreoConstants.red; - default: - return null; - } - } - - String? feedbackText(BuildContext context) { - final L10n l10n = L10n.of(context); - switch (level) { - case ChoreoConstants.levelThresholdForGreen: - return l10n.greenFeedback; - case ChoreoConstants.levelThresholdForYellow: - return l10n.yellowFeedback; - case ChoreoConstants.levelThresholdForRed: - return l10n.redFeedback; - default: - return null; - } - } -} diff --git a/lib/pangea/choreographer/models/it_step.dart b/lib/pangea/choreographer/models/it_step.dart index f06bd7401..93011d898 100644 --- a/lib/pangea/choreographer/models/it_step.dart +++ b/lib/pangea/choreographer/models/it_step.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:fluffychat/l10n/l10n.dart'; import '../constants/choreo_constants.dart'; -import 'it_response_model.dart'; class ITStep { List continuances; @@ -68,3 +68,101 @@ class ITStep { ); } } + +class Continuance { + /// only saving this top set in a condensed json form + double probability; + int level; + String text; + // List tokens; + + /// saving this in a full json form + String description; + int? indexSavedByServer; + bool wasClicked; + bool inDictionary; + bool hasInfo; + bool gold; + + Continuance({ + required this.probability, + required this.level, + required this.text, + required this.description, + required this.indexSavedByServer, + required this.wasClicked, + required this.inDictionary, + required this.hasInfo, + required this.gold, + // required this.tokens, + }); + + factory Continuance.fromJson(Map json) { + // final List tokensInternal = (json[ModelKey.tokens] != null) + // ? (json[ModelKey.tokens] as Iterable) + // .map( + // (e) => PangeaToken.fromJson(e as Map), + // ) + // .toList() + // .cast() + // : []; + return Continuance( + probability: json['probability'].toDouble(), + level: json['level'], + text: json['text'], + description: json['description'] ?? "", + indexSavedByServer: json["index"], + inDictionary: json['in_dictionary'] ?? true, + wasClicked: json['clkd'] ?? false, + hasInfo: json['has_info'] ?? false, + gold: json['gold'] ?? false, + // tokens: tokensInternal, + ); + } + + Map toJson([bool condensed = false]) { + final Map data = {}; + data['probability'] = probability; + data['level'] = level; + data['text'] = text; + data['clkd'] = wasClicked; + // data[ModelKey.tokens] = tokens.map((e) => e.toJson()).toList(); + + if (!condensed) { + data['description'] = description; + data['in_dictionary'] = inDictionary; + data['has_info'] = hasInfo; + data["index"] = indexSavedByServer; + data['gold'] = gold; + } + return data; + } + + Color? get color { + if (!wasClicked) return null; + switch (level) { + case ChoreoConstants.levelThresholdForGreen: + return ChoreoConstants.green; + case ChoreoConstants.levelThresholdForYellow: + return ChoreoConstants.yellow; + case ChoreoConstants.levelThresholdForRed: + return ChoreoConstants.red; + default: + return null; + } + } + + String? feedbackText(BuildContext context) { + final L10n l10n = L10n.of(context); + switch (level) { + case ChoreoConstants.levelThresholdForGreen: + return l10n.greenFeedback; + case ChoreoConstants.levelThresholdForYellow: + return l10n.yellowFeedback; + case ChoreoConstants.levelThresholdForRed: + return l10n.redFeedback; + default: + return null; + } + } +} diff --git a/lib/pangea/choreographer/models/word_data_model.dart b/lib/pangea/choreographer/models/word_data_model.dart deleted file mode 100644 index add2f23cc..000000000 --- a/lib/pangea/choreographer/models/word_data_model.dart +++ /dev/null @@ -1,202 +0,0 @@ -import 'dart:developer'; - -import 'package:flutter/foundation.dart'; - -import 'package:fluffychat/pangea/choreographer/widgets/igc/word_data_card.dart'; -import 'package:fluffychat/pangea/common/constants/model_keys.dart'; - -class WordData { - final String word; - final String fullText; - final String? userL1; - final String? userL2; - // final List languageSenses; - - final String targetPartOfSpeech; - final String basePartOfSpeech; - final String partOfSpeech; - final String targetDefinition; - final String baseDefinition; - final String targetWord; - final String baseWord; - final String baseExampleSentence; - final String targetExampleSentence; - - WordData({ - // required this.languageSenses, - required this.fullText, - required this.word, - required this.userL1, - required this.userL2, - required this.baseDefinition, - required this.targetDefinition, - required this.basePartOfSpeech, - required this.targetPartOfSpeech, - required this.partOfSpeech, - required this.baseWord, - required this.targetWord, - required this.baseExampleSentence, - required this.targetExampleSentence, - }); - - // static const String _languageSensesKey = 'sense_responses'; - static const String _dataFullKey = 'data_full'; - - Map toJson() => { - // _languageSensesKey: languageSenses.map((e) => e.toJson()).toList(), - ModelKey.word: word, - ModelKey.userL1: userL1, - ModelKey.userL2: userL2, - ModelKey.baseDefinition: baseDefinition, - ModelKey.targetDefinition: targetDefinition, - ModelKey.basePartOfSpeech: basePartOfSpeech, - ModelKey.targetPartOfSpeech: targetPartOfSpeech, - ModelKey.partOfSpeech: partOfSpeech, - ModelKey.baseWord: baseWord, - ModelKey.targetWord: targetWord, - ModelKey.baseExampleSentence: baseExampleSentence, - ModelKey.targetExampleSentence: targetExampleSentence, - }; - - factory WordData.fromJson( - Map json, { - required String word, - required String fullText, - required String userL1, - required String userL2, - }) { - try { - return WordData( - // languageSenses: (json[_languageSensesKey] as List) - // .map( - // (e) => LanguageSense.fromJson(e as Map), - // ) - // .toList() - // .cast(), - baseDefinition: json[_dataFullKey][ModelKey.baseDefinition], - targetDefinition: json[_dataFullKey][ModelKey.targetDefinition], - basePartOfSpeech: json[_dataFullKey][ModelKey.basePartOfSpeech], - targetPartOfSpeech: json[_dataFullKey][ModelKey.targetPartOfSpeech], - partOfSpeech: json[_dataFullKey][ModelKey.partOfSpeech], - baseWord: json[_dataFullKey][ModelKey.baseWord], - targetWord: json[_dataFullKey][ModelKey.targetWord], - baseExampleSentence: json[_dataFullKey][ModelKey.baseExampleSentence], - targetExampleSentence: json[_dataFullKey] - [ModelKey.targetExampleSentence], - word: word, - userL1: userL1, - userL2: userL2, - fullText: fullText, - ); - } catch (err) { - debugger(when: kDebugMode); - return [] as WordData; - } - } - - bool isMatch({ - required String w, - required String f, - required String? l1, - required String? l2, - }) => - word == w && userL1 == l1 && userL2 == l2 && fullText == f; - - String? formattedPartOfSpeech(LanguageType languageType) { - final String pos = languageType == LanguageType.base - ? basePartOfSpeech - : targetPartOfSpeech; - if (pos.isEmpty) return null; - return pos[0].toUpperCase() + pos.substring(1); - } - - // List sensesForLanguage(String code) => - // languageSenses.where((langSense) => langSense.langCode == code).toList(); -} - -// class LanguageSense { -// List senses; -// String langCode; - -// LanguageSense({ -// required this.senses, -// required this.langCode, -// }); - -// static const String _sensesKey = "senses"; -// static const String _langCodeKey = "lang_code"; - -// Map toJson() => { -// _sensesKey: senses.map((e) => e.toJson()).toList(), -// _langCodeKey: langCode, -// }; - -// factory LanguageSense.fromJson(Map json) => LanguageSense( -// senses: (json[_sensesKey] as List) -// .map( -// (e) => Sense.fromJson(e as Map), -// ) -// .toList() -// .cast(), -// langCode: json[_langCodeKey], -// ); - -// List get partsOfSpeech => -// senses.map((sense) => sense.partOfSpeech).toSet().toList(); - -// List definitionsForPartOfSpeech(String partOfSpeech) { -// final List definitions = []; -// for (final Sense sense in senses) { -// if (sense.partOfSpeech == partOfSpeech && -// sense.definition != null && -// sense.definition!.isNotEmpty) { -// definitions.add(sense.definition!); -// } -// } -// return definitions; -// } - -// // List partOfSpeechSense(partOfSpeech) { -// // return senses -// // .where((sense) => sense.partOfSpeech == partOfSpeech) -// // .map((sense) => sense.lemmas.join(', ')) -// // .toSet() -// // .toList(); -// // } - -// // Map> get partOfSpeechSenses { -// // final Map> senses = {}; -// // for (final partOfSpeech in partsOfSpeech) { -// // senses[partOfSpeech] = partOfSpeechSense(partOfSpeech); -// // } -// // return senses; -// // } -// } - -// class Sense { -// String partOfSpeech; -// List lemmas; -// String? definition; - -// Sense({ -// required this.partOfSpeech, -// required this.lemmas, -// required this.definition, -// }); - -// static const String _posKey = "pos"; -// static const String _lemmasKey = "lemmas"; -// static const String _definitionKey = "definition"; - -// Map toJson() => { -// _posKey: partOfSpeech, -// _lemmasKey: lemmas.toString(), -// _definitionKey: definition -// }; - -// factory Sense.fromJson(Map json) => Sense( -// partOfSpeech: json[_posKey], -// lemmas: (json[_lemmasKey] as List).cast(), -// definition: json[_definitionKey], -// ); -// } diff --git a/lib/pangea/choreographer/repo/contextual_definition_repo.dart b/lib/pangea/choreographer/repo/contextual_definition_repo.dart new file mode 100644 index 000000000..90bff2f17 --- /dev/null +++ b/lib/pangea/choreographer/repo/contextual_definition_repo.dart @@ -0,0 +1,109 @@ +import 'dart:convert'; + +import 'package:async/async.dart'; +import 'package:http/http.dart'; + +import 'package:fluffychat/pangea/choreographer/repo/contextual_definition_request_model.dart'; +import 'package:fluffychat/pangea/choreographer/repo/contextual_definition_response_model.dart'; +import 'package:fluffychat/pangea/common/config/environment.dart'; +import 'package:fluffychat/pangea/common/utils/error_handler.dart'; +import '../../common/network/requests.dart'; +import '../../common/network/urls.dart'; + +class ContextualDefinitionRepo { + static final Map> _cache = + {}; + + static Future> get( + String accessToken, + ContextualDefinitionRequestModel request, + ) async { + final cached = _getCached(request); + if (cached != null) { + try { + return Result.value(await cached); + } catch (e, s) { + _cache.remove(request.hashCode.toString()); + ErrorHandler.logError( + e: e, + s: s, + data: request.toJson(), + ); + return Result.error(e); + } + } + + final future = _fetch(accessToken, request); + _setCached(request, future); + return _getResult(request, future); + } + + static Future _fetch( + String accessToken, + ContextualDefinitionRequestModel request, + ) async { + final Requests req = Requests( + choreoApiKey: Environment.choreoApiKey, + accessToken: accessToken, + ); + + final Response res = await req.post( + url: PApiUrls.contextualDefinition, + body: request.toJson(), + ); + + if (res.statusCode != 200) { + throw res; + } + + final ContextualDefinitionResponseModel response = + ContextualDefinitionResponseModel.fromJson( + jsonDecode( + utf8.decode(res.bodyBytes).toString(), + ), + ); + + if (response.text.isEmpty) { + ErrorHandler.logError( + e: Exception( + "empty text in contextual definition response", + ), + data: { + "request": request.toJson(), + "accessToken": accessToken, + }, + ); + } + + return response; + } + + static Future> _getResult( + ContextualDefinitionRequestModel request, + Future future, + ) async { + try { + final res = await future; + return Result.value(res); + } catch (e, s) { + _cache.remove(request.hashCode.toString()); + ErrorHandler.logError( + e: e, + s: s, + data: request.toJson(), + ); + return Result.error(e); + } + } + + static Future? _getCached( + ContextualDefinitionRequestModel request, + ) => + _cache[request.hashCode.toString()]; + + static void _setCached( + ContextualDefinitionRequestModel request, + Future response, + ) => + _cache[request.hashCode.toString()] = response; +} diff --git a/lib/pangea/choreographer/repo/contextual_definition_request_model.dart b/lib/pangea/choreographer/repo/contextual_definition_request_model.dart new file mode 100644 index 000000000..46e6f064a --- /dev/null +++ b/lib/pangea/choreographer/repo/contextual_definition_request_model.dart @@ -0,0 +1,44 @@ +import 'package:fluffychat/pangea/common/constants/model_keys.dart'; + +class ContextualDefinitionRequestModel { + final String fullText; + final String word; + final String feedbackLang; + final String fullTextLang; + final String wordLang; + + ContextualDefinitionRequestModel({ + required this.fullText, + required this.word, + required this.feedbackLang, + required this.fullTextLang, + required this.wordLang, + }); + + Map toJson() => { + ModelKey.fullText: fullText, + ModelKey.word: word, + ModelKey.lang: feedbackLang, + ModelKey.fullTextLang: fullTextLang, + ModelKey.wordLang: wordLang, + }; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is ContextualDefinitionRequestModel && + runtimeType == other.runtimeType && + fullText == other.fullText && + word == other.word && + feedbackLang == other.feedbackLang && + fullTextLang == other.fullTextLang && + wordLang == other.wordLang; + + @override + int get hashCode => + fullText.hashCode ^ + word.hashCode ^ + feedbackLang.hashCode ^ + fullTextLang.hashCode ^ + wordLang.hashCode; +} diff --git a/lib/pangea/choreographer/repo/contextual_definition_response_model.dart b/lib/pangea/choreographer/repo/contextual_definition_response_model.dart new file mode 100644 index 000000000..424955d35 --- /dev/null +++ b/lib/pangea/choreographer/repo/contextual_definition_response_model.dart @@ -0,0 +1,10 @@ +class ContextualDefinitionResponseModel { + String text; + + ContextualDefinitionResponseModel({required this.text}); + + factory ContextualDefinitionResponseModel.fromJson( + Map json, + ) => + ContextualDefinitionResponseModel(text: json["response"]); +} diff --git a/lib/pangea/choreographer/models/custom_input_translation_model.dart b/lib/pangea/choreographer/repo/custom_input_request_model.dart similarity index 95% rename from lib/pangea/choreographer/models/custom_input_translation_model.dart rename to lib/pangea/choreographer/repo/custom_input_request_model.dart index c74eb53b4..64e348d0f 100644 --- a/lib/pangea/choreographer/models/custom_input_translation_model.dart +++ b/lib/pangea/choreographer/repo/custom_input_request_model.dart @@ -1,4 +1,4 @@ -import 'package:fluffychat/pangea/choreographer/models/it_response_model.dart'; +import 'package:fluffychat/pangea/choreographer/repo/it_response_model.dart'; import 'package:fluffychat/pangea/common/constants/model_keys.dart'; class CustomInputRequestModel { diff --git a/lib/pangea/choreographer/repo/full_text_translation_repo.dart b/lib/pangea/choreographer/repo/full_text_translation_repo.dart index a3763951d..ab5250f9c 100644 --- a/lib/pangea/choreographer/repo/full_text_translation_repo.dart +++ b/lib/pangea/choreographer/repo/full_text_translation_repo.dart @@ -5,8 +5,9 @@ import 'dart:convert'; import 'package:http/http.dart'; +import 'package:fluffychat/pangea/choreographer/repo/full_text_translation_request_model.dart'; +import 'package:fluffychat/pangea/choreographer/repo/full_text_translation_response_model.dart'; import '../../common/config/environment.dart'; -import '../../common/constants/model_keys.dart'; import '../../common/network/requests.dart'; import '../../common/network/urls.dart'; @@ -87,95 +88,3 @@ class FullTextTranslationRepo { return responseModel; } } - -class FullTextTranslationRequestModel { - String text; - String? srcLang; - String tgtLang; - String userL1; - String userL2; - bool? deepL; - int? offset; - int? length; - - FullTextTranslationRequestModel({ - required this.text, - this.srcLang, - required this.tgtLang, - required this.userL2, - required this.userL1, - this.deepL = false, - this.offset, - this.length, - }); - - //PTODO throw error for null - - Map toJson() => { - "text": text, - ModelKey.srcLang: srcLang, - ModelKey.tgtLang: tgtLang, - ModelKey.userL2: userL2, - ModelKey.userL1: userL1, - ModelKey.deepL: deepL, - ModelKey.offset: offset, - ModelKey.length: length, - }; - - // override equals and hashcode - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is FullTextTranslationRequestModel && - other.text == text && - other.srcLang == srcLang && - other.tgtLang == tgtLang && - other.userL2 == userL2 && - other.userL1 == userL1 && - other.deepL == deepL && - other.offset == offset && - other.length == length; - } - - @override - int get hashCode => - text.hashCode ^ - srcLang.hashCode ^ - tgtLang.hashCode ^ - userL2.hashCode ^ - userL1.hashCode ^ - deepL.hashCode ^ - offset.hashCode ^ - length.hashCode; -} - -class FullTextTranslationResponseModel { - List translations; - - /// detected source - /// PTODO - - String source; - String? deepL; - - FullTextTranslationResponseModel({ - required this.translations, - required this.source, - required this.deepL, - }); - - factory FullTextTranslationResponseModel.fromJson(Map json) { - return FullTextTranslationResponseModel( - translations: (json["translations"] as Iterable) - .map( - (e) => e, - ) - .toList() - .cast(), - source: json[ModelKey.srcLang], - deepL: json['deepl_res'], - ); - } - - String get bestTranslation => deepL ?? translations.first; -} diff --git a/lib/pangea/choreographer/repo/full_text_translation_request_model.dart b/lib/pangea/choreographer/repo/full_text_translation_request_model.dart new file mode 100644 index 000000000..39e046e71 --- /dev/null +++ b/lib/pangea/choreographer/repo/full_text_translation_request_model.dart @@ -0,0 +1,63 @@ +import 'package:fluffychat/pangea/common/constants/model_keys.dart'; + +class FullTextTranslationRequestModel { + String text; + String? srcLang; + String tgtLang; + String userL1; + String userL2; + bool? deepL; + int? offset; + int? length; + + FullTextTranslationRequestModel({ + required this.text, + this.srcLang, + required this.tgtLang, + required this.userL2, + required this.userL1, + this.deepL = false, + this.offset, + this.length, + }); + + //PTODO throw error for null + + Map toJson() => { + "text": text, + ModelKey.srcLang: srcLang, + ModelKey.tgtLang: tgtLang, + ModelKey.userL2: userL2, + ModelKey.userL1: userL1, + ModelKey.deepL: deepL, + ModelKey.offset: offset, + ModelKey.length: length, + }; + + // override equals and hashcode + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is FullTextTranslationRequestModel && + other.text == text && + other.srcLang == srcLang && + other.tgtLang == tgtLang && + other.userL2 == userL2 && + other.userL1 == userL1 && + other.deepL == deepL && + other.offset == offset && + other.length == length; + } + + @override + int get hashCode => + text.hashCode ^ + srcLang.hashCode ^ + tgtLang.hashCode ^ + userL2.hashCode ^ + userL1.hashCode ^ + deepL.hashCode ^ + offset.hashCode ^ + length.hashCode; +} diff --git a/lib/pangea/choreographer/repo/full_text_translation_response_model.dart b/lib/pangea/choreographer/repo/full_text_translation_response_model.dart new file mode 100644 index 000000000..15e791101 --- /dev/null +++ b/lib/pangea/choreographer/repo/full_text_translation_response_model.dart @@ -0,0 +1,31 @@ +import 'package:fluffychat/pangea/common/constants/model_keys.dart'; + +class FullTextTranslationResponseModel { + List translations; + + /// detected source + /// PTODO - + String source; + String? deepL; + + FullTextTranslationResponseModel({ + required this.translations, + required this.source, + required this.deepL, + }); + + factory FullTextTranslationResponseModel.fromJson(Map json) { + return FullTextTranslationResponseModel( + translations: (json["translations"] as Iterable) + .map( + (e) => e, + ) + .toList() + .cast(), + source: json[ModelKey.srcLang], + deepL: json['deepl_res'], + ); + } + + String get bestTranslation => deepL ?? translations.first; +} diff --git a/lib/pangea/choreographer/repo/igc_repo.dart b/lib/pangea/choreographer/repo/igc_repo.dart index 72d82e87d..293a33e18 100644 --- a/lib/pangea/choreographer/repo/igc_repo.dart +++ b/lib/pangea/choreographer/repo/igc_repo.dart @@ -2,10 +2,8 @@ import 'dart:convert'; import 'package:http/http.dart'; -import 'package:fluffychat/pangea/choreographer/models/pangea_match_model.dart'; -import 'package:fluffychat/pangea/choreographer/repo/span_data_repo.dart'; +import 'package:fluffychat/pangea/choreographer/repo/igc_request_model.dart'; import 'package:fluffychat/pangea/common/config/environment.dart'; -import '../../common/constants/model_keys.dart'; import '../../common/network/requests.dart'; import '../../common/network/urls.dart'; import '../models/igc_text_data_model.dart'; @@ -13,7 +11,7 @@ import '../models/igc_text_data_model.dart'; class IgcRepo { static Future getIGC( String? accessToken, { - required IGCRequestBody igcRequest, + required IGCRequestModel igcRequest, }) async { final Requests req = Requests( accessToken: accessToken, @@ -31,130 +29,4 @@ class IgcRepo { return response; } - - static Future getMockData() async { - await Future.delayed(const Duration(seconds: 2)); - - final IGCTextData igcTextData = IGCTextData( - matches: [ - PangeaMatch( - match: spanDataRepomockSpan, - status: PangeaMatchStatus.open, - ), - ], - originalInput: "This be a sample text", - fullTextCorrection: "This is a sample text", - userL1: "es", - userL2: "en", - enableIT: true, - enableIGC: true, - ); - - return igcTextData; - } -} - -/// Previous text/audio message sent in chat -/// Contain message content, sender, and timestamp -class PreviousMessage { - String content; - String sender; - DateTime timestamp; - - PreviousMessage({ - required this.content, - required this.sender, - required this.timestamp, - }); - - factory PreviousMessage.fromJson(Map json) => - PreviousMessage( - content: json[ModelKey.prevContent] ?? "", - sender: json[ModelKey.prevSender] ?? "", - timestamp: json[ModelKey.prevTimestamp] == null - ? DateTime.now() - : DateTime.parse(json[ModelKey.prevTimestamp]), - ); - - Map toJson() => { - ModelKey.prevContent: content, - ModelKey.prevSender: sender, - ModelKey.prevTimestamp: timestamp.toIso8601String(), - }; - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - if (other is! PreviousMessage) return false; - - return content == other.content && - sender == other.sender && - timestamp == other.timestamp; - } - - @override - int get hashCode { - return Object.hash( - content, - sender, - timestamp, - ); - } -} - -class IGCRequestBody { - String fullText; - String userL1; - String userL2; - bool enableIT; - bool enableIGC; - String userId; - List prevMessages; - - IGCRequestBody({ - required this.fullText, - required this.userL1, - required this.userL2, - required this.enableIGC, - required this.enableIT, - required this.userId, - required this.prevMessages, - }); - - Map toJson() => { - ModelKey.fullText: fullText, - ModelKey.userL1: userL1, - ModelKey.userL2: userL2, - "enable_it": enableIT, - "enable_igc": enableIGC, - ModelKey.userId: userId, - ModelKey.prevMessages: - jsonEncode(prevMessages.map((x) => x.toJson()).toList()), - }; - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - if (other is! IGCRequestBody) return false; - - return fullText.trim() == other.fullText.trim() && - fullText == other.fullText && - userL1 == other.userL1 && - userL2 == other.userL2 && - enableIT == other.enableIT && - userId == other.userId; - } - - @override - int get hashCode => Object.hash( - fullText.trim(), - userL1, - userL2, - enableIT, - enableIGC, - userId, - Object.hashAll(prevMessages), - ); } diff --git a/lib/pangea/choreographer/repo/igc_request_model.dart b/lib/pangea/choreographer/repo/igc_request_model.dart new file mode 100644 index 000000000..731b12f50 --- /dev/null +++ b/lib/pangea/choreographer/repo/igc_request_model.dart @@ -0,0 +1,108 @@ +import 'dart:convert'; + +import 'package:fluffychat/pangea/common/constants/model_keys.dart'; + +class IGCRequestModel { + String fullText; + String userL1; + String userL2; + bool enableIT; + bool enableIGC; + String userId; + List prevMessages; + + IGCRequestModel({ + required this.fullText, + required this.userL1, + required this.userL2, + required this.enableIGC, + required this.enableIT, + required this.userId, + required this.prevMessages, + }); + + Map toJson() => { + ModelKey.fullText: fullText, + ModelKey.userL1: userL1, + ModelKey.userL2: userL2, + "enable_it": enableIT, + "enable_igc": enableIGC, + ModelKey.userId: userId, + ModelKey.prevMessages: + jsonEncode(prevMessages.map((x) => x.toJson()).toList()), + }; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + if (other is! IGCRequestModel) return false; + + return fullText.trim() == other.fullText.trim() && + fullText == other.fullText && + userL1 == other.userL1 && + userL2 == other.userL2 && + enableIT == other.enableIT && + userId == other.userId; + } + + @override + int get hashCode => Object.hash( + fullText.trim(), + userL1, + userL2, + enableIT, + enableIGC, + userId, + Object.hashAll(prevMessages), + ); +} + +/// Previous text/audio message sent in chat +/// Contain message content, sender, and timestamp +class PreviousMessage { + String content; + String sender; + DateTime timestamp; + + PreviousMessage({ + required this.content, + required this.sender, + required this.timestamp, + }); + + factory PreviousMessage.fromJson(Map json) => + PreviousMessage( + content: json[ModelKey.prevContent] ?? "", + sender: json[ModelKey.prevSender] ?? "", + timestamp: json[ModelKey.prevTimestamp] == null + ? DateTime.now() + : DateTime.parse(json[ModelKey.prevTimestamp]), + ); + + Map toJson() => { + ModelKey.prevContent: content, + ModelKey.prevSender: sender, + ModelKey.prevTimestamp: timestamp.toIso8601String(), + }; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + if (other is! PreviousMessage) return false; + + return content == other.content && + sender == other.sender && + timestamp == other.timestamp; + } + + @override + int get hashCode { + return Object.hash( + content, + sender, + timestamp, + ); + } +} diff --git a/lib/pangea/choreographer/repo/interactive_translation_repo.dart b/lib/pangea/choreographer/repo/interactive_translation_repo.dart index 645616085..cc213224d 100644 --- a/lib/pangea/choreographer/repo/interactive_translation_repo.dart +++ b/lib/pangea/choreographer/repo/interactive_translation_repo.dart @@ -6,8 +6,8 @@ import 'package:fluffychat/pangea/common/config/environment.dart'; import 'package:fluffychat/widgets/matrix.dart'; import '../../common/network/requests.dart'; import '../../common/network/urls.dart'; -import '../models/custom_input_translation_model.dart'; -import '../models/it_response_model.dart'; +import 'custom_input_request_model.dart'; +import 'it_response_model.dart'; class ITRepo { static Future customInputTranslate( @@ -24,20 +24,4 @@ class ITRepo { return ITResponseModel.fromJson(json); } - - // static Future systemChoiceTranslate( - // SystemChoiceRequestModel subseqText, - // ) async { - // final Requests req = Requests( - // choreoApiKey: Environment.choreoApiKey, - // accessToken: MatrixState.pangeaController.userController.accessToken, - // ); - - // final Response res = - // await req.post(url: PApiUrls.subseqStep, body: subseqText.toJson()); - - // final decodedBody = jsonDecode(utf8.decode(res.bodyBytes).toString()); - - // return ITResponseModel.fromJson(decodedBody); - // } } diff --git a/lib/pangea/choreographer/repo/it_response_model.dart b/lib/pangea/choreographer/repo/it_response_model.dart new file mode 100644 index 000000000..dd0a14939 --- /dev/null +++ b/lib/pangea/choreographer/repo/it_response_model.dart @@ -0,0 +1,74 @@ +import 'package:flutter/material.dart'; + +import 'package:collection/collection.dart'; + +import 'package:fluffychat/pangea/choreographer/constants/choreo_constants.dart'; +import 'package:fluffychat/pangea/choreographer/models/it_step.dart'; + +class ITResponseModel { + String fullTextTranslation; + List continuances; + List? goldContinuances; + bool isFinal; + String? translationId; + int payloadId; + + ITResponseModel({ + required this.fullTextTranslation, + required this.continuances, + required this.translationId, + required this.goldContinuances, + required this.isFinal, + required this.payloadId, + }); + + factory ITResponseModel.fromJson(Map json) { + //PTODO - is continuances a variable type? can we change that? + if (json['continuances'].runtimeType == String) { + debugPrint("continuances was string - ${json['continuances']}"); + json['continuances'] = []; + json['finished'] = true; + } + + final List interimCont = (json['continuances'] as List) + .mapIndexed((index, e) { + e["index"] = index; + return Continuance.fromJson(e); + }) + .toList() + .take(ChoreoConstants.numberOfITChoices) + .toList() + .cast() + //can't do this on the backend because step translation can't filter them out + .where((element) => element.inDictionary) + .toList(); + + interimCont.shuffle(); + + return ITResponseModel( + fullTextTranslation: json["full_text_translation"] ?? json["translation"], + continuances: interimCont, + translationId: json['translation_id'], + payloadId: json['payload_id'] ?? 0, + isFinal: json['finished'] ?? false, + goldContinuances: json['gold_continuances'] != null + ? (json['gold_continuances'] as Iterable).map((e) { + e["gold"] = true; + return Continuance.fromJson(e); + }).toList() + : null, + ); + } + + Map toJson() { + final Map data = {}; + data['full_text_translation'] = fullTextTranslation; + data['continuances'] = continuances.map((v) => v.toJson()).toList(); + if (translationId != null) { + data['translation_id'] = translationId; + } + data['payload_id'] = payloadId; + data["finished"] = isFinal; + return data; + } +} diff --git a/lib/pangea/choreographer/repo/language_detection_repo.dart b/lib/pangea/choreographer/repo/language_detection_repo.dart deleted file mode 100644 index 8deb39802..000000000 --- a/lib/pangea/choreographer/repo/language_detection_repo.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'dart:convert'; - -import 'package:http/http.dart'; - -import 'package:fluffychat/pangea/choreographer/models/language_detection_model.dart'; -import 'package:fluffychat/pangea/common/config/environment.dart'; -import 'package:fluffychat/pangea/common/network/requests.dart'; -import 'package:fluffychat/pangea/common/network/urls.dart'; - -class LanguageDetectionRepo { - static Future get( - String? accessToken, { - required LanguageDetectionRequest request, - }) async { - final Requests req = Requests( - accessToken: accessToken, - choreoApiKey: Environment.choreoApiKey, - ); - final Response res = await req.post( - url: PApiUrls.languageDetection, - body: request.toJson(), - ); - - final Map json = - jsonDecode(utf8.decode(res.bodyBytes).toString()); - - final LanguageDetectionResponse response = - LanguageDetectionResponse.fromJson(json); - - return response; - } -} - -class LanguageDetectionRequest { - final String text; - final String? senderl1; - final String? senderl2; - - LanguageDetectionRequest({ - required this.text, - this.senderl1, - this.senderl2, - }); - - Map toJson() { - return { - 'full_text': text, - 'sender_l1': senderl1, - 'sender_l2': senderl2, - }; - } -} - -class LanguageDetectionResponse { - List detections; - String fullText; - - LanguageDetectionResponse({ - required this.detections, - required this.fullText, - }); - - factory LanguageDetectionResponse.fromJson(Map json) { - return LanguageDetectionResponse( - detections: List.from( - (json['detections'] as Iterable).map( - (e) => LanguageDetection.fromJson(e), - ), - ), - fullText: json['full_text'], - ); - } - - Map toJson() { - return { - 'detections': detections.map((e) => e.toJson()).toList(), - 'full_text': fullText, - }; - } -} diff --git a/lib/pangea/choreographer/utils/language_mismatch_repo.dart b/lib/pangea/choreographer/repo/language_mismatch_repo.dart similarity index 100% rename from lib/pangea/choreographer/utils/language_mismatch_repo.dart rename to lib/pangea/choreographer/repo/language_mismatch_repo.dart diff --git a/lib/pangea/choreographer/repo/span_data_repo.dart b/lib/pangea/choreographer/repo/span_data_repo.dart index 954a04e29..3789cd6d2 100644 --- a/lib/pangea/choreographer/repo/span_data_repo.dart +++ b/lib/pangea/choreographer/repo/span_data_repo.dart @@ -3,8 +3,6 @@ import 'dart:convert'; import 'package:collection/collection.dart'; import 'package:http/http.dart'; -import 'package:fluffychat/pangea/choreographer/enums/span_choice_type.dart'; -import 'package:fluffychat/pangea/choreographer/enums/span_data_type.dart'; import 'package:fluffychat/pangea/choreographer/models/span_data.dart'; import 'package:fluffychat/pangea/common/config/environment.dart'; import '../../common/constants/model_keys.dart'; @@ -32,16 +30,6 @@ class SpanDataRepo { } } -// Future getMock(SpanDetailsRepoReqAndRes req) async { -// await Future.delayed(const Duration(seconds: 2)); -// if (req.span.choices != null && -// req.span.choices!.any((element) => element.selected)) { -// return req..span = mockReponseWithHintOne.span; -// } else { -// return req..span = mockReponseWithChoices.span; -// } -// } - class SpanDetailsRepoReqAndRes { String userL1; String userL2; @@ -113,40 +101,3 @@ class SpanDetailsRepoReqAndRes { ]); } } - -final spanDataRepomockSpan = SpanData( - offset: 5, - length: 2, - fullText: "This be a sample text", - type: SpanDataType(typeName: SpanDataTypeEnum.correction), - choices: [SpanChoice(value: "is", type: SpanChoiceType.bestCorrection)], - message: null, - rule: null, - shortMessage: null, -); - -//json mock request -final mockRequest = SpanDetailsRepoReqAndRes( - userL1: "es", - userL2: "en", - enableIGC: true, - enableIT: true, - span: spanDataRepomockSpan, -); - -SpanDetailsRepoReqAndRes get mockReponseWithChoices { - final SpanDetailsRepoReqAndRes res = mockRequest; - res.span.choices = [ - SpanChoice(value: "is", type: SpanChoiceType.bestCorrection), - SpanChoice(value: "are", type: SpanChoiceType.distractor), - SpanChoice(value: "was", type: SpanChoiceType.distractor), - ]; - return res; -} - -// SpanDetailsRepoReqAndRes get mockReponseWithHintOne { -// final SpanDetailsRepoReqAndRes res = mockReponseWithChoices; -// res.span.choices![1].selected = true; -// res.span.message = "Conjugation error"; -// return res; -// } diff --git a/lib/pangea/choreographer/repo/system_choice_translation_model.dart b/lib/pangea/choreographer/repo/system_choice_translation_model.dart deleted file mode 100644 index fffffd42f..000000000 --- a/lib/pangea/choreographer/repo/system_choice_translation_model.dart +++ /dev/null @@ -1,45 +0,0 @@ -import '../../common/constants/model_keys.dart'; - -class SystemChoiceRequestModel { - String translationId; - int? nextWordIndex; - String? customInput; - String userId; - String roomId; - String targetLangCode; - String sourceLangCode; - String? classId; - - SystemChoiceRequestModel({ - required this.translationId, - this.nextWordIndex, - this.customInput, - required this.userId, - required this.roomId, - required this.targetLangCode, - required this.sourceLangCode, - this.classId, - }); - - toJson() => { - 'translation_id': translationId, - 'next_word_index': nextWordIndex, - 'custom_input': customInput, - 'user_id': userId, - 'room_id': roomId, - ModelKey.tgtLang: targetLangCode, - ModelKey.srcLang: sourceLangCode, - 'class_id': classId, - }; - - factory SystemChoiceRequestModel.fromJson(json) => SystemChoiceRequestModel( - translationId: json['translation_id'], - nextWordIndex: json['next_word_index'], - customInput: json['custom_input'], - userId: json['user_id'], - roomId: json['room_id'], - targetLangCode: json[ModelKey.tgtLang], - sourceLangCode: json[ModelKey.srcLang], - classId: json['class_id'], - ); -} diff --git a/lib/pangea/choreographer/utils/input_paste_listener.dart b/lib/pangea/choreographer/utils/input_paste_listener.dart index b50bf20e6..de2d0122a 100644 --- a/lib/pangea/choreographer/utils/input_paste_listener.dart +++ b/lib/pangea/choreographer/utils/input_paste_listener.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:fluffychat/pangea/choreographer/enums/edit_type.dart'; -import 'package:fluffychat/pangea/choreographer/widgets/igc/pangea_text_controller.dart'; +import 'package:fluffychat/pangea/choreographer/utils/pangea_text_controller.dart'; class InputPasteListener { final PangeaTextController controller; diff --git a/lib/pangea/choreographer/utils/normalize_text.dart b/lib/pangea/choreographer/utils/normalize_text.dart deleted file mode 100644 index 114a23193..000000000 --- a/lib/pangea/choreographer/utils/normalize_text.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:diacritic/diacritic.dart'; - -import 'package:fluffychat/pangea/common/utils/error_handler.dart'; - -String normalizeString(String input) { - try { - // Step 1: Remove diacritics (accents) - String normalized = removeDiacritics(input); - normalized = normalized.replaceAll(RegExp(r'[^\x00-\x7F]'), ''); - - // Step 2: Remove punctuation - normalized = normalized.replaceAll(RegExp(r'[^\w\s]'), ''); - - // Step 3: Convert to lowercase - normalized = normalized.toLowerCase(); - - // Step 4: Trim and normalize whitespace - normalized = normalized.replaceAll(RegExp(r'\s+'), ' ').trim(); - - return normalized.isEmpty ? input : normalized; - } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: {'input': input}, - ); - return input; - } -} diff --git a/lib/pangea/choreographer/widgets/igc/pangea_text_controller.dart b/lib/pangea/choreographer/utils/pangea_text_controller.dart similarity index 97% rename from lib/pangea/choreographer/widgets/igc/pangea_text_controller.dart rename to lib/pangea/choreographer/utils/pangea_text_controller.dart index 98384a32a..bf152807d 100644 --- a/lib/pangea/choreographer/widgets/igc/pangea_text_controller.dart +++ b/lib/pangea/choreographer/utils/pangea_text_controller.dart @@ -10,9 +10,9 @@ import 'package:fluffychat/pangea/choreographer/widgets/igc/paywall_card.dart'; import 'package:fluffychat/pangea/choreographer/widgets/igc/span_card.dart'; import 'package:fluffychat/pangea/subscription/controllers/subscription_controller.dart'; import 'package:fluffychat/widgets/matrix.dart'; -import '../../../common/utils/overlay.dart'; -import '../../controllers/choreographer.dart'; -import '../../enums/edit_type.dart'; +import '../../common/utils/overlay.dart'; +import '../controllers/choreographer.dart'; +import '../enums/edit_type.dart'; class PangeaTextController extends TextEditingController { Choreographer choreographer; diff --git a/lib/pangea/choreographer/utils/text_normalization_util.dart b/lib/pangea/choreographer/utils/text_normalization_util.dart new file mode 100644 index 000000000..64eec5dda --- /dev/null +++ b/lib/pangea/choreographer/utils/text_normalization_util.dart @@ -0,0 +1,31 @@ +import 'package:diacritic/diacritic.dart'; + +import 'package:fluffychat/pangea/common/utils/error_handler.dart'; + +class TextNormalizationUtil { + static String normalizeString(String input) { + try { + // Step 1: Remove diacritics (accents) + String normalized = removeDiacritics(input); + normalized = normalized.replaceAll(RegExp(r'[^\x00-\x7F]'), ''); + + // Step 2: Remove punctuation + normalized = normalized.replaceAll(RegExp(r'[^\w\s]'), ''); + + // Step 3: Convert to lowercase + normalized = normalized.toLowerCase(); + + // Step 4: Trim and normalize whitespace + normalized = normalized.replaceAll(RegExp(r'\s+'), ' ').trim(); + + return normalized.isEmpty ? input : normalized; + } catch (e, s) { + ErrorHandler.logError( + e: e, + s: s, + data: {'input': input}, + ); + return input; + } + } +} diff --git a/lib/pangea/choreographer/widgets/igc/word_data_card.dart b/lib/pangea/choreographer/widgets/igc/word_data_card.dart index d4228270f..8ea42b171 100644 --- a/lib/pangea/choreographer/widgets/igc/word_data_card.dart +++ b/lib/pangea/choreographer/widgets/igc/word_data_card.dart @@ -1,6 +1,3 @@ -import 'dart:developer'; - -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart'; @@ -9,16 +6,15 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/bot/utils/bot_style.dart'; -import 'package:fluffychat/pangea/choreographer/controllers/contextual_definition_controller.dart'; +import 'package:fluffychat/pangea/choreographer/repo/contextual_definition_repo.dart'; +import 'package:fluffychat/pangea/choreographer/repo/contextual_definition_request_model.dart'; +import 'package:fluffychat/pangea/choreographer/repo/contextual_definition_response_model.dart'; import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; -import 'package:fluffychat/pangea/common/utils/firebase_analytics.dart'; import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; import 'package:fluffychat/pangea/toolbar/widgets/toolbar_content_loading_indicator.dart'; import 'package:fluffychat/widgets/matrix.dart'; -import '../../../learning_settings/widgets/flag.dart'; -import '../../models/word_data_model.dart'; import 'card_error_widget.dart'; class WordDataCard extends StatefulWidget { @@ -80,40 +76,33 @@ class WordDataCardController extends State { } Future getContextualDefinition() async { - ContextualDefinitionRequestModel? req; - try { - req = ContextualDefinitionRequestModel( - fullText: widget.fullText, - word: widget.word, - feedbackLang: activeL1?.langCode ?? LanguageKeys.defaultLanguage, - fullTextLang: widget.fullTextLang, - wordLang: widget.wordLang, - ); - if (!mounted) return; + final ContextualDefinitionRequestModel req = + ContextualDefinitionRequestModel( + fullText: widget.fullText, + word: widget.word, + feedbackLang: activeL1?.langCode ?? LanguageKeys.defaultLanguage, + fullTextLang: widget.fullTextLang, + wordLang: widget.wordLang, + ); + if (!mounted) return; - setState(() { - contextualDefinitionRes = null; - definitionError = null; - isLoadingContextualDefinition = true; - }); + setState(() { + contextualDefinitionRes = null; + definitionError = null; + isLoadingContextualDefinition = true; + }); - contextualDefinitionRes = await controller.definitions.get(req); - if (contextualDefinitionRes == null) { - definitionError = Exception("Error getting definition"); - } - GoogleAnalytics.contextualRequest(); - } catch (err, stack) { - debugger(when: kDebugMode); - ErrorHandler.logError( - e: err, - s: stack, - data: { - "request": req?.toJson(), - }, - ); + final resp = await ContextualDefinitionRepo.get( + MatrixState.pangeaController.userController.accessToken, + req, + ); + + if (resp.isError) { definitionError = Exception("Error getting definition"); - } finally { - if (mounted) setState(() => isLoadingContextualDefinition = false); + } + + if (mounted) { + setState(() => isLoadingContextualDefinition = false); } } @@ -190,133 +179,3 @@ class WordDataCardView extends StatelessWidget { ); } } - -class WordNetInfo extends StatelessWidget { - final WordData wordData; - final LanguageModel activeL1; - final LanguageModel activeL2; - - const WordNetInfo({ - super.key, - required this.wordData, - required this.activeL1, - required this.activeL2, - }); - - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SensesForLanguage( - wordData: wordData, - languageType: LanguageType.target, - language: activeL2, - ), - const SizedBox(height: 10), - SensesForLanguage( - wordData: wordData, - languageType: LanguageType.base, - language: activeL1, - ), - ], - ); - } -} - -enum LanguageType { - target, - base, -} - -class SensesForLanguage extends StatelessWidget { - String get exampleSentence => languageType == LanguageType.target - ? wordData.targetExampleSentence - : wordData.baseExampleSentence; - - String get definition => languageType == LanguageType.target - ? wordData.targetDefinition - : wordData.baseDefinition; - - String formattedTitle(BuildContext context) { - final String word = languageType == LanguageType.target - ? wordData.targetWord - : wordData.baseWord; - String? pos = wordData.formattedPartOfSpeech(languageType); - if (pos == null || pos.isEmpty) pos = L10n.of(context).unkDisplayName; - return "$word (${wordData.formattedPartOfSpeech(languageType)})"; - } - - const SensesForLanguage({ - super.key, - required this.wordData, - required this.languageType, - required this.language, - }); - - final LanguageModel language; - final LanguageType languageType; - final WordData wordData; - - @override - Widget build(BuildContext context) { - return Row( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - LanguageFlag(language: language), - const SizedBox(width: 10), - Flexible( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - formattedTitle(context), - style: BotStyle.text(context, italics: true, bold: false), - ), - const SizedBox(height: 4), - if (definition.isNotEmpty) - RichText( - text: TextSpan( - style: BotStyle.text( - context, - italics: false, - bold: false, - ), - children: [ - TextSpan( - text: "${L10n.of(context).definition}: ", - style: const TextStyle(fontWeight: FontWeight.bold), - ), - TextSpan(text: definition), - ], - ), - ), - const SizedBox(height: 4), - if (exampleSentence.isNotEmpty) - RichText( - text: TextSpan( - style: BotStyle.text( - context, - italics: false, - bold: false, - ), - children: [ - TextSpan( - text: "${L10n.of(context).exampleSentence}: ", - style: const TextStyle( - fontWeight: FontWeight.bold, - ), - ), - TextSpan(text: exampleSentence), - ], - ), - ), - ], - ), - ), - ], - ); - } -} diff --git a/lib/pangea/choreographer/widgets/it_bar.dart b/lib/pangea/choreographer/widgets/it_bar.dart index b9954e630..cf670fb23 100644 --- a/lib/pangea/choreographer/widgets/it_bar.dart +++ b/lib/pangea/choreographer/widgets/it_bar.dart @@ -8,7 +8,8 @@ import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/choreographer/constants/choreo_constants.dart'; import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart'; import 'package:fluffychat/pangea/choreographer/controllers/it_controller.dart'; -import 'package:fluffychat/pangea/choreographer/controllers/it_feedback_controller.dart'; +import 'package:fluffychat/pangea/choreographer/models/it_step.dart'; +import 'package:fluffychat/pangea/choreographer/repo/full_text_translation_request_model.dart'; import 'package:fluffychat/pangea/choreographer/widgets/igc/word_data_card.dart'; import 'package:fluffychat/pangea/choreographer/widgets/it_feedback_card.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; @@ -18,7 +19,6 @@ import 'package:fluffychat/pangea/instructions/instructions_inline_tooltip.dart' import 'package:fluffychat/pangea/learning_settings/pages/settings_learning.dart'; import 'package:fluffychat/widgets/matrix.dart'; import '../../common/utils/overlay.dart'; -import '../models/it_response_model.dart'; import 'choice_array.dart'; class ITBar extends StatefulWidget { @@ -80,8 +80,7 @@ class ITBarState extends State with SingleTickerProviderStateMixin { !itController.isEditingSourceText && !itController.isTranslationDone && itController.currentITStep != null && - itController.currentITStep!.continuances.isNotEmpty && - !itController.showChoiceFeedback; + itController.currentITStep!.continuances.isNotEmpty; } @override @@ -225,13 +224,9 @@ class ITBarState extends State with SingleTickerProviderStateMixin { child: Center( child: itController.choreographer.errorService.isError ? ITError(controller: itController) - : itController.showChoiceFeedback - ? ChoiceFeedbackText( - controller: itController, - ) - : itController.isTranslationDone - ? const SizedBox() - : ITChoices(controller: itController), + : itController.isTranslationDone + ? const SizedBox() + : ITChoices(controller: itController), ), ), ), @@ -246,35 +241,6 @@ class ITBarState extends State with SingleTickerProviderStateMixin { } } -class ChoiceFeedbackText extends StatelessWidget { - const ChoiceFeedbackText({ - super.key, - required this.controller, - }); - - final ITController controller; - - @override - Widget build(BuildContext context) { - //reimplement if we decide we want it - return const SizedBox(); - // return AnimatedTextKit( - // isRepeatingAnimation: false, - // animatedTexts: [ - // ScaleAnimatedText( - // controller.latestChoiceFeedback(context), - // duration: Duration( - // milliseconds: - // (ChoreoConstants.millisecondsToDisplayFeedback / 2).round(), - // ), - // scalingFactor: 1.4, - // textStyle: BotStyle.text(context), - // ), - // ], - // ); - } -} - class ITChoices extends StatelessWidget { const ITChoices({ super.key, @@ -342,19 +308,11 @@ class ITChoices extends StatelessWidget { room: controller.choreographer.chatController.room, ) : ITFeedbackCard( - req: ITFeedbackRequestModel( - sourceText: sourceText!, - currentText: controller.choreographer.currentText, - chosenContinuance: - controller.currentITStep!.continuances[index].text, - bestContinuance: controller.currentITStep!.best.text, - // TODO: we want this to eventually switch between target and source lang, - // based on the learner's proficiency - maybe with the words involved in the translation - // maybe overall. For now, we'll just use the source lang. - feedbackLang: controller.choreographer.l1Lang?.langCode ?? - controller.sourceLangCode, - sourceTextLang: controller.sourceLangCode, - targetLang: controller.targetLangCode, + req: FullTextTranslationRequestModel( + text: controller.currentITStep!.continuances[index].text, + tgtLang: controller.sourceLangCode, + userL1: controller.sourceLangCode, + userL2: controller.targetLangCode, ), choiceFeedback: choiceFeedback, ), diff --git a/lib/pangea/choreographer/widgets/it_feedback_card.dart b/lib/pangea/choreographer/widgets/it_feedback_card.dart index 4e572b5ce..183347953 100644 --- a/lib/pangea/choreographer/widgets/it_feedback_card.dart +++ b/lib/pangea/choreographer/widgets/it_feedback_card.dart @@ -7,15 +7,16 @@ import 'package:http/http.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/analytics_misc/text_loading_shimmer.dart'; import 'package:fluffychat/pangea/choreographer/repo/full_text_translation_repo.dart'; +import 'package:fluffychat/pangea/choreographer/repo/full_text_translation_request_model.dart'; +import 'package:fluffychat/pangea/choreographer/repo/full_text_translation_response_model.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import '../../../widgets/matrix.dart'; import '../../bot/utils/bot_style.dart'; import '../../common/controllers/pangea_controller.dart'; -import '../controllers/it_feedback_controller.dart'; import 'igc/card_error_widget.dart'; class ITFeedbackCard extends StatefulWidget { - final ITFeedbackRequestModel req; + final FullTextTranslationRequestModel req; final String choiceFeedback; const ITFeedbackCard({ @@ -34,7 +35,7 @@ class ITFeedbackCardController extends State { Object? error; bool isLoadingFeedback = false; bool isTranslating = false; - ITFeedbackResponseModel? res; + FullTextTranslationResponseModel? res; String? translatedFeedback; Response get noLanguages => Response("", 405); @@ -53,19 +54,10 @@ class ITFeedbackCardController extends State { }); try { - final resp = await FullTextTranslationRepo.translate( + res = await FullTextTranslationRepo.translate( accessToken: controller.userController.accessToken, - request: FullTextTranslationRequestModel( - text: widget.req.chosenContinuance, - tgtLang: controller.languageController.userL1?.langCode ?? - widget.req.sourceTextLang, - userL1: controller.languageController.userL1?.langCode ?? - widget.req.sourceTextLang, - userL2: controller.languageController.userL2?.langCode ?? - widget.req.targetLang, - ), + request: widget.req, ); - res = ITFeedbackResponseModel(text: resp.bestTranslation); } catch (e, s) { error = e; ErrorHandler.logError( @@ -112,7 +104,7 @@ class ITFeedbackCardView extends StatelessWidget { alignment: WrapAlignment.center, children: [ Text( - controller.widget.req.chosenContinuance, + controller.widget.req.text, style: BotStyle.text(context), ), const SizedBox(width: 10), @@ -121,16 +113,15 @@ class ITFeedbackCardView extends StatelessWidget { style: BotStyle.text(context), ), const SizedBox(width: 10), - controller.res?.text != null + controller.res?.bestTranslation != null ? Text( - controller.res!.text, + controller.res!.bestTranslation, style: BotStyle.text(context), ) : TextLoadingShimmer( width: min( 140, - characterWidth * - controller.widget.req.chosenContinuance.length, + characterWidth * controller.widget.req.text.length, ), ), ], diff --git a/lib/pangea/choreographer/widgets/it_feedback_stars.dart b/lib/pangea/choreographer/widgets/it_feedback_stars.dart deleted file mode 100644 index c9f5a1b21..000000000 --- a/lib/pangea/choreographer/widgets/it_feedback_stars.dart +++ /dev/null @@ -1,64 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/pangea/choreographer/widgets/choice_animation.dart'; - -class FillingStars extends StatefulWidget { - final int rating; - - const FillingStars({ - super.key, - required this.rating, - }); - - @override - State createState() => _FillingStarsState(); -} - -class _FillingStarsState extends State { - final List _isFilledList = List.filled(5, false); - - @override - void initState() { - super.initState(); - WidgetsBinding.instance.addPostFrameCallback((_) => _animate()); - } - - Future _animate() async { - for (int i = 0; i < widget.rating; i++) { - await Future.delayed( - const Duration(milliseconds: choiceArrayAnimationDuration), () { - if (mounted) { - setState(() => _isFilledList[i] = true); - } - }); - } - } - - @override - Widget build(BuildContext context) { - return Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: List.generate(5, (index) { - return AnimatedSwitcher( - duration: const Duration(milliseconds: choiceArrayAnimationDuration), - transitionBuilder: (Widget child, Animation animation) { - return ScaleTransition( - scale: animation, - child: child, - ); - }, - child: Icon( - _isFilledList[index] ? Icons.star_rounded : Icons.star_rounded, - key: ValueKey(_isFilledList[index]), - color: _isFilledList[index] - ? AppConfig.gold - : Theme.of(context).cardColor.withAlpha(180), - size: 32.0, - ), - ); - }), - ); - } -} diff --git a/lib/pangea/common/controllers/pangea_controller.dart b/lib/pangea/common/controllers/pangea_controller.dart index fd01ea217..4448302c6 100644 --- a/lib/pangea/common/controllers/pangea_controller.dart +++ b/lib/pangea/common/controllers/pangea_controller.dart @@ -14,7 +14,6 @@ import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.dart'; import 'package:fluffychat/pangea/bot/utils/bot_room_extension.dart'; import 'package:fluffychat/pangea/chat_settings/models/bot_options_model.dart'; import 'package:fluffychat/pangea/chat_settings/utils/bot_client_extension.dart'; -import 'package:fluffychat/pangea/choreographer/controllers/contextual_definition_controller.dart'; import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/events/controllers/message_data_controller.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; @@ -42,8 +41,6 @@ class PangeaController { late PutAnalyticsController putAnalytics; late MessageDataController messageData; - // TODO: make these static so we can remove from here - late ContextualDefinitionController definitions; late SubscriptionController subscriptionController; late TextToSpeechController textToSpeech; late SpeechToTextController speechToText; @@ -90,7 +87,6 @@ class PangeaController { getAnalytics = GetAnalyticsController(this); putAnalytics = PutAnalyticsController(this); messageData = MessageDataController(this); - definitions = ContextualDefinitionController(this); subscriptionController = SubscriptionController(this); textToSpeech = TextToSpeechController(this); speechToText = SpeechToTextController(this); diff --git a/lib/pangea/course_settings/course_settings.dart b/lib/pangea/course_settings/course_settings.dart index 600de91cf..295360fe0 100644 --- a/lib/pangea/course_settings/course_settings.dart +++ b/lib/pangea/course_settings/course_settings.dart @@ -6,6 +6,7 @@ import 'package:collection/collection.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart'; +import 'package:shimmer/shimmer.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; @@ -20,7 +21,6 @@ import 'package:fluffychat/pangea/course_settings/pin_clipper.dart'; import 'package:fluffychat/pangea/course_settings/topic_participant_list.dart'; import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; import 'package:fluffychat/widgets/matrix.dart'; -import 'package:shimmer/shimmer.dart'; class CourseSettings extends StatelessWidget { // final Room room; diff --git a/lib/pangea/events/controllers/message_data_controller.dart b/lib/pangea/events/controllers/message_data_controller.dart index 6bbcc5af2..1cb1e1b74 100644 --- a/lib/pangea/events/controllers/message_data_controller.dart +++ b/lib/pangea/events/controllers/message_data_controller.dart @@ -6,7 +6,8 @@ import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/pangea/choreographer/repo/full_text_translation_repo.dart'; -import 'package:fluffychat/pangea/choreographer/repo/tokens_repo.dart'; +import 'package:fluffychat/pangea/choreographer/repo/full_text_translation_request_model.dart'; +import 'package:fluffychat/pangea/choreographer/repo/full_text_translation_response_model.dart'; import 'package:fluffychat/pangea/common/controllers/base_controller.dart'; import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; @@ -16,6 +17,7 @@ import 'package:fluffychat/pangea/events/models/representation_content_model.dar import 'package:fluffychat/pangea/events/models/stt_translation_model.dart'; import 'package:fluffychat/pangea/events/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/events/repo/token_api_models.dart'; +import 'package:fluffychat/pangea/events/repo/tokens_repo.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; // TODO - make this static and take it out of the _pangeaController diff --git a/lib/pangea/events/event_wrappers/pangea_message_event.dart b/lib/pangea/events/event_wrappers/pangea_message_event.dart index fd410a3ba..4f7e943e9 100644 --- a/lib/pangea/events/event_wrappers/pangea_message_event.dart +++ b/lib/pangea/events/event_wrappers/pangea_message_event.dart @@ -9,14 +9,16 @@ import 'package:matrix/matrix.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:fluffychat/pangea/choreographer/models/choreo_record.dart'; -import 'package:fluffychat/pangea/choreographer/repo/full_text_translation_repo.dart'; -import 'package:fluffychat/pangea/choreographer/repo/language_detection_repo.dart'; +import 'package:fluffychat/pangea/choreographer/repo/full_text_translation_request_model.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/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'; import 'package:fluffychat/pangea/events/models/tokens_event_content_model.dart'; +import 'package:fluffychat/pangea/events/repo/language_detection_repo.dart'; +import 'package:fluffychat/pangea/events/repo/language_detection_request.dart'; +import 'package:fluffychat/pangea/events/repo/language_detection_response.dart'; import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; import 'package:fluffychat/pangea/morphs/morph_features_enum.dart'; import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart'; diff --git a/lib/pangea/events/event_wrappers/pangea_representation_event.dart b/lib/pangea/events/event_wrappers/pangea_representation_event.dart index 741c60920..0962faabd 100644 --- a/lib/pangea/events/event_wrappers/pangea_representation_event.dart +++ b/lib/pangea/events/event_wrappers/pangea_representation_event.dart @@ -13,7 +13,7 @@ import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart'; import 'package:fluffychat/pangea/choreographer/event_wrappers/pangea_choreo_event.dart'; import 'package:fluffychat/pangea/choreographer/models/choreo_record.dart'; import 'package:fluffychat/pangea/choreographer/models/language_detection_model.dart'; -import 'package:fluffychat/pangea/choreographer/repo/full_text_translation_repo.dart'; +import 'package:fluffychat/pangea/choreographer/repo/full_text_translation_request_model.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/events/extensions/pangea_event_extension.dart'; diff --git a/lib/pangea/events/repo/language_detection_repo.dart b/lib/pangea/events/repo/language_detection_repo.dart new file mode 100644 index 000000000..97ee1a5e7 --- /dev/null +++ b/lib/pangea/events/repo/language_detection_repo.dart @@ -0,0 +1,33 @@ +import 'dart:convert'; + +import 'package:http/http.dart'; + +import 'package:fluffychat/pangea/common/config/environment.dart'; +import 'package:fluffychat/pangea/common/network/requests.dart'; +import 'package:fluffychat/pangea/common/network/urls.dart'; +import 'package:fluffychat/pangea/events/repo/language_detection_request.dart'; +import 'package:fluffychat/pangea/events/repo/language_detection_response.dart'; + +class LanguageDetectionRepo { + static Future get( + String? accessToken, { + required LanguageDetectionRequest request, + }) async { + final Requests req = Requests( + accessToken: accessToken, + choreoApiKey: Environment.choreoApiKey, + ); + final Response res = await req.post( + url: PApiUrls.languageDetection, + body: request.toJson(), + ); + + final Map json = + jsonDecode(utf8.decode(res.bodyBytes).toString()); + + final LanguageDetectionResponse response = + LanguageDetectionResponse.fromJson(json); + + return response; + } +} diff --git a/lib/pangea/events/repo/language_detection_request.dart b/lib/pangea/events/repo/language_detection_request.dart new file mode 100644 index 000000000..eb28d6507 --- /dev/null +++ b/lib/pangea/events/repo/language_detection_request.dart @@ -0,0 +1,19 @@ +class LanguageDetectionRequest { + final String text; + final String? senderl1; + final String? senderl2; + + LanguageDetectionRequest({ + required this.text, + this.senderl1, + this.senderl2, + }); + + Map toJson() { + return { + 'full_text': text, + 'sender_l1': senderl1, + 'sender_l2': senderl2, + }; + } +} diff --git a/lib/pangea/events/repo/language_detection_response.dart b/lib/pangea/events/repo/language_detection_response.dart new file mode 100644 index 000000000..9a7dd7214 --- /dev/null +++ b/lib/pangea/events/repo/language_detection_response.dart @@ -0,0 +1,29 @@ +import 'package:fluffychat/pangea/choreographer/models/language_detection_model.dart'; + +class LanguageDetectionResponse { + List detections; + String fullText; + + LanguageDetectionResponse({ + required this.detections, + required this.fullText, + }); + + factory LanguageDetectionResponse.fromJson(Map json) { + return LanguageDetectionResponse( + detections: List.from( + (json['detections'] as Iterable).map( + (e) => LanguageDetection.fromJson(e), + ), + ), + fullText: json['full_text'], + ); + } + + Map toJson() { + return { + 'detections': detections.map((e) => e.toJson()).toList(), + 'full_text': fullText, + }; + } +} diff --git a/lib/pangea/choreographer/repo/tokens_repo.dart b/lib/pangea/events/repo/tokens_repo.dart similarity index 100% rename from lib/pangea/choreographer/repo/tokens_repo.dart rename to lib/pangea/events/repo/tokens_repo.dart