From c2defa90237fb4f27865a97b99afc63c1585b71c Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 9 Dec 2025 14:37:15 -0500 Subject: [PATCH] fix: always pass lemma info and phonetic transcription in token feedback request --- .../constructs/construct_identifier.dart | 4 +- lib/pangea/lemmas/lemma_info_repo.dart | 7 +- lib/pangea/lemmas/lemma_info_response.dart | 5 + lib/pangea/lemmas/lemma_meaning_builder.dart | 58 ++++--- .../phonetic_transcription_builder.dart | 102 ++++++++++++ .../phonetic_transcription_widget.dart | 149 +++++------------- .../token_info_feedback_dialog.dart | 20 +-- .../token_info_feedback_request.dart | 10 +- .../word_card/lemma_meaning_display.dart | 72 +++++++++ .../word_card/lemma_reaction_picker.dart | 80 ++++++---- .../word_card/reading_assistance_content.dart | 5 +- .../word_card/token_feedback_button.dart | 62 ++++++++ .../toolbar/word_card/word_zoom_widget.dart | 106 ++++--------- 13 files changed, 419 insertions(+), 261 deletions(-) create mode 100644 lib/pangea/phonetic_transcription/phonetic_transcription_builder.dart create mode 100644 lib/pangea/toolbar/word_card/lemma_meaning_display.dart create mode 100644 lib/pangea/toolbar/word_card/token_feedback_button.dart diff --git a/lib/pangea/constructs/construct_identifier.dart b/lib/pangea/constructs/construct_identifier.dart index 75878cb74..df4887fbb 100644 --- a/lib/pangea/constructs/construct_identifier.dart +++ b/lib/pangea/constructs/construct_identifier.dart @@ -150,7 +150,7 @@ class ConstructIdentifier { uses: [], ); - LemmaInfoRequest get _lemmaInfoRequest => LemmaInfoRequest( + LemmaInfoRequest get lemmaInfoRequest => LemmaInfoRequest( partOfSpeech: category, lemmaLang: MatrixState.pangeaController.userController.userL2?.langCodeShort ?? @@ -163,7 +163,7 @@ class ConstructIdentifier { /// [lemmmaLang] if not set, assumed to be userL2 Future getLemmaInfo() => LemmaInfoRepo.get( - _lemmaInfoRequest, + lemmaInfoRequest, ); List get userSetEmoji => userLemmaInfo.emojis ?? []; diff --git a/lib/pangea/lemmas/lemma_info_repo.dart b/lib/pangea/lemmas/lemma_info_repo.dart index bdb3d3763..420c50420 100644 --- a/lib/pangea/lemmas/lemma_info_repo.dart +++ b/lib/pangea/lemmas/lemma_info_repo.dart @@ -13,10 +13,13 @@ import 'package:fluffychat/widgets/matrix.dart'; class LemmaInfoRepo { static final GetStorage _lemmaStorage = GetStorage('lemma_storage'); - static void set(LemmaInfoRequest request, LemmaInfoResponse response) { + static Future set( + LemmaInfoRequest request, + LemmaInfoResponse response, + ) async { // set expireAt if not set response.expireAt ??= DateTime.now().add(const Duration(days: 100)); - _lemmaStorage.write(request.storageKey, response.toJson()); + await _lemmaStorage.write(request.storageKey, response.toJson()); } static LemmaInfoResponse? getCached(LemmaInfoRequest request) { diff --git a/lib/pangea/lemmas/lemma_info_response.dart b/lib/pangea/lemmas/lemma_info_response.dart index fb5bca198..ffd5d41bd 100644 --- a/lib/pangea/lemmas/lemma_info_response.dart +++ b/lib/pangea/lemmas/lemma_info_response.dart @@ -22,6 +22,11 @@ class LemmaInfoResponse implements JsonSerializable { ); } + static LemmaInfoResponse get error => LemmaInfoResponse( + emoji: [], + meaning: 'ERROR', + ); + @override Map toJson() { return { diff --git a/lib/pangea/lemmas/lemma_meaning_builder.dart b/lib/pangea/lemmas/lemma_meaning_builder.dart index f1b1560dc..405e8491d 100644 --- a/lib/pangea/lemmas/lemma_meaning_builder.dart +++ b/lib/pangea/lemmas/lemma_meaning_builder.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:fluffychat/pangea/common/utils/async_state.dart'; import 'package:fluffychat/pangea/constructs/construct_identifier.dart'; import 'package:fluffychat/pangea/languages/language_constants.dart'; import 'package:fluffychat/pangea/lemmas/lemma_info_repo.dart'; @@ -7,6 +8,14 @@ import 'package:fluffychat/pangea/lemmas/lemma_info_request.dart'; import 'package:fluffychat/pangea/lemmas/lemma_info_response.dart'; import 'package:fluffychat/widgets/matrix.dart'; +class _LemmaMeaningLoader extends AsyncLoader { + final LemmaInfoRequest request; + _LemmaMeaningLoader(this.request) : super(); + + @override + Future fetch() => LemmaInfoRepo.get(request); +} + class LemmaMeaningBuilder extends StatefulWidget { final String langCode; final ConstructIdentifier constructId; @@ -27,14 +36,12 @@ class LemmaMeaningBuilder extends StatefulWidget { } class LemmaMeaningBuilderState extends State { - LemmaInfoResponse? lemmaInfo; - bool isLoading = true; - Object? error; + late _LemmaMeaningLoader _loader; @override void initState() { super.initState(); - _fetchLemmaMeaning(); + _reload(); } @override @@ -42,10 +49,25 @@ class LemmaMeaningBuilderState extends State { super.didUpdateWidget(oldWidget); if (oldWidget.constructId != widget.constructId || oldWidget.langCode != widget.langCode) { - _fetchLemmaMeaning(); + _loader.dispose(); + _reload(); } } + @override + void dispose() { + _loader.dispose(); + super.dispose(); + } + + bool get isLoading => _loader.isLoading; + bool get isError => _loader.isError; + + Object? get error => + isError ? (_loader.state.value as AsyncError).error : null; + + LemmaInfoResponse? get lemmaInfo => _loader.value; + LemmaInfoRequest get _request => LemmaInfoRequest( lemma: widget.constructId.lemma, partOfSpeech: widget.constructId.category, @@ -54,27 +76,19 @@ class LemmaMeaningBuilderState extends State { LanguageKeys.defaultLanguage, ); - Future _fetchLemmaMeaning() async { - setState(() { - isLoading = true; - error = null; - }); - - try { - final resp = await LemmaInfoRepo.get(_request); - lemmaInfo = resp; - } catch (e) { - error = e; - } finally { - if (mounted) setState(() => isLoading = false); - } + void _reload() { + _loader = _LemmaMeaningLoader(_request); + _loader.load(); } @override Widget build(BuildContext context) { - return widget.builder( - context, - this, + return ValueListenableBuilder( + valueListenable: _loader.state, + builder: (context, _, __) => widget.builder( + context, + this, + ), ); } } diff --git a/lib/pangea/phonetic_transcription/phonetic_transcription_builder.dart b/lib/pangea/phonetic_transcription/phonetic_transcription_builder.dart new file mode 100644 index 000000000..41c25101b --- /dev/null +++ b/lib/pangea/phonetic_transcription/phonetic_transcription_builder.dart @@ -0,0 +1,102 @@ +import 'package:flutter/material.dart'; + +import 'package:fluffychat/pangea/common/utils/async_state.dart'; +import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart'; +import 'package:fluffychat/pangea/languages/language_arc_model.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; +import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_request.dart'; +import 'package:fluffychat/widgets/matrix.dart'; +import 'phonetic_transcription_repo.dart'; + +class _TranscriptLoader extends AsyncLoader { + final PhoneticTranscriptionRequest request; + _TranscriptLoader(this.request) : super(); + + @override + Future fetch() async { + final resp = await PhoneticTranscriptionRepo.get(request); + return resp.phoneticTranscriptionResult.phoneticTranscription.first + .phoneticL1Transcription.content; + } +} + +class PhoneticTranscriptionBuilder extends StatefulWidget { + final LanguageModel textLanguage; + final String text; + + final Widget Function( + BuildContext context, + PhoneticTranscriptionBuilderState controller, + ) builder; + + const PhoneticTranscriptionBuilder({ + super.key, + required this.textLanguage, + required this.text, + required this.builder, + }); + + @override + PhoneticTranscriptionBuilderState createState() => + PhoneticTranscriptionBuilderState(); +} + +class PhoneticTranscriptionBuilderState + extends State { + late _TranscriptLoader _loader; + + @override + void initState() { + super.initState(); + _reload(); + } + + @override + void didUpdateWidget(covariant PhoneticTranscriptionBuilder oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.text != widget.text || + oldWidget.textLanguage != widget.textLanguage) { + _loader.dispose(); + _reload(); + } + } + + @override + void dispose() { + _loader.dispose(); + super.dispose(); + } + + bool get isLoading => _loader.isLoading; + bool get isError => _loader.isError; + + Object? get error => + isError ? (_loader.state.value as AsyncError).error : null; + + String? get transcription => _loader.value; + + PhoneticTranscriptionRequest get _transcriptRequest => + PhoneticTranscriptionRequest( + arc: LanguageArc( + l1: MatrixState.pangeaController.userController.userL1!, + l2: widget.textLanguage, + ), + content: PangeaTokenText.fromString(widget.text), + ); + + void _reload() { + _loader = _TranscriptLoader(_transcriptRequest); + _loader.load(); + } + + @override + Widget build(BuildContext context) { + return ValueListenableBuilder( + valueListenable: _loader.state, + builder: (context, _, __) => widget.builder( + context, + this, + ), + ); + } +} diff --git a/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart b/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart index ae7b533b1..afe549d73 100644 --- a/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart +++ b/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart @@ -4,13 +4,9 @@ import 'package:flutter/material.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/network/requests.dart'; -import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/common/widgets/error_indicator.dart'; -import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart'; -import 'package:fluffychat/pangea/languages/language_arc_model.dart'; import 'package:fluffychat/pangea/languages/language_model.dart'; -import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_repo.dart'; -import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_request.dart'; +import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_builder.dart'; import 'package:fluffychat/pangea/text_to_speech/tts_controller.dart'; import 'package:fluffychat/widgets/hover_builder.dart'; import 'package:fluffychat/widgets/matrix.dart'; @@ -43,79 +39,6 @@ class PhoneticTranscriptionWidget extends StatefulWidget { class _PhoneticTranscriptionWidgetState extends State { bool _isPlaying = false; - bool _isLoading = false; - Object? _error; - - String? _transcription; - - @override - void initState() { - super.initState(); - _fetchTranscription(); - } - - @override - void didUpdateWidget( - covariant PhoneticTranscriptionWidget oldWidget, - ) { - super.didUpdateWidget(oldWidget); - if (oldWidget.text != widget.text || - oldWidget.textLanguage != widget.textLanguage) { - _fetchTranscription(); - } - } - - Future _fetchTranscription() async { - try { - setState(() { - _isLoading = true; - _error = null; - _transcription = null; - }); - - if (MatrixState.pangeaController.userController.userL1 == null) { - ErrorHandler.logError( - e: Exception('User L1 is not set'), - data: { - 'text': widget.text, - 'textLanguageCode': widget.textLanguage.langCode, - }, - ); - _error = Exception('User L1 is not set'); - return; - } - final req = PhoneticTranscriptionRequest( - arc: LanguageArc( - l1: MatrixState.pangeaController.userController.userL1!, - l2: widget.textLanguage, - ), - content: PangeaTokenText.fromString(widget.text), - // arc can be omitted for default empty map - ); - final res = await PhoneticTranscriptionRepo.get(req); - _transcription = res.phoneticTranscriptionResult.phoneticTranscription - .first.phoneticL1Transcription.content; - } catch (e, s) { - _error = e; - if (e is! UnsubscribedException) { - ErrorHandler.logError( - e: e, - s: s, - data: { - 'text': widget.text, - 'textLanguageCode': widget.textLanguage.langCode, - }, - ); - } - } finally { - if (mounted) { - setState(() { - _isLoading = false; - widget.onTranscriptionFetched?.call(); - }); - } - } - } Future _handleAudioTap() async { if (_isPlaying) { @@ -156,14 +79,15 @@ class _PhoneticTranscriptionWidgetState link: MatrixState.pAnyState .layerLinkAndKey("phonetic-transcription-${widget.text}") .link, - child: Row( + child: PhoneticTranscriptionBuilder( key: MatrixState.pAnyState .layerLinkAndKey("phonetic-transcription-${widget.text}") .key, - mainAxisSize: MainAxisSize.min, - children: [ - if (_error != null) - _error is UnsubscribedException + textLanguage: widget.textLanguage, + text: widget.text, + builder: (context, controller) { + if (controller.isError) { + return controller.error is UnsubscribedException ? ErrorIndicator( message: L10n.of(context) .subscribeToUnlockTranscriptions, @@ -176,37 +100,44 @@ class _PhoneticTranscriptionWidgetState : ErrorIndicator( message: L10n.of(context).failedToFetchTranscription, - ) - else if (_isLoading || _transcription == null) - const SizedBox( + ); + } + + if (controller.isLoading || + controller.transcription == null) { + return const SizedBox( width: 16, height: 16, child: CircularProgressIndicator.adaptive(), - ) - else - Flexible( - child: Text( - _transcription!, - textScaler: TextScaler.noScaling, - style: widget.style ?? - Theme.of(context).textTheme.bodyMedium, + ); + } + + return Row( + spacing: 8.0, + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: Text( + controller.transcription!, + textScaler: TextScaler.noScaling, + style: widget.style ?? + Theme.of(context).textTheme.bodyMedium, + ), ), - ), - if (_transcription != null && _error == null) - const SizedBox(width: 8), - if (_transcription != null && _error == null) - Tooltip( - message: _isPlaying - ? L10n.of(context).stop - : L10n.of(context).playAudio, - child: Icon( - _isPlaying ? Icons.pause_outlined : Icons.volume_up, - size: widget.iconSize ?? 24, - color: widget.iconColor ?? - Theme.of(context).iconTheme.color, + Tooltip( + message: _isPlaying + ? L10n.of(context).stop + : L10n.of(context).playAudio, + child: Icon( + _isPlaying ? Icons.pause_outlined : Icons.volume_up, + size: widget.iconSize ?? 24, + color: widget.iconColor ?? + Theme.of(context).iconTheme.color, + ), ), - ), - ], + ], + ); + }, ), ), ), 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 0660185bf..dbe165a96 100644 --- a/lib/pangea/token_info_feedback/token_info_feedback_dialog.dart +++ b/lib/pangea/token_info_feedback/token_info_feedback_dialog.dart @@ -8,8 +8,8 @@ import 'package:fluffychat/pangea/events/models/tokens_event_content_model.dart' import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/pangea/languages/language_arc_model.dart'; import 'package:fluffychat/pangea/languages/p_language_store.dart'; +import 'package:fluffychat/pangea/lemmas/lemma_info_repo.dart'; import 'package:fluffychat/pangea/lemmas/lemma_info_response.dart'; -import 'package:fluffychat/pangea/lemmas/user_set_lemma_info.dart'; import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_repo.dart'; import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_request.dart'; import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_response.dart'; @@ -115,19 +115,11 @@ class TokenInfoFeedbackDialog extends StatelessWidget { Future _updateLemmaInfo( PangeaToken token, LemmaInfoResponse response, - ) async { - final construct = token.vocabConstructID; - - final currentLemmaInfo = construct.userLemmaInfo; - final updatedLemmaInfo = UserSetLemmaInfo( - meaning: response.meaning, - emojis: response.emoji, - ); - - if (currentLemmaInfo != updatedLemmaInfo) { - await construct.setUserLemmaInfo(updatedLemmaInfo); - } - } + ) => + LemmaInfoRepo.set( + token.vocabConstructID.lemmaInfoRequest, + response, + ); Future _updatePhoneticTranscription( PhoneticTranscriptionResponse response, diff --git a/lib/pangea/token_info_feedback/token_info_feedback_request.dart b/lib/pangea/token_info_feedback/token_info_feedback_request.dart index 9caf6c4ea..815979493 100644 --- a/lib/pangea/token_info_feedback/token_info_feedback_request.dart +++ b/lib/pangea/token_info_feedback/token_info_feedback_request.dart @@ -8,8 +8,8 @@ class TokenInfoFeedbackRequestData { final String detectedLanguage; final List tokens; final int selectedToken; - final LemmaInfoResponse? lemmaInfo; - final String? phonetics; + final LemmaInfoResponse lemmaInfo; + final String phonetics; final String wordCardL1; TokenInfoFeedbackRequestData({ @@ -19,8 +19,8 @@ class TokenInfoFeedbackRequestData { required this.detectedLanguage, required this.tokens, required this.selectedToken, - this.lemmaInfo, - this.phonetics, + required this.lemmaInfo, + required this.phonetics, required this.wordCardL1, }); @@ -67,7 +67,7 @@ class TokenInfoFeedbackRequest { 'detected_language': data.detectedLanguage, 'tokens': data.tokens.map((token) => token.toJson()).toList(), 'selected_token': data.selectedToken, - 'lemma_info': data.lemmaInfo?.toJson(), + 'lemma_info': data.lemmaInfo.toJson(), 'phonetics': data.phonetics, 'user_feedback': userFeedback, 'word_card_l1': data.wordCardL1, diff --git a/lib/pangea/toolbar/word_card/lemma_meaning_display.dart b/lib/pangea/toolbar/word_card/lemma_meaning_display.dart new file mode 100644 index 000000000..aac4f1f8d --- /dev/null +++ b/lib/pangea/toolbar/word_card/lemma_meaning_display.dart @@ -0,0 +1,72 @@ +import 'package:flutter/material.dart'; + +import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/pangea/common/widgets/error_indicator.dart'; +import 'package:fluffychat/pangea/constructs/construct_identifier.dart'; +import 'package:fluffychat/pangea/lemmas/lemma_meaning_builder.dart'; + +class LemmaMeaningDisplay extends StatelessWidget { + final String langCode; + final ConstructIdentifier constructId; + final String text; + + const LemmaMeaningDisplay({ + super.key, + required this.langCode, + required this.constructId, + required this.text, + }); + + @override + Widget build(BuildContext context) { + return LemmaMeaningBuilder( + langCode: langCode, + constructId: constructId, + builder: (context, controller) { + if (controller.isError) { + return ErrorIndicator( + message: L10n.of(context).errorFetchingDefinition, + style: const TextStyle(fontSize: 14.0), + ); + } + + if (controller.isLoading || controller.lemmaInfo == null) { + return const CircularProgressIndicator.adaptive(); + } + + if (constructId.lemma.toLowerCase() == text.toLowerCase()) { + return Text( + controller.lemmaInfo!.meaning, + style: const TextStyle( + fontSize: 14.0, + ), + textAlign: TextAlign.center, + ); + } + + return RichText( + text: TextSpan( + style: DefaultTextStyle.of(context).style.copyWith( + fontSize: 14.0, + ), + children: [ + TextSpan( + text: constructId.lemma, + ), + const WidgetSpan( + child: SizedBox(width: 8.0), + ), + const TextSpan(text: ":"), + const WidgetSpan( + child: SizedBox(width: 8.0), + ), + TextSpan( + text: controller.lemmaInfo!.meaning, + ), + ], + ), + ); + }, + ); + } +} diff --git a/lib/pangea/toolbar/word_card/lemma_reaction_picker.dart b/lib/pangea/toolbar/word_card/lemma_reaction_picker.dart index b4d69b71c..00ba9ee27 100644 --- a/lib/pangea/toolbar/word_card/lemma_reaction_picker.dart +++ b/lib/pangea/toolbar/word_card/lemma_reaction_picker.dart @@ -4,21 +4,26 @@ import 'package:collection/collection.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; +import 'package:fluffychat/pangea/constructs/construct_identifier.dart'; +import 'package:fluffychat/pangea/lemmas/lemma_meaning_builder.dart'; import 'package:fluffychat/pangea/toolbar/reading_assistance/lemma_emoji_picker.dart'; class LemmaReactionPicker extends StatelessWidget { - final List emojis; - final bool loading; final Event? event; + final ConstructIdentifier construct; + final String langCode; const LemmaReactionPicker({ super.key, - required this.emojis, - required this.loading, - required this.event, + required this.construct, + required this.langCode, + this.event, }); - Future setEmoji(String emoji) async { + Future setEmoji( + String emoji, + List emojis, + ) async { if (event?.room.timeline == null) { throw Exception("Timeline is null in reaction picker"); } @@ -63,33 +68,44 @@ class LemmaReactionPicker extends StatelessWidget { @override Widget build(BuildContext context) { - final sentReactions = {}; - if (event?.room.timeline != null) { - sentReactions.addAll( - event! - .aggregatedEvents( - event!.room.timeline!, - RelationshipTypes.reaction, - ) - .where( - (event) => - event.senderId == event.room.client.userID && - event.type == 'm.reaction', - ) - .map( - (event) => event.content - .tryGetMap('m.relates_to') - ?.tryGet('key'), - ) - .whereType(), - ); - } + return LemmaMeaningBuilder( + langCode: langCode, + constructId: construct, + builder: (context, controller) { + final sentReactions = {}; + if (event?.room.timeline != null) { + sentReactions.addAll( + event! + .aggregatedEvents( + event!.room.timeline!, + RelationshipTypes.reaction, + ) + .where( + (event) => + event.senderId == event.room.client.userID && + event.type == 'm.reaction', + ) + .map( + (event) => event.content + .tryGetMap('m.relates_to') + ?.tryGet('key'), + ) + .whereType(), + ); + } - return LemmaEmojiPicker( - emojis: emojis, - onSelect: event?.room.timeline != null ? setEmoji : null, - disabled: (emoji) => sentReactions.contains(emoji), - loading: loading, + return LemmaEmojiPicker( + emojis: controller.lemmaInfo?.emoji ?? [], + onSelect: event?.room.timeline != null + ? (emoji) => setEmoji( + emoji, + controller.lemmaInfo?.emoji ?? [], + ) + : null, + disabled: (emoji) => sentReactions.contains(emoji), + loading: controller.isLoading, + ); + }, ); } } diff --git a/lib/pangea/toolbar/word_card/reading_assistance_content.dart b/lib/pangea/toolbar/word_card/reading_assistance_content.dart index b607b0d1d..71d2e0d97 100644 --- a/lib/pangea/toolbar/word_card/reading_assistance_content.dart +++ b/lib/pangea/toolbar/word_card/reading_assistance_content.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:matrix/matrix_api_lite/model/message_types.dart'; import 'package:fluffychat/config/themes.dart'; +import 'package:fluffychat/pangea/lemmas/lemma_info_response.dart'; import 'package:fluffychat/pangea/token_info_feedback/token_info_feedback_request.dart'; import 'package:fluffychat/pangea/toolbar/message_selection_overlay.dart'; import 'package:fluffychat/pangea/toolbar/word_card/word_zoom_widget.dart'; @@ -50,7 +51,7 @@ class ReadingAssistanceContent extends StatelessWidget { onClose: () => overlayController.updateSelectedSpan(null), langCode: overlayController.pangeaMessageEvent.messageDisplayLangCode, onDismissNewWordOverlay: () => overlayController.setState(() {}), - onFlagTokenInfo: () { + onFlagTokenInfo: (LemmaInfoResponse lemmaInfo, String phonetics) { if (selectedTokenIndex < 0) return; final requestData = TokenInfoFeedbackRequestData( userId: Matrix.of(context).client.userID!, @@ -61,6 +62,8 @@ class ReadingAssistanceContent extends StatelessWidget { tokens: tokens ?? [], selectedToken: selectedTokenIndex, wordCardL1: MatrixState.pangeaController.userController.userL1Code!, + lemmaInfo: lemmaInfo, + phonetics: phonetics, ); overlayController.widget.chatController.showTokenFeedbackDialog( requestData, diff --git a/lib/pangea/toolbar/word_card/token_feedback_button.dart b/lib/pangea/toolbar/word_card/token_feedback_button.dart new file mode 100644 index 000000000..dc2a6cff4 --- /dev/null +++ b/lib/pangea/toolbar/word_card/token_feedback_button.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; + +import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/pangea/constructs/construct_identifier.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; +import 'package:fluffychat/pangea/lemmas/lemma_info_response.dart'; +import 'package:fluffychat/pangea/lemmas/lemma_meaning_builder.dart'; +import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_builder.dart'; + +class TokenFeedbackButton extends StatelessWidget { + final LanguageModel textLanguage; + final ConstructIdentifier constructId; + final String text; + + final Function(LemmaInfoResponse, String) onFlagTokenInfo; + + const TokenFeedbackButton({ + super.key, + required this.textLanguage, + required this.constructId, + required this.text, + required this.onFlagTokenInfo, + }); + + @override + Widget build(BuildContext context) { + return LemmaMeaningBuilder( + langCode: textLanguage.langCode, + constructId: constructId, + builder: (context, lemmaController) { + return PhoneticTranscriptionBuilder( + textLanguage: textLanguage, + text: text, + builder: (context, transcriptController) { + final enabled = (lemmaController.lemmaInfo != null || + lemmaController.isError) && + (transcriptController.transcription != null || + transcriptController.isError); + + final lemmaInfo = + lemmaController.lemmaInfo ?? LemmaInfoResponse.error; + + final transcript = transcriptController.transcription ?? 'ERROR'; + + return IconButton( + icon: const Icon(Icons.flag_outlined), + onPressed: enabled + ? () { + onFlagTokenInfo( + lemmaInfo, + transcript, + ); + } + : null, + tooltip: enabled ? L10n.of(context).reportWordIssueTooltip : null, + ); + }, + ); + }, + ); + } +} diff --git a/lib/pangea/toolbar/word_card/word_zoom_widget.dart b/lib/pangea/toolbar/word_card/word_zoom_widget.dart index d88a4e787..3bb8b0bb9 100644 --- a/lib/pangea/toolbar/word_card/word_zoom_widget.dart +++ b/lib/pangea/toolbar/word_card/word_zoom_widget.dart @@ -3,18 +3,18 @@ import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/pangea/common/widgets/error_indicator.dart'; import 'package:fluffychat/pangea/common/widgets/word_audio_button.dart'; import 'package:fluffychat/pangea/constructs/construct_identifier.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart'; import 'package:fluffychat/pangea/languages/language_model.dart'; import 'package:fluffychat/pangea/languages/p_language_store.dart'; -import 'package:fluffychat/pangea/lemmas/lemma_meaning_builder.dart'; +import 'package:fluffychat/pangea/lemmas/lemma_info_response.dart'; import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_widget.dart'; import 'package:fluffychat/pangea/toolbar/reading_assistance/new_word_overlay.dart'; +import 'package:fluffychat/pangea/toolbar/word_card/lemma_meaning_display.dart'; import 'package:fluffychat/pangea/toolbar/word_card/lemma_reaction_picker.dart'; import 'package:fluffychat/pangea/toolbar/word_card/message_unsubscribed_card.dart'; +import 'package:fluffychat/pangea/toolbar/word_card/token_feedback_button.dart'; import 'package:fluffychat/widgets/matrix.dart'; class WordZoomWidget extends StatelessWidget { @@ -28,10 +28,7 @@ class WordZoomWidget extends StatelessWidget { final Event? event; final VoidCallback? onDismissNewWordOverlay; - final VoidCallback? onFlagTokenInfo; - - // final TokenInfoFeedbackRequestData? requestData; - // final PangeaMessageEvent? pangeaMessageEvent; + final Function(LemmaInfoResponse, String)? onFlagTokenInfo; const WordZoomWidget({ super.key, @@ -55,6 +52,8 @@ class WordZoomWidget extends StatelessWidget { final bool? subscribed = MatrixState.pangeaController.subscriptionController.isSubscribed; final overlayColor = Theme.of(context).scaffoldBackgroundColor; + final showTranscript = + MatrixState.pangeaController.userController.showTranscription; final Widget content = subscribed != null && !subscribed ? const MessageUnsubscribedCard() @@ -106,11 +105,14 @@ class WordZoomWidget extends StatelessWidget { ), ), onFlagTokenInfo != null - ? IconButton( - icon: const Icon(Icons.flag_outlined), - onPressed: onFlagTokenInfo, - tooltip: - L10n.of(context).reportWordIssueTooltip, + ? TokenFeedbackButton( + textLanguage: PLanguageStore.byLangCode( + langCode, + ) ?? + LanguageModel.unknown, + constructId: construct, + text: token.content, + onFlagTokenInfo: onFlagTokenInfo!, ) : const SizedBox( width: 40.0, @@ -118,17 +120,12 @@ class WordZoomWidget extends StatelessWidget { ), ], ), - LemmaMeaningBuilder( - langCode: langCode, - constructId: construct, - builder: (context, controller) { - return Column( - spacing: 12.0, - mainAxisSize: MainAxisSize.min, - children: [ - if (MatrixState.pangeaController.userController - .showTranscription) - PhoneticTranscriptionWidget( + Column( + spacing: 12.0, + mainAxisSize: MainAxisSize.min, + children: [ + showTranscript + ? PhoneticTranscriptionWidget( text: token.content, textLanguage: PLanguageStore.byLangCode( langCode, @@ -137,62 +134,23 @@ class WordZoomWidget extends StatelessWidget { style: const TextStyle(fontSize: 14.0), iconSize: 24.0, ) - else - WordAudioButton( + : WordAudioButton( text: token.content, uniqueID: "lemma-content-${token.content}", langCode: langCode, iconSize: 24.0, ), - LemmaReactionPicker( - emojis: controller.lemmaInfo?.emoji ?? [], - loading: controller.isLoading, - event: event, - ), - if (controller.error != null) - ErrorIndicator( - message: L10n.of(context) - .errorFetchingDefinition, - style: const TextStyle(fontSize: 14.0), - ) - else if (controller.isLoading || - controller.lemmaInfo == null) - const CircularProgressIndicator.adaptive() - else - construct.lemma.toLowerCase() == - token.content.toLowerCase() - ? Text( - controller.lemmaInfo!.meaning, - style: - const TextStyle(fontSize: 14.0), - textAlign: TextAlign.center, - ) - : RichText( - text: TextSpan( - style: DefaultTextStyle.of(context) - .style - .copyWith( - fontSize: 14.0, - ), - children: [ - TextSpan(text: construct.lemma), - const WidgetSpan( - child: SizedBox(width: 8.0), - ), - const TextSpan(text: ":"), - const WidgetSpan( - child: SizedBox(width: 8.0), - ), - TextSpan( - text: controller - .lemmaInfo!.meaning, - ), - ], - ), - ), - ], - ); - }, + LemmaReactionPicker( + construct: construct, + langCode: langCode, + event: event, + ), + LemmaMeaningDisplay( + langCode: langCode, + constructId: construct, + text: token.content, + ), + ], ), ], ),