Morph-repo-2 (#1681)

* feat(morphs): repo for getting lang-specific list of morphs

* integrated repo into use of morph features and tags

* generated

* merged with previous push

* generated

* generated

* chore: fix .env file path

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: ggurdin <46800240+ggurdin@users.noreply.github.com>
Co-authored-by: ggurdin <ggurdin@gmail.com>
This commit is contained in:
wcjord 2025-02-03 12:21:29 -05:00 committed by GitHub
parent 9eb83de21f
commit d773347d6e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
91 changed files with 2025 additions and 1443 deletions

View file

@ -4782,7 +4782,13 @@
"numberOfLearners": "Number of learners",
"mustBeInteger": "Must be an integer e.g. 1, 2, 3, ...",
"noBookmarkedActivities": "No bookmarked activities",
"noLemmasFound": "No lemmas found",
"noLemmasFound": "There are no vocabulary with more than {xp} XP. Keep practicing!",
"@noLemmasFound": {
"type": "text",
"placeholders": {
"xp": {}
}
},
"constructUsePvmDesc": "Produced in voice message",
"lockedMorphFeature": "Waiting to be unlocked",
"leaveSpaceDescription": "The space will be moved to the archive. Other users will be able to see that you have left the chat."

View file

@ -29,9 +29,9 @@ import 'package:fluffychat/pages/chat/chat_view.dart';
import 'package:fluffychat/pages/chat/event_info_dialog.dart';
import 'package:fluffychat/pages/chat/recording_dialog.dart';
import 'package:fluffychat/pages/chat_details/chat_details.dart';
import 'package:fluffychat/pangea/analytics/controllers/put_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics/models/constructs_model.dart';
import 'package:fluffychat/pangea/analytics/widgets/level_up/level_up.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/analytics_misc/level_up.dart';
import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.dart';
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
import 'package:fluffychat/pangea/choreographer/models/choreo_record.dart';
import 'package:fluffychat/pangea/choreographer/widgets/igc/pangea_text_controller.dart';

View file

@ -17,8 +17,8 @@ import 'package:fluffychat/pages/chat/chat_event_list.dart';
import 'package:fluffychat/pages/chat/pinned_events.dart';
import 'package:fluffychat/pages/chat/reply_display.dart';
import 'package:fluffychat/pangea/activity_planner/activity_plan_page_launch_icon_button.dart';
import 'package:fluffychat/pangea/analytics/controllers/put_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics/widgets/gain_points.dart';
import 'package:fluffychat/pangea/analytics_misc/gain_points_animation.dart';
import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.dart';
import 'package:fluffychat/pangea/chat/widgets/chat_floating_action_button.dart';
import 'package:fluffychat/pangea/chat/widgets/chat_view_background.dart';
import 'package:fluffychat/pangea/chat/widgets/input_bar_wrapper.dart';

View file

@ -5,7 +5,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/chat_list/chat_list.dart';
import 'package:fluffychat/pages/chat_list/client_chooser_button.dart';
import 'package:fluffychat/pangea/analytics/widgets/analytics_summary/learning_progress_indicators.dart';
import 'package:fluffychat/pangea/analytics_summary/learning_progress_indicators.dart';
class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
final ChatListController controller;

View file

@ -1,230 +0,0 @@
import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
const Map<String, List<String>> morphCategoriesAndLabels = {
"pos": [
"ADJ",
"ADP",
"ADV",
"AFFIX",
"AUX",
"CCONJ",
"DET",
"INTJ",
"NOUN",
"NUM",
"PART",
"PRON",
"PROPN",
"PUNCT",
"SCONJ",
"SPACE",
"SYM",
"VERB",
"X",
],
"advtype": ["Adverbial", "Tim"],
"aspect": [
"Imp",
"Perf",
"Prog",
"Hab",
],
"case": [
"Nom",
"Acc",
"Dat",
"Gen",
"Voc",
"Abl",
"Loc",
"All",
"Ins",
"Ess",
"Tra",
"Com",
"Par",
"Adv",
"Ref",
"Rel",
"Equ",
"Dis",
"Abs",
"Erg",
"Cau",
"Ben",
"Sub",
"Sup",
"Tem",
"Obl",
"Acc,Dat",
"Acc,Nom",
"Pre",
],
"conjtype": ["Coord", "Sub", "Cmp"],
"definite": ["Def", "Ind", "Cons"],
"degree": [
"Pos",
"Cmp",
"Sup",
"Abs",
],
"evident": ["Fh", "Nfh"],
"foreign": ["Yes"],
"gender": ["Masc", "Fem", "Neut", "Com"],
"mood": [
"Ind",
"Imp",
"Sub",
"Cnd",
"Opt",
"Jus",
"Adm",
"Des",
"Nec",
"Pot",
"Prp",
"Qot",
"Int",
],
"nountype": ["Prop", "Comm", "Not_proper"],
"numform": [
"Digit",
"Word",
"Roman",
"Letter",
],
"numtype": [
"Card",
"Ord",
"Mult",
"Frac",
"Sets",
"Range",
"Dist",
],
"number": [
"Sing",
"Plur",
"Dual",
"Tri",
"Pauc",
"Grpa",
"Grpl",
"Inv",
],
"number[psor]": ["Sing", "Plur", "Dual"],
"person": [
"0",
"1",
"2",
"3",
"4",
],
"polarity": ["Pos", "Neg"],
"polite": ["Infm", "Form", "Elev", "Humb"],
"poss": ["Yes"],
"prepcase": ["Npr"],
"prontype": [
"Prs",
"Int",
"Rel",
"Dem",
"Tot",
"Neg",
"Art",
"Emp",
"Exc",
"Ind",
"Rcp",
"Int,Rel",
],
"punctside": ["Ini", "Fin"],
"puncttype": [
"Brck",
"Dash",
"Excl",
"Peri",
"Qest",
"Quot",
"Semi",
"Colo",
"Comm",
],
"reflex": ["Yes"],
"tense": ["Pres", "Past", "Fut", "Imp", "Pqp", "Aor", "Eps", "Prosp"],
"verbform": [
"Fin",
"Inf",
"Sup",
"Part",
"Conv",
"Vnoun",
"Ger",
"Adn",
"Lng",
],
"verbtype": ["Mod", "Caus"],
"voice": ["Act", "Mid", "Pass", "Antip", "Cau", "Dir", "Inv", "Rcp", "Caus"],
"x": ["X"],
};
// TODO Use the icons that Khue is creating
IconData getIconForMorphFeature(String feature) {
// Define a function to get the icon based on the universal dependency morphological feature (key)
switch (feature.toLowerCase()) {
case 'number':
// google material 123 icon
return Icons.format_list_numbered;
case 'gender':
return Icons.wc;
case 'tense':
return Icons.access_time;
case 'mood':
return Icons.mood;
case 'person':
return Icons.person;
case 'case':
return Icons.format_list_bulleted;
case 'degree':
return Icons.trending_up;
case 'verbform':
return Icons.text_format;
case 'voice':
return Icons.record_voice_over;
case 'aspect':
return Icons.aspect_ratio;
case 'prontype':
return Icons.text_fields;
case 'numtype':
return Icons.format_list_numbered;
case 'poss':
return Icons.account_balance;
case 'reflex':
return Icons.refresh;
case 'foreign':
return Icons.language;
case 'abbr':
return Icons.text_format;
case 'nountype':
return Symbols.abc;
case 'pos':
return Symbols.toys_and_games;
case 'polarity':
return Icons.swap_vert;
case 'definite':
return Icons.check_circle_outline;
case 'prepcase':
return Icons.location_on_outlined;
case 'conjtype':
return Icons.compare_arrows;
default:
return Icons.help_outline;
}
}
List<String> getLabelsForMorphCategory(String feature) =>
morphCategoriesAndLabels[feature.toLowerCase()] ?? [];
List<String> getMorphCategories() => morphCategoriesAndLabels.keys.toList();

View file

@ -1,351 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/analytics/constants/morph_categories_and_labels.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics/enums/progress_indicators_enum.dart';
import 'package:fluffychat/pangea/analytics/models/construct_list_model.dart';
import 'package:fluffychat/pangea/analytics/models/construct_use_model.dart';
import 'package:fluffychat/pangea/analytics/utils/get_grammar_copy.dart';
import 'package:fluffychat/pangea/analytics/utils/get_svg_link.dart';
import 'package:fluffychat/pangea/common/widgets/customized_svg.dart';
import 'package:fluffychat/pangea/common/widgets/full_width_dialog.dart';
import 'package:fluffychat/utils/color_value.dart';
import 'package:fluffychat/widgets/matrix.dart';
class MorphAnalyticsPopup extends StatelessWidget {
const MorphAnalyticsPopup({
super.key,
});
ConstructListModel get _constructsModel =>
MatrixState.pangeaController.getAnalytics.constructListModel;
Map<String, List<ConstructUses>> get _categoriesToUses =>
_constructsModel.categoriesToUses(type: ConstructTypeEnum.morph);
List<MapEntry<String, List<ConstructUses>>> get _sortedEntries {
final entries = _categoriesToUses.entries.toList();
// Sort the list with custom logic
entries.sort((a, b) {
// Check if one of the keys is 'Other'
if (a.key.toLowerCase() == 'other') return 1;
if (b.key.toLowerCase() == 'other') return -1;
// Sort by the length of the list in descending order
final aTotalPoints = a.value.fold<int>(
0,
(previousValue, element) => previousValue + element.points,
);
final bTotalPoints = b.value.fold<int>(
0,
(previousValue, element) => previousValue + element.points,
);
return bTotalPoints.compareTo(aTotalPoints);
});
return entries;
}
@override
Widget build(BuildContext context) {
final availableFeatures = getMorphCategories();
final List<MapEntry<String, List<ConstructUses>>> morphUses =
List.from(_sortedEntries);
return FullWidthDialog(
dialogContent: Scaffold(
appBar: AppBar(
title: Text(ConstructTypeEnum.morph.indicator.tooltip(context)),
leading: IconButton(
icon: const Icon(Icons.close),
onPressed: Navigator.of(context).pop,
),
),
body: Padding(
padding: const EdgeInsets.symmetric(vertical: 20),
child: _constructsModel
.constructList(type: ConstructTypeEnum.morph)
.isEmpty
? Center(child: Text(L10n.of(context).noDataFound))
: ListView.builder(
itemCount: availableFeatures.length,
itemBuilder: (context, index) {
final category =
index < morphUses.length ? morphUses[index] : null;
final ids = category?.value
.map(
(use) => MorphIdentifier(
morphFeature: category.key,
morphTag: use.lemma,
),
)
.toList() ??
[];
debugPrint(
"category: ${category?.key}, points: ${category?.value.fold<int>(
0,
(previousValue, element) =>
previousValue + element.points,
)}");
return MorphFeatureBox(morphIdentifiers: ids);
},
),
),
),
maxWidth: 600,
maxHeight: 800,
);
}
}
class MorphFeatureBox extends StatelessWidget {
final List<MorphIdentifier> morphIdentifiers;
const MorphFeatureBox({
super.key,
required this.morphIdentifiers,
});
String get _morphFeature => morphIdentifiers.first.morphFeature;
String _categoryCopy(
String category,
BuildContext context,
) {
if (category.toLowerCase() == "other") {
return L10n.of(context).other;
}
return ConstructTypeEnum.morph.getDisplayCopy(
category,
context,
) ??
category;
}
Set<String> get _lockedTags {
final availableLabels = getLabelsForMorphCategory(_morphFeature)
.map((tag) => tag.toLowerCase())
.toSet();
final usedLabels =
morphIdentifiers.map((morph) => morph.morphTag.toLowerCase()).toSet();
return availableLabels.difference(usedLabels);
}
@override
Widget build(BuildContext context) {
if (morphIdentifiers.isEmpty) {
return Opacity(
opacity: 0.5,
child: Container(
padding: const EdgeInsets.all(24.0),
margin: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.0),
color: Theme.of(context).disabledColor,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.lock, size: 24.0),
const SizedBox(width: 24.0),
Text(
L10n.of(context).lockedMorphFeature,
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
],
),
),
);
}
final allMorphIdentifiers = List.from(morphIdentifiers);
allMorphIdentifiers.addAll(
_lockedTags.map(
(tag) => MorphIdentifier(
morphFeature: _morphFeature,
morphTag: tag,
isLocked: true,
),
),
);
final theme = Theme.of(context);
return Container(
padding: const EdgeInsets.all(16.0),
margin: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.0),
border: Border.all(color: AppConfig.gold, width: 2),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 16.0,
children: [
SizedBox(
height: 30.0,
width: 30.0,
child: CustomizedSvg(
svgUrl: getMorphSvgLink(
morphFeature: _morphFeature,
context: context,
),
colorReplacements: theme.brightness == Brightness.dark
? {
"white": theme.cardColor.hexValue.toString(),
"black": "white",
}
: {},
errorIcon: Icon(getIconForMorphFeature(_morphFeature)),
),
),
Text(
_categoryCopy(_morphFeature, context),
style: theme.textTheme.bodyLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 16.0),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Flexible(
child: Wrap(
alignment: WrapAlignment.center,
spacing: 16.0,
runSpacing: 16.0,
children: [
for (final morph in allMorphIdentifiers)
MorphTagChip(morphIdentifier: morph),
],
),
),
],
),
],
),
);
}
}
class MorphTagChip extends StatelessWidget {
final MorphIdentifier morphIdentifier;
const MorphTagChip({
super.key,
required this.morphIdentifier,
});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
if (morphIdentifier.isLocked) {
return Container(
padding: const EdgeInsets.symmetric(
vertical: 4.0,
horizontal: 24.0,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(32.0),
color: theme.disabledColor,
),
child: SizedBox(
width: 28.0,
height: 28.0,
child: Icon(
Icons.lock,
color: theme.brightness == Brightness.dark
? Colors.black
: Colors.white,
),
),
);
}
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(32.0),
gradient: LinearGradient(
begin: Alignment.topLeft,
colors: <Color>[
Color.alphaBlend(
theme.brightness == Brightness.dark
? Colors.black.withAlpha(220)
: Colors.white.withAlpha(220),
AppConfig.gold,
),
AppConfig.gold,
],
),
),
padding: const EdgeInsets.symmetric(
vertical: 4.0,
horizontal: 8.0,
),
child: Row(
mainAxisSize: MainAxisSize.min,
spacing: 8.0,
children: [
SizedBox(
width: 28.0,
height: 28.0,
child: CustomizedSvg(
svgUrl: getMorphSvgLink(
morphFeature: morphIdentifier.morphFeature,
morphTag: morphIdentifier.morphTag,
context: context,
),
colorReplacements: theme.brightness == Brightness.dark
? {
"white": theme.cardColor.hexValue.toString(),
"black": "white",
}
: {},
errorIcon: Icon(
getIconForMorphFeature(morphIdentifier.morphFeature),
),
),
),
Text(
getGrammarCopy(
category: morphIdentifier.morphFeature,
lemma: morphIdentifier.morphTag,
context: context,
) ??
morphIdentifier.morphTag,
style: TextStyle(
fontWeight: FontWeight.bold,
color: theme.brightness == Brightness.dark
? Colors.white
: Colors.black,
),
),
],
),
);
}
}
class MorphIdentifier {
final String morphFeature;
final String morphTag;
final bool isLocked;
const MorphIdentifier({
required this.morphFeature,
required this.morphTag,
this.isLocked = false,
});
}

View file

@ -1,62 +0,0 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics/enums/progress_indicators_enum.dart';
import 'package:fluffychat/pangea/analytics/models/construct_use_model.dart';
import 'package:fluffychat/pangea/analytics/utils/get_grammar_copy.dart';
class ConstructUsesXPTile extends StatelessWidget {
final ConstructUses constructUses;
const ConstructUsesXPTile(
this.constructUses, {
super.key,
});
@override
Widget build(BuildContext context) {
final ProgressIndicatorEnum indicator =
constructUses.constructType == ConstructTypeEnum.morph
? ProgressIndicatorEnum.morphsUsed
: ProgressIndicatorEnum.wordsUsed;
return Tooltip(
message:
"${constructUses.points} / ${constructUses.constructType.maxXPPerLemma}",
child: ListTile(
onTap: () {},
title: Text(
constructUses.constructType == ConstructTypeEnum.morph
? getGrammarCopy(
category: constructUses.category,
lemma: constructUses.lemma,
context: context,
) ??
constructUses.lemma
: constructUses.lemma,
),
subtitle: Row(
children: [
Expanded(
child: LinearProgressIndicator(
value: constructUses.points /
constructUses.constructType.maxXPPerLemma,
minHeight: 20,
borderRadius: const BorderRadius.all(
Radius.circular(AppConfig.borderRadius),
),
color: indicator.color(context),
),
),
const SizedBox(width: 12),
Text("${constructUses.points}xp"),
],
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 20,
),
),
);
}
}

View file

@ -1,278 +0,0 @@
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/analytics/constants/analytics_constants.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics/enums/lemma_category_enum.dart';
import 'package:fluffychat/pangea/analytics/enums/progress_indicators_enum.dart';
import 'package:fluffychat/pangea/analytics/models/construct_list_model.dart';
import 'package:fluffychat/pangea/analytics/models/construct_use_model.dart';
import 'package:fluffychat/pangea/analytics/utils/get_grammar_copy.dart';
import 'package:fluffychat/pangea/analytics/widgets/analytics_summary/vocab_analytics_popup/vocab_definition_popup.dart';
import 'package:fluffychat/pangea/common/widgets/customized_svg.dart';
import 'package:fluffychat/pangea/common/widgets/full_width_dialog.dart';
import 'package:fluffychat/widgets/matrix.dart';
/// Displays vocab analytics, sorted into categories
/// (flowers, greens, and seeds) by points
class VocabAnalyticsPopup extends StatefulWidget {
const VocabAnalyticsPopup({
super.key,
});
@override
VocabAnalyticsPopupState createState() => VocabAnalyticsPopupState();
}
class VocabAnalyticsPopupState extends State<VocabAnalyticsPopup> {
ConstructListModel get _constructsModel =>
MatrixState.pangeaController.getAnalytics.constructListModel;
/// Sort entries alphabetically, to better detect duplicates
List<ConstructUses> get _sortedEntries {
final entries =
_constructsModel.constructList(type: ConstructTypeEnum.vocab);
entries
.sort((a, b) => a.lemma.toLowerCase().compareTo(b.lemma.toLowerCase()));
return entries;
}
ConstructUses? _selectedConstruct;
void _setSelectedConstruct(ConstructUses? construct) {
if (mounted) {
setState(() {
_selectedConstruct = construct;
});
}
}
@override
Widget build(BuildContext context) {
return FullWidthDialog(
dialogContent: _selectedConstruct == null
? LemmaListDialogContent(
lemmas: _sortedEntries,
onTap: _setSelectedConstruct,
)
: VocabDefinitionPopup(
construct: _selectedConstruct!,
onClose: () => _setSelectedConstruct(null),
),
maxWidth: 600,
maxHeight: 800,
);
}
}
class VocabChip {
final ConstructUses construct;
final String? displayText;
VocabChip({
required this.construct,
this.displayText,
});
}
class LemmaListSection extends StatelessWidget {
final LemmaCategoryEnum type;
final List<VocabChip> lemmas;
final Function(ConstructUses) onTap;
const LemmaListSection({
super.key,
required this.type,
required this.lemmas,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(20),
margin: const EdgeInsets.symmetric(vertical: 6.0, horizontal: 16.0),
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(
Radius.circular(AppConfig.borderRadius),
),
color: type.color,
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CustomizedSvg(
svgUrl: type.svgURL,
colorReplacements: const {},
errorIcon: Text(type.emoji),
),
Text(
" ${type.xpString} XP",
style: TextStyle(
fontSize: Theme.of(context).textTheme.bodyLarge?.fontSize,
color: Theme.of(context).colorScheme.onPrimaryFixed,
),
),
],
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: lemmas.isEmpty
? Text(
L10n.of(context).noLemmasFound,
style: TextStyle(
color: Theme.of(context).colorScheme.onPrimaryFixed,
),
)
: Wrap(
spacing: 0,
runSpacing: 0,
children: lemmas.mapIndexed((index, lemma) {
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () => onTap(lemma.construct),
child: Text(
"${lemma.displayText ?? lemma.construct.lemma}${index < lemmas.length - 1 ? ', ' : ''}",
style: TextStyle(
shadows: [
Shadow(
color: Theme.of(context)
.colorScheme
.onPrimaryFixed,
offset: const Offset(0, -2.5),
),
],
color: Colors.transparent,
decoration: TextDecoration.underline,
decorationStyle: TextDecorationStyle.dotted,
decorationColor:
Theme.of(context).colorScheme.onPrimaryFixed,
decorationThickness: 1,
fontSize: Theme.of(context)
.textTheme
.bodyLarge
?.fontSize,
),
),
),
);
}).toList(),
),
),
],
),
);
}
}
class LemmaListDialogContent extends StatelessWidget {
final List<ConstructUses> lemmas;
final Function(ConstructUses) onTap;
const LemmaListDialogContent({
super.key,
required this.lemmas,
required this.onTap,
});
@override
Widget build(BuildContext context) {
if (lemmas.isEmpty) {
return Scaffold(
appBar: AppBar(
title: Text(ProgressIndicatorEnum.wordsUsed.tooltip(context)),
leading: IconButton(
icon: const Icon(Icons.close),
onPressed: Navigator.of(context).pop,
),
// TODO: add search and training buttons?
),
body: Center(child: Text(L10n.of(context).noDataFound)),
);
}
// Get lists of lemmas by category
final List<VocabChip> flowerLemmas = [];
final List<VocabChip> greenLemmas = [];
final List<VocabChip> seedLemmas = [];
for (int i = 0; i < lemmas.length; i++) {
final construct = lemmas[i];
if (construct.lemma.isEmpty) {
continue;
}
final int points = construct.points;
String? displayText;
// Check if previous or next entry has same lemma as this entry
if ((i > 0 && lemmas[i - 1].lemma.equals(construct.lemma)) ||
((i < lemmas.length - 1 &&
lemmas[i + 1].lemma.equals(construct.lemma)))) {
final String pos = getGrammarCopy(
category: "pos",
lemma: construct.category,
context: context,
) ??
construct.category;
displayText = "${lemmas[i].lemma} (${pos.toLowerCase()})";
}
final lemma = VocabChip(
construct: construct,
displayText: displayText,
);
if (points < AnalyticsConstants.xpForGreens) {
seedLemmas.add(lemma);
} else if (points >= AnalyticsConstants.xpForFlower) {
flowerLemmas.add(lemma);
} else {
greenLemmas.add(lemma);
}
}
// Pass sorted lemmas to background tile widgets
final Widget flowers = LemmaListSection(
type: LemmaCategoryEnum.flowers,
lemmas: flowerLemmas,
onTap: onTap,
);
final Widget greens = LemmaListSection(
type: LemmaCategoryEnum.greens,
lemmas: greenLemmas,
onTap: onTap,
);
final Widget seeds = LemmaListSection(
type: LemmaCategoryEnum.seeds,
lemmas: seedLemmas,
onTap: onTap,
);
return Scaffold(
appBar: AppBar(
title: Text(ProgressIndicatorEnum.wordsUsed.tooltip(context)),
leading: IconButton(
icon: const Icon(Icons.close),
onPressed: Navigator.of(context).pop,
),
// TODO: add search and training buttons?
),
body: Padding(
padding: const EdgeInsets.symmetric(vertical: 20),
child: ListView(
children: [flowers, greens, seeds],
),
),
);
}
}

View file

@ -0,0 +1,87 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/analytics_details_popup/morph_analytics_view.dart';
import 'package:fluffychat/pangea/analytics_details_popup/vocab_analytics_view.dart';
import 'package:fluffychat/pangea/analytics_details_popup/vocab_details_view.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_identifier.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_summary/progress_indicators_enum.dart';
import 'package:fluffychat/pangea/common/widgets/full_width_dialog.dart';
class AnalyticsPopupWrapper extends StatefulWidget {
AnalyticsPopupWrapper({super.key, this.constructZoom, required this.view});
ConstructTypeEnum view;
ConstructIdentifier? constructZoom;
@override
_AnalyticsPopupWrapperState createState() => _AnalyticsPopupWrapperState();
}
class _AnalyticsPopupWrapperState extends State<AnalyticsPopupWrapper> {
ConstructIdentifier? localConstructZoom;
ConstructTypeEnum localView = ConstructTypeEnum.vocab;
@override
void initState() {
super.initState();
localView = widget.view;
setConstructZoom(widget.constructZoom);
}
void setConstructZoom(ConstructIdentifier? id) {
if (id != null && id.type != localView) {
localView = id.type;
}
localConstructZoom = id;
setState(() => {});
}
@override
Widget build(BuildContext context) {
return FullWidthDialog(
dialogContent: Scaffold(
appBar: AppBar(
title: Text(
localView == ConstructTypeEnum.morph
? ConstructTypeEnum.morph.indicator.tooltip(context)
: ConstructTypeEnum.vocab.indicator.tooltip(context),
),
leading: IconButton(
icon: localConstructZoom == null
? const Icon(Icons.close)
: const Icon(Icons.arrow_back),
onPressed: localConstructZoom == null
? () => Navigator.of(context).pop()
: () => setConstructZoom(null),
),
actions: ConstructTypeEnum.values
.map(
(c) => IconButton(
icon: Icon(c.indicator.icon),
onPressed: () => setState(() {
localView = c;
localConstructZoom = null;
}),
isSelected: localView == c,
color: localView == c
? Theme.of(context).brightness == Brightness.dark
? AppConfig.primaryColorLight
: AppConfig.primaryColor
: null,
),
)
.toList(),
),
body: localView == ConstructTypeEnum.morph
? const MorphAnalyticsView()
: localConstructZoom == null
? VocabAnalyticsView(onConstructZoom: setConstructZoom)
: VocabDetailsView(constructId: localConstructZoom!),
),
maxWidth: 600,
maxHeight: 800,
);
}
}

View file

@ -0,0 +1,88 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_level_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/analytics_misc/learning_skills_enum.dart';
class LemmaUsageDots extends StatelessWidget {
final ConstructUses construct;
final LearningSkillsEnum category;
final String tooltip;
final IconData icon;
const LemmaUsageDots({
required this.construct,
required this.category,
required this.tooltip,
required this.icon,
super.key,
});
/// Find lemma uses for the given exercise type, to create dot list
List<bool> sortedUses(LearningSkillsEnum category) {
final List<bool> useList = [];
for (final OneConstructUse use in construct.uses) {
if (use.useType.pointValue == 0) {
continue;
}
// If the use type matches the given category, save to list
// Usage with positive XP is saved as true, else false
if (category == use.useType.skillsEnumType) {
useList.add(use.useType.pointValue > 0);
}
}
return useList;
}
@override
Widget build(BuildContext context) {
final List<Widget> dots = [];
for (final bool use in sortedUses(category)) {
dots.add(
Container(
width: 15.0,
height: 15.0,
decoration: BoxDecoration(
color: use ? AppConfig.success : Colors.red,
shape: BoxShape.circle,
),
),
);
}
final Color textColor = Theme.of(context).brightness != Brightness.light
? construct.lemmaCategory.color
: construct.lemmaCategory.darkColor;
return Padding(
padding: const EdgeInsets.symmetric(vertical: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Tooltip(
triggerMode: TooltipTriggerMode.tap,
message: tooltip,
child: Icon(
icon,
size: 24,
color: textColor.withValues(alpha: 0.7),
),
),
const SizedBox(width: 8.0),
Flexible(
child: Wrap(
spacing: 3,
runSpacing: 5,
children: dots,
),
),
],
),
);
}
}

View file

@ -0,0 +1,157 @@
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_level_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/analytics_misc/learning_skills_enum.dart';
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/widgets/matrix.dart';
class LemmaUseExampleMessages extends StatelessWidget {
final ConstructUses construct;
const LemmaUseExampleMessages({
super.key,
required this.construct,
});
Future<List<ExampleMessage>> _getExampleMessages() async {
final Set<ExampleMessage> examples = {};
for (final OneConstructUse use in construct.uses) {
if (use.useType.skillsEnumType != LearningSkillsEnum.writing ||
use.metadata.eventId == null ||
use.form == null ||
use.pointValue <= 0) {
continue;
}
final Room? room = MatrixState.pangeaController.matrixState.client
.getRoomById(use.metadata.roomId);
if (room == null) continue;
Timeline? timeline = room.timeline;
if (room.timeline == null) {
timeline = await room.getTimeline();
}
final Event? event = await room.getEventById(use.metadata.eventId!);
if (event == null) continue;
final PangeaMessageEvent pangeaMessageEvent = PangeaMessageEvent(
event: event,
timeline: timeline!,
ownMessage: event.senderId ==
MatrixState.pangeaController.matrixState.client.userID,
);
final tokens = pangeaMessageEvent.messageDisplayRepresentation?.tokens;
if (tokens == null || tokens.isEmpty) continue;
final token =
tokens.firstWhereOrNull((token) => token.text.content == use.form);
if (token == null) continue;
final int offset = token.text.offset;
examples.add(
ExampleMessage(
message: pangeaMessageEvent.messageDisplayText,
offset: offset,
length: use.form!.length,
),
);
if (examples.length > 4) break;
}
return examples.toList();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _getExampleMessages(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Align(
alignment: Alignment.topLeft,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: snapshot.data!.map((e) {
return Container(
decoration: BoxDecoration(
color: construct.lemmaCategory.color,
borderRadius: BorderRadius.circular(4),
),
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
margin: const EdgeInsets.only(bottom: 8),
constraints: const BoxConstraints(
maxWidth: FluffyThemes.columnWidth * 1.5,
),
child: RichText(
text: TextSpan(
style: TextStyle(
color: Theme.of(context).colorScheme.onPrimaryFixed,
fontSize: AppConfig.fontSizeFactor *
AppConfig.messageFontSize,
),
children: [
TextSpan(text: e.message.substring(0, e.offset)),
TextSpan(
text: e.message
.substring(e.offset, e.offset + e.length),
style: const TextStyle(fontWeight: FontWeight.bold),
),
TextSpan(
text: e.message.substring(e.offset + e.length),
),
],
),
),
);
}).toList(),
),
);
} else {
return const Column(
children: [
SizedBox(height: 10),
CircularProgressIndicator.adaptive(
strokeWidth: 2,
),
],
);
}
},
);
}
}
class ExampleMessage {
final String message;
final int offset;
final int length;
ExampleMessage({
required this.message,
required this.offset,
required this.length,
});
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is ExampleMessage &&
other.message == message &&
other.offset == offset &&
other.length == length;
}
@override
int get hashCode => message.hashCode ^ offset.hashCode ^ length.hashCode;
}

View file

@ -0,0 +1,222 @@
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_identifier.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_level_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart';
import 'package:fluffychat/pangea/morphs/morph_icon.dart';
import 'package:fluffychat/pangea/morphs/morph_models.dart';
import 'package:fluffychat/widgets/matrix.dart';
import '../morphs/morph_repo.dart';
class MorphAnalyticsView extends StatelessWidget {
const MorphAnalyticsView({
super.key,
});
List<MorphFeature> get availableFeatures => MorphsRepo.get().displayFeatures;
@override
Widget build(BuildContext context) => Padding(
padding: const EdgeInsets.symmetric(vertical: 20),
child: ListView.builder(
itemCount: availableFeatures.length,
itemBuilder: (context, index) =>
availableFeatures[index].displayTags.isNotEmpty
? MorphFeatureBox(
morphFeature: availableFeatures[index].feature,
)
: const SizedBox.shrink(),
),
);
}
class MorphFeatureBox extends StatelessWidget {
final String morphFeature;
const MorphFeatureBox({
super.key,
required this.morphFeature,
});
// get constructData => MatrixState.pangeaController.
String _categoryCopy(
String category,
BuildContext context,
) {
if (category.toLowerCase() == "other") {
return L10n.of(context).other;
}
return ConstructTypeEnum.morph.getDisplayCopy(
category,
context,
) ??
category;
}
Set<String> get allTags => MorphsRepo.get()
.getDisplayTags(morphFeature)
.map((tag) => tag.toLowerCase())
.toSet();
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Container(
padding: const EdgeInsets.all(16.0),
margin: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.0),
border: Border.all(
color: Theme.of(context).brightness == Brightness.dark
? AppConfig.primaryColorLight
: AppConfig.primaryColor,
width: 2,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 16.0,
children: [
SizedBox(
height: 30.0,
width: 30.0,
child: MorphIcon(morphFeature: morphFeature, morphTag: null),
),
Text(
_categoryCopy(morphFeature, context),
style: theme.textTheme.bodyLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 16.0),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Flexible(
child: Wrap(
alignment: WrapAlignment.center,
spacing: 16.0,
runSpacing: 16.0,
children: allTags
.map(
(morphTag) => MorphTagChip(
morphFeature: morphFeature,
morphTag: morphTag,
constructAnalytics: MatrixState.pangeaController
.getAnalytics.constructListModel
.getConstructUses(
ConstructIdentifier(
lemma: morphTag,
type: ConstructTypeEnum.morph,
category: morphFeature,
),
) ??
ConstructUses(
lemma: morphTag,
constructType: ConstructTypeEnum.morph,
category: morphFeature,
uses: [],
),
),
)
.sortedBy<num>((chip) => chip.constructAnalytics.points)
.reversed
.toList(),
),
),
],
),
],
),
);
}
}
class MorphTagChip extends StatelessWidget {
final String morphFeature;
final String morphTag;
final ConstructUses constructAnalytics;
const MorphTagChip({
super.key,
required this.morphFeature,
required this.morphTag,
required this.constructAnalytics,
});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Opacity(
opacity: constructAnalytics.points > 0 ? 1.0 : 0.3,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(32.0),
gradient: constructAnalytics.points > 0
? LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: <Color>[
Colors.transparent,
constructAnalytics.lemmaCategory.color,
],
)
: null,
color: constructAnalytics.points > 0 ? null : theme.disabledColor,
),
padding: const EdgeInsets.symmetric(
vertical: 4.0,
horizontal: 8.0,
),
child: Row(
mainAxisSize: MainAxisSize.min,
spacing: 8.0,
children: [
SizedBox(
width: 28.0,
height: 28.0,
child: constructAnalytics.points > 0
? MorphIcon(
morphFeature: morphFeature,
morphTag: morphTag,
)
: const Icon(
Icons.lock,
color: Colors.white,
),
),
Text(
getGrammarCopy(
category: morphFeature,
lemma: morphTag,
context: context,
) ??
morphTag,
style: TextStyle(
fontWeight: FontWeight.bold,
color: theme.brightness == Brightness.dark
? Colors.white
: Colors.black,
),
),
],
),
),
);
}
}

View file

@ -1,10 +1,10 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics/enums/progress_indicators_enum.dart';
import 'package:fluffychat/pangea/analytics/models/construct_use_model.dart';
import 'package:fluffychat/pangea/analytics/utils/get_grammar_copy.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
import 'package:fluffychat/pangea/analytics_summary/progress_indicators_enum.dart';
import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart';
class ConstructUsesXPTile extends StatelessWidget {
final ConstructUses constructUses;

View file

@ -0,0 +1,181 @@
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pangea/analytics_misc/analytics_constants.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_identifier.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_level_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
import 'package:fluffychat/pangea/common/widgets/customized_svg.dart';
import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart';
import 'package:fluffychat/widgets/matrix.dart';
/// Displays vocab analytics, sorted into categories
/// (flowers, greens, and seeds) by points
class VocabAnalyticsView extends StatelessWidget {
final void Function(ConstructIdentifier) onConstructZoom;
const VocabAnalyticsView({
super.key,
required this.onConstructZoom,
});
@override
Widget build(BuildContext context) {
final lemmas = MatrixState.pangeaController.getAnalytics.constructListModel
.constructList(type: ConstructTypeEnum.vocab)
..sort((a, b) => a.lemma.toLowerCase().compareTo(b.lemma.toLowerCase()));
final flowerLemmas = <VocabChip>[];
final greenLemmas = <VocabChip>[];
final seedLemmas = <VocabChip>[];
for (int i = 0; i < lemmas.length; i++) {
final construct = lemmas[i];
if (construct.lemma.isEmpty) continue;
final int points = construct.points;
String? displayText;
// Check if previous or next entry has same lemma as this entry
if ((i > 0 && lemmas[i - 1].lemma.equals(construct.lemma)) ||
(i < lemmas.length - 1 &&
lemmas[i + 1].lemma.equals(construct.lemma))) {
final pos = getGrammarCopy(
category: "pos",
lemma: construct.category,
context: context,
) ??
construct.category;
displayText = "${construct.lemma} (${pos.toLowerCase()})";
}
final lemma = VocabChip(
construct: construct,
displayText: displayText,
);
// Sort lemmas into categories
if (points < AnalyticsConstants.xpForGreens) {
seedLemmas.add(lemma);
} else if (points >= AnalyticsConstants.xpForFlower) {
flowerLemmas.add(lemma);
} else {
greenLemmas.add(lemma);
}
}
final flowers = LemmaListSection(
type: ConstructLevelEnum.flowers,
lemmas: flowerLemmas,
onTap: onConstructZoom,
);
final greens = LemmaListSection(
type: ConstructLevelEnum.greens,
lemmas: greenLemmas,
onTap: onConstructZoom,
);
final seeds = LemmaListSection(
type: ConstructLevelEnum.seeds,
lemmas: seedLemmas,
onTap: onConstructZoom,
);
return Padding(
padding: const EdgeInsets.symmetric(vertical: 20),
child: ListView(
children: [flowers, greens, seeds],
),
);
}
}
class VocabChip {
final ConstructUses construct;
final String? displayText;
VocabChip({
required this.construct,
this.displayText,
});
}
class LemmaListSection extends StatelessWidget {
final ConstructLevelEnum type;
final List<VocabChip> lemmas;
final Function(ConstructIdentifier) onTap;
const LemmaListSection({
super.key,
required this.type,
required this.lemmas,
required this.onTap,
});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final fontColor =
theme.brightness == Brightness.dark ? type.color : type.darkColor;
return Container(
padding: const EdgeInsets.all(16.0),
margin: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.0),
border: Border.all(color: type.color, width: 2),
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CustomizedSvg(
svgUrl: type.svgURL,
colorReplacements: const {},
errorIcon: Text(type.emoji),
),
],
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: lemmas.isEmpty
? Text(
L10n.of(context).noLemmasFound(type.xpNeeded),
style: TextStyle(
color: fontColor,
fontStyle: FontStyle.italic,
),
)
: Wrap(
spacing: 0,
runSpacing: 0,
children: lemmas.mapIndexed((index, lemma) {
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () => onTap(lemma.construct.id),
child: Text(
"${lemma.displayText ?? lemma.construct.lemma}${index < lemmas.length - 1 ? ', ' : ''}",
style: TextStyle(
shadows: [
Shadow(
color: fontColor,
offset: const Offset(0, -2.5),
),
],
color: Colors.transparent,
decoration: TextDecoration.underline,
decorationStyle: TextDecorationStyle.dotted,
decorationColor: fontColor,
decorationThickness: 1,
fontSize: theme.textTheme.bodyLarge?.fontSize,
),
),
),
);
}).toList(),
),
),
],
),
);
}
}

View file

@ -1,42 +1,39 @@
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics/enums/learning_skills_enum.dart';
import 'package:fluffychat/pangea/analytics/enums/lemma_category_enum.dart';
import 'package:fluffychat/pangea/analytics/models/construct_use_model.dart';
import 'package:fluffychat/pangea/analytics/models/constructs_model.dart';
import 'package:fluffychat/pangea/analytics/models/lemma.dart';
import 'package:fluffychat/pangea/analytics/repo/lemma_info_repo.dart';
import 'package:fluffychat/pangea/analytics/repo/lemma_info_request.dart';
import 'package:fluffychat/pangea/analytics/repo/lemma_info_response.dart';
import 'package:fluffychat/pangea/analytics/utils/get_grammar_copy.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_identifier.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_level_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/learning_skills_enum.dart';
import 'package:fluffychat/pangea/common/widgets/customized_svg.dart';
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart';
import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart';
import 'package:fluffychat/pangea/lemmas/lemma.dart';
import 'package:fluffychat/pangea/lemmas/lemma_info_repo.dart';
import 'package:fluffychat/pangea/lemmas/lemma_info_request.dart';
import 'package:fluffychat/pangea/lemmas/lemma_info_response.dart';
import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart';
import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart';
import 'package:fluffychat/pangea/toolbar/widgets/practice_activity/word_audio_button.dart';
import 'package:fluffychat/widgets/matrix.dart';
/// Displays information about selected lemma, and its usage
class VocabDefinitionPopup extends StatelessWidget {
final ConstructUses construct;
final VoidCallback onClose;
class VocabDetailsView extends StatelessWidget {
final ConstructIdentifier constructId;
const VocabDefinitionPopup({
const VocabDetailsView({
super.key,
required this.construct,
required this.onClose,
required this.constructId,
});
ConstructUses get construct => constructId.constructUses;
String? get emoji => PangeaToken(
text: PangeaTokenText(
offset: 0,
@ -101,6 +98,7 @@ class VocabDefinitionPopup extends StatelessWidget {
return Scaffold(
appBar: AppBar(
leading: const SizedBox(),
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
@ -132,10 +130,10 @@ class VocabDefinitionPopup extends StatelessWidget {
],
),
centerTitle: true,
leading: SizedBox(
width: 24,
child: BackButton(onPressed: onClose),
),
// leading: SizedBox(
// width: 24,
// child: BackButton(onPressed: onClose),
// ),
),
body: SingleChildScrollView(
child: Padding(
@ -311,226 +309,3 @@ class VocabDefinitionPopup extends StatelessWidget {
);
}
}
class LemmaUseExampleMessages extends StatelessWidget {
final ConstructUses construct;
const LemmaUseExampleMessages({
super.key,
required this.construct,
});
Future<List<ExampleMessage>> _getExampleMessages() async {
final Set<ExampleMessage> examples = {};
for (final OneConstructUse use in construct.uses) {
if (use.useType.skillsEnumType != LearningSkillsEnum.writing ||
use.metadata.eventId == null ||
use.form == null ||
use.pointValue <= 0) {
continue;
}
final Room? room = MatrixState.pangeaController.matrixState.client
.getRoomById(use.metadata.roomId);
if (room == null) continue;
Timeline? timeline = room.timeline;
if (room.timeline == null) {
timeline = await room.getTimeline();
}
final Event? event = await room.getEventById(use.metadata.eventId!);
if (event == null) continue;
final PangeaMessageEvent pangeaMessageEvent = PangeaMessageEvent(
event: event,
timeline: timeline!,
ownMessage: event.senderId ==
MatrixState.pangeaController.matrixState.client.userID,
);
final tokens = pangeaMessageEvent.messageDisplayRepresentation?.tokens;
if (tokens == null || tokens.isEmpty) continue;
final token =
tokens.firstWhereOrNull((token) => token.text.content == use.form);
if (token == null) continue;
final int offset = token.text.offset;
examples.add(
ExampleMessage(
message: pangeaMessageEvent.messageDisplayText,
offset: offset,
length: use.form!.length,
),
);
if (examples.length > 4) break;
}
return examples.toList();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _getExampleMessages(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Align(
alignment: Alignment.topLeft,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: snapshot.data!.map((e) {
return Container(
decoration: BoxDecoration(
color: construct.lemmaCategory.color,
borderRadius: BorderRadius.circular(4),
),
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
margin: const EdgeInsets.only(bottom: 8),
constraints: const BoxConstraints(
maxWidth: FluffyThemes.columnWidth * 1.5,
),
child: RichText(
text: TextSpan(
style: TextStyle(
color: Theme.of(context).colorScheme.onPrimaryFixed,
fontSize: AppConfig.fontSizeFactor *
AppConfig.messageFontSize,
),
children: [
TextSpan(text: e.message.substring(0, e.offset)),
TextSpan(
text: e.message
.substring(e.offset, e.offset + e.length),
style: const TextStyle(fontWeight: FontWeight.bold),
),
TextSpan(
text: e.message.substring(e.offset + e.length),
),
],
),
),
);
}).toList(),
),
);
} else {
return const Column(
children: [
SizedBox(height: 10),
CircularProgressIndicator.adaptive(
strokeWidth: 2,
),
],
);
}
},
);
}
}
class LemmaUsageDots extends StatelessWidget {
final ConstructUses construct;
final LearningSkillsEnum category;
final String tooltip;
final IconData icon;
const LemmaUsageDots({
required this.construct,
required this.category,
required this.tooltip,
required this.icon,
super.key,
});
/// Find lemma uses for the given exercise type, to create dot list
List<bool> sortedUses(LearningSkillsEnum category) {
final List<bool> useList = [];
for (final OneConstructUse use in construct.uses) {
if (use.useType.pointValue == 0) {
continue;
}
// If the use type matches the given category, save to list
// Usage with positive XP is saved as true, else false
if (category == use.useType.skillsEnumType) {
useList.add(use.useType.pointValue > 0);
}
}
return useList;
}
@override
Widget build(BuildContext context) {
final List<Widget> dots = [];
for (final bool use in sortedUses(category)) {
dots.add(
Container(
width: 15.0,
height: 15.0,
decoration: BoxDecoration(
color: use ? AppConfig.success : Colors.red,
shape: BoxShape.circle,
),
),
);
}
final Color textColor = Theme.of(context).brightness != Brightness.light
? construct.lemmaCategory.color
: construct.lemmaCategory.darkColor;
return Padding(
padding: const EdgeInsets.symmetric(vertical: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Tooltip(
triggerMode: TooltipTriggerMode.tap,
message: tooltip,
child: Icon(
icon,
size: 24,
color: textColor.withValues(alpha: 0.7),
),
),
const SizedBox(width: 8.0),
Flexible(
child: Wrap(
spacing: 3,
runSpacing: 5,
children: dots,
),
),
],
),
);
}
}
class ExampleMessage {
final String message;
final int offset;
final int length;
ExampleMessage({
required this.message,
required this.offset,
required this.length,
});
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is ExampleMessage &&
other.message == message &&
other.offset == offset &&
other.length == length;
}
@override
int get hashCode => message.hashCode ^ offset.hashCode ^ length.hashCode;
}

View file

@ -2,11 +2,11 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/pangea/analytics/enums/analytics_summary_enum.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics/models/construct_list_model.dart';
import 'package:fluffychat/pangea/analytics/models/construct_use_model.dart';
import 'package:fluffychat/pangea/analytics_downloads/analytics_summary_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_list_model.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
class AnalyticsSummaryModel {
String username;

View file

@ -5,8 +5,10 @@ import 'package:flutter/foundation.dart';
import 'package:collection/collection.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/widgets/matrix.dart';
class ConstructIdentifier {
final String lemma;
@ -89,4 +91,16 @@ class ConstructIdentifier {
}
String get partialKey => "$lemma-${type.string}";
ConstructUses get constructUses =>
MatrixState.pangeaController.getAnalytics.constructListModel
.getConstructUses(
this,
) ??
ConstructUses(
lemma: lemma,
constructType: ConstructTypeEnum.morph,
category: category,
uses: [],
);
}

View file

@ -1,71 +1,82 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/analytics/constants/analytics_constants.dart';
import 'package:fluffychat/pangea/analytics_misc/analytics_constants.dart';
enum LemmaCategoryEnum {
enum ConstructLevelEnum {
flowers,
greens,
seeds,
}
extension LemmaCategoryExtension on LemmaCategoryEnum {
extension ConstructLevelEnumExt on ConstructLevelEnum {
Color get color {
switch (this) {
case LemmaCategoryEnum.flowers:
case ConstructLevelEnum.flowers:
return Color.lerp(AppConfig.primaryColor, Colors.white, 0.6) ??
AppConfig.primaryColor;
case LemmaCategoryEnum.greens:
case ConstructLevelEnum.greens:
return Color.lerp(AppConfig.success, Colors.white, 0.6) ??
AppConfig.success;
case LemmaCategoryEnum.seeds:
case ConstructLevelEnum.seeds:
return Color.lerp(AppConfig.gold, Colors.white, 0.6) ?? AppConfig.gold;
}
}
Color get darkColor {
switch (this) {
case LemmaCategoryEnum.flowers:
case ConstructLevelEnum.flowers:
return Color.lerp(AppConfig.primaryColor, Colors.white, 0.3) ??
AppConfig.primaryColor;
case LemmaCategoryEnum.greens:
case ConstructLevelEnum.greens:
return Color.lerp(AppConfig.success, Colors.black, 0.3) ??
AppConfig.success;
case LemmaCategoryEnum.seeds:
case ConstructLevelEnum.seeds:
return Color.lerp(AppConfig.gold, Colors.black, 0.3) ?? AppConfig.gold;
}
}
String get svgURL {
switch (this) {
case LemmaCategoryEnum.seeds:
case ConstructLevelEnum.seeds:
return "${AppConfig.assetsBaseURL}/${AnalyticsConstants.seedSvgFileName}";
case LemmaCategoryEnum.greens:
case ConstructLevelEnum.greens:
return "${AppConfig.assetsBaseURL}/${AnalyticsConstants.leafSvgFileName}";
case LemmaCategoryEnum.flowers:
case ConstructLevelEnum.flowers:
return "${AppConfig.assetsBaseURL}/${AnalyticsConstants.flowerSvgFileName}";
}
}
String get emoji {
switch (this) {
case LemmaCategoryEnum.flowers:
case ConstructLevelEnum.flowers:
return AnalyticsConstants.emojiForFlower;
case LemmaCategoryEnum.greens:
case ConstructLevelEnum.greens:
return AnalyticsConstants.emojiForGreen;
case LemmaCategoryEnum.seeds:
case ConstructLevelEnum.seeds:
return AnalyticsConstants.emojiForSeed;
}
}
String get xpString {
switch (this) {
case LemmaCategoryEnum.flowers:
case ConstructLevelEnum.flowers:
return ">${AnalyticsConstants.xpForFlower}";
case LemmaCategoryEnum.greens:
case ConstructLevelEnum.greens:
return ">${AnalyticsConstants.xpForGreens}";
case LemmaCategoryEnum.seeds:
case ConstructLevelEnum.seeds:
return "<${AnalyticsConstants.xpForGreens}";
}
}
int get xpNeeded {
switch (this) {
case ConstructLevelEnum.flowers:
return AnalyticsConstants.xpForFlower;
case ConstructLevelEnum.greens:
return AnalyticsConstants.xpForGreens;
case ConstructLevelEnum.seeds:
return 0;
}
}
}

View file

@ -4,13 +4,13 @@ import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:fluffychat/pangea/analytics/constants/analytics_constants.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics/models/construct_identifier.dart';
import 'package:fluffychat/pangea/analytics/models/construct_use_model.dart';
import 'package:fluffychat/pangea/analytics/models/constructs_model.dart';
import 'package:fluffychat/pangea/analytics/utils/get_grammar_copy.dart';
import 'package:fluffychat/pangea/analytics_misc/analytics_constants.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_identifier.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart';
/// A wrapper around a list of [OneConstructUse]s, used to simplify
/// the process of filtering / sorting / displaying the events.
@ -187,6 +187,7 @@ class ConstructListModel {
}
}
// TODO; make this non-nullable, returning empty if not found
ConstructUses? getConstructUses(ConstructIdentifier identifier) {
final partialKey = "${identifier.lemma}-${identifier.type.string}";

View file

@ -3,10 +3,10 @@ import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:fluffychat/pangea/analytics/constants/analytics_constants.dart';
import 'package:fluffychat/pangea/analytics/enums/morph_categories_enum.dart';
import 'package:fluffychat/pangea/analytics/enums/parts_of_speech_enum.dart';
import 'package:fluffychat/pangea/analytics/enums/progress_indicators_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/analytics_constants.dart';
import 'package:fluffychat/pangea/analytics_summary/progress_indicators_enum.dart';
import 'package:fluffychat/pangea/morphs/morph_categories_enum.dart';
import 'package:fluffychat/pangea/morphs/parts_of_speech_enum.dart';
enum ConstructTypeEnum {
/// for vocabulary words

View file

@ -1,9 +1,9 @@
import 'package:fluffychat/pangea/analytics/constants/analytics_constants.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics/enums/lemma_category_enum.dart';
import 'package:fluffychat/pangea/analytics/models/construct_identifier.dart';
import 'package:fluffychat/pangea/analytics/models/constructs_model.dart';
import 'package:fluffychat/pangea/analytics_misc/analytics_constants.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_identifier.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_level_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
/// One lemma and a list of construct uses for that lemma
class ConstructUses {
@ -68,12 +68,12 @@ class ConstructUses {
}
/// Get the lemma category, based on points
LemmaCategoryEnum get lemmaCategory {
ConstructLevelEnum get lemmaCategory {
if (points < AnalyticsConstants.xpForGreens) {
return LemmaCategoryEnum.seeds;
return ConstructLevelEnum.seeds;
} else if (points >= AnalyticsConstants.xpForFlower) {
return LemmaCategoryEnum.flowers;
return ConstructLevelEnum.flowers;
}
return LemmaCategoryEnum.greens;
return ConstructLevelEnum.greens;
}
}

View file

@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/pangea/analytics/enums/analytics_summary_enum.dart';
import 'package:fluffychat/pangea/analytics/enums/learning_skills_enum.dart';
import 'package:fluffychat/pangea/analytics_downloads/analytics_summary_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/learning_skills_enum.dart';
import 'package:fluffychat/pangea/toolbar/enums/activity_type_enum.dart';
enum ConstructUseTypeEnum {

View file

@ -1,7 +1,7 @@
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pangea/analytics/models/constructs_model.dart';
import '../../events/constants/pangea_event_types.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import '../events/constants/pangea_event_types.dart';
class ConstructAnalyticsEvent {
late Event _event;

View file

@ -4,11 +4,12 @@ import 'package:flutter/foundation.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pangea/analytics/constants/morph_categories_and_labels.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics/models/construct_identifier.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_identifier.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import '../enums/construct_type_enum.dart';
import 'package:fluffychat/pangea/morphs/morph_models.dart';
import '../morphs/morph_repo.dart';
import 'construct_type_enum.dart';
class ConstructAnalyticsModel {
List<OneConstructUse> uses;
@ -154,13 +155,15 @@ class OneConstructUse {
return category ?? "Other";
}
final MorphFeatuuresAndTags morphs = MorphsRepo.get();
if (categoryEntry == null) {
return _guessGrammarCategory(json["lemma"]);
return morphs.guessMorphCategory(json["lemma"]);
}
if ((categoryEntry is List)) {
if (categoryEntry.isEmpty) {
return _guessGrammarCategory(json["lemma"]);
return morphs.guessMorphCategory(json["lemma"]);
}
return categoryEntry.first;
} else if (categoryEntry is String) {
@ -170,25 +173,7 @@ class OneConstructUse {
debugPrint(
"Category entry is not a list or string -${json['cat'] ?? json['categories']}-",
);
return _guessGrammarCategory(json["lemma"]);
}
static String _guessGrammarCategory(String morphLemma) {
for (final String category in morphCategoriesAndLabels.keys) {
if (morphCategoriesAndLabels[category]!.contains(morphLemma)) {
// debugPrint(
// "found missing construct category for $morphLemma: $category",
// );
return category;
}
}
ErrorHandler.logError(
m: "Morph construct lemma $morphLemma not found in morph categories and labels",
data: {
"morphLemma": morphLemma,
},
);
return "Other";
return morphs.guessMorphCategory(json["lemma"]);
}
Room? getRoom(Client client) {

View file

@ -4,8 +4,8 @@ import 'dart:math';
import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/analytics/controllers/get_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics/controllers/put_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics_misc/get_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.dart';
import 'package:fluffychat/pangea/bot/utils/bot_style.dart';
import 'package:fluffychat/widgets/matrix.dart';

View file

@ -6,13 +6,13 @@ import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:fluffychat/pangea/analytics/controllers/message_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics/controllers/put_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics/extensions/client_analytics_extension.dart';
import 'package:fluffychat/pangea/analytics/models/construct_list_model.dart';
import 'package:fluffychat/pangea/analytics/models/constructs_event.dart';
import 'package:fluffychat/pangea/analytics/models/constructs_model.dart';
import 'package:fluffychat/pangea/analytics_misc/client_analytics_extension.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_list_model.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_event.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/analytics_misc/message_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.dart';
import 'package:fluffychat/pangea/common/constants/local.key.dart';
import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';

View file

@ -8,7 +8,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:http/http.dart' as http;
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/analytics/constants/analytics_constants.dart';
import 'package:fluffychat/pangea/analytics_misc/analytics_constants.dart';
class LevelUpUtil {
static void showLevelUpDialog(

View file

@ -2,8 +2,8 @@ import 'dart:math';
import 'package:flutter/foundation.dart';
import 'package:fluffychat/pangea/analytics/controllers/get_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics/models/construct_identifier.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_identifier.dart';
import 'package:fluffychat/pangea/analytics_misc/get_analytics_controller.dart';
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/toolbar/enums/activity_type_enum.dart';

View file

@ -4,10 +4,10 @@ import 'package:flutter/foundation.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics/extensions/client_analytics_extension.dart';
import 'package:fluffychat/pangea/analytics/models/constructs_model.dart';
import 'package:fluffychat/pangea/analytics_misc/client_analytics_extension.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/common/constants/local.key.dart';
import 'package:fluffychat/pangea/common/controllers/base_controller.dart';
import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart';

View file

@ -1,4 +1,4 @@
part of "../../extensions/pangea_room_extension.dart";
part of "../extensions/pangea_room_extension.dart";
extension AnalyticsRoomExtension on Room {
/// Get next n analytics rooms via the space hierarchy

View file

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/pangea/analytics/widgets/progress_bar/progress_bar.dart';
import 'package:fluffychat/pangea/analytics/widgets/progress_bar/progress_bar_details.dart';
import 'package:fluffychat/pangea/analytics_summary/progress_bar/progress_bar.dart';
import 'package:fluffychat/pangea/analytics_summary/progress_bar/progress_bar_details.dart';
import 'package:fluffychat/widgets/matrix.dart';
class LearningProgressBar extends StatelessWidget {

View file

@ -5,16 +5,16 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/pages/chat_list/client_chooser_button.dart';
import 'package:fluffychat/pangea/analytics/controllers/get_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics/enums/progress_indicators_enum.dart';
import 'package:fluffychat/pangea/analytics/models/construct_list_model.dart';
import 'package:fluffychat/pangea/analytics/widgets/analytics_summary/learning_progress_bar.dart';
import 'package:fluffychat/pangea/analytics/widgets/analytics_summary/learning_settings_button.dart';
import 'package:fluffychat/pangea/analytics/widgets/analytics_summary/level_badge.dart';
import 'package:fluffychat/pangea/analytics/widgets/analytics_summary/level_bar_popup.dart';
import 'package:fluffychat/pangea/analytics/widgets/analytics_summary/morph_analytics_popup/morph_analytics_popup.dart';
import 'package:fluffychat/pangea/analytics/widgets/analytics_summary/progress_indicator.dart';
import 'package:fluffychat/pangea/analytics/widgets/analytics_summary/vocab_analytics_popup/vocab_analytics_popup.dart';
import 'package:fluffychat/pangea/analytics_details_popup/analytics_details_popup.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_list_model.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/get_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics_summary/learning_progress_bar.dart';
import 'package:fluffychat/pangea/analytics_summary/learning_settings_button.dart';
import 'package:fluffychat/pangea/analytics_summary/level_badge.dart';
import 'package:fluffychat/pangea/analytics_summary/level_bar_popup.dart';
import 'package:fluffychat/pangea/analytics_summary/progress_indicator.dart';
import 'package:fluffychat/pangea/analytics_summary/progress_indicators_enum.dart';
import 'package:fluffychat/pangea/learning_settings/pages/settings_learning.dart';
import 'package:fluffychat/widgets/matrix.dart';
@ -110,30 +110,23 @@ class LearningProgressIndicatorsState
l2: userL2?.getDisplayName(context) ?? userL2?.langCode,
),
Row(
children: [
ProgressIndicatorBadge(
points: uniqueLemmas(ProgressIndicatorEnum.wordsUsed),
loading: _loading,
onTap: () {
showDialog<VocabAnalyticsPopup>(
context: context,
builder: (c) => const VocabAnalyticsPopup(),
);
},
indicator: ProgressIndicatorEnum.wordsUsed,
),
ProgressIndicatorBadge(
points: uniqueLemmas(ProgressIndicatorEnum.morphsUsed),
loading: _loading,
onTap: () {
showDialog<MorphAnalyticsPopup>(
context: context,
builder: (c) => const MorphAnalyticsPopup(),
);
},
indicator: ProgressIndicatorEnum.morphsUsed,
),
],
children: ConstructTypeEnum.values
.map(
(c) => ProgressIndicatorBadge(
points: uniqueLemmas(c.indicator),
loading: _loading,
onTap: () {
showDialog<AnalyticsPopupWrapper>(
context: context,
builder: (context) => AnalyticsPopupWrapper(
view: c,
),
);
},
indicator: c.indicator,
),
)
.toList(),
),
],
),

View file

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/analytics/widgets/analytics_summary/level_bar_popup.dart';
import 'package:fluffychat/pangea/analytics_summary/level_bar_popup.dart';
import 'package:fluffychat/pangea/common/widgets/pressable_button.dart';
class LevelBadge extends StatelessWidget {

View file

@ -3,12 +3,12 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/analytics/controllers/get_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics/models/constructs_model.dart';
import 'package:fluffychat/pangea/analytics/utils/get_grammar_copy.dart';
import 'package:fluffychat/pangea/analytics/widgets/analytics_summary/learning_progress_bar.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/analytics_misc/get_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics_summary/learning_progress_bar.dart';
import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart';
import 'package:fluffychat/widgets/matrix.dart';
class LevelBarPopup extends StatelessWidget {

View file

@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/analytics/widgets/progress_bar/animated_level_dart.dart';
import 'package:fluffychat/pangea/analytics/widgets/progress_bar/progress_bar_details.dart';
import 'package:fluffychat/pangea/analytics_summary/progress_bar/animated_level_dart.dart';
import 'package:fluffychat/pangea/analytics_summary/progress_bar/progress_bar_details.dart';
class LevelBar extends StatefulWidget {
final LevelBarDetails details;

View file

@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/pangea/analytics/widgets/progress_bar/level_bar.dart';
import 'package:fluffychat/pangea/analytics/widgets/progress_bar/progress_bar_background.dart';
import 'package:fluffychat/pangea/analytics/widgets/progress_bar/progress_bar_details.dart';
import 'package:fluffychat/pangea/analytics_summary/progress_bar/level_bar.dart';
import 'package:fluffychat/pangea/analytics_summary/progress_bar/progress_bar_background.dart';
import 'package:fluffychat/pangea/analytics_summary/progress_bar/progress_bar_details.dart';
// Provide an order list of level indicators, each with it's color
// and stream. Also provide an overall width and pointsPerLevel.

View file

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/analytics/widgets/progress_bar/progress_bar_details.dart';
import 'package:fluffychat/pangea/analytics_summary/progress_bar/progress_bar_details.dart';
class ProgressBarBackground extends StatelessWidget {
final ProgressBarDetails details;

View file

@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/pangea/analytics/enums/progress_indicators_enum.dart';
import 'package:fluffychat/pangea/analytics_summary/progress_indicators_enum.dart';
import 'package:fluffychat/pangea/common/widgets/pressable_button.dart';
/// A badge that represents one learning progress indicator (i.e., construct uses)

View file

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
enum ProgressIndicatorEnum {
level,
@ -23,6 +23,17 @@ extension ProgressIndicatorsExtension on ProgressIndicatorEnum {
}
}
IconData get iconSelected {
switch (this) {
case ProgressIndicatorEnum.wordsUsed:
return Symbols.dictionary;
case ProgressIndicatorEnum.morphsUsed:
return Symbols.toys_and_games;
case ProgressIndicatorEnum.level:
return Icons.star;
}
}
static bool isDarkMode(BuildContext context) =>
Theme.of(context).brightness == Brightness.dark;

View file

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/pages/chat_list/chat_list.dart';
import 'package:fluffychat/pangea/analytics/widgets/analytics_summary/learning_progress_indicators.dart';
import 'package:fluffychat/pangea/analytics_summary/learning_progress_indicators.dart';
class PangeaChatListHeader extends StatelessWidget
implements PreferredSizeWidget {

View file

@ -1,14 +1,15 @@
import 'package:fluffychat/pangea/common/config/environment.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/spaces/constants/space_constants.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart';
import 'package:universal_html/html.dart' as html;
import 'package:fluffychat/pangea/common/config/environment.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/spaces/constants/space_constants.dart';
import 'package:fluffychat/widgets/matrix.dart';
import '../../../utils/fluffy_share.dart';
class ClassInvitationButtons extends StatelessWidget {

View file

@ -7,8 +7,8 @@ import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:fluffychat/pangea/analytics/controllers/put_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.dart';
import 'package:fluffychat/pangea/choreographer/constants/choreo_constants.dart';
import 'package:fluffychat/pangea/choreographer/controllers/error_service.dart';
import 'package:fluffychat/pangea/choreographer/enums/edit_type.dart';

View file

@ -2,7 +2,6 @@ import 'dart:convert';
import 'package:http/http.dart';
import 'package:fluffychat/pangea/analytics/models/lemma.dart';
import 'package:fluffychat/pangea/choreographer/models/language_detection_model.dart';
import 'package:fluffychat/pangea/choreographer/models/pangea_match_model.dart';
import 'package:fluffychat/pangea/choreographer/repo/language_detection_request.dart';
@ -10,6 +9,7 @@ import 'package:fluffychat/pangea/choreographer/repo/span_data_repo.dart';
import 'package:fluffychat/pangea/common/config/environment.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart';
import 'package:fluffychat/pangea/lemmas/lemma.dart';
import '../../common/constants/model_keys.dart';
import '../../common/network/requests.dart';
import '../../common/network/urls.dart';

View file

@ -6,9 +6,9 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/analytics/controllers/put_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics/widgets/gain_points.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/gain_points_animation.dart';
import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.dart';
import 'package:fluffychat/pangea/bot/utils/bot_style.dart';
import 'package:fluffychat/pangea/choreographer/enums/span_data_type.dart';
import 'package:fluffychat/pangea/choreographer/models/span_data.dart';

View file

@ -4,9 +4,9 @@ import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:fluffychat/pangea/analytics/controllers/put_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics/widgets/gain_points.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/gain_points_animation.dart';
import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.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';

View file

@ -8,8 +8,8 @@ import 'package:get_storage/get_storage.dart';
import 'package:matrix/matrix.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:fluffychat/pangea/analytics/controllers/get_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics/controllers/put_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics_misc/get_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.dart';
import 'package:fluffychat/pangea/bot/utils/bot_name.dart';
import 'package:fluffychat/pangea/chat_settings/constants/bot_mode.dart';
import 'package:fluffychat/pangea/chat_settings/models/bot_options_model.dart';

View file

@ -72,6 +72,8 @@ class PApiUrls {
static String objectiveList = "${PApiUrls.choreoEndpoint}/objectives";
static String topicList = "${PApiUrls.choreoEndpoint}/topics";
static String morphFeaturesAndTags = "${PApiUrls.choreoEndpoint}/morphs";
///-------------------------------- revenue cat --------------------------
static String rcAppsChoreo = "${PApiUrls.subscriptionEndpoint}/app_ids";
static String rcProductsChoreo =

View file

@ -5,26 +5,26 @@ import 'package:flutter/foundation.dart';
import 'package:collection/collection.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pangea/analytics/constants/analytics_constants.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics/enums/lemma_category_enum.dart';
import 'package:fluffychat/pangea/analytics/extensions/client_analytics_extension.dart';
import 'package:fluffychat/pangea/analytics/models/construct_identifier.dart';
import 'package:fluffychat/pangea/analytics/models/construct_use_model.dart';
import 'package:fluffychat/pangea/analytics/models/constructs_model.dart';
import 'package:fluffychat/pangea/analytics/repo/lemma_info_repo.dart';
import 'package:fluffychat/pangea/analytics/repo/lemma_info_request.dart';
import 'package:fluffychat/pangea/analytics_misc/analytics_constants.dart';
import 'package:fluffychat/pangea/analytics_misc/client_analytics_extension.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_identifier.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_level_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_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/models/pangea_token_text_model.dart';
import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart';
import 'package:fluffychat/pangea/lemmas/lemma_info_repo.dart';
import 'package:fluffychat/pangea/lemmas/lemma_info_request.dart';
import 'package:fluffychat/pangea/toolbar/enums/activity_type_enum.dart';
import 'package:fluffychat/pangea/toolbar/repo/lemma_activity_generator.dart';
import 'package:fluffychat/pangea/toolbar/repo/lemma_meaning_activity_generator.dart';
import 'package:fluffychat/widgets/matrix.dart';
import '../../analytics/models/lemma.dart';
import '../../common/constants/model_keys.dart';
import '../../lemmas/lemma.dart';
class PangeaToken {
PangeaTokenText text;
@ -624,13 +624,13 @@ class PangeaToken {
}
}
LemmaCategoryEnum get lemmaXPCategory {
ConstructLevelEnum get lemmaXPCategory {
if (vocabConstruct.points >= AnalyticsConstants.xpForFlower) {
return LemmaCategoryEnum.flowers;
return ConstructLevelEnum.flowers;
} else if (vocabConstruct.points >= AnalyticsConstants.xpForGreens) {
return LemmaCategoryEnum.greens;
return ConstructLevelEnum.greens;
} else {
return LemmaCategoryEnum.seeds;
return ConstructLevelEnum.seeds;
}
}

View file

@ -1,8 +1,8 @@
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics/models/constructs_model.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/choreographer/models/choreo_record.dart';
import 'package:fluffychat/pangea/choreographer/models/pangea_match_model.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';

View file

@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/pangea/analytics/controllers/message_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics_misc/message_analytics_controller.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';

View file

@ -15,8 +15,8 @@ import 'package:matrix/matrix.dart';
import 'package:matrix/src/utils/markdown.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:fluffychat/pangea/analytics/models/constructs_event.dart';
import 'package:fluffychat/pangea/analytics/models/constructs_model.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_event.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/bot/utils/bot_name.dart';
import 'package:fluffychat/pangea/chat_settings/constants/bot_mode.dart';
import 'package:fluffychat/pangea/chat_settings/constants/pangea_room_types.dart';
@ -30,7 +30,7 @@ import '../choreographer/models/choreo_record.dart';
import '../events/constants/pangea_event_types.dart';
import '../events/models/representation_content_model.dart';
part "../analytics/extensions/room_analytics_extension.dart";
part "../analytics_misc/room_analytics_extension.dart";
part "room_children_and_parents_extension.dart";
part "room_events_extension.dart";
part "room_information_extension.dart";

View file

@ -6,14 +6,14 @@ import 'package:flutter/foundation.dart';
import 'package:get_storage/get_storage.dart';
import 'package:http/http.dart';
import 'package:fluffychat/pangea/analytics/repo/lemma_info_request.dart';
import 'package:fluffychat/pangea/analytics/repo/lemma_info_response.dart';
import 'package:fluffychat/pangea/common/network/urls.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/events/models/content_feedback.dart';
import 'package:fluffychat/pangea/lemmas/lemma_info_request.dart';
import 'package:fluffychat/pangea/lemmas/lemma_info_response.dart';
import 'package:fluffychat/widgets/matrix.dart';
import '../../common/config/environment.dart';
import '../../common/network/requests.dart';
import '../common/config/environment.dart';
import '../common/network/requests.dart';
class LemmaInfoRepo {
static final GetStorage _lemmaStorage = GetStorage('lemma_storage');

View file

@ -1,5 +1,5 @@
import 'package:fluffychat/pangea/analytics/repo/lemma_info_response.dart';
import 'package:fluffychat/pangea/events/models/content_feedback.dart';
import 'package:fluffychat/pangea/lemmas/lemma_info_response.dart';
class LemmaInfoRequest {
final String lemma;

View file

@ -0,0 +1,235 @@
import 'package:fluffychat/pangea/morphs/morph_models.dart';
final MorphFeatuuresAndTags defaultMorphMapping =
MorphFeatuuresAndTags.fromJson({
"language_code": "default",
"features": [
{
"feature": "pos",
"tag": [
"ADJ",
"ADP",
"ADV",
"AFFIX",
"AUX",
"CCONJ",
"DET",
"INTJ",
"NOUN",
"NUM",
"PART",
"PRON",
"PROPN",
"PUNCT",
"SCONJ",
"SPACE",
"SYM",
"VERB",
"X",
],
},
{
"feature": "advtype",
"tag": ["Adverbial", "Tim"],
},
{
"feature": "aspect",
"tag": ["Imp", "Perf", "Prog", "Hab"],
},
{
"feature": "case",
"tag": [
"Nom",
"Acc",
"Dat",
"Gen",
"Voc",
"Abl",
"Loc",
"All",
"Ins",
"Ess",
"Tra",
"Com",
"Par",
"Adv",
"Ref",
"Rel",
"Equ",
"Dis",
"Abs",
"Erg",
"Cau",
"Ben",
"Sub",
"Sup",
"Tem",
"Obl",
"Acc,Dat",
"Acc,Nom",
"Pre",
],
},
{
"feature": "conjtype",
"tag": ["Coord", "Sub", "Cmp"],
},
{
"feature": "definite",
"tag": ["Def", "Ind", "Cons"],
},
{
"feature": "degree",
"tag": ["Pos", "Cmp", "Sup", "Abs"],
},
{
"feature": "evident",
"tag": ["Fh", "Nfh"],
},
{
"feature": "foreign",
"tag": ["Yes"],
},
{
"feature": "gender",
"tag": ["Masc", "Fem", "Neut", "Com"],
},
{
"feature": "mood",
"tag": [
"Ind",
"Imp",
"Sub",
"Cnd",
"Opt",
"Jus",
"Adm",
"Des",
"Nec",
"Pot",
"Prp",
"Qot",
"Int",
],
},
{
"feature": "nountype",
"tag": ["Prop", "Comm", "Not_proper"],
},
{
"feature": "numform",
"tag": ["Digit", "Word", "Roman", "Letter"],
},
{
"feature": "numtype",
"tag": ["Card", "Ord", "Mult", "Frac", "Sets", "Range", "Dist"],
},
{
"feature": "number",
"tag": ["Sing", "Plur", "Dual", "Tri", "Pauc", "Grpa", "Grpl", "Inv"],
},
{
"feature": "number[psor]",
"tag": ["Sing", "Plur", "Dual"],
},
{
"feature": "person",
"tag": ["0", "1", "2", "3", "4"],
},
{
"feature": "polarity",
"tag": ["Pos", "Neg"],
},
{
"feature": "polite",
"tag": ["Infm", "Form", "Elev", "Humb"],
},
{
"feature": "poss",
"tag": ["Yes"],
},
{
"feature": "prepcase",
"tag": ["Npr"],
},
{
"feature": "prontype",
"tag": [
"Prs",
"Int",
"Rel",
"Dem",
"Tot",
"Neg",
"Art",
"Emp",
"Exc",
"Ind",
"Rcp",
"Int,Rel",
],
},
{
"feature": "punctside",
"tag": ["Ini", "Fin"],
},
{
"feature": "puncttype",
"tag": [
"Brck",
"Dash",
"Excl",
"Peri",
"Qest",
"Quot",
"Semi",
"Colo",
"Comm",
],
},
{
"feature": "reflex",
"tag": ["Yes"],
},
{
"feature": "tense",
"tag": ["Pres", "Past", "Fut", "Imp", "Pqp", "Aor", "Eps", "Prosp"],
},
{
"feature": "verbform",
"tag": [
"Fin",
"Inf",
"Sup",
"Part",
"Conv",
"Vnoun",
"Ger",
"Adn",
"Lng",
],
},
{
"feature": "verbtype",
"tag": ["Mod", "Caus"],
},
{
"feature": "voice",
"tag": [
"Act",
"Mid",
"Pass",
"Antip",
"Cau",
"Dir",
"Inv",
"Rcp",
"Caus",
],
},
{
"feature": "x",
"tag": ["X"],
}
],
});

View file

@ -0,0 +1,234 @@
import 'package:fluffychat/pangea/morphs/morph_models.dart';
final MorphFeatuuresAndTags defaultUDMapping = MorphFeatuuresAndTags.fromJson({
"language_code": "default",
"features": [
{
"feature": "pos",
"tag": [
"ADJ",
"ADP",
"ADV",
"AFFIX",
"AUX",
"CCONJ",
"DET",
"INTJ",
"NOUN",
"NUM",
"PART",
"PRON",
"PROPN",
"PUNCT",
"SCONJ",
"SPACE",
"SYM",
"VERB",
"X",
],
},
{
"feature": "advtype",
"tag": ["Adverbial", "Tim"],
},
{
"feature": "aspect",
"tag": ["Imp", "Perf", "Prog", "Hab"],
},
{
"feature": "case",
"tag": [
"Nom",
"Acc",
"Dat",
"Gen",
"Voc",
"Abl",
"Loc",
"All",
"Ins",
"Ess",
"Tra",
"Com",
"Par",
"Adv",
"Ref",
"Rel",
"Equ",
"Dis",
"Abs",
"Erg",
"Cau",
"Ben",
"Sub",
"Sup",
"Tem",
"Obl",
"Acc,Dat",
"Acc,Nom",
"Pre",
],
},
{
"feature": "conjtype",
"tag": ["Coord", "Sub", "Cmp"],
},
{
"feature": "definite",
"tag": ["Def", "Ind", "Cons"],
},
{
"feature": "degree",
"tag": ["Pos", "Cmp", "Sup", "Abs"],
},
{
"feature": "evident",
"tag": ["Fh", "Nfh"],
},
{
"feature": "foreign",
"tag": ["Yes"],
},
{
"feature": "gender",
"tag": ["Masc", "Fem", "Neut", "Com"],
},
{
"feature": "mood",
"tag": [
"Ind",
"Imp",
"Sub",
"Cnd",
"Opt",
"Jus",
"Adm",
"Des",
"Nec",
"Pot",
"Prp",
"Qot",
"Int",
],
},
{
"feature": "nountype",
"tag": ["Prop", "Comm", "Not_proper"],
},
{
"feature": "numform",
"tag": ["Digit", "Word", "Roman", "Letter"],
},
{
"feature": "numtype",
"tag": ["Card", "Ord", "Mult", "Frac", "Sets", "Range", "Dist"],
},
{
"feature": "number",
"tag": ["Sing", "Plur", "Dual", "Tri", "Pauc", "Grpa", "Grpl", "Inv"],
},
{
"feature": "number[psor]",
"tag": ["Sing", "Plur", "Dual"],
},
{
"feature": "person",
"tag": ["0", "1", "2", "3", "4"],
},
{
"feature": "polarity",
"tag": ["Pos", "Neg"],
},
{
"feature": "polite",
"tag": ["Infm", "Form", "Elev", "Humb"],
},
{
"feature": "poss",
"tag": ["Yes"],
},
{
"feature": "prepcase",
"tag": ["Npr"],
},
{
"feature": "prontype",
"tag": [
"Prs",
"Int",
"Rel",
"Dem",
"Tot",
"Neg",
"Art",
"Emp",
"Exc",
"Ind",
"Rcp",
"Int,Rel",
],
},
{
"feature": "punctside",
"tag": ["Ini", "Fin"],
},
{
"feature": "puncttype",
"tag": [
"Brck",
"Dash",
"Excl",
"Peri",
"Qest",
"Quot",
"Semi",
"Colo",
"Comm",
],
},
{
"feature": "reflex",
"tag": ["Yes"],
},
{
"feature": "tense",
"tag": ["Pres", "Past", "Fut", "Imp", "Pqp", "Aor", "Eps", "Prosp"],
},
{
"feature": "verbform",
"tag": [
"Fin",
"Inf",
"Sup",
"Part",
"Conv",
"Vnoun",
"Ger",
"Adn",
"Lng",
],
},
{
"feature": "verbtype",
"tag": ["Mod", "Caus"],
},
{
"feature": "voice",
"tag": [
"Act",
"Mid",
"Pass",
"Antip",
"Cau",
"Dir",
"Inv",
"Rcp",
"Caus",
],
},
{
"feature": "x",
"tag": ["X"],
}
],
});

View file

@ -0,0 +1,57 @@
import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
// TODO Use the icons that Khue is creating
IconData getIconForMorphFeature(String feature) {
// Define a function to get the icon based on the universal dependency morphological feature (key)
switch (feature.toLowerCase()) {
case 'number':
// google material 123 icon
return Icons.format_list_numbered;
case 'gender':
return Icons.wc;
case 'tense':
return Icons.access_time;
case 'mood':
return Icons.mood;
case 'person':
return Icons.person;
case 'case':
return Icons.format_list_bulleted;
case 'degree':
return Icons.trending_up;
case 'verbform':
return Icons.text_format;
case 'voice':
return Icons.record_voice_over;
case 'aspect':
return Icons.aspect_ratio;
case 'prontype':
return Icons.text_fields;
case 'numtype':
return Icons.format_list_numbered;
case 'poss':
return Icons.account_balance;
case 'reflex':
return Icons.refresh;
case 'foreign':
return Icons.language;
case 'abbr':
return Icons.text_format;
case 'nountype':
return Symbols.abc;
case 'pos':
return Symbols.toys_and_games;
case 'polarity':
return Icons.swap_vert;
case 'definite':
return Icons.check_circle_outline;
case 'prepcase':
return Icons.location_on_outlined;
case 'conjtype':
return Icons.compare_arrows;
default:
return Icons.help_outline;
}
}

View file

@ -0,0 +1,232 @@
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_identifier.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_level_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
import 'package:fluffychat/pangea/analytics_summary/progress_indicators_enum.dart';
import 'package:fluffychat/pangea/common/widgets/full_width_dialog.dart';
import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart';
import 'package:fluffychat/pangea/morphs/morph_icon.dart';
import 'package:fluffychat/pangea/morphs/morph_models.dart';
import 'package:fluffychat/widgets/matrix.dart';
import '../../morphs/morph_repo.dart';
class MorphAnalyticsPopup extends StatelessWidget {
const MorphAnalyticsPopup({
super.key,
});
List<MorphFeature> get availableFeatures => MorphsRepo.get().displayFeatures;
@override
Widget build(BuildContext context) => FullWidthDialog(
dialogContent: Scaffold(
appBar: AppBar(
title: Text(ConstructTypeEnum.morph.indicator.tooltip(context)),
leading: IconButton(
icon: const Icon(Icons.close),
onPressed: Navigator.of(context).pop,
),
),
body: Padding(
padding: const EdgeInsets.symmetric(vertical: 20),
child: ListView.builder(
itemCount: availableFeatures.length,
itemBuilder: (context, index) =>
availableFeatures[index].displayTags.isNotEmpty
? MorphFeatureBox(
morphFeature: availableFeatures[index].feature,
)
: const SizedBox.shrink(),
),
),
),
maxWidth: 600,
maxHeight: 800,
);
}
class MorphFeatureBox extends StatelessWidget {
final String morphFeature;
const MorphFeatureBox({
super.key,
required this.morphFeature,
});
// get constructData => MatrixState.pangeaController.
String _categoryCopy(
String category,
BuildContext context,
) {
if (category.toLowerCase() == "other") {
return L10n.of(context).other;
}
return ConstructTypeEnum.morph.getDisplayCopy(
category,
context,
) ??
category;
}
Set<String> get allTags => MorphsRepo.get()
.getDisplayTags(morphFeature)
.map((tag) => tag.toLowerCase())
.toSet();
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Container(
padding: const EdgeInsets.all(16.0),
margin: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.0),
border: Border.all(color: AppConfig.gold.withAlpha(100), width: 2),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 16.0,
children: [
SizedBox(
height: 30.0,
width: 30.0,
child: MorphIcon(morphFeature: morphFeature, morphTag: null),
),
Text(
_categoryCopy(morphFeature, context),
style: theme.textTheme.bodyLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 16.0),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Flexible(
child: Wrap(
alignment: WrapAlignment.center,
spacing: 16.0,
runSpacing: 16.0,
children: allTags
.map(
(morphTag) => MorphTagChip(
morphFeature: morphFeature,
morphTag: morphTag,
constructAnalytics: MatrixState.pangeaController
.getAnalytics.constructListModel
.getConstructUses(
ConstructIdentifier(
lemma: morphTag,
type: ConstructTypeEnum.morph,
category: morphFeature,
),
) ??
ConstructUses(
lemma: morphTag,
constructType: ConstructTypeEnum.morph,
category: morphFeature,
uses: [],
),
),
)
.sortedBy<num>((chip) => chip.constructAnalytics.points)
.reversed
.toList(),
),
),
],
),
],
),
);
}
}
class MorphTagChip extends StatelessWidget {
final String morphFeature;
final String morphTag;
final ConstructUses constructAnalytics;
const MorphTagChip({
super.key,
required this.morphFeature,
required this.morphTag,
required this.constructAnalytics,
});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Opacity(
opacity: constructAnalytics.points > 0 ? 1.0 : 0.3,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(32.0),
gradient: constructAnalytics.points > 0
? LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: <Color>[
Colors.transparent,
constructAnalytics.lemmaCategory.color,
],
)
: null,
color: constructAnalytics.points > 0 ? null : theme.disabledColor,
),
padding: const EdgeInsets.symmetric(
vertical: 4.0,
horizontal: 8.0,
),
child: Row(
mainAxisSize: MainAxisSize.min,
spacing: 8.0,
children: [
SizedBox(
width: 28.0,
height: 28.0,
child: constructAnalytics.points > 0
? MorphIcon(
morphFeature: morphFeature,
morphTag: morphTag,
)
: const Icon(
Icons.lock,
color: Colors.white,
),
),
Text(
getGrammarCopy(
category: morphFeature,
lemma: morphTag,
context: context,
) ??
morphTag,
style: TextStyle(
fontWeight: FontWeight.bold,
color: theme.brightness == Brightness.dark
? Colors.white
: Colors.black,
),
),
],
),
),
);
}
}

View file

@ -0,0 +1,37 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/pangea/common/widgets/customized_svg.dart';
import 'package:fluffychat/pangea/morphs/get_icon_for_morph_feature.dart';
import 'package:fluffychat/pangea/morphs/get_svg_link.dart';
import 'package:fluffychat/utils/color_value.dart';
class MorphIcon extends StatelessWidget {
const MorphIcon({
super.key,
required this.morphFeature,
required this.morphTag,
});
final String morphFeature;
final String? morphTag;
@override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
return CustomizedSvg(
svgUrl: getMorphSvgLink(
morphFeature: morphFeature,
morphTag: morphTag,
context: context,
),
colorReplacements: theme.brightness == Brightness.dark
? {
"white": theme.cardColor.hexValue.toString(),
"black": "white",
}
: {},
errorIcon: Icon(getIconForMorphFeature(morphFeature)),
);
}
}

View file

@ -0,0 +1,95 @@
import 'package:collection/collection.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
class MorphFeature {
final String feature;
final List<String> tags;
MorphFeature({required this.feature, required this.tags});
factory MorphFeature.fromJson(Map<String, dynamic> json) {
return MorphFeature(
feature: json['feature'],
tags: List<String>.from(json['tag']),
);
}
List<String> get displayTags => tags
.where(
(t) =>
!["punct", "space", "sym", "x", "other"].contains(t.toLowerCase()),
)
.toList();
Map<String, dynamic> toJson() {
return {
'feature': feature,
'tag': tags,
};
}
}
class MorphFeatuuresAndTags {
final String languageCode;
final List<MorphFeature> features;
MorphFeatuuresAndTags({required this.languageCode, required this.features});
factory MorphFeatuuresAndTags.fromJson(Map<String, dynamic> json) {
return MorphFeatuuresAndTags(
languageCode: json['language_code'],
features: List<MorphFeature>.from(
json['features'].map((x) => MorphFeature.fromJson(x)),
),
);
}
Map<String, dynamic> toJson() {
return {
'language_code': languageCode,
'features': features.map((x) => x.toJson()).toList(),
};
}
/// Returns the tags for a given feature
List<String> getAllTags(String feature) =>
features
.firstWhereOrNull((element) => element.feature == feature)
?.tags ??
[];
/// Returns the display tags for a given feature
/// i.e. minus punc, space, x, etc
List<String> getDisplayTags(String feature) =>
features
.firstWhereOrNull((element) => element.feature == feature)
?.displayTags ??
[];
List<MorphFeature> get displayFeatures => features
.where(
(f) => f.feature.toLowerCase() != "foreign",
)
.toList();
List<String> get categories => features.map((e) => e.feature).toList();
String guessMorphCategory(String morphLemma) {
for (final MorphFeature feature in features) {
if (feature.tags.contains(morphLemma)) {
// debugPrint(
// "found missing construct category for $morphLemma: $category",
// );
return feature.feature;
}
}
ErrorHandler.logError(
m: "Morph construct lemma $morphLemma not found in morph categories and labels",
data: {
"morphLemma": morphLemma,
},
);
return "Other";
}
}

View file

@ -0,0 +1,110 @@
import 'dart:convert';
import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:get_storage/get_storage.dart';
import 'package:http/http.dart';
import 'package:fluffychat/pangea/common/config/environment.dart';
import 'package:fluffychat/pangea/common/network/urls.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/morphs/default_morph_mapping.dart';
import 'package:fluffychat/pangea/morphs/morph_models.dart';
import 'package:fluffychat/widgets/matrix.dart';
import '../common/network/requests.dart';
class _APICallCacheItem {
final DateTime time;
final Future<MorphFeatuuresAndTags> future;
_APICallCacheItem(this.time, this.future);
}
class MorphsRepo {
// long-term storage of morphs
static final GetStorage _morphsStorage = GetStorage('morphs_storage');
// to avoid multiple fetches for the same language code
// by different parts of the app within a short time
static final shortTermCache = <String, _APICallCacheItem>{};
static const int _cacheDurationMinutes = 1;
static void set(String languageCode, MorphFeatuuresAndTags response) {
_morphsStorage.write(
languageCode,
response.toJson(),
);
}
static MorphFeatuuresAndTags fromJson(Map<String, dynamic> json) {
return MorphFeatuuresAndTags.fromJson(json);
}
static Future<MorphFeatuuresAndTags> _fetch(String languageCode) async {
try {
final Requests req = Requests(
choreoApiKey: Environment.choreoApiKey,
accessToken: MatrixState.pangeaController.userController.accessToken,
);
final Response res = await req.get(
url: '${PApiUrls.morphFeaturesAndTags}/$languageCode',
);
final decodedBody = jsonDecode(utf8.decode(res.bodyBytes));
final response = MorphsRepo.fromJson(decodedBody);
set(languageCode, response);
return response;
} catch (e, s) {
debugger(when: kDebugMode);
ErrorHandler.logError(
e: e,
s: s,
data: {
"languageCode": languageCode,
},
);
return defaultMorphMapping;
}
}
/// this function fetches the morphs for a given language code
/// while remaining synchronous by using a default value
/// if the morphs are not yet fetched. we'll see if this works well
/// if not, we can make it async and update uses of this function
/// to be async as well
static MorphFeatuuresAndTags get([String? languageCode]) {
languageCode ??=
MatrixState.pangeaController.languageController.userL2?.langCode;
if (languageCode == null) {
return defaultMorphMapping;
}
// check if we have a cached morphs for this language code
final cachedJson = _morphsStorage.read(languageCode);
if (cachedJson != null) {
return MorphsRepo.fromJson(cachedJson);
}
// check if we have a cached call for this language code
final _APICallCacheItem? cachedCall = shortTermCache[languageCode];
if (cachedCall != null) {
if (DateTime.now().difference(cachedCall.time).inMinutes <
_cacheDurationMinutes) {
return defaultMorphMapping;
} else {
shortTermCache.remove(languageCode);
}
}
// fetch the morphs but don't wait for it
final future = _fetch(languageCode);
shortTermCache[languageCode] = _APICallCacheItem(DateTime.now(), future);
return defaultMorphMapping;
}
}

View file

@ -7,16 +7,16 @@ import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pangea/analytics/enums/analytics_summary_enum.dart';
import 'package:fluffychat/pangea/analytics/models/analytics_summary_model.dart';
import 'package:fluffychat/pangea/analytics/models/construct_list_model.dart';
import 'package:fluffychat/pangea/analytics/models/construct_use_model.dart';
import 'package:fluffychat/pangea/analytics/models/constructs_model.dart';
import 'package:fluffychat/pangea/analytics/utils/get_grammar_copy.dart';
import 'package:fluffychat/pangea/analytics_downloads/analytics_summary_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/analytics_summary_model.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_list_model.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/bot/utils/bot_name.dart';
import 'package:fluffychat/pangea/chat_settings/utils/download_file.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart';
import 'package:fluffychat/widgets/matrix.dart';
class DownloadAnalyticsDialog extends StatefulWidget {

View file

@ -2,9 +2,9 @@ import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics/models/construct_identifier.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_identifier.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
enum ActivityTypeEnum {
wordMeaning,

View file

@ -7,10 +7,10 @@ import 'package:collection/collection.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics/enums/morph_categories_enum.dart';
import 'package:fluffychat/pangea/analytics/models/construct_identifier.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_identifier.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/morphs/morph_categories_enum.dart';
import 'package:fluffychat/pangea/toolbar/enums/activity_display_instructions_enum.dart';
import 'package:fluffychat/pangea/toolbar/enums/activity_type_enum.dart';
import 'package:fluffychat/pangea/toolbar/models/multiple_choice_activity_model.dart';

View file

@ -7,9 +7,9 @@ import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics/models/constructs_model.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/toolbar/enums/activity_type_enum.dart';
import 'package:fluffychat/pangea/toolbar/models/practice_activity_model.dart';

View file

@ -5,7 +5,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/toolbar/enums/activity_type_enum.dart';
import 'package:fluffychat/pangea/toolbar/models/message_activity_request.dart';

View file

@ -2,12 +2,12 @@ import 'dart:async';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics/models/construct_identifier.dart';
import 'package:fluffychat/pangea/analytics/models/construct_use_model.dart';
import 'package:fluffychat/pangea/analytics/repo/lemma_info_repo.dart';
import 'package:fluffychat/pangea/analytics/repo/lemma_info_request.dart';
import 'package:fluffychat/pangea/analytics/repo/lemma_info_response.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_identifier.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
import 'package:fluffychat/pangea/lemmas/lemma_info_repo.dart';
import 'package:fluffychat/pangea/lemmas/lemma_info_request.dart';
import 'package:fluffychat/pangea/lemmas/lemma_info_response.dart';
import 'package:fluffychat/pangea/toolbar/enums/activity_type_enum.dart';
import 'package:fluffychat/pangea/toolbar/models/message_activity_request.dart';
import 'package:fluffychat/pangea/toolbar/models/multiple_choice_activity_model.dart';

View file

@ -2,11 +2,11 @@ import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics/enums/morph_categories_enum.dart';
import 'package:fluffychat/pangea/analytics/models/construct_identifier.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_identifier.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/morphs/morph_categories_enum.dart';
import 'package:fluffychat/pangea/toolbar/enums/activity_type_enum.dart';
import 'package:fluffychat/pangea/toolbar/models/message_activity_request.dart';
import 'package:fluffychat/pangea/toolbar/models/multiple_choice_activity_model.dart';

View file

@ -7,7 +7,7 @@ import 'package:collection/collection.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pangea/analytics/controllers/message_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics_misc/message_analytics_controller.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';

View file

@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/analytics/controllers/message_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics_misc/message_analytics_controller.dart';
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/events/utils/message_text_util.dart';

View file

@ -6,10 +6,10 @@ import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/analytics/controllers/put_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics/utils/get_grammar_copy.dart';
import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.dart';
import 'package:fluffychat/pangea/choreographer/widgets/choice_array.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart';
import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart';
import 'package:fluffychat/pangea/toolbar/enums/activity_type_enum.dart';
import 'package:fluffychat/pangea/toolbar/models/practice_activity_model.dart';

View file

@ -6,11 +6,11 @@ import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:fluffychat/pangea/analytics/controllers/message_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics/controllers/put_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics/enums/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics/models/constructs_model.dart';
import 'package:fluffychat/pangea/analytics/widgets/gain_points.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/analytics_misc/gain_points_animation.dart';
import 'package:fluffychat/pangea/analytics_misc/message_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.dart';
import 'package:fluffychat/pangea/choreographer/widgets/igc/card_error_widget.dart';
import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';

View file

@ -5,12 +5,12 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/pangea/analytics/repo/lemma_info_repo.dart';
import 'package:fluffychat/pangea/analytics/repo/lemma_info_request.dart';
import 'package:fluffychat/pangea/analytics/repo/lemma_info_response.dart';
import 'package:fluffychat/pangea/analytics/widgets/text_loading_shimmer.dart';
import 'package:fluffychat/pangea/analytics_misc/text_loading_shimmer.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart';
import 'package:fluffychat/pangea/lemmas/lemma_info_repo.dart';
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 LemmaMeaningWidget extends StatefulWidget {

View file

@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/pangea/analytics/enums/lemma_category_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_level_enum.dart';
import 'package:fluffychat/pangea/common/widgets/customized_svg.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';

View file

@ -5,15 +5,15 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/pangea/analytics/constants/morph_categories_and_labels.dart';
import 'package:fluffychat/pangea/analytics/enums/morph_categories_enum.dart';
import 'package:fluffychat/pangea/analytics/utils/get_grammar_copy.dart';
import 'package:fluffychat/pangea/common/constants/model_keys.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/events/models/tokens_event_content_model.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart';
import 'package:fluffychat/pangea/morphs/morph_categories_enum.dart';
import 'package:fluffychat/pangea/morphs/morph_repo.dart';
import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
@ -140,12 +140,7 @@ class MorphologicalCenterWidgetState extends State<MorphologicalCenterWidget> {
/// all morphological tags for the selected morphological category
/// that are eligible for setting as the morphological tag
List<String> get allMorphTagsForEdit =>
getLabelsForMorphCategory(widget.morphFeature)
.where(
(tag) => !["punct", "space", "sym", "x", "other"]
.contains(tag.toLowerCase()),
)
.toList();
MorphsRepo.get().getDisplayTags(widget.morphFeature);
String get morphCopy =>
getMorphologicalCategoryCopy(widget.morphFeature, context) ??

View file

@ -1,17 +1,14 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/pangea/analytics/enums/morph_categories_enum.dart';
import 'package:fluffychat/pangea/analytics/utils/get_grammar_copy.dart';
import 'package:fluffychat/pangea/common/widgets/customized_svg.dart';
import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart';
import 'package:fluffychat/pangea/morphs/morph_categories_enum.dart';
import 'package:fluffychat/pangea/morphs/morph_icon.dart';
import 'package:fluffychat/pangea/toolbar/widgets/practice_activity/word_zoom_activity_button.dart';
import 'package:fluffychat/utils/color_value.dart';
class MorphologicalListItem extends StatelessWidget {
final Function(String) onPressed;
final String morphFeature;
final String morphTag;
final IconData icon;
final String? svgLink;
final bool isUnlocked;
final bool isSelected;
@ -20,8 +17,6 @@ class MorphologicalListItem extends StatelessWidget {
required this.onPressed,
required this.morphFeature,
required this.morphTag,
required this.icon,
this.svgLink,
this.isUnlocked = true,
this.isSelected = false,
super.key,
@ -33,20 +28,7 @@ class MorphologicalListItem extends StatelessWidget {
width: 40,
height: 40,
child: WordZoomActivityButton(
icon: svgLink != null
? CustomizedSvg(
svgUrl: svgLink!,
colorReplacements:
Theme.of(context).brightness == Brightness.dark
? {
"white":
Theme.of(context).cardColor.hexValue.toString(),
"black": "white",
}
: {},
errorIcon: Icon(icon),
)
: Icon(icon),
icon: MorphIcon(morphFeature: morphFeature, morphTag: morphTag),
isSelected: isSelected,
onPressed: () => onPressed(morphFeature),
tooltip: isUnlocked

View file

@ -1,7 +1,5 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/pangea/analytics/constants/morph_categories_and_labels.dart';
import 'package:fluffychat/pangea/analytics/utils/get_svg_link.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/toolbar/widgets/word_zoom/morphs/morphological_list_item.dart';
@ -76,12 +74,6 @@ class MorphologicalListWidget extends StatelessWidget {
onPressed: setMorphFeature,
morphFeature: morph.morphFeature,
morphTag: morph.morphTag,
icon: getIconForMorphFeature(morph.morphFeature),
svgLink: getMorphSvgLink(
morphFeature: morph.morphFeature,
morphTag: morph.revealed ? morph.morphTag : null,
context: context,
),
isUnlocked: morph.revealed,
isSelected: selectedMorphFeature == morph.morphFeature,
),

View file

@ -3,7 +3,7 @@ import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:fluffychat/pangea/analytics/controllers/message_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics_misc/message_analytics_controller.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart';
import 'package:fluffychat/pangea/toolbar/widgets/practice_activity/practice_activity_card.dart';