added level bar popup with XP, level, and recent construct use info (#1217)

pull/1544/head
ggurdin 11 months ago committed by GitHub
parent c845c971d6
commit dee409e41d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -4595,5 +4595,32 @@
"mandatoryUpdateRequiredDesc": "A new version of the app is required to continue. Please update now to proceed.",
"updateAvailableDesc": "A new version of the app is available. Update now for the latest features and improvements!",
"updateNow": "Update Now",
"updateLater": "Later"
"updateLater": "Later",
"constructUseWaDesc": "Used without help",
"constructUseGaDesc": "Grammar mistake",
"constructUseUnkDesc": "Unknown",
"constructUseCorITDesc": "Correct in translation",
"constructUseIgnITDesc": "Ignored in translation",
"constructUseIncITDesc": "Incorrect in translation",
"constructUseIgnIGCDesc": "Ignored in grammar correction",
"constructUseCorIGCDesc": "Correct in grammar correction",
"constructUseIncIGCDesc": "Incorrect in grammar correction",
"constructUseCorPADesc": "Correct in word meaning activity",
"constructUseIgnPADesc": "Ignored in word meaning activity",
"constructUseIncPADesc": "Incorrect in word meaning activity",
"constructUseCorWLDesc": "Correct in word listening activity",
"constructUseIncWLDesc": "Incorrect in word listening activity",
"constructUseIngWLDesc": "Ignored in word listening activity",
"constructUseCorHWLDesc": "Correct in hidden word activity",
"constructUseIncHWLDesc": "Incorrect in hidden word activity",
"constructUseIgnHWLDesc": "Ignored in hidden word activity",
"constructUseNanDesc": "Not applicable",
"xpIntoLevel": "{currentXP} / {maxXP} XP",
"@xpIntoLevel": {
"type": "text",
"placeholders": {
"currentXP": {},
"maxXP": {}
}
}
}

@ -50,6 +50,8 @@ class GetAnalyticsController {
return _calculateMinXpForLevel(constructListModel.level + 1);
}
int get minXPForNextLevel => _minXPForNextLevel;
/// Calculates the minimum XP required for a specific level.
int _calculateMinXpForLevel(int level) {
if (level == 1) return 0; // Ensure level 1 starts at 0 XP

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
enum ConstructUseTypeEnum {
/// produced in chat by user, igc was run, and we've judged it to be a correct use
@ -64,6 +65,49 @@ enum ConstructUseTypeEnum {
extension ConstructUseTypeExtension on ConstructUseTypeEnum {
String get string => toString().split('.').last;
String description(BuildContext context) {
switch (this) {
case ConstructUseTypeEnum.wa:
return L10n.of(context).constructUseWaDesc;
case ConstructUseTypeEnum.ga:
return L10n.of(context).constructUseGaDesc;
case ConstructUseTypeEnum.unk:
return L10n.of(context).constructUseUnkDesc;
case ConstructUseTypeEnum.corIt:
return L10n.of(context).constructUseCorITDesc;
case ConstructUseTypeEnum.ignIt:
return L10n.of(context).constructUseIgnITDesc;
case ConstructUseTypeEnum.incIt:
return L10n.of(context).constructUseIncITDesc;
case ConstructUseTypeEnum.ignIGC:
return L10n.of(context).constructUseIgnIGCDesc;
case ConstructUseTypeEnum.corIGC:
return L10n.of(context).constructUseCorIGCDesc;
case ConstructUseTypeEnum.incIGC:
return L10n.of(context).constructUseIncIGCDesc;
case ConstructUseTypeEnum.corPA:
return L10n.of(context).constructUseCorPADesc;
case ConstructUseTypeEnum.ignPA:
return L10n.of(context).constructUseIgnPADesc;
case ConstructUseTypeEnum.incPA:
return L10n.of(context).constructUseIncPADesc;
case ConstructUseTypeEnum.corWL:
return L10n.of(context).constructUseCorWLDesc;
case ConstructUseTypeEnum.incWL:
return L10n.of(context).constructUseIncWLDesc;
case ConstructUseTypeEnum.ignWL:
return L10n.of(context).constructUseIngWLDesc;
case ConstructUseTypeEnum.corHWL:
return L10n.of(context).constructUseCorHWLDesc;
case ConstructUseTypeEnum.incHWL:
return L10n.of(context).constructUseIncHWLDesc;
case ConstructUseTypeEnum.ignHWL:
return L10n.of(context).constructUseIgnHWLDesc;
case ConstructUseTypeEnum.nan:
return L10n.of(context).constructUseNanDesc;
}
}
IconData get icon {
switch (this) {
case ConstructUseTypeEnum.wa:

@ -19,8 +19,12 @@ class ConstructListModel {
level = 0;
vocabLemmas = 0;
grammarLemmas = 0;
_uses.clear();
}
final List<OneConstructUse> _uses = [];
List<OneConstructUse> get uses => _uses;
/// A map of lemmas to ConstructUses, each of which contains a lemma
/// key = lemmma + constructType.string, value = ConstructUses
Map<String, ConstructUses> _constructMap = {};
@ -49,6 +53,7 @@ class ConstructListModel {
/// IDs to ConstructUses and re-sort the list of ConstructUses
void updateConstructs(List<OneConstructUse> newUses) {
try {
_updateUsesList(newUses);
_updateConstructMap(newUses);
_updateConstructList();
_updateCategoriesToUses();
@ -64,6 +69,11 @@ class ConstructListModel {
return a.lemma.compareTo(b.lemma);
}
void _updateUsesList(List<OneConstructUse> newUses) {
newUses.sort((a, b) => a.timeStamp.compareTo(b.timeStamp));
_uses.addAll(newUses);
}
/// A map of lemmas to ConstructUses, each of which contains a lemma
/// key = lemmma + constructType.string, value = ConstructUses
void _updateConstructMap(final List<OneConstructUse> newUses) {

@ -101,9 +101,10 @@ class AnimatedLevelBarState extends State<AnimatedLevelBar>
),
Positioned(
top: 2,
left: 8,
child: Container(
height: 6,
width: _animation.value >= 8 ? _animation.value - 8 : 0,
width: _animation.value >= 16 ? _animation.value - 16 : 0,
decoration: BoxDecoration(
color: widget.highlightColor,
borderRadius: const BorderRadius.all(

@ -8,10 +8,12 @@ import 'package:flutter/material.dart';
class ProgressBar extends StatefulWidget {
final List<LevelBarDetails> levelBars;
final double? height;
const ProgressBar({
super.key,
required this.levelBars,
this.height,
});
@override
@ -29,6 +31,7 @@ class ProgressBarState extends State<ProgressBar> {
get progressBarDetails => ProgressBarDetails(
totalWidth: width,
borderColor: Theme.of(context).colorScheme.primary.withOpacity(0.5),
height: widget.height ?? 14,
);
@override

@ -4,15 +4,21 @@ import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
class LearningProgressBar extends StatelessWidget {
final int level;
final int totalXP;
final double? height;
const LearningProgressBar({
required this.level,
required this.totalXP,
this.height,
super.key,
});
@override
Widget build(BuildContext context) {
return ProgressBar(
height: height,
levelBars: [
LevelBarDetails(
fillColor: Theme.of(context).colorScheme.primary,

@ -9,6 +9,7 @@ import 'package:fluffychat/pangea/widgets/chat_list/analytics_summary/analytics_
import 'package:fluffychat/pangea/widgets/chat_list/analytics_summary/learning_progress_bar.dart';
import 'package:fluffychat/pangea/widgets/chat_list/analytics_summary/learning_settings_button.dart';
import 'package:fluffychat/pangea/widgets/chat_list/analytics_summary/level_badge.dart';
import 'package:fluffychat/pangea/widgets/chat_list/analytics_summary/level_bar_popup.dart';
import 'package:fluffychat/pangea/widgets/chat_list/analytics_summary/progress_indicator.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
@ -131,23 +132,35 @@ class LearningProgressIndicatorsState
],
),
const SizedBox(height: 6),
SizedBox(
height: 26,
child: Stack(
alignment: Alignment.center,
children: [
Positioned(
left: 16,
right: 0,
child: LearningProgressBar(
totalXP: _constructsModel.totalXP,
),
),
Positioned(
left: 0,
child: LevelBadge(level: _constructsModel.level),
MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () {
showDialog<LevelBarPopup>(
context: context,
builder: (c) => const LevelBarPopup(),
);
},
child: SizedBox(
height: 26,
child: Stack(
alignment: Alignment.center,
children: [
Positioned(
left: 16,
right: 0,
child: LearningProgressBar(
level: _constructsModel.level,
totalXP: _constructsModel.totalXP,
),
),
Positioned(
left: 0,
child: LevelBadge(level: _constructsModel.level),
),
],
),
],
),
),
),
],

@ -0,0 +1,162 @@
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/controllers/get_analytics_controller.dart';
import 'package:fluffychat/pangea/enum/construct_type_enum.dart';
import 'package:fluffychat/pangea/enum/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/models/analytics/constructs_model.dart';
import 'package:fluffychat/pangea/utils/grammar/get_grammar_copy.dart';
import 'package:fluffychat/pangea/widgets/chat_list/analytics_summary/learning_progress_bar.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
class LevelBarPopup extends StatelessWidget {
const LevelBarPopup({
super.key,
});
GetAnalyticsController get getAnalyticsController =>
MatrixState.pangeaController.getAnalytics;
int get level => getAnalyticsController.constructListModel.level;
int get totalXP => getAnalyticsController.constructListModel.totalXP;
int get maxLevelXP => getAnalyticsController.minXPForNextLevel;
List<OneConstructUse> get uses =>
getAnalyticsController.constructListModel.uses;
@override
Widget build(BuildContext context) {
return Dialog(
child: ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 400,
maxHeight: 600,
),
child: ClipRRect(
borderRadius: BorderRadius.circular(20.0),
child: Scaffold(
appBar: AppBar(
titleSpacing: 0,
automaticallyImplyLeading: false,
title: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
const CircleAvatar(
radius: 20,
backgroundColor: AppConfig.gold,
child: Icon(
size: 30,
Icons.star,
color: Colors.white,
),
),
const SizedBox(width: 10),
Text(
L10n.of(context).levelShort(level),
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.w900,
color: AppConfig.gold,
),
),
],
),
Opacity(
opacity: 0.25,
child: Text(
L10n.of(context).levelShort(level + 1),
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.w900,
),
),
),
],
),
),
),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LearningProgressBar(
height: 24,
level: level,
totalXP: totalXP,
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 6,
),
child: Text(
L10n.of(context).xpIntoLevel(totalXP, maxLevelXP),
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w900,
color: AppConfig.gold,
),
),
),
const Divider(),
],
),
),
Expanded(
child: ListView.builder(
itemCount: uses.length,
itemBuilder: (context, index) {
final use = uses[(uses.length - 1) - index];
String lemmaCopy = use.lemma;
if (use.constructType == ConstructTypeEnum.morph) {
lemmaCopy = getGrammarCopy(
category: use.category,
lemma: use.lemma,
context: context,
) ??
use.lemma;
}
return ListTile(
leading: Icon(use.useType.icon),
title: Text(
"\"$lemmaCopy\" - ${use.useType.description(context)}",
style: const TextStyle(fontSize: 14),
),
trailing: Container(
alignment: Alignment.centerRight,
width: 60,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"${use.pointValue > 0 ? '+' : ''}${use.pointValue}",
),
const SizedBox(width: 5),
const CircleAvatar(
radius: 10,
child: Icon(
size: 12,
Icons.star,
color: Colors.white,
),
),
],
),
),
);
},
),
),
],
),
),
),
),
);
}
}
Loading…
Cancel
Save