From 602020cb404428baf54522c6cceb24ac40c47d18 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 17 Dec 2025 11:31:52 -0500 Subject: [PATCH] feat: show word card in vocab details page --- .../analytics_details_popup_content.dart | 82 ------- .../analytics_details_usage_content.dart | 32 +++ .../morph_details_view.dart | 58 +++-- .../vocab_analytics_details_view.dart | 232 +++++++----------- .../word_card/lemma_meaning_display.dart | 18 +- 5 files changed, 175 insertions(+), 247 deletions(-) delete mode 100644 lib/pangea/analytics_details_popup/analytics_details_popup_content.dart create mode 100644 lib/pangea/analytics_details_popup/analytics_details_usage_content.dart diff --git a/lib/pangea/analytics_details_popup/analytics_details_popup_content.dart b/lib/pangea/analytics_details_popup/analytics_details_popup_content.dart deleted file mode 100644 index 4cf7bca0e..000000000 --- a/lib/pangea/analytics_details_popup/analytics_details_popup_content.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:fluffychat/pangea/analytics_details_popup/lemma_usage_dots.dart'; -import 'package:fluffychat/pangea/analytics_details_popup/lemma_use_example_messages.dart'; -import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart'; -import 'package:fluffychat/pangea/analytics_misc/learning_skills_enum.dart'; -import 'package:fluffychat/pangea/constructs/construct_identifier.dart'; -import 'package:fluffychat/pangea/constructs/construct_level_enum.dart'; - -class AnalyticsDetailsViewContent extends StatelessWidget { - final Widget title; - final Widget subtitle; - final Widget headerContent; - final Widget xpIcon; - final ConstructIdentifier constructId; - - const AnalyticsDetailsViewContent({ - required this.title, - required this.subtitle, - required this.xpIcon, - required this.headerContent, - required this.constructId, - super.key, - }); - - ConstructUses get construct => constructId.constructUses; - - @override - Widget build(BuildContext context) { - final Color textColor = (Theme.of(context).brightness != Brightness.light - ? construct.lemmaCategory.color(context) - : construct.lemmaCategory.darkColor(context)); - - return SingleChildScrollView( - child: Column( - children: [ - title, - const SizedBox(height: 16.0), - subtitle, - const SizedBox(height: 16.0), - headerContent, - const Padding( - padding: EdgeInsets.symmetric(vertical: 16.0), - child: Divider(), - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - xpIcon, - const SizedBox(width: 16.0), - Text( - "${construct.points} XP", - style: Theme.of(context).textTheme.titleMedium?.copyWith( - color: textColor, - ), - ), - ], - ), - const SizedBox(height: 20), - Padding( - padding: const EdgeInsets.all(20.0), - child: Column( - children: [ - LemmaUseExampleMessages(construct: construct), - ...LearningSkillsEnum.values - .where((v) => v.isVisible) - .map((skill) { - return LemmaUsageDots( - construct: construct, - category: skill, - tooltip: skill.tooltip(context), - icon: skill.icon, - ); - }), - ], - ), - ), - ], - ), - ); - } -} diff --git a/lib/pangea/analytics_details_popup/analytics_details_usage_content.dart b/lib/pangea/analytics_details_popup/analytics_details_usage_content.dart new file mode 100644 index 000000000..3c373cb89 --- /dev/null +++ b/lib/pangea/analytics_details_popup/analytics_details_usage_content.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; + +import 'package:fluffychat/pangea/analytics_details_popup/lemma_usage_dots.dart'; +import 'package:fluffychat/pangea/analytics_details_popup/lemma_use_example_messages.dart'; +import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart'; +import 'package:fluffychat/pangea/analytics_misc/learning_skills_enum.dart'; + +class AnalyticsDetailsUsageContent extends StatelessWidget { + final ConstructUses construct; + + const AnalyticsDetailsUsageContent({ + required this.construct, + super.key, + }); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + LemmaUseExampleMessages(construct: construct), + ...LearningSkillsEnum.values.where((v) => v.isVisible).map((skill) { + return LemmaUsageDots( + construct: construct, + category: skill, + tooltip: skill.tooltip(context), + icon: skill.icon, + ); + }), + ], + ); + } +} diff --git a/lib/pangea/analytics_details_popup/morph_details_view.dart b/lib/pangea/analytics_details_popup/morph_details_view.dart index 15cacb55c..785cb5eb2 100644 --- a/lib/pangea/analytics_details_popup/morph_details_view.dart +++ b/lib/pangea/analytics_details_popup/morph_details_view.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:fluffychat/pangea/analytics_details_popup/analytics_details_popup_content.dart'; +import 'package:fluffychat/pangea/analytics_details_popup/analytics_details_usage_content.dart'; import 'package:fluffychat/pangea/analytics_details_popup/morph_meaning_widget.dart'; -import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart'; import 'package:fluffychat/pangea/constructs/construct_identifier.dart'; import 'package:fluffychat/pangea/constructs/construct_level_enum.dart'; import 'package:fluffychat/pangea/lemmas/construct_xp_widget.dart'; @@ -18,31 +17,54 @@ class MorphDetailsView extends StatelessWidget { super.key, }); - ConstructUses get _construct => constructId.constructUses; MorphFeaturesEnum get _morphFeature => MorphFeaturesEnumExtension.fromString(constructId.category); String get _morphTag => constructId.lemma; @override Widget build(BuildContext context) { + final construct = constructId.constructUses; final Color textColor = Theme.of(context).brightness != Brightness.light - ? _construct.lemmaCategory.color(context) - : _construct.lemmaCategory.darkColor(context); + ? construct.lemmaCategory.color(context) + : construct.lemmaCategory.darkColor(context); - return AnalyticsDetailsViewContent( - subtitle: MorphFeatureDisplay(morphFeature: _morphFeature), - title: MorphTagDisplay( - morphFeature: _morphFeature, - morphTag: _morphTag, - textColor: textColor, + return SingleChildScrollView( + child: Column( + spacing: 16.0, + children: [ + MorphTagDisplay( + morphFeature: _morphFeature, + morphTag: _morphTag, + textColor: textColor, + ), + MorphFeatureDisplay(morphFeature: _morphFeature), + MorphMeaningWidget( + feature: _morphFeature, + tag: _morphTag, + style: Theme.of(context).textTheme.bodyLarge, + ), + const Divider(), + Row( + spacing: 16.0, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ConstructXpWidget(id: constructId), + Text( + "${construct.points} XP", + style: Theme.of(context).textTheme.titleMedium?.copyWith( + color: textColor, + ), + ), + ], + ), + Padding( + padding: const EdgeInsets.all(20.0), + child: AnalyticsDetailsUsageContent( + construct: construct, + ), + ), + ], ), - headerContent: MorphMeaningWidget( - feature: _morphFeature, - tag: _morphTag, - style: Theme.of(context).textTheme.bodyLarge, - ), - xpIcon: ConstructXpWidget(id: constructId), - constructId: constructId, ); } } diff --git a/lib/pangea/analytics_details_popup/vocab_analytics_details_view.dart b/lib/pangea/analytics_details_popup/vocab_analytics_details_view.dart index 6f43ba802..743a9c1c1 100644 --- a/lib/pangea/analytics_details_popup/vocab_analytics_details_view.dart +++ b/lib/pangea/analytics_details_popup/vocab_analytics_details_view.dart @@ -3,18 +3,12 @@ import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/pangea/analytics_details_popup/analytics_details_popup_content.dart'; -import 'package:fluffychat/pangea/analytics_details_popup/vocab_details_emoji_selector.dart'; +import 'package:fluffychat/pangea/analytics_details_popup/analytics_details_usage_content.dart'; import 'package:fluffychat/pangea/analytics_details_popup/word_text_with_audio_button.dart'; -import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart'; -import 'package:fluffychat/pangea/common/widgets/shrinkable_text.dart'; import 'package:fluffychat/pangea/constructs/construct_identifier.dart'; import 'package:fluffychat/pangea/constructs/construct_level_enum.dart'; -import 'package:fluffychat/pangea/lemmas/lemma_meaning_widget.dart'; -import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart'; -import 'package:fluffychat/pangea/morphs/morph_features_enum.dart'; -import 'package:fluffychat/pangea/morphs/morph_icon.dart'; -import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_widget.dart'; +import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart'; +import 'package:fluffychat/pangea/toolbar/word_card/word_zoom_widget.dart'; import 'package:fluffychat/widgets/matrix.dart'; /// Displays information about selected lemma, and its usage @@ -26,15 +20,9 @@ class VocabDetailsView extends StatelessWidget { required this.constructId, }); - ConstructUses get _construct => constructId.constructUses; - - /// Get the language code for the current lemma - String? get _userL2 => - MatrixState.pangeaController.userController.userL2?.langCode; - List get forms => MatrixState.pangeaController.getAnalytics.constructListModel - .getConstructUsesByLemma(_construct.lemma) + .getConstructUsesByLemma(constructId.lemma) .map((e) => e.uses) .expand((element) => element) .map((e) => e.form?.toLowerCase()) @@ -46,140 +34,110 @@ class VocabDetailsView extends StatelessWidget { @override Widget build(BuildContext context) { + final construct = constructId.constructUses; final Color textColor = (Theme.of(context).brightness != Brightness.light - ? _construct.lemmaCategory.color(context) - : _construct.lemmaCategory.darkColor(context)); + ? construct.lemmaCategory.color(context) + : construct.lemmaCategory.darkColor(context)); - return AnalyticsDetailsViewContent( - title: Column( + return SingleChildScrollView( + child: Column( + spacing: 16.0, children: [ - LayoutBuilder( - builder: (context, constraints) { - return ShrinkableText( - text: _construct.lemma, - maxWidth: constraints.maxWidth - 40.0, - style: Theme.of(context).textTheme.headlineLarge?.copyWith( - color: textColor, - ), - ); - }, + WordZoomWidget( + token: PangeaTokenText.fromString(constructId.lemma), + langCode: MatrixState.pangeaController.userController.userL2Code!, + construct: constructId, ), - if (MatrixState.pangeaController.userController.showTranscription) - Padding( - padding: const EdgeInsets.only(top: 4.0), - child: PhoneticTranscriptionWidget( - text: _construct.lemma, - textLanguage: - MatrixState.pangeaController.userController.userL2!, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: textColor.withAlpha((0.7 * 255).toInt()), - fontSize: 18, - ), - iconSize: _iconSize * 0.8, - ), - ), - ], - ), - subtitle: Column( - children: [ - Row( - mainAxisSize: MainAxisSize.min, - spacing: 8.0, + Column( children: [ - Text( - getGrammarCopy( - category: "POS", - lemma: _construct.category, - context: context, - ) ?? - _construct.lemma, - style: Theme.of(context).textTheme.bodyLarge?.copyWith( - color: textColor, + Padding( + padding: const EdgeInsets.all(20.0), + child: Row( + spacing: 16.0, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + construct.lemmaCategory.icon(_iconSize + 6.0), + Text( + "${construct.points} XP", + style: Theme.of(context).textTheme.titleMedium?.copyWith( + color: textColor, + ), ), + ], + ), ), - SizedBox( - width: _iconSize, - height: _iconSize, - child: MorphIcon( - morphFeature: MorphFeaturesEnum.Pos, - morphTag: _construct.category, + Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + children: [ + Align( + alignment: Alignment.centerLeft, + child: _VocabForms( + lemma: constructId.lemma, + forms: forms, + textColor: textColor, + ), + ), + AnalyticsDetailsUsageContent( + construct: construct, + ), + ], ), ), ], ), - const SizedBox(height: 16.0), - Text( - L10n.of(context).vocabEmoji, - style: Theme.of(context).textTheme.labelMedium?.copyWith( - fontStyle: FontStyle.italic, - color: textColor, - ), - ), - Padding( - padding: const EdgeInsets.all(16.0), - child: VocabDetailsEmojiSelector(constructId), - ), ], ), - headerContent: Padding( - padding: const EdgeInsets.fromLTRB(20, 10, 20, 20), - child: Column( - spacing: 8.0, - children: [ - Align( - alignment: Alignment.topLeft, - child: _userL2 == null - ? Text(L10n.of(context).meaningNotFound) - : LemmaMeaningWidget( - constructId: constructId, - style: Theme.of(context).textTheme.bodyLarge, - leading: TextSpan( - text: L10n.of(context).meaningSectionHeader, - style: Theme.of(context).textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.bold, - ), - ), - ), - ), - Align( - alignment: Alignment.topLeft, - child: Wrap( - alignment: WrapAlignment.start, - runAlignment: WrapAlignment.start, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Text( - L10n.of(context).formSectionHeader, - style: Theme.of(context).textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(width: 6.0), - ...forms.mapIndexed( - (i, form) => Row( - mainAxisSize: MainAxisSize.min, - children: [ - WordTextWithAudioButton( - text: form, - style: - Theme.of(context).textTheme.bodyLarge?.copyWith( - color: textColor, - ), - uniqueID: "$form-${_construct.lemma}-$i", - langCode: _userL2!, - ), - if (i != forms.length - 1) const Text(", "), - ], - ), - ), - ], - ), - ), - ], - ), - ), - xpIcon: _construct.lemmaCategory.icon(_iconSize + 6.0), - constructId: constructId, + ); + } +} + +class _VocabForms extends StatelessWidget { + final String lemma; + final List forms; + final Color textColor; + + const _VocabForms({ + required this.lemma, + required this.forms, + required this.textColor, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Wrap( + alignment: WrapAlignment.start, + runAlignment: WrapAlignment.start, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + Text( + L10n.of(context).formSectionHeader, + style: Theme.of(context).textTheme.bodyLarge?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(width: 6.0), + ...forms.mapIndexed( + (i, form) => Row( + mainAxisSize: MainAxisSize.min, + children: [ + WordTextWithAudioButton( + text: form, + style: Theme.of(context).textTheme.bodyLarge?.copyWith( + color: textColor, + ), + uniqueID: "$form-$lemma-$i", + langCode: + MatrixState.pangeaController.userController.userL2Code!, + ), + if (i != forms.length - 1) const Text(", "), + ], + ), + ), + ], + ), ); } } diff --git a/lib/pangea/toolbar/word_card/lemma_meaning_display.dart b/lib/pangea/toolbar/word_card/lemma_meaning_display.dart index a7c1552b8..84f9200d3 100644 --- a/lib/pangea/toolbar/word_card/lemma_meaning_display.dart +++ b/lib/pangea/toolbar/word_card/lemma_meaning_display.dart @@ -5,6 +5,7 @@ import 'package:fluffychat/pangea/analytics_misc/text_loading_shimmer.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'; +import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart'; class LemmaMeaningDisplay extends StatelessWidget { final String langCode; @@ -38,15 +39,12 @@ class LemmaMeaningDisplay extends StatelessWidget { ); } - if (constructId.lemma.toLowerCase() == text.toLowerCase()) { - return Text( - controller.lemmaInfo!.meaning, - style: const TextStyle( - fontSize: 14.0, - ), - textAlign: TextAlign.center, - ); - } + final pos = getGrammarCopy( + category: "POS", + lemma: constructId.category, + context: context, + ) ?? + L10n.of(context).other; return RichText( text: TextSpan( @@ -55,7 +53,7 @@ class LemmaMeaningDisplay extends StatelessWidget { ), children: [ TextSpan( - text: constructId.lemma, + text: "${constructId.lemma} ($pos)", ), const WidgetSpan( child: SizedBox(width: 8.0),