From 2dac558e1a8f3834d97a07cbb72cad9ebea6c2c7 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 7 Nov 2024 14:42:55 -0500 Subject: [PATCH] initial work on expanding popup to the side --- .../analytics_summary/analytics_popup.dart | 230 ------------------ .../analytics_popup/analytics_popup.dart | 136 +++++++++++ .../analytics_popup/analytics_xp_tile.dart | 61 +++++ .../learning_progress_indicators.dart | 2 +- 4 files changed, 198 insertions(+), 231 deletions(-) delete mode 100644 lib/pangea/widgets/chat_list/analytics_summary/analytics_popup.dart create mode 100644 lib/pangea/widgets/chat_list/analytics_summary/analytics_popup/analytics_popup.dart create mode 100644 lib/pangea/widgets/chat_list/analytics_summary/analytics_popup/analytics_xp_tile.dart diff --git a/lib/pangea/widgets/chat_list/analytics_summary/analytics_popup.dart b/lib/pangea/widgets/chat_list/analytics_summary/analytics_popup.dart deleted file mode 100644 index e0f9c8bb9..000000000 --- a/lib/pangea/widgets/chat_list/analytics_summary/analytics_popup.dart +++ /dev/null @@ -1,230 +0,0 @@ -import 'dart:math'; - -import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/pangea/enum/construct_type_enum.dart'; -import 'package:fluffychat/pangea/enum/progress_indicators_enum.dart'; -import 'package:fluffychat/pangea/models/analytics/construct_list_model.dart'; -import 'package:fluffychat/pangea/utils/get_grammar_copy.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/l10n.dart'; - -class AnalyticsPopup extends StatelessWidget { - final ProgressIndicatorEnum indicator; - final ConstructListModel constructsModel; - final bool showGroups; - - const AnalyticsPopup({ - required this.indicator, - required this.constructsModel, - this.showGroups = true, - super.key, - }); - - List>> 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( - 0, - (previousValue, element) => previousValue + element.points, - ); - final bTotalPoints = b.value.fold( - 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 || !showGroups) { - 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: [ - ConstructUsesExpansionTile( - indicator: indicator, - constructsModel: constructsModel, - category: category.key, - constructUses: category.value, - ), - const Divider(height: 1), - ], - ); - }, - ); - } - - return Dialog( - child: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 400, - maxHeight: 600, - ), - child: ClipRRect( - borderRadius: BorderRadius.circular(20.0), - child: Scaffold( - appBar: AppBar( - title: Text(indicator.tooltip(context)), - leading: IconButton( - icon: const Icon(Icons.close), - onPressed: Navigator.of(context).pop, - ), - ), - body: Padding( - padding: const EdgeInsets.symmetric(vertical: 20), - child: dialogContent, - ), - ), - ), - ), - ); - } -} - -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) { - return Tooltip( - message: "${constructUses.points} / ${constructsModel.maxXPPerLemma}", - child: ListTile( - onTap: () {}, - title: Text( - constructsModel.type == ConstructTypeEnum.morph - ? getGrammarCopy( - category: constructUses.category, - lemma: constructUses.lemma, - context: context, - ) ?? - constructUses.lemma - : constructUses.lemma, - ), - subtitle: Row( - children: [ - Expanded( - child: LinearProgressIndicator( - value: constructUses.points / constructsModel.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, - ), - ), - ); - } -} - -class ConstructUsesExpansionTile extends StatefulWidget { - final ProgressIndicatorEnum indicator; - final ConstructListModel constructsModel; - final String category; - final List constructUses; - - const ConstructUsesExpansionTile({ - required this.indicator, - required this.constructsModel, - required this.category, - required this.constructUses, - super.key, - }); - - @override - ConstructUsesExpansionTileState createState() => - ConstructUsesExpansionTileState(); -} - -class ConstructUsesExpansionTileState - extends State { - int _lastLoadedIndex = 50; - int get endIndex => min(_lastLoadedIndex, widget.constructUses.length); - - @override - Widget build(BuildContext context) { - final List xpTiles = widget.constructUses - .sublist(0, endIndex) - .map((constructUses) { - return ConstructUsesXPTile( - indicator: widget.indicator, - constructsModel: widget.constructsModel, - constructUses: constructUses, - ); - }) - .cast() - .toList(); - - if (widget.constructUses.length > _lastLoadedIndex) { - xpTiles.add( - Padding( - padding: const EdgeInsets.all(10), - child: TextButton( - child: Text(L10n.of(context)!.loadMore), - onPressed: () => setState(() => _lastLoadedIndex += 50), - ), - ), - ); - } - - return ExpansionTile( - title: Text( - widget.constructsModel.type?.getDisplayCopy( - widget.category, - context, - ) ?? - widget.category, - ), - children: xpTiles, - onExpansionChanged: (expanded) { - if (expanded) { - setState(() => _lastLoadedIndex = 50); - } - }, - ); - } -} diff --git a/lib/pangea/widgets/chat_list/analytics_summary/analytics_popup/analytics_popup.dart b/lib/pangea/widgets/chat_list/analytics_summary/analytics_popup/analytics_popup.dart new file mode 100644 index 000000000..2c561ad7a --- /dev/null +++ b/lib/pangea/widgets/chat_list/analytics_summary/analytics_popup/analytics_popup.dart @@ -0,0 +1,136 @@ +import 'package:fluffychat/pangea/enum/construct_type_enum.dart'; +import 'package:fluffychat/pangea/enum/progress_indicators_enum.dart'; +import 'package:fluffychat/pangea/models/analytics/construct_list_model.dart'; +import 'package:fluffychat/pangea/widgets/chat_list/analytics_summary/analytics_popup/analytics_xp_tile.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; + +class AnalyticsPopup extends StatefulWidget { + final ProgressIndicatorEnum indicator; + final ConstructListModel constructsModel; + final bool showGroups; + + const AnalyticsPopup({ + required this.indicator, + required this.constructsModel, + this.showGroups = true, + super.key, + }); + + @override + AnalyticsPopupState createState() => AnalyticsPopupState(); +} + +class AnalyticsPopupState extends State { + String? selectedCategory; + + List>> get categoriesToUses { + final entries = widget.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( + 0, + (previousValue, element) => previousValue + element.points, + ); + final bTotalPoints = b.value.fold( + 0, + (previousValue, element) => previousValue + element.points, + ); + return bTotalPoints.compareTo(aTotalPoints); + }); + return entries; + } + + void setSelectedCategory(String? category) => setState(() { + selectedCategory = category; + }); + + @override + Widget build(BuildContext context) { + Widget? dialogContent; + final bool hasNoData = + widget.constructsModel.constructListWithPoints.isEmpty; + final bool hasNoCategories = + widget.constructsModel.categoriesToUses.length == 1 && + widget.constructsModel.categoriesToUses.keys.first == "Other"; + + if (selectedCategory != null) { + dialogContent = ListView.builder( + itemCount: + widget.constructsModel.categoriesToUses[selectedCategory]!.length, + itemBuilder: (context, index) { + final constructUses = + widget.constructsModel.categoriesToUses[selectedCategory]![index]; + return ConstructUsesXPTile(constructUses); + }, + ); + } else if (hasNoData) { + dialogContent = Center(child: Text(L10n.of(context)!.noDataFound)); + } else if (hasNoCategories || !widget.showGroups) { + dialogContent = ListView.builder( + itemCount: widget.constructsModel.constructListWithPoints.length, + itemBuilder: (context, index) { + final constructUses = + widget.constructsModel.constructListWithPoints[index]; + return ConstructUsesXPTile(constructUses); + }, + ); + } else { + dialogContent = ListView.builder( + itemCount: categoriesToUses.length, + itemBuilder: (context, index) { + final category = categoriesToUses[index]; + final copy = widget.constructsModel.type?.getDisplayCopy( + category.key, + context, + ) ?? + category.key; + return Column( + children: [ + ListTile( + title: Text(copy), + trailing: const Icon(Icons.chevron_right_outlined), + onTap: () => setSelectedCategory(category.key), + ), + const Divider(height: 1), + ], + ); + }, + ); + } + + return Dialog( + child: ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 400, + maxHeight: 600, + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(20.0), + child: Scaffold( + appBar: AppBar( + title: Text(widget.indicator.tooltip(context)), + leading: IconButton( + icon: selectedCategory == null + ? const Icon(Icons.close) + : const Icon(Icons.chevron_left_outlined), + onPressed: selectedCategory == null + ? Navigator.of(context).pop + : () => setSelectedCategory(null), + ), + ), + body: Padding( + padding: const EdgeInsets.symmetric(vertical: 20), + child: dialogContent, + ), + ), + ), + ), + ); + } +} diff --git a/lib/pangea/widgets/chat_list/analytics_summary/analytics_popup/analytics_xp_tile.dart b/lib/pangea/widgets/chat_list/analytics_summary/analytics_popup/analytics_xp_tile.dart new file mode 100644 index 000000000..b297418f7 --- /dev/null +++ b/lib/pangea/widgets/chat_list/analytics_summary/analytics_popup/analytics_xp_tile.dart @@ -0,0 +1,61 @@ +import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/pangea/enum/construct_type_enum.dart'; +import 'package:fluffychat/pangea/enum/progress_indicators_enum.dart'; +import 'package:fluffychat/pangea/models/analytics/construct_list_model.dart'; +import 'package:fluffychat/pangea/utils/get_grammar_copy.dart'; +import 'package:flutter/material.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, + ), + ), + ); + } +} diff --git a/lib/pangea/widgets/chat_list/analytics_summary/learning_progress_indicators.dart b/lib/pangea/widgets/chat_list/analytics_summary/learning_progress_indicators.dart index 830bddca4..53e9ae172 100644 --- a/lib/pangea/widgets/chat_list/analytics_summary/learning_progress_indicators.dart +++ b/lib/pangea/widgets/chat_list/analytics_summary/learning_progress_indicators.dart @@ -9,7 +9,7 @@ import 'package:fluffychat/pangea/models/analytics/constructs_model.dart'; import 'package:fluffychat/pangea/pages/settings_learning/settings_learning.dart'; import 'package:fluffychat/pangea/widgets/animations/progress_bar/progress_bar.dart'; import 'package:fluffychat/pangea/widgets/animations/progress_bar/progress_bar_details.dart'; -import 'package:fluffychat/pangea/widgets/chat_list/analytics_summary/analytics_popup.dart'; +import 'package:fluffychat/pangea/widgets/chat_list/analytics_summary/analytics_popup/analytics_popup.dart'; import 'package:fluffychat/pangea/widgets/chat_list/analytics_summary/progress_indicator.dart'; import 'package:fluffychat/pangea/widgets/flag.dart'; import 'package:fluffychat/widgets/matrix.dart';