From b3155c1f478c6bae63120006216c83620ac77317 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Mon, 30 Jun 2025 11:57:41 -0400 Subject: [PATCH 1/2] feat: unified analytics page --- lib/config/routes.dart | 38 +--- .../activity_planner_page.dart | 8 +- .../activity_planner_page_appbar.dart | 14 +- .../activity_suggestions_area.dart | 28 --- .../suggestions_page.dart | 62 ----- .../analytics_details_popup.dart | 171 ++++++++------ lib/pangea/analytics_page/analytics_page.dart | 22 ++ .../analytics_page_constants.dart | 3 + .../analytics_page/analytics_page_view.dart | 83 +++++++ .../learning_progress_indicator_button.dart | 4 +- .../learning_progress_indicators.dart | 30 ++- .../analytics_summary/level_bar_popup.dart | 156 +------------ .../level_dialog_content.dart | 157 +++++++++++++ .../chat/utils/unlocked_morphs_snackbar.dart | 16 +- .../igc/message_analytics_feedback.dart | 8 +- .../widgets/pangea_side_view.dart} | 23 +- .../public_spaces/public_space_card.dart | 154 ------------- .../public_spaces/public_spaces_area.dart | 215 ------------------ .../word_zoom/morphological_list_item.dart | 18 +- .../widgets/word_zoom/word_zoom_widget.dart | 10 +- lib/widgets/navigation_rail.dart | 8 +- 21 files changed, 462 insertions(+), 766 deletions(-) delete mode 100644 lib/pangea/activity_suggestions/suggestions_page.dart create mode 100644 lib/pangea/analytics_page/analytics_page.dart create mode 100644 lib/pangea/analytics_page/analytics_page_constants.dart create mode 100644 lib/pangea/analytics_page/analytics_page_view.dart create mode 100644 lib/pangea/analytics_summary/level_dialog_content.dart rename lib/pangea/{find_your_people/find_your_people_side_view.dart => common/widgets/pangea_side_view.dart} (70%) delete mode 100644 lib/pangea/public_spaces/public_space_card.dart delete mode 100644 lib/pangea/public_spaces/public_spaces_area.dart diff --git a/lib/config/routes.dart b/lib/config/routes.dart index e7fb1d376..d2a3efc5e 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -31,9 +31,9 @@ import 'package:fluffychat/pages/settings_security/settings_security.dart'; import 'package:fluffychat/pages/settings_style/settings_style.dart'; import 'package:fluffychat/pangea/activity_generator/activity_generator.dart'; import 'package:fluffychat/pangea/activity_planner/activity_planner_page.dart'; -import 'package:fluffychat/pangea/activity_suggestions/suggestions_page.dart'; +import 'package:fluffychat/pangea/analytics_page/analytics_page.dart'; +import 'package:fluffychat/pangea/common/widgets/pangea_side_view.dart'; import 'package:fluffychat/pangea/find_your_people/find_your_people.dart'; -import 'package:fluffychat/pangea/find_your_people/find_your_people_side_view.dart'; import 'package:fluffychat/pangea/guard/p_vguard.dart'; import 'package:fluffychat/pangea/learning_settings/pages/settings_learning.dart'; import 'package:fluffychat/pangea/login/pages/login_or_signup_view.dart'; @@ -196,7 +196,8 @@ abstract class AppRoutes { // state.fullPath?.startsWith('/rooms/settings') == false FluffyThemes.isColumnMode(context) && state.fullPath?.startsWith('/rooms/settings') == false && - state.fullPath?.startsWith('/rooms/communities') == false + state.fullPath?.startsWith('/rooms/communities') == false && + state.fullPath?.startsWith('/rooms/analytics') == false // Pangea# ? TwoColumnLayout( mainView: ChatList( @@ -309,7 +310,7 @@ abstract class AppRoutes { state, FluffyThemes.isColumnMode(context) ? TwoColumnLayout( - mainView: const FindYourPeopleSideView(), + mainView: PangeaSideView(path: state.fullPath), sideView: child, dividerColor: Colors.transparent, ) @@ -325,37 +326,14 @@ abstract class AppRoutes { const FindYourPeople(), ), ), - ], - ), - GoRoute( - path: 'homepage', - redirect: loggedOutRedirect, - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - const SuggestionsPage(), - ), - routes: [ - ...newRoomRoutes, GoRoute( - path: '/planner', + path: 'analytics', + redirect: loggedOutRedirect, pageBuilder: (context, state) => defaultPageBuilder( context, state, - const ActivityPlannerPage(), + const AnalyticsPage(), ), - redirect: loggedOutRedirect, - routes: [ - GoRoute( - path: '/generator', - redirect: loggedOutRedirect, - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - const ActivityGenerator(), - ), - ), - ], ), ], ), diff --git a/lib/pangea/activity_planner/activity_planner_page.dart b/lib/pangea/activity_planner/activity_planner_page.dart index 166cbb2d9..eac6c9596 100644 --- a/lib/pangea/activity_planner/activity_planner_page.dart +++ b/lib/pangea/activity_planner/activity_planner_page.dart @@ -14,8 +14,8 @@ enum PageMode { } class ActivityPlannerPage extends StatefulWidget { - final String? roomID; - const ActivityPlannerPage({super.key, this.roomID}); + final String roomID; + const ActivityPlannerPage({super.key, required this.roomID}); @override ActivityPlannerPageState createState() => ActivityPlannerPageState(); @@ -23,9 +23,7 @@ class ActivityPlannerPage extends StatefulWidget { class ActivityPlannerPageState extends State { PageMode pageMode = PageMode.featuredActivities; - Room? get room => widget.roomID != null - ? Matrix.of(context).client.getRoomById(widget.roomID!) - : null; + Room? get room => Matrix.of(context).client.getRoomById(widget.roomID); void _setPageMode(PageMode? mode) { if (mode == null) return; diff --git a/lib/pangea/activity_planner/activity_planner_page_appbar.dart b/lib/pangea/activity_planner/activity_planner_page_appbar.dart index 94ee72890..fd7352de7 100644 --- a/lib/pangea/activity_planner/activity_planner_page_appbar.dart +++ b/lib/pangea/activity_planner/activity_planner_page_appbar.dart @@ -12,11 +12,11 @@ import 'package:fluffychat/pangea/common/widgets/customized_svg.dart'; class ActivityPlannerPageAppBar extends StatelessWidget implements PreferredSizeWidget { final PageMode pageMode; - final String? roomID; + final String roomID; const ActivityPlannerPageAppBar({ required this.pageMode, - this.roomID, + required this.roomID, super.key, }); @@ -71,9 +71,8 @@ class ActivityPlannerPageAppBar extends StatelessWidget alignment: Alignment.center, child: InkWell( customBorder: const CircleBorder(), - onTap: () => roomID != null - ? context.go('/rooms/$roomID/details/planner/generator') - : context.go("/rooms/homepage/planner/generator"), + onTap: () => + context.go('/rooms/$roomID/details/planner/generator'), child: Container( decoration: BoxDecoration( color: theme.colorScheme.surfaceContainerHighest, @@ -114,9 +113,8 @@ class ActivityPlannerPageAppBar extends StatelessWidget ) : IconButton( icon: const Icon(Icons.add), - onPressed: () => roomID != null - ? context.go('/rooms/$roomID/details/planner/generator') - : context.go("/rooms/homepage/planner/generator"), + onPressed: () => + context.go('/rooms/$roomID/details/planner/generator'), ), ], ); diff --git a/lib/pangea/activity_suggestions/activity_suggestions_area.dart b/lib/pangea/activity_suggestions/activity_suggestions_area.dart index 82d0ca74b..848923717 100644 --- a/lib/pangea/activity_suggestions/activity_suggestions_area.dart +++ b/lib/pangea/activity_suggestions/activity_suggestions_area.dart @@ -5,7 +5,6 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart'; import 'package:shimmer/shimmer.dart'; @@ -25,14 +24,11 @@ import 'package:fluffychat/widgets/matrix.dart'; class ActivitySuggestionsArea extends StatefulWidget { final Axis? scrollDirection; - final bool showTitle; - final Room? room; const ActivitySuggestionsArea({ super.key, this.scrollDirection, - this.showTitle = false, this.room, }); @override @@ -141,7 +137,6 @@ class ActivitySuggestionsAreaState extends State { @override Widget build(BuildContext context) { final theme = Theme.of(context); - final isColumnMode = FluffyThemes.isColumnMode(context); final List cards = _loading ? List.generate(5, (i) { @@ -196,29 +191,6 @@ class ActivitySuggestionsAreaState extends State { spacing: 8.0, crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (widget.showTitle) - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - child: Text( - L10n.of(context).chatWithActivities, - style: isColumnMode - ? theme.textTheme.titleLarge - ?.copyWith(fontWeight: FontWeight.bold) - : theme.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.bold, - ), - overflow: TextOverflow.ellipsis, - ), - ), - IconButton( - icon: const Icon(Icons.event_note_outlined), - onPressed: () => context.go('/rooms/homepage/planner'), - tooltip: L10n.of(context).activityPlannerTitle, - ), - ], - ), AnimatedSize( duration: FluffyThemes.animationDuration, child: (_timeout || !_loading && cards.isEmpty) diff --git a/lib/pangea/activity_suggestions/suggestions_page.dart b/lib/pangea/activity_suggestions/suggestions_page.dart deleted file mode 100644 index b8d2a9038..000000000 --- a/lib/pangea/activity_suggestions/suggestions_page.dart +++ /dev/null @@ -1,62 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:go_router/go_router.dart'; - -import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/config/themes.dart'; -import 'package:fluffychat/pangea/activity_suggestions/activity_suggestions_area.dart'; -import 'package:fluffychat/pangea/analytics_summary/learning_progress_indicators.dart'; -import 'package:fluffychat/pangea/public_spaces/public_spaces_area.dart'; -import 'package:fluffychat/widgets/navigation_rail.dart'; - -class SuggestionsPage extends StatelessWidget { - const SuggestionsPage({super.key}); - - @override - Widget build(BuildContext context) { - final isColumnMode = FluffyThemes.isColumnMode(context); - return Scaffold( - resizeToAvoidBottomInset: true, - body: SafeArea( - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (!isColumnMode && AppConfig.displayNavigationRail) ...[ - SpacesNavigationRail( - activeSpaceId: null, - onGoToChats: () => context.go('/rooms'), - onGoToSpaceId: (spaceId) => - context.go('/rooms?spaceId=$spaceId'), - ), - Container( - color: Theme.of(context).dividerColor, - width: 1, - ), - ], - Expanded( - child: SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 16.0, - ), - child: Column( - spacing: 24.0, - children: [ - if (!isColumnMode) const LearningProgressIndicators(), - const ActivitySuggestionsArea( - showTitle: true, - scrollDirection: Axis.horizontal, - ), - const PublicSpacesArea(), - ], - ), - ), - ), - ), - ], - ), - ), - ); - } -} diff --git a/lib/pangea/analytics_details_popup/analytics_details_popup.dart b/lib/pangea/analytics_details_popup/analytics_details_popup.dart index 9cb085479..bc5dced54 100644 --- a/lib/pangea/analytics_details_popup/analytics_details_popup.dart +++ b/lib/pangea/analytics_details_popup/analytics_details_popup.dart @@ -26,11 +26,33 @@ class AnalyticsPopupWrapper extends StatefulWidget { this.constructZoom, required this.view, this.backButtonOverride, + this.showAppBar = true, }); final ConstructTypeEnum view; final ConstructIdentifier? constructZoom; final Widget? backButtonOverride; + final bool showAppBar; + + static void show( + BuildContext context, { + ConstructIdentifier? constructZoom, + ConstructTypeEnum view = ConstructTypeEnum.vocab, + Widget? backButtonOverride, + }) { + showDialog( + context: context, + builder: (context) => FullWidthDialog( + maxWidth: 600, + maxHeight: 800, + dialogContent: AnalyticsPopupWrapper( + constructZoom: constructZoom, + view: view, + backButtonOverride: backButtonOverride, + ), + ), + ); + } @override AnalyticsPopupWrapperState createState() => AnalyticsPopupWrapperState(); @@ -58,6 +80,19 @@ class AnalyticsPopupWrapperState extends State { }); } + @override + void didUpdateWidget(covariant AnalyticsPopupWrapper oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.constructZoom != oldWidget.constructZoom) { + setConstructZoom(widget.constructZoom); + } + if (widget.view != oldWidget.view) { + localView = widget.view; + localConstructZoom = null; + setState(() {}); + } + } + @override void dispose() { searchController.dispose(); @@ -109,74 +144,80 @@ class AnalyticsPopupWrapperState extends State { @override Widget build(BuildContext context) { - return FullWidthDialog( - dialogContent: Scaffold( - appBar: AppBar( - title: kIsWeb - ? Text( - localView == ConstructTypeEnum.morph - ? ConstructTypeEnum.morph.indicator.tooltip(context) - : ConstructTypeEnum.vocab.indicator.tooltip(context), - ) - : null, - leading: widget.backButtonOverride ?? - IconButton( - icon: localConstructZoom == null - ? const Icon(Icons.close) - : const Icon(Icons.arrow_back), - onPressed: localConstructZoom == null - ? () => Navigator.of(context).pop() - : () => setConstructZoom(null), - ), - actions: [ - TextButton.icon( - style: TextButton.styleFrom( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10.0), + return Scaffold( + appBar: widget.showAppBar + ? AppBar( + title: kIsWeb + ? Text( + localView == ConstructTypeEnum.morph + ? ConstructTypeEnum.morph.indicator.tooltip(context) + : ConstructTypeEnum.vocab.indicator.tooltip(context), + ) + : null, + leading: widget.backButtonOverride ?? + IconButton( + icon: localConstructZoom == null + ? const Icon(Icons.close) + : const Icon(Icons.arrow_back), + onPressed: localConstructZoom == null + ? () => Navigator.of(context).pop() + : () => setConstructZoom(null), + ), + actions: [ + TextButton.icon( + style: TextButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0), + ), + backgroundColor: localView == ConstructTypeEnum.vocab + ? Theme.of(context).colorScheme.primary.withAlpha(50) + : Theme.of(context).colorScheme.surface, + ), + label: Text(L10n.of(context).vocab), + icon: const Icon(Symbols.dictionary), + onPressed: () => setState(() { + localView = ConstructTypeEnum.vocab; + localConstructZoom = null; + }), ), - backgroundColor: localView == ConstructTypeEnum.vocab - ? Theme.of(context).colorScheme.primary.withAlpha(50) - : Theme.of(context).colorScheme.surface, - ), - label: Text(L10n.of(context).vocab), - icon: const Icon(Symbols.dictionary), - onPressed: () => setState(() { - localView = ConstructTypeEnum.vocab; - localConstructZoom = null; - }), - ), - const SizedBox(width: 4.0), - TextButton.icon( - style: TextButton.styleFrom( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10.0), + const SizedBox(width: 4.0), + TextButton.icon( + style: TextButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0), + ), + backgroundColor: localView == ConstructTypeEnum.morph + ? Theme.of(context).colorScheme.primary.withAlpha(50) + : Theme.of(context).colorScheme.surface, + ), + label: Text(L10n.of(context).grammar), + icon: const Icon(Symbols.toys_and_games), + onPressed: () => setState(() { + localView = ConstructTypeEnum.morph; + localConstructZoom = null; + }), ), - backgroundColor: localView == ConstructTypeEnum.morph - ? Theme.of(context).colorScheme.primary.withAlpha(50) - : Theme.of(context).colorScheme.surface, - ), - label: Text(L10n.of(context).grammar), - icon: const Icon(Symbols.toys_and_games), - onPressed: () => setState(() { - localView = ConstructTypeEnum.morph; - localConstructZoom = null; - }), + const SizedBox(width: 4.0), + if (kIsWeb) const DownloadAnalyticsButton(), + if (kIsWeb) const SizedBox(width: 4.0), + ], + ) + : AppBar( + leading: widget.backButtonOverride ?? + (localConstructZoom != null + ? IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () => setConstructZoom(null), + ) + : const SizedBox()), ), - const SizedBox(width: 4.0), - if (kIsWeb) const DownloadAnalyticsButton(), - if (kIsWeb) const SizedBox(width: 4.0), - ], - ), - body: localView == ConstructTypeEnum.morph - ? localConstructZoom == null - ? MorphAnalyticsListView(controller: this) - : MorphDetailsView(constructId: localConstructZoom!) - : localConstructZoom == null - ? VocabAnalyticsListView(controller: this) - : VocabDetailsView(constructId: localConstructZoom!), - ), - maxWidth: 600, - maxHeight: 800, + body: localView == ConstructTypeEnum.morph + ? localConstructZoom == null + ? MorphAnalyticsListView(controller: this) + : MorphDetailsView(constructId: localConstructZoom!) + : localConstructZoom == null + ? VocabAnalyticsListView(controller: this) + : VocabDetailsView(constructId: localConstructZoom!), ); } } diff --git a/lib/pangea/analytics_page/analytics_page.dart b/lib/pangea/analytics_page/analytics_page.dart new file mode 100644 index 000000000..e8f8243dd --- /dev/null +++ b/lib/pangea/analytics_page/analytics_page.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +import 'package:fluffychat/pangea/analytics_page/analytics_page_view.dart'; +import 'package:fluffychat/pangea/analytics_summary/progress_indicators_enum.dart'; + +class AnalyticsPage extends StatefulWidget { + const AnalyticsPage({super.key}); + + @override + AnalyticsPageState createState() => AnalyticsPageState(); +} + +class AnalyticsPageState extends State { + ProgressIndicatorEnum? selectedIndicator = ProgressIndicatorEnum.level; + + void onIndicatorSelected(ProgressIndicatorEnum indicator) => setState(() { + selectedIndicator = indicator; + }); + + @override + Widget build(BuildContext context) => AnalyticsPageView(controller: this); +} diff --git a/lib/pangea/analytics_page/analytics_page_constants.dart b/lib/pangea/analytics_page/analytics_page_constants.dart new file mode 100644 index 000000000..075bde772 --- /dev/null +++ b/lib/pangea/analytics_page/analytics_page_constants.dart @@ -0,0 +1,3 @@ +class AnalyticsPageConstants { + static const String dinoBotFileName = 'Analytic_DinoBot.png'; +} diff --git a/lib/pangea/analytics_page/analytics_page_view.dart b/lib/pangea/analytics_page/analytics_page_view.dart new file mode 100644 index 000000000..4e15c90d3 --- /dev/null +++ b/lib/pangea/analytics_page/analytics_page_view.dart @@ -0,0 +1,83 @@ +import 'package:flutter/material.dart'; + +import 'package:go_router/go_router.dart'; + +import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/themes.dart'; +import 'package:fluffychat/pangea/analytics_details_popup/analytics_details_popup.dart'; +import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart'; +import 'package:fluffychat/pangea/analytics_page/analytics_page.dart'; +import 'package:fluffychat/pangea/analytics_summary/learning_progress_indicators.dart'; +import 'package:fluffychat/pangea/analytics_summary/level_dialog_content.dart'; +import 'package:fluffychat/pangea/analytics_summary/progress_indicators_enum.dart'; +import 'package:fluffychat/widgets/navigation_rail.dart'; + +class AnalyticsPageView extends StatelessWidget { + final AnalyticsPageState controller; + const AnalyticsPageView({ + super.key, + required this.controller, + }); + + @override + Widget build(BuildContext context) { + final isColumnMode = FluffyThemes.isColumnMode(context); + + return Row( + children: [ + if (!isColumnMode && AppConfig.displayNavigationRail) ...[ + SpacesNavigationRail( + activeSpaceId: null, + onGoToChats: () => context.go('/rooms'), + onGoToSpaceId: (spaceId) => context.go('/rooms?spaceId=$spaceId'), + ), + Container( + color: Theme.of(context).dividerColor, + width: 1, + ), + ], + Expanded( + child: Scaffold( + body: Padding( + padding: const EdgeInsetsGeometry.all(16.0), + child: Column( + spacing: 16.0, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LearningProgressIndicators( + selected: controller.selectedIndicator, + onIndicatorSelected: controller.onIndicatorSelected, + ), + Expanded( + child: Builder( + builder: (context) { + if (controller.selectedIndicator == + ProgressIndicatorEnum.level) { + return const LevelDialogContent(); + } else if (controller.selectedIndicator == + ProgressIndicatorEnum.morphsUsed) { + return const AnalyticsPopupWrapper( + view: ConstructTypeEnum.morph, + showAppBar: false, + ); + } else if (controller.selectedIndicator == + ProgressIndicatorEnum.wordsUsed) { + return const AnalyticsPopupWrapper( + view: ConstructTypeEnum.vocab, + showAppBar: false, + ); + } + + return const SizedBox(); + }, + ), + ), + ], + ), + ), + ), + ), + ], + ); + } +} diff --git a/lib/pangea/analytics_summary/learning_progress_indicator_button.dart b/lib/pangea/analytics_summary/learning_progress_indicator_button.dart index 30ea42199..1e4632e0b 100644 --- a/lib/pangea/analytics_summary/learning_progress_indicator_button.dart +++ b/lib/pangea/analytics_summary/learning_progress_indicator_button.dart @@ -7,6 +7,7 @@ class HoverButton extends StatelessWidget { final Widget child; final BorderRadius? borderRadius; final double hoverOpacity; + final bool selected; const HoverButton({ super.key, @@ -14,6 +15,7 @@ class HoverButton extends StatelessWidget { required this.child, this.borderRadius, this.hoverOpacity = 0.2, + this.selected = false, }); @override @@ -26,7 +28,7 @@ class HoverButton extends StatelessWidget { onTap: onPressed, child: Container( decoration: BoxDecoration( - color: hovered + color: hovered || selected ? Theme.of(context) .colorScheme .primary diff --git a/lib/pangea/analytics_summary/learning_progress_indicators.dart b/lib/pangea/analytics_summary/learning_progress_indicators.dart index d7a6d1c98..328a3a419 100644 --- a/lib/pangea/analytics_summary/learning_progress_indicators.dart +++ b/lib/pangea/analytics_summary/learning_progress_indicators.dart @@ -19,7 +19,13 @@ import 'package:fluffychat/widgets/matrix.dart'; /// messages sent, words used, and error types, which can /// be clicked to access more fine-grained analytics data. class LearningProgressIndicators extends StatefulWidget { - const LearningProgressIndicators({super.key}); + final ProgressIndicatorEnum? selected; + final Function(ProgressIndicatorEnum)? onIndicatorSelected; + const LearningProgressIndicators({ + super.key, + this.selected, + this.onIndicatorSelected, + }); @override State createState() => @@ -106,12 +112,18 @@ class LearningProgressIndicatorsState children: ConstructTypeEnum.values .map( (c) => HoverButton( + selected: widget.selected == c.indicator, onPressed: () { - showDialog( - context: context, - builder: (context) => AnalyticsPopupWrapper( - view: c, - ), + if (widget.onIndicatorSelected != null) { + widget.onIndicatorSelected?.call( + c.indicator, + ); + return; + } + + AnalyticsPopupWrapper.show( + context, + view: c, ); }, child: ProgressIndicatorBadge( @@ -168,6 +180,12 @@ class LearningProgressIndicatorsState cursor: SystemMouseCursors.click, child: GestureDetector( onTap: () { + if (widget.onIndicatorSelected != null) { + widget.onIndicatorSelected + ?.call(ProgressIndicatorEnum.level); + return; + } + showDialog( context: context, builder: (c) => const LevelBarPopup(), diff --git a/lib/pangea/analytics_summary/level_bar_popup.dart b/lib/pangea/analytics_summary/level_bar_popup.dart index f7bf790ab..2d7af94d9 100644 --- a/lib/pangea/analytics_summary/level_bar_popup.dart +++ b/lib/pangea/analytics_summary/level_bar_popup.dart @@ -1,28 +1,12 @@ import 'package:flutter/material.dart'; -import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/l10n/l10n.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'; +import 'package:fluffychat/pangea/analytics_summary/level_dialog_content.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 get uses => - getAnalyticsController.constructListModel.truncatedUses; - @override Widget build(BuildContext context) { return Dialog( @@ -33,143 +17,7 @@ class LevelBarPopup extends StatelessWidget { ), 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: [ - 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[index]; - String lemmaCopy = use.lemma; - if (use.constructType == ConstructTypeEnum.morph) { - lemmaCopy = getGrammarCopy( - category: use.category, - lemma: use.lemma, - context: context, - ) ?? - use.lemma; - } - return Padding( - padding: const EdgeInsets.symmetric( - vertical: 12, - horizontal: 16, - ), - child: IntrinsicHeight( - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Align( - alignment: Alignment.centerLeft, - child: Container( - width: 40, - alignment: Alignment.centerLeft, - child: Icon(use.useType.icon), - ), - ), - const SizedBox(width: 10), - Expanded( - child: Text( - "\"$lemmaCopy\" - ${use.useType.description(context)}", - style: const TextStyle(fontSize: 14), - ), - ), - Container( - alignment: Alignment.topRight, - width: 60, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Text( - "${use.xp > 0 ? '+' : ''}${use.xp}", - style: TextStyle( - fontWeight: FontWeight.w900, - fontSize: 14, - height: 1, - color: use.pointValueColor(context), - ), - ), - // const SizedBox(width: 5), - // const CircleAvatar( - // radius: 8, - // child: Icon( - // size: 10, - // Icons.star, - // color: Colors.white, - // ), - // ), - ], - ), - ), - ], - ), - ), - ); - }, - ), - ), - ], - ), - ), + child: const LevelDialogContent(), ), ), ); diff --git a/lib/pangea/analytics_summary/level_dialog_content.dart b/lib/pangea/analytics_summary/level_dialog_content.dart new file mode 100644 index 000000000..ac1a71e5e --- /dev/null +++ b/lib/pangea/analytics_summary/level_dialog_content.dart @@ -0,0 +1,157 @@ +import 'package:flutter/material.dart'; + +import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/l10n/l10n.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 LevelDialogContent extends StatelessWidget { + const LevelDialogContent({ + 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 get uses => + getAnalyticsController.constructListModel.truncatedUses; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + titleSpacing: 0, + automaticallyImplyLeading: false, + title: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + 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[index]; + String lemmaCopy = use.lemma; + if (use.constructType == ConstructTypeEnum.morph) { + lemmaCopy = getGrammarCopy( + category: use.category, + lemma: use.lemma, + context: context, + ) ?? + use.lemma; + } + return Padding( + padding: const EdgeInsets.symmetric( + vertical: 12, + horizontal: 16, + ), + child: IntrinsicHeight( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Align( + alignment: Alignment.centerLeft, + child: Container( + width: 40, + alignment: Alignment.centerLeft, + child: Icon(use.useType.icon), + ), + ), + const SizedBox(width: 10), + Expanded( + child: Text( + "\"$lemmaCopy\" - ${use.useType.description(context)}", + style: const TextStyle(fontSize: 14), + ), + ), + Container( + alignment: Alignment.topRight, + width: 60, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "${use.xp > 0 ? '+' : ''}${use.xp}", + style: TextStyle( + fontWeight: FontWeight.w900, + fontSize: 14, + height: 1, + color: use.pointValueColor(context), + ), + ), + ], + ), + ), + ], + ), + ), + ); + }, + ), + ), + ], + ), + ); + } +} diff --git a/lib/pangea/chat/utils/unlocked_morphs_snackbar.dart b/lib/pangea/chat/utils/unlocked_morphs_snackbar.dart index 16f4649a2..93ea3006b 100644 --- a/lib/pangea/chat/utils/unlocked_morphs_snackbar.dart +++ b/lib/pangea/chat/utils/unlocked_morphs_snackbar.dart @@ -153,15 +153,13 @@ class ConstructNotificationOverlayState } void _showDetails() { - showDialog( - context: context, - builder: (context) => AnalyticsPopupWrapper( - constructZoom: widget.construct, - view: ConstructTypeEnum.morph, - backButtonOverride: IconButton( - icon: const Icon(Icons.close), - onPressed: () => Navigator.of(context).pop(), - ), + AnalyticsPopupWrapper.show( + context, + constructZoom: widget.construct, + view: ConstructTypeEnum.morph, + backButtonOverride: IconButton( + icon: const Icon(Icons.close), + onPressed: () => Navigator.of(context).pop(), ), ); } diff --git a/lib/pangea/choreographer/widgets/igc/message_analytics_feedback.dart b/lib/pangea/choreographer/widgets/igc/message_analytics_feedback.dart index fb3d61bbe..766c6dc14 100644 --- a/lib/pangea/choreographer/widgets/igc/message_analytics_feedback.dart +++ b/lib/pangea/choreographer/widgets/igc/message_analytics_feedback.dart @@ -99,11 +99,9 @@ class MessageAnalyticsFeedbackState extends State } void _showAnalyticsDialog(ConstructTypeEnum? type) { - showDialog( - context: context, - builder: (context) => AnalyticsPopupWrapper( - view: type ?? ConstructTypeEnum.vocab, - ), + AnalyticsPopupWrapper.show( + context, + view: type ?? ConstructTypeEnum.vocab, ); } diff --git a/lib/pangea/find_your_people/find_your_people_side_view.dart b/lib/pangea/common/widgets/pangea_side_view.dart similarity index 70% rename from lib/pangea/find_your_people/find_your_people_side_view.dart rename to lib/pangea/common/widgets/pangea_side_view.dart index 0514455e7..46c2f0feb 100644 --- a/lib/pangea/find_your_people/find_your_people_side_view.dart +++ b/lib/pangea/common/widgets/pangea_side_view.dart @@ -5,11 +5,27 @@ import 'package:go_router/go_router.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/themes.dart'; +import 'package:fluffychat/pangea/analytics_page/analytics_page_constants.dart'; import 'package:fluffychat/pangea/find_your_people/find_your_people_constants.dart'; import 'package:fluffychat/widgets/navigation_rail.dart'; -class FindYourPeopleSideView extends StatelessWidget { - const FindYourPeopleSideView({super.key}); +class PangeaSideView extends StatelessWidget { + final String? path; + const PangeaSideView({ + super.key, + required this.path, + }); + + String get _asset { + const defaultAsset = FindYourPeopleConstants.sideBearFileName; + if (path == null || path!.isEmpty) return defaultAsset; + + if (path!.contains('analytics')) { + return AnalyticsPageConstants.dinoBotFileName; + } + + return defaultAsset; + } @override Widget build(BuildContext context) { @@ -32,8 +48,7 @@ class FindYourPeopleSideView extends StatelessWidget { child: SizedBox( width: 250.0, child: CachedNetworkImage( - imageUrl: - "${AppConfig.assetsBaseURL}/${FindYourPeopleConstants.sideBearFileName}", + imageUrl: "${AppConfig.assetsBaseURL}/$_asset", errorWidget: (context, url, error) => const SizedBox(), placeholder: (context, url) => const Center( child: CircularProgressIndicator.adaptive(), diff --git a/lib/pangea/public_spaces/public_space_card.dart b/lib/pangea/public_spaces/public_space_card.dart deleted file mode 100644 index 29521f67b..000000000 --- a/lib/pangea/public_spaces/public_space_card.dart +++ /dev/null @@ -1,154 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:cached_network_image/cached_network_image.dart'; -import 'package:matrix/matrix.dart'; - -import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/pangea/common/widgets/pressable_button.dart'; -import 'package:fluffychat/pangea/extensions/pangea_rooms_chunk_extension.dart'; -import 'package:fluffychat/pangea/public_spaces/public_room_bottom_sheet.dart'; -import 'package:fluffychat/widgets/mxc_image.dart'; - -class PublicSpaceCard extends StatelessWidget { - final PublicRoomsChunk space; - final double width; - final double height; - - const PublicSpaceCard({ - super.key, - required this.space, - required this.width, - required this.height, - }); - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - - return PressableButton( - onPressed: () => PublicRoomBottomSheet.show( - roomAlias: space.canonicalAlias ?? space.roomId, - chunk: space, - context: context, - ), - borderRadius: BorderRadius.circular(24.0), - color: theme.brightness == Brightness.dark - ? theme.colorScheme.primary - : theme.colorScheme.surfaceContainerHighest, - colorFactor: theme.brightness == Brightness.dark ? 0.6 : 0.2, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(24.0), - ), - height: height, - width: width, - child: Stack( - children: [ - Container( - decoration: BoxDecoration( - color: theme.colorScheme.surfaceContainer, - borderRadius: BorderRadius.circular(24.0), - ), - ), - Row( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - height: height, - width: height, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(24.0), - ), - child: ClipRRect( - borderRadius: BorderRadius.circular(24.0), - child: space.avatarUrl != null - ? MxcImage( - uri: space.avatarUrl!, - width: width, - height: width, - fit: BoxFit.cover, - ) - : CachedNetworkImage( - imageUrl: space.defaultAvatar(), - width: width, - height: width, - fit: BoxFit.cover, - ), - ), - ), - Expanded( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - spacing: 4.0, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Column( - spacing: 4.0, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Flexible( - child: Text( - space.name ?? '', - style: const TextStyle( - fontWeight: FontWeight.bold, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - Container( - decoration: BoxDecoration( - color: theme.colorScheme.primaryContainer, - borderRadius: BorderRadius.circular(24.0), - ), - padding: const EdgeInsets.symmetric( - vertical: 2.0, - horizontal: 8.0, - ), - child: Row( - mainAxisSize: MainAxisSize.min, - spacing: 8.0, - children: [ - const Icon( - Icons.group_outlined, - size: 12.0, - ), - Text( - L10n.of(context).countParticipants( - space.numJoinedMembers, - ), - style: theme.textTheme.labelSmall, - ), - ], - ), - ), - ], - ), - Flexible( - child: Text( - space.topic ?? - L10n.of(context).noSpaceDescriptionYet, - style: theme.textTheme.bodySmall, - overflow: TextOverflow.ellipsis, - textAlign: TextAlign.start, - maxLines: 5, - ), - ), - ], - ), - ), - ), - ], - ), - ], - ), - ), - ); - } -} diff --git a/lib/pangea/public_spaces/public_spaces_area.dart b/lib/pangea/public_spaces/public_spaces_area.dart deleted file mode 100644 index 0e8fadf6b..000000000 --- a/lib/pangea/public_spaces/public_spaces_area.dart +++ /dev/null @@ -1,215 +0,0 @@ -// shows n rows of activity suggestions vertically, where n is the number of rows -// as the user tries to scroll horizontally to the right, the client will fetch more activity suggestions - -import 'dart:async'; - -import 'package:flutter/material.dart'; - -import 'package:matrix/matrix.dart'; -import 'package:shimmer/shimmer.dart'; - -import 'package:fluffychat/config/themes.dart'; -import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/pangea/public_spaces/public_space_card.dart'; -import 'package:fluffychat/widgets/matrix.dart'; - -class PublicSpacesArea extends StatefulWidget { - const PublicSpacesArea({super.key}); - - @override - PublicSpacesAreaState createState() => PublicSpacesAreaState(); -} - -class PublicSpacesAreaState extends State { - @override - void initState() { - super.initState(); - _setSpaceItems(); - } - - @override - void dispose() { - _scrollController.dispose(); - _searchController.dispose(); - _coolDown?.cancel(); - super.dispose(); - } - - bool _loading = true; - bool _isSearching = false; - - final List _spaceItems = []; - - final ScrollController _scrollController = ScrollController(); - final TextEditingController _searchController = TextEditingController(); - Timer? _coolDown; - - final double cardHeight = 150.0; - final double cardWidth = 325.0; - - Future _setSpaceItems() async { - _spaceItems.clear(); - setState(() => _loading = true); - try { - final resp = await Matrix.of(context).client.queryPublicRooms( - filter: PublicRoomQueryFilter( - roomTypes: ['m.space'], - genericSearchTerm: _searchController.text, - ), - limit: 100, - ); - _spaceItems.addAll(resp.chunk); - _spaceItems.sort((a, b) { - int getPriority(item) { - final bool hasTopic = item.topic != null && item.topic!.isNotEmpty; - final bool hasAvatar = item.avatarUrl != null; - - if (hasTopic && hasAvatar) return 0; // Highest priority - if (hasAvatar) return 1; // Second priority - if (hasTopic) return 2; // Third priority - return 3; // Lowest priority - } - - return getPriority(a).compareTo(getPriority(b)); - }); - } finally { - if (mounted) setState(() => _loading = false); - } - } - - void _onSearchEnter(String text, {bool globalSearch = true}) { - if (text.isEmpty) { - _setSpaceItems(); - return; - } - - _coolDown?.cancel(); - _coolDown = Timer(const Duration(milliseconds: 500), _setSpaceItems); - } - - void _toggleSearching() { - setState(() { - _isSearching = !_isSearching; - _searchController.clear(); - _setSpaceItems(); - }); - } - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - final isColumnMode = FluffyThemes.isColumnMode(context); - - final List cards = _loading && _spaceItems.isEmpty - ? List.generate(5, (i) { - return Shimmer.fromColors( - baseColor: theme.colorScheme.primary.withAlpha(20), - highlightColor: theme.colorScheme.primary.withAlpha(50), - child: Container( - height: cardHeight, - width: cardWidth, - decoration: BoxDecoration( - color: theme.colorScheme.surfaceContainer, - borderRadius: BorderRadius.circular(24.0), - ), - ), - ); - }) - : _spaceItems - .map((space) { - return PublicSpaceCard( - space: space, - width: cardWidth, - height: cardHeight, - ); - }) - .cast() - .toList(); - - if (_loading && _spaceItems.isNotEmpty) { - cards.add( - const Padding( - padding: EdgeInsets.all(16.0), - child: CircularProgressIndicator.adaptive(), - ), - ); - } - - return Column( - spacing: 8.0, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - AnimatedSwitcher( - duration: const Duration(milliseconds: 300), - transitionBuilder: (child, animation) => FadeTransition( - opacity: animation, - child: child, - ), - child: _isSearching - ? Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - key: const ValueKey('search'), - children: [ - Expanded( - child: TextField( - autofocus: true, - controller: _searchController, - onChanged: _onSearchEnter, - decoration: const InputDecoration( - contentPadding: EdgeInsets.symmetric( - vertical: 6.0, - horizontal: 12.0, - ), - isDense: true, - border: OutlineInputBorder(), - ), - ), - ), - IconButton( - icon: const Icon(Icons.close), - onPressed: _toggleSearching, - ), - ], - ) - : Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - key: const ValueKey('title'), - children: [ - Text( - L10n.of(context).findYourPeople, - style: isColumnMode - ? theme.textTheme.titleLarge - ?.copyWith(fontWeight: FontWeight.bold) - : theme.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.bold, - ), - ), - IconButton( - icon: const Icon(Icons.search), - onPressed: _toggleSearching, - ), - ], - ), - ), - Container( - decoration: const BoxDecoration(), - child: Scrollbar( - thumbVisibility: true, - controller: _scrollController, - child: Padding( - padding: const EdgeInsets.only(bottom: 16.0), - child: SingleChildScrollView( - controller: _scrollController, - scrollDirection: Axis.horizontal, - child: Row( - spacing: 8.0, - children: cards, - ), - ), - ), - ), - ), - ], - ); - } -} diff --git a/lib/pangea/toolbar/widgets/word_zoom/morphological_list_item.dart b/lib/pangea/toolbar/widgets/word_zoom/morphological_list_item.dart index fef3814c1..74e3faa80 100644 --- a/lib/pangea/toolbar/widgets/word_zoom/morphological_list_item.dart +++ b/lib/pangea/toolbar/widgets/word_zoom/morphological_list_item.dart @@ -278,16 +278,14 @@ class MorphMeaningPopupState extends State { null) ConstructXpWidget( id: widget.cId, - onTap: () => showDialog( - context: context, - builder: (context) => AnalyticsPopupWrapper( - constructZoom: widget.cId, - view: ConstructTypeEnum.morph, - backButtonOverride: IconButton( - icon: const Icon(Icons.close), - onPressed: () => - Navigator.of(context).pop(), - ), + onTap: () => AnalyticsPopupWrapper.show( + context, + constructZoom: widget.cId, + view: ConstructTypeEnum.morph, + backButtonOverride: IconButton( + icon: const Icon(Icons.close), + onPressed: () => + Navigator.of(context).pop(), ), ), ), diff --git a/lib/pangea/toolbar/widgets/word_zoom/word_zoom_widget.dart b/lib/pangea/toolbar/widgets/word_zoom/word_zoom_widget.dart index dcb86b6eb..19cc7eb2a 100644 --- a/lib/pangea/toolbar/widgets/word_zoom/word_zoom_widget.dart +++ b/lib/pangea/toolbar/widgets/word_zoom/word_zoom_widget.dart @@ -89,12 +89,10 @@ class WordZoomWidget extends StatelessWidget { ), ConstructXpWidget( id: token.vocabConstructID, - onTap: () => showDialog( - context: context, - builder: (context) => AnalyticsPopupWrapper( - constructZoom: token.vocabConstructID, - view: ConstructTypeEnum.vocab, - ), + onTap: () => AnalyticsPopupWrapper.show( + context, + constructZoom: token.vocabConstructID, + view: ConstructTypeEnum.vocab, ), ), ], diff --git a/lib/widgets/navigation_rail.dart b/lib/widgets/navigation_rail.dart index ac14abdb9..7d8a93f39 100644 --- a/lib/widgets/navigation_rail.dart +++ b/lib/widgets/navigation_rail.dart @@ -42,7 +42,7 @@ class SpacesNavigationRail extends StatelessWidget { .startsWith('/rooms/settings'); // #Pangea final path = GoRouter.of(context).routeInformationProvider.value.uri.path; - final isHomepage = path.contains('homepage'); + final isAnalytics = path.contains('analytics'); final isCommunities = path.contains('communities'); final isColumnMode = FluffyThemes.isColumnMode(context); @@ -89,10 +89,10 @@ class SpacesNavigationRail extends StatelessWidget { // #Pangea if (i == 0) { return NaviRailItem( - isSelected: isHomepage, + isSelected: isAnalytics, onTap: () { clearActiveSpace?.call(); - context.go("/rooms/homepage"); + context.go("/rooms/analytics"); }, backgroundColor: Colors.transparent, icon: FutureBuilder( @@ -125,7 +125,7 @@ class SpacesNavigationRail extends StatelessWidget { // isSelected: activeSpaceId == null && !isSettings, isSelected: activeSpaceId == null && !isSettings && - !isHomepage && + !isAnalytics && !isCommunities, // Pangea# onTap: onGoToChats, From 312dc269fff9fed9a6c30439866c9fb28e88d45c Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 2 Jul 2025 11:19:13 -0400 Subject: [PATCH 2/2] chore: set vocab as default analytics page content --- .../analytics_details_popup.dart | 20 +-- .../morph_analytics_list_view.dart | 28 ++-- .../vocab_analytics_list_view.dart | 147 ++++++++---------- lib/pangea/analytics_page/analytics_page.dart | 2 +- 4 files changed, 97 insertions(+), 100 deletions(-) diff --git a/lib/pangea/analytics_details_popup/analytics_details_popup.dart b/lib/pangea/analytics_details_popup/analytics_details_popup.dart index bc5dced54..e43cbc998 100644 --- a/lib/pangea/analytics_details_popup/analytics_details_popup.dart +++ b/lib/pangea/analytics_details_popup/analytics_details_popup.dart @@ -202,15 +202,17 @@ class AnalyticsPopupWrapperState extends State { if (kIsWeb) const SizedBox(width: 4.0), ], ) - : AppBar( - leading: widget.backButtonOverride ?? - (localConstructZoom != null - ? IconButton( - icon: const Icon(Icons.arrow_back), - onPressed: () => setConstructZoom(null), - ) - : const SizedBox()), - ), + : localConstructZoom != null + ? AppBar( + leading: widget.backButtonOverride ?? + (localConstructZoom != null + ? IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () => setConstructZoom(null), + ) + : const SizedBox()), + ) + : null, body: localView == ConstructTypeEnum.morph ? localConstructZoom == null ? MorphAnalyticsListView(controller: this) diff --git a/lib/pangea/analytics_details_popup/morph_analytics_list_view.dart b/lib/pangea/analytics_details_popup/morph_analytics_list_view.dart index 7d5ccde38..bef1f92b2 100644 --- a/lib/pangea/analytics_details_popup/morph_analytics_list_view.dart +++ b/lib/pangea/analytics_details_popup/morph_analytics_list_view.dart @@ -227,18 +227,22 @@ class MorphTagChip extends StatelessWidget { 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, + Flexible( + child: Text( + getGrammarCopy( + category: morphFeature, + lemma: morphTag, + context: context, + ) ?? + morphTag, + style: TextStyle( + fontWeight: FontWeight.bold, + color: theme.brightness == Brightness.dark + ? Colors.white + : Colors.black, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, ), ), ], diff --git a/lib/pangea/analytics_details_popup/vocab_analytics_list_view.dart b/lib/pangea/analytics_details_popup/vocab_analytics_list_view.dart index c1e00254a..7db44d463 100644 --- a/lib/pangea/analytics_details_popup/vocab_analytics_list_view.dart +++ b/lib/pangea/analytics_details_popup/vocab_analytics_list_view.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; +import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pangea/analytics_details_popup/analytics_details_popup.dart'; import 'package:fluffychat/pangea/analytics_details_popup/vocab_analytics_list_tile.dart'; import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart'; @@ -78,92 +79,82 @@ class VocabAnalyticsListView extends StatelessWidget { ), ); - return Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - children: [ - const InstructionsInlineTooltip( - instructionsEnum: InstructionsEnum.analyticsVocabList, + return Column( + children: [ + const InstructionsInlineTooltip( + instructionsEnum: InstructionsEnum.analyticsVocabList, + ), + AnimatedContainer( + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + padding: EdgeInsets.symmetric( + horizontal: controller.isSearching ? 8.0 : 24.0, ), - AnimatedContainer( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - padding: EdgeInsets.symmetric( - horizontal: controller.isSearching ? 8.0 : 24.0, - ), - child: Container( - height: 60, - alignment: Alignment.center, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 250.0), - child: AnimatedSwitcher( - duration: const Duration(milliseconds: 300), - transitionBuilder: (child, animation) => FadeTransition( - opacity: animation, - child: child, - ), - child: controller.isSearching - ? Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - key: const ValueKey('search'), - children: [ - Expanded( - child: TextField( - autofocus: true, - controller: controller.searchController, - decoration: const InputDecoration( - contentPadding: EdgeInsets.symmetric( - vertical: 6.0, - horizontal: 12.0, - ), - isDense: true, - border: OutlineInputBorder(), - ), - ), - ), - IconButton( - icon: const Icon(Icons.close), - onPressed: controller.toggleSearching, - ), - ], - ) - : Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - key: const ValueKey('filters'), - children: filters, + child: Container( + height: 60, + alignment: Alignment.center, + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + transitionBuilder: (child, animation) => FadeTransition( + opacity: animation, + child: child, + ), + child: controller.isSearching + ? Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + key: const ValueKey('search'), + children: [ + Expanded( + child: TextField( + autofocus: true, + controller: controller.searchController, + decoration: const InputDecoration( + contentPadding: EdgeInsets.symmetric( + vertical: 6.0, + horizontal: 12.0, + ), + isDense: true, + border: OutlineInputBorder(), ), + ), + ), + IconButton( + icon: const Icon(Icons.close), + onPressed: controller.toggleSearching, + ), + ], + ) + : Row( + spacing: FluffyThemes.isColumnMode(context) ? 16.0 : 4.0, + mainAxisAlignment: MainAxisAlignment.center, + key: const ValueKey('filters'), + children: filters, ), - ), - ], - ), ), ), - Expanded( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: GridView.builder( - gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: 100.0, - mainAxisExtent: 100.0, - crossAxisSpacing: 8.0, - mainAxisSpacing: 8.0, - ), - itemCount: _filteredVocab.length, - itemBuilder: (context, index) { - final vocabItem = _filteredVocab[index]; - return VocabAnalyticsListTile( - onTap: () => controller.setConstructZoom(vocabItem.id), - constructUse: vocabItem, - ); - }, + ), + Expanded( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: GridView.builder( + gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 100.0, + mainAxisExtent: 100.0, + crossAxisSpacing: 8.0, + mainAxisSpacing: 8.0, ), + itemCount: _filteredVocab.length, + itemBuilder: (context, index) { + final vocabItem = _filteredVocab[index]; + return VocabAnalyticsListTile( + onTap: () => controller.setConstructZoom(vocabItem.id), + constructUse: vocabItem, + ); + }, ), ), - ], - ), + ), + ], ); } } diff --git a/lib/pangea/analytics_page/analytics_page.dart b/lib/pangea/analytics_page/analytics_page.dart index e8f8243dd..c285a1030 100644 --- a/lib/pangea/analytics_page/analytics_page.dart +++ b/lib/pangea/analytics_page/analytics_page.dart @@ -11,7 +11,7 @@ class AnalyticsPage extends StatefulWidget { } class AnalyticsPageState extends State { - ProgressIndicatorEnum? selectedIndicator = ProgressIndicatorEnum.level; + ProgressIndicatorEnum? selectedIndicator = ProgressIndicatorEnum.wordsUsed; void onIndicatorSelected(ProgressIndicatorEnum indicator) => setState(() { selectedIndicator = indicator;