add categories to analytics display
This commit is contained in:
parent
6b643a841a
commit
da6d64972b
3 changed files with 138 additions and 44 deletions
|
|
@ -23,8 +23,8 @@ class ConstructListModel {
|
|||
List<OneConstructUse> get uses =>
|
||||
_uses.where((use) => use.constructType == type || type == null).toList();
|
||||
|
||||
/// All unique lemmas used in the construct events
|
||||
List<String> get lemmas => constructList.map((e) => e.lemma).toSet().toList();
|
||||
// /// All unique lemmas used in the construct events
|
||||
// List<String> get lemmas => constructList.map((e) => e.lemma).toSet().toList();
|
||||
|
||||
/// All unique lemmas used in the construct events with non-zero points
|
||||
List<String> get lemmasWithPoints =>
|
||||
|
|
@ -36,8 +36,13 @@ class ConstructListModel {
|
|||
final Map<String, List<OneConstructUse>> lemmaToUses = {};
|
||||
for (final use in uses) {
|
||||
if (use.lemma == null) continue;
|
||||
lemmaToUses[use.lemma! + use.constructType.string] ??= [];
|
||||
lemmaToUses[use.lemma! + use.constructType.string]!.add(use);
|
||||
lemmaToUses[use.lemma! +
|
||||
use.constructType.string +
|
||||
(use.category ?? "Other")] ??= [];
|
||||
lemmaToUses[use.lemma! +
|
||||
use.constructType.string +
|
||||
(use.category ?? "Other")]!
|
||||
.add(use);
|
||||
}
|
||||
|
||||
_constructMap = lemmaToUses.map(
|
||||
|
|
@ -47,6 +52,7 @@ class ConstructListModel {
|
|||
uses: value,
|
||||
constructType: value.first.constructType,
|
||||
lemma: value.first.lemma!,
|
||||
category: value.first.category,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
@ -68,7 +74,7 @@ class ConstructListModel {
|
|||
_constructList = _constructMap!.values.toList();
|
||||
|
||||
_constructList!.sort((a, b) {
|
||||
final comp = b.uses.length.compareTo(a.uses.length);
|
||||
final comp = b.points.compareTo(a.points);
|
||||
if (comp != 0) return comp;
|
||||
return a.lemma.compareTo(b.lemma);
|
||||
});
|
||||
|
|
@ -79,6 +85,15 @@ class ConstructListModel {
|
|||
List<ConstructUses> get constructListWithPoints =>
|
||||
constructList.where((constructUse) => constructUse.points > 0).toList();
|
||||
|
||||
Map<String, List<ConstructUses>> get categoriesToUses {
|
||||
final Map<String, List<ConstructUses>> categoriesMap = {};
|
||||
for (final use in constructListWithPoints) {
|
||||
categoriesMap[use.category] ??= [];
|
||||
categoriesMap[use.category]!.add(use);
|
||||
}
|
||||
return categoriesMap;
|
||||
}
|
||||
|
||||
get maxXPPerLemma {
|
||||
return type != null
|
||||
? type!.maxXPPerLemma
|
||||
|
|
@ -141,12 +156,14 @@ class ConstructUses {
|
|||
final List<OneConstructUse> uses;
|
||||
final ConstructTypeEnum constructType;
|
||||
final String lemma;
|
||||
final String? _category;
|
||||
|
||||
ConstructUses({
|
||||
required this.uses,
|
||||
required this.constructType,
|
||||
required this.lemma,
|
||||
});
|
||||
required category,
|
||||
}) : _category = category;
|
||||
|
||||
// Total points for all uses of this lemma
|
||||
int get points {
|
||||
|
|
@ -165,6 +182,8 @@ class ConstructUses {
|
|||
});
|
||||
return _lastUsed = lastUse;
|
||||
}
|
||||
|
||||
String get category => _category ?? "Other";
|
||||
}
|
||||
|
||||
/// One lemma, a use type, and a list of uses
|
||||
|
|
|
|||
|
|
@ -74,7 +74,11 @@ class ConstructAnalyticsModel {
|
|||
class OneConstructUse {
|
||||
String? lemma;
|
||||
String? form;
|
||||
|
||||
/// For vocab constructs, this is the POS. For morph
|
||||
/// constructs, this is the morphological category.
|
||||
String? category;
|
||||
|
||||
ConstructTypeEnum constructType;
|
||||
ConstructUseTypeEnum useType;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,76 @@ class AnalyticsPopup extends StatelessWidget {
|
|||
super.key,
|
||||
});
|
||||
|
||||
List<MapEntry<String, List<ConstructUses>>> get categoriesToUses {
|
||||
final entries = constructsModel.categoriesToUses.entries.toList();
|
||||
// Sort the list with custom logic
|
||||
entries.sort((a, b) {
|
||||
// Check if one of the keys is 'Other'
|
||||
if (a.key == 'Other') return 1;
|
||||
if (b.key == '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) {
|
||||
Widget? dialogContent;
|
||||
final bool hasNoData = constructsModel.constructListWithPoints.isEmpty;
|
||||
final bool hasNoCategories = constructsModel.categoriesToUses.length == 1 &&
|
||||
constructsModel.categoriesToUses.keys.first == "Other";
|
||||
|
||||
if (hasNoData) {
|
||||
dialogContent = Center(child: Text(L10n.of(context)!.noDataFound));
|
||||
} else if (hasNoCategories) {
|
||||
dialogContent = ListView.builder(
|
||||
itemCount: constructsModel.constructListWithPoints.length,
|
||||
itemBuilder: (context, index) {
|
||||
return ConstructUsesXPTile(
|
||||
indicator: indicator,
|
||||
constructsModel: constructsModel,
|
||||
constructUses: constructsModel.constructListWithPoints[index],
|
||||
);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
dialogContent = ListView.builder(
|
||||
itemCount: categoriesToUses.length,
|
||||
itemBuilder: (context, index) {
|
||||
final category = categoriesToUses[index];
|
||||
return Column(
|
||||
children: [
|
||||
ExpansionTile(
|
||||
title: Text(
|
||||
category.key != 'Other'
|
||||
? getGrammarCopy(category.key, context)
|
||||
: category.key,
|
||||
),
|
||||
children: category.value.map((constructUses) {
|
||||
return ConstructUsesXPTile(
|
||||
indicator: indicator,
|
||||
constructsModel: constructsModel,
|
||||
constructUses: constructUses,
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
const Divider(height: 1),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return Dialog(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
|
|
@ -36,44 +104,7 @@ class AnalyticsPopup extends StatelessWidget {
|
|||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20),
|
||||
child: constructsModel.constructListWithPoints.isEmpty
|
||||
? Center(
|
||||
child: Text(L10n.of(context)!.noDataFound),
|
||||
)
|
||||
: ListView.builder(
|
||||
itemCount: constructsModel.constructListWithPoints.length,
|
||||
itemBuilder: (context, index) {
|
||||
return Tooltip(
|
||||
message:
|
||||
"${constructsModel.constructListWithPoints[index].points} / ${constructsModel.maxXPPerLemma}",
|
||||
child: ListTile(
|
||||
onTap: () {},
|
||||
title: Text(
|
||||
constructsModel.type == ConstructTypeEnum.morph
|
||||
? getGrammarCopy(
|
||||
constructsModel
|
||||
.constructListWithPoints[index].lemma,
|
||||
context,
|
||||
)
|
||||
: constructsModel
|
||||
.constructListWithPoints[index].lemma,
|
||||
),
|
||||
subtitle: LinearProgressIndicator(
|
||||
value: constructsModel
|
||||
.constructListWithPoints[index].points /
|
||||
constructsModel.maxXPPerLemma,
|
||||
minHeight: 20,
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(AppConfig.borderRadius),
|
||||
),
|
||||
color: indicator.color(context),
|
||||
),
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(horizontal: 20),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
child: dialogContent,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -81,3 +112,43 @@ class AnalyticsPopup extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ConstructUsesXPTile extends StatelessWidget {
|
||||
final ProgressIndicatorEnum indicator;
|
||||
final ConstructListModel constructsModel;
|
||||
final ConstructUses constructUses;
|
||||
|
||||
const ConstructUsesXPTile({
|
||||
required this.indicator,
|
||||
required this.constructsModel,
|
||||
required this.constructUses,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final lemma = constructUses.lemma;
|
||||
return Tooltip(
|
||||
message: "${constructUses.points} / ${constructsModel.maxXPPerLemma}",
|
||||
child: ListTile(
|
||||
onTap: () {},
|
||||
title: Text(
|
||||
constructsModel.type == ConstructTypeEnum.morph
|
||||
? getGrammarCopy(lemma, context)
|
||||
: lemma,
|
||||
),
|
||||
subtitle: LinearProgressIndicator(
|
||||
value: constructUses.points / constructsModel.maxXPPerLemma,
|
||||
minHeight: 20,
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(AppConfig.borderRadius),
|
||||
),
|
||||
color: indicator.color(context),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 20,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue