From 13d43611fa8c066a2c789236851fefd5fc146fdb Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 18 Dec 2025 11:45:58 -0500 Subject: [PATCH] feat: send message info in lemma info request --- .../vocab_details_emoji_selector.dart | 1 + lib/pangea/constructs/construct_identifier.dart | 11 ++++++++--- lib/pangea/events/models/content_feedback.dart | 2 +- lib/pangea/lemmas/lemma_highlight_emoji_row.dart | 3 +++ lib/pangea/lemmas/lemma_info_repo.dart | 8 ++++++-- lib/pangea/lemmas/lemma_info_request.dart | 16 ++++++++++++---- lib/pangea/lemmas/lemma_info_response.dart | 9 ++++----- lib/pangea/lemmas/lemma_meaning_builder.dart | 4 ++++ lib/pangea/lemmas/lemma_meaning_widget.dart | 3 +++ .../phonetic_transcription_repo.dart | 5 ++++- .../emoji_activity_generator.dart | 14 ++++++++------ .../lemma_meaning_activity_generator.dart | 7 ++++--- .../practice_generation_repo.dart | 11 +++++++---- .../token_info_feedback_dialog.dart | 2 +- .../message_practice/practice_controller.dart | 5 ++++- .../toolbar/word_card/lemma_meaning_display.dart | 3 +++ .../toolbar/word_card/lemma_reaction_picker.dart | 1 + .../toolbar/word_card/token_feedback_button.dart | 3 +++ .../toolbar/word_card/word_zoom_widget.dart | 2 ++ 19 files changed, 79 insertions(+), 31 deletions(-) diff --git a/lib/pangea/analytics_details_popup/vocab_details_emoji_selector.dart b/lib/pangea/analytics_details_popup/vocab_details_emoji_selector.dart index 85b99b602..e55016aed 100644 --- a/lib/pangea/analytics_details_popup/vocab_details_emoji_selector.dart +++ b/lib/pangea/analytics_details_popup/vocab_details_emoji_selector.dart @@ -63,6 +63,7 @@ class VocabDetailsEmojiSelectorState extends State langCode: MatrixState.pangeaController.userController.userL2Code!, emoji: selectedEmoji, onEmojiSelected: _setEmoji, + messageInfo: const {}, ); } } diff --git a/lib/pangea/constructs/construct_identifier.dart b/lib/pangea/constructs/construct_identifier.dart index aa980be0a..50dcf1dca 100644 --- a/lib/pangea/constructs/construct_identifier.dart +++ b/lib/pangea/constructs/construct_identifier.dart @@ -151,7 +151,8 @@ class ConstructIdentifier { uses: [], ); - LemmaInfoRequest get lemmaInfoRequest => LemmaInfoRequest( + LemmaInfoRequest lemmaInfoRequest(Map messageInfo) => + LemmaInfoRequest( partOfSpeech: category, lemmaLang: MatrixState.pangeaController.userController.userL2?.langCodeShort ?? @@ -160,12 +161,16 @@ class ConstructIdentifier { MatrixState.pangeaController.userController.userL1?.langCodeShort ?? LanguageKeys.defaultLanguage, lemma: lemma, + messageInfo: messageInfo, ); /// [lemmmaLang] if not set, assumed to be userL2 - Future> getLemmaInfo() => LemmaInfoRepo.get( + Future> getLemmaInfo( + Map messageInfo, + ) => + LemmaInfoRepo.get( MatrixState.pangeaController.userController.accessToken, - lemmaInfoRequest, + lemmaInfoRequest(messageInfo), ); List get userSetEmoji => userLemmaInfo.emojis ?? []; diff --git a/lib/pangea/events/models/content_feedback.dart b/lib/pangea/events/models/content_feedback.dart index cb12bae1a..7b554706f 100644 --- a/lib/pangea/events/models/content_feedback.dart +++ b/lib/pangea/events/models/content_feedback.dart @@ -6,7 +6,7 @@ abstract class JsonSerializable { } class ContentFeedback { - final JsonSerializable content; + final T content; final String feedback; ContentFeedback(this.content, this.feedback); diff --git a/lib/pangea/lemmas/lemma_highlight_emoji_row.dart b/lib/pangea/lemmas/lemma_highlight_emoji_row.dart index 8fcd2d29f..58093312c 100644 --- a/lib/pangea/lemmas/lemma_highlight_emoji_row.dart +++ b/lib/pangea/lemmas/lemma_highlight_emoji_row.dart @@ -18,6 +18,7 @@ class LemmaHighlightEmojiRow extends StatefulWidget { final String langCode; final Function(String) onEmojiSelected; + final Map messageInfo; final String? emoji; final Widget? selectedEmojiBadge; @@ -27,6 +28,7 @@ class LemmaHighlightEmojiRow extends StatefulWidget { required this.cId, required this.langCode, required this.onEmojiSelected, + required this.messageInfo, this.emoji, this.selectedEmojiBadge, }); @@ -63,6 +65,7 @@ class LemmaHighlightEmojiRowState extends State { return LemmaMeaningBuilder( langCode: widget.langCode, constructId: widget.cId, + messageInfo: widget.messageInfo, builder: (context, controller) { if (controller.error != null) { return const SizedBox.shrink(); diff --git a/lib/pangea/lemmas/lemma_info_repo.dart b/lib/pangea/lemmas/lemma_info_repo.dart index 02d6712ef..8dfd5a711 100644 --- a/lib/pangea/lemmas/lemma_info_repo.dart +++ b/lib/pangea/lemmas/lemma_info_repo.dart @@ -34,7 +34,9 @@ class LemmaInfoRepo { static Future> get( String accessToken, LemmaInfoRequest request, - ) { + ) async { + await GetStorage.init('lemma_storage'); + // 1. Try memory cache final cached = _getCached(request); if (cached != null) { @@ -44,7 +46,7 @@ class LemmaInfoRepo { // 2. Try disk cache final stored = _getStored(request); if (stored != null) { - return Future.value(Result.value(stored)); + return Result.value(stored); } // 3. Fetch from network (safe future) @@ -66,6 +68,8 @@ class LemmaInfoRepo { LemmaInfoRequest request, LemmaInfoResponse resultFuture, ) async { + await GetStorage.init('lemma_storage'); + final key = request.hashCode.toString(); try { await _storage.write(key, resultFuture.toJson()); diff --git a/lib/pangea/lemmas/lemma_info_request.dart b/lib/pangea/lemmas/lemma_info_request.dart index f934a02af..75ca7dd8a 100644 --- a/lib/pangea/lemmas/lemma_info_request.dart +++ b/lib/pangea/lemmas/lemma_info_request.dart @@ -1,6 +1,7 @@ +import 'package:collection/collection.dart'; + import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart'; import 'package:fluffychat/pangea/constructs/construct_identifier.dart'; -import 'package:fluffychat/pangea/events/models/content_feedback.dart'; import 'package:fluffychat/pangea/lemmas/lemma_info_response.dart'; class LemmaInfoRequest { @@ -8,14 +9,16 @@ class LemmaInfoRequest { final String partOfSpeech; final String lemmaLang; final String userL1; + final Map messageInfo; - List> feedback; + List feedback; LemmaInfoRequest({ required String partOfSpeech, required String lemmaLang, required this.userL1, required this.lemma, + required this.messageInfo, this.feedback = const [], }) : partOfSpeech = partOfSpeech.toLowerCase(), lemmaLang = lemmaLang.toLowerCase(); @@ -27,6 +30,7 @@ class LemmaInfoRequest { 'lemma_lang': lemmaLang, 'user_l1': userL1, 'feedback': feedback.map((e) => e.toJson()).toList(), + 'message_info': messageInfo, }; } @@ -37,11 +41,15 @@ class LemmaInfoRequest { runtimeType == other.runtimeType && lemma == other.lemma && partOfSpeech == other.partOfSpeech && - feedback == other.feedback; + const ListEquality().equals(feedback, other.feedback) && + userL1 == other.userL1; @override int get hashCode => - lemma.hashCode ^ partOfSpeech.hashCode ^ feedback.hashCode; + lemma.hashCode ^ + partOfSpeech.hashCode ^ + const ListEquality().hash(feedback) ^ + userL1.hashCode; String get storageKey { return 'l:$lemma,p:$partOfSpeech,lang:$lemmaLang,l1:$userL1'; diff --git a/lib/pangea/lemmas/lemma_info_response.dart b/lib/pangea/lemmas/lemma_info_response.dart index 6edec1fc7..1035cd6e7 100644 --- a/lib/pangea/lemmas/lemma_info_response.dart +++ b/lib/pangea/lemmas/lemma_info_response.dart @@ -1,3 +1,5 @@ +import 'package:collection/collection.dart'; + import 'package:fluffychat/pangea/events/models/content_feedback.dart'; class LemmaInfoResponse implements JsonSerializable { @@ -35,12 +37,9 @@ class LemmaInfoResponse implements JsonSerializable { identical(this, other) || other is LemmaInfoResponse && runtimeType == other.runtimeType && - emoji.length == other.emoji.length && - emoji.every((element) => other.emoji.contains(element)) && + const ListEquality().equals(emoji, other.emoji) && meaning == other.meaning; @override - int get hashCode => - emoji.fold(0, (prev, element) => prev ^ element.hashCode) ^ - meaning.hashCode; + int get hashCode => const ListEquality().hash(emoji) ^ meaning.hashCode; } diff --git a/lib/pangea/lemmas/lemma_meaning_builder.dart b/lib/pangea/lemmas/lemma_meaning_builder.dart index 30603329c..b6cfcd0f3 100644 --- a/lib/pangea/lemmas/lemma_meaning_builder.dart +++ b/lib/pangea/lemmas/lemma_meaning_builder.dart @@ -30,6 +30,8 @@ class _LemmaMeaningLoader extends AsyncLoader { class LemmaMeaningBuilder extends StatefulWidget { final String langCode; final ConstructIdentifier constructId; + final Map messageInfo; + final Widget Function( BuildContext context, LemmaMeaningBuilderState controller, @@ -40,6 +42,7 @@ class LemmaMeaningBuilder extends StatefulWidget { required this.langCode, required this.constructId, required this.builder, + required this.messageInfo, }); @override @@ -85,6 +88,7 @@ class LemmaMeaningBuilderState extends State { lemmaLang: widget.langCode, userL1: MatrixState.pangeaController.userController.userL1?.langCode ?? LanguageKeys.defaultLanguage, + messageInfo: widget.messageInfo, ); void _reload() { diff --git a/lib/pangea/lemmas/lemma_meaning_widget.dart b/lib/pangea/lemmas/lemma_meaning_widget.dart index 191d4a161..e2daea447 100644 --- a/lib/pangea/lemmas/lemma_meaning_widget.dart +++ b/lib/pangea/lemmas/lemma_meaning_widget.dart @@ -12,10 +12,12 @@ class LemmaMeaningWidget extends StatelessWidget { final ConstructIdentifier constructId; final TextStyle? style; final InlineSpan? leading; + final Map messageInfo; const LemmaMeaningWidget({ super.key, required this.constructId, + required this.messageInfo, this.style, this.leading, }); @@ -25,6 +27,7 @@ class LemmaMeaningWidget extends StatelessWidget { return LemmaMeaningBuilder( langCode: MatrixState.pangeaController.userController.userL2!.langCode, constructId: constructId, + messageInfo: messageInfo, builder: (context, controller) { if (controller.isLoading) { return const TextLoadingShimmer(); diff --git a/lib/pangea/phonetic_transcription/phonetic_transcription_repo.dart b/lib/pangea/phonetic_transcription/phonetic_transcription_repo.dart index 33760bc9a..d42728437 100644 --- a/lib/pangea/phonetic_transcription/phonetic_transcription_repo.dart +++ b/lib/pangea/phonetic_transcription/phonetic_transcription_repo.dart @@ -34,7 +34,9 @@ class PhoneticTranscriptionRepo { static Future> get( String accessToken, PhoneticTranscriptionRequest request, - ) { + ) async { + await GetStorage.init('phonetic_transcription_storage'); + // 1. Try memory cache final cached = _getCached(request); if (cached != null) { @@ -66,6 +68,7 @@ class PhoneticTranscriptionRepo { PhoneticTranscriptionRequest request, PhoneticTranscriptionResponse resultFuture, ) async { + await GetStorage.init('phonetic_transcription_storage'); final key = request.hashCode.toString(); try { await _storage.write(key, resultFuture.toJson()); diff --git a/lib/pangea/practice_activities/emoji_activity_generator.dart b/lib/pangea/practice_activities/emoji_activity_generator.dart index 3cb5584bd..88607e05d 100644 --- a/lib/pangea/practice_activities/emoji_activity_generator.dart +++ b/lib/pangea/practice_activities/emoji_activity_generator.dart @@ -10,18 +10,20 @@ import 'package:fluffychat/pangea/practice_activities/practice_match.dart'; class EmojiActivityGenerator { static Future get( - MessageActivityRequest req, - ) async { + MessageActivityRequest req, { + required Map messageInfo, + }) async { if (req.targetTokens.length <= 1) { throw Exception("Emoji activity requires at least 2 tokens"); } - return _matchActivity(req); + return _matchActivity(req, messageInfo: messageInfo); } static Future _matchActivity( - MessageActivityRequest req, - ) async { + MessageActivityRequest req, { + required Map messageInfo, + }) async { final Map> matchInfo = {}; final List missingEmojis = []; @@ -39,7 +41,7 @@ class EmojiActivityGenerator { final List>> lemmaInfoFutures = missingEmojis - .map((token) => token.vocabConstructID.getLemmaInfo()) + .map((token) => token.vocabConstructID.getLemmaInfo(messageInfo)) .toList(); final List> lemmaInfos = diff --git a/lib/pangea/practice_activities/lemma_meaning_activity_generator.dart b/lib/pangea/practice_activities/lemma_meaning_activity_generator.dart index 02d839940..274334fb3 100644 --- a/lib/pangea/practice_activities/lemma_meaning_activity_generator.dart +++ b/lib/pangea/practice_activities/lemma_meaning_activity_generator.dart @@ -12,11 +12,12 @@ import 'package:fluffychat/widgets/future_loading_dialog.dart'; class LemmaMeaningActivityGenerator { static Future get( - MessageActivityRequest req, - ) async { + MessageActivityRequest req, { + required Map messageInfo, + }) async { final List>> lemmaInfoFutures = req .targetTokens - .map((token) => token.vocabConstructID.getLemmaInfo()) + .map((token) => token.vocabConstructID.getLemmaInfo(messageInfo)) .toList(); final List> lemmaInfos = diff --git a/lib/pangea/practice_activities/practice_generation_repo.dart b/lib/pangea/practice_activities/practice_generation_repo.dart index 423f4033a..07b9b2944 100644 --- a/lib/pangea/practice_activities/practice_generation_repo.dart +++ b/lib/pangea/practice_activities/practice_generation_repo.dart @@ -57,8 +57,9 @@ class PracticeRepo { /// [event] is optional and used for saving the activity event to Matrix static Future> getPracticeActivity( - MessageActivityRequest req, - ) async { + MessageActivityRequest req, { + required Map messageInfo, + }) async { final cached = _getCached(req); if (cached != null) return Result.value(cached); @@ -66,6 +67,7 @@ class PracticeRepo { final MessageActivityResponse res = await _routePracticeActivity( accessToken: MatrixState.pangeaController.userController.accessToken, req: req, + messageInfo: messageInfo, ); _setCached(req, res); @@ -109,18 +111,19 @@ class PracticeRepo { static Future _routePracticeActivity({ required String accessToken, required MessageActivityRequest req, + required Map messageInfo, }) async { // some activities we'll get from the server and others we'll generate locally switch (req.targetType) { case ActivityTypeEnum.emoji: - return EmojiActivityGenerator.get(req); + return EmojiActivityGenerator.get(req, messageInfo: messageInfo); case ActivityTypeEnum.lemmaId: return LemmaActivityGenerator.get(req); case ActivityTypeEnum.morphId: return MorphActivityGenerator.get(req); case ActivityTypeEnum.wordMeaning: debugger(when: kDebugMode); - return LemmaMeaningActivityGenerator.get(req); + return LemmaMeaningActivityGenerator.get(req, messageInfo: messageInfo); case ActivityTypeEnum.messageMeaning: case ActivityTypeEnum.wordFocusListening: return WordFocusListeningGenerator.get(req); diff --git a/lib/pangea/token_info_feedback/token_info_feedback_dialog.dart b/lib/pangea/token_info_feedback/token_info_feedback_dialog.dart index dbe165a96..ea8811360 100644 --- a/lib/pangea/token_info_feedback/token_info_feedback_dialog.dart +++ b/lib/pangea/token_info_feedback/token_info_feedback_dialog.dart @@ -117,7 +117,7 @@ class TokenInfoFeedbackDialog extends StatelessWidget { LemmaInfoResponse response, ) => LemmaInfoRepo.set( - token.vocabConstructID.lemmaInfoRequest, + token.vocabConstructID.lemmaInfoRequest(event.event.content), response, ); diff --git a/lib/pangea/toolbar/message_practice/practice_controller.dart b/lib/pangea/toolbar/message_practice/practice_controller.dart index 0e388d57e..f712924b0 100644 --- a/lib/pangea/toolbar/message_practice/practice_controller.dart +++ b/lib/pangea/toolbar/message_practice/practice_controller.dart @@ -93,7 +93,10 @@ class PracticeController with ChangeNotifier { targetMorphFeature: target.morphFeature, ); - final result = await PracticeRepo.getPracticeActivity(req); + final result = await PracticeRepo.getPracticeActivity( + req, + messageInfo: pangeaMessageEvent.event.content, + ); if (result.isValue) { _activity = result.result; } diff --git a/lib/pangea/toolbar/word_card/lemma_meaning_display.dart b/lib/pangea/toolbar/word_card/lemma_meaning_display.dart index 84f9200d3..290a30931 100644 --- a/lib/pangea/toolbar/word_card/lemma_meaning_display.dart +++ b/lib/pangea/toolbar/word_card/lemma_meaning_display.dart @@ -11,12 +11,14 @@ class LemmaMeaningDisplay extends StatelessWidget { final String langCode; final ConstructIdentifier constructId; final String text; + final Map messageInfo; const LemmaMeaningDisplay({ super.key, required this.langCode, required this.constructId, required this.text, + required this.messageInfo, }); @override @@ -24,6 +26,7 @@ class LemmaMeaningDisplay extends StatelessWidget { return LemmaMeaningBuilder( langCode: langCode, constructId: constructId, + messageInfo: messageInfo, builder: (context, controller) { if (controller.isError) { return ErrorIndicator( diff --git a/lib/pangea/toolbar/word_card/lemma_reaction_picker.dart b/lib/pangea/toolbar/word_card/lemma_reaction_picker.dart index 67f6922af..047f3b5ef 100644 --- a/lib/pangea/toolbar/word_card/lemma_reaction_picker.dart +++ b/lib/pangea/toolbar/word_card/lemma_reaction_picker.dart @@ -118,6 +118,7 @@ class LemmaReactionPickerState extends State ? _setEmoji(emoji) : _sendOrRedactReaction(emoji), emoji: _selectedEmoji, + messageInfo: widget.event?.content ?? {}, selectedEmojiBadge: widget.event != null && _selectedEmoji != null && _sentReaction(_selectedEmoji!) == null diff --git a/lib/pangea/toolbar/word_card/token_feedback_button.dart b/lib/pangea/toolbar/word_card/token_feedback_button.dart index dc2a6cff4..3e2c379a5 100644 --- a/lib/pangea/toolbar/word_card/token_feedback_button.dart +++ b/lib/pangea/toolbar/word_card/token_feedback_button.dart @@ -13,6 +13,7 @@ class TokenFeedbackButton extends StatelessWidget { final String text; final Function(LemmaInfoResponse, String) onFlagTokenInfo; + final Map messageInfo; const TokenFeedbackButton({ super.key, @@ -20,6 +21,7 @@ class TokenFeedbackButton extends StatelessWidget { required this.constructId, required this.text, required this.onFlagTokenInfo, + required this.messageInfo, }); @override @@ -27,6 +29,7 @@ class TokenFeedbackButton extends StatelessWidget { return LemmaMeaningBuilder( langCode: textLanguage.langCode, constructId: constructId, + messageInfo: messageInfo, builder: (context, lemmaController) { return PhoneticTranscriptionBuilder( textLanguage: textLanguage, diff --git a/lib/pangea/toolbar/word_card/word_zoom_widget.dart b/lib/pangea/toolbar/word_card/word_zoom_widget.dart index caabccaa5..85fa724de 100644 --- a/lib/pangea/toolbar/word_card/word_zoom_widget.dart +++ b/lib/pangea/toolbar/word_card/word_zoom_widget.dart @@ -115,6 +115,7 @@ class WordZoomWidget extends StatelessWidget { constructId: construct, text: token.content, onFlagTokenInfo: onFlagTokenInfo!, + messageInfo: event?.content ?? {}, ) : const SizedBox( width: 40.0, @@ -152,6 +153,7 @@ class WordZoomWidget extends StatelessWidget { langCode: langCode, constructId: construct, text: token.content, + messageInfo: event?.content ?? {}, ), ], ),