From 0a627ef85675fac44edfc082d888b8f62ac79786 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Fri, 1 Nov 2024 10:54:56 -0400 Subject: [PATCH] updates to main menu design --- lib/pages/chat_list/chat_list_body.dart | 7 +- lib/pages/chat_list/chat_list_header.dart | 16 ++- .../chat_list/client_chooser_button.dart | 57 +++++--- .../animations/progress_bar/level_bar.dart | 6 +- .../animations/progress_bar/progress_bar.dart | 56 +++++--- .../progress_bar/progress_bar_details.dart | 4 +- .../learning_progress_indicators.dart | 127 +++++++----------- .../analytics_summary/progress_indicator.dart | 60 ++++----- .../chat_list/pangea_chat_list_header.dart | 82 +++++++++++ 9 files changed, 266 insertions(+), 149 deletions(-) create mode 100644 lib/pangea/widgets/chat_list/pangea_chat_list_header.dart diff --git a/lib/pages/chat_list/chat_list_body.dart b/lib/pages/chat_list/chat_list_body.dart index 19e46b9ae..738a65126 100644 --- a/lib/pages/chat_list/chat_list_body.dart +++ b/lib/pages/chat_list/chat_list_body.dart @@ -1,11 +1,11 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pages/chat_list/chat_list.dart'; -import 'package:fluffychat/pages/chat_list/chat_list_header.dart'; import 'package:fluffychat/pages/chat_list/chat_list_item.dart'; import 'package:fluffychat/pages/chat_list/dummy_chat_list_item.dart'; import 'package:fluffychat/pages/chat_list/search_title.dart'; import 'package:fluffychat/pages/chat_list/space_view.dart'; import 'package:fluffychat/pages/user_bottom_sheet/user_bottom_sheet.dart'; +import 'package:fluffychat/pangea/widgets/chat_list/pangea_chat_list_header.dart'; import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; import 'package:fluffychat/utils/stream_extension.dart'; import 'package:fluffychat/widgets/avatar.dart'; @@ -78,7 +78,10 @@ class ChatListViewBody extends StatelessWidget { child: CustomScrollView( controller: controller.scrollController, slivers: [ - ChatListHeader(controller: controller), + // #Pangea + // ChatListHeader(controller: controller), + PangeaChatListHeader(controller: controller), + // Pangea# SliverList( delegate: SliverChildListDelegate( [ diff --git a/lib/pages/chat_list/chat_list_header.dart b/lib/pages/chat_list/chat_list_header.dart index 1f7a1fbe9..58655259a 100644 --- a/lib/pages/chat_list/chat_list_header.dart +++ b/lib/pages/chat_list/chat_list_header.dart @@ -107,9 +107,13 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget { ), ) // #Pangea - : SizedBox( + : const SizedBox( width: 0, - child: ClientChooserButton(controller), + child: ClientChooserButton( + // #Pangea + // controller + // Pangea# + ), ) // : TextButton.icon( // onPressed: controller.setServer, @@ -127,9 +131,13 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget { // ), // ) // Pangea# - : SizedBox( + : const SizedBox( width: 0, - child: ClientChooserButton(controller), + child: ClientChooserButton( + // #Pangea + // controller + // Pangea# + ), ), ), ), diff --git a/lib/pages/chat_list/client_chooser_button.dart b/lib/pages/chat_list/client_chooser_button.dart index 0ac5ff0a8..46f5b944a 100644 --- a/lib/pages/chat_list/client_chooser_button.dart +++ b/lib/pages/chat_list/client_chooser_button.dart @@ -11,12 +11,18 @@ import 'package:go_router/go_router.dart'; // import 'package:keyboard_shortcuts/keyboard_shortcuts.dart'; import 'package:matrix/matrix.dart'; -import 'chat_list.dart'; - class ClientChooserButton extends StatelessWidget { - final ChatListController controller; + // #Pangea + // final ChatListController controller; + // Pangea# - const ClientChooserButton(this.controller, {super.key}); + const ClientChooserButton( + // #Pangea + // this.controller, + // Pangea# + { + super.key, + }); List> _bundleMenuItems(BuildContext context) { final matrix = Matrix.of(context); @@ -268,12 +274,27 @@ class ClientChooserButton extends StatelessWidget { child: Material( color: Colors.transparent, borderRadius: BorderRadius.circular(99), - child: Avatar( - mxContent: snapshot.data?.avatarUrl, - name: snapshot.data?.displayName ?? - matrix.client.userID!.localpart, - size: 32, + child: + // #Pangea + Stack( + alignment: Alignment.bottomRight, + children: [ + Padding( + padding: const EdgeInsets.all(4), + child: + // Pangea# + Avatar( + mxContent: snapshot.data?.avatarUrl, + name: snapshot.data?.displayName ?? + matrix.client.userID!.localpart, + size: 50, + ), + // #Pangea + ), + const Icon(Icons.settings_outlined, size: 20), + ], ), + // Pangea# ), ), ], @@ -296,11 +317,14 @@ class ClientChooserButton extends StatelessWidget { Object object, BuildContext context, ) async { - if (object is Client) { - controller.setActiveClient(object); - } else if (object is String) { - controller.setActiveBundle(object); - } else if (object is SettingsAction) { + // #Pangea + // if (object is Client) { + // controller.setActiveClient(object); + // } else if (object is String) { + // controller.setActiveBundle(object); + // } else + // Pangea# + if (object is SettingsAction) { switch (object) { case SettingsAction.addAccount: final consent = await showOkCancelAlertDialog( @@ -319,7 +343,10 @@ class ClientChooserButton extends StatelessWidget { // break; // Pangea# case SettingsAction.newSpace: - controller.createNewSpace(); + // #Pangea + // controller.createNewSpace(); + context.push('/rooms/newspace'); + // Pangea# break; // #Pangea // case SettingsAction.invite: diff --git a/lib/pangea/widgets/animations/progress_bar/level_bar.dart b/lib/pangea/widgets/animations/progress_bar/level_bar.dart index fb8461f43..78b4d4944 100644 --- a/lib/pangea/widgets/animations/progress_bar/level_bar.dart +++ b/lib/pangea/widgets/animations/progress_bar/level_bar.dart @@ -19,12 +19,14 @@ class LevelBar extends StatefulWidget { class LevelBarState extends State { double prevWidth = 0; + double get width => + widget.progressBarDetails.totalWidth * widget.details.widthMultiplier; @override void didUpdateWidget(covariant LevelBar oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.details.currentPoints != widget.details.currentPoints) { - setState(() => prevWidth = widget.details.width); + setState(() => prevWidth = width); } } @@ -33,7 +35,7 @@ class LevelBarState extends State { return AnimatedLevelBar( height: widget.progressBarDetails.height, beginWidth: prevWidth, - endWidth: widget.details.width, + endWidth: width, decoration: BoxDecoration( borderRadius: const BorderRadius.all( Radius.circular(AppConfig.borderRadius), diff --git a/lib/pangea/widgets/animations/progress_bar/progress_bar.dart b/lib/pangea/widgets/animations/progress_bar/progress_bar.dart index ea0263a3c..a8383bae5 100644 --- a/lib/pangea/widgets/animations/progress_bar/progress_bar.dart +++ b/lib/pangea/widgets/animations/progress_bar/progress_bar.dart @@ -6,31 +6,55 @@ import 'package:flutter/material.dart'; // Provide an order list of level indicators, each with it's color // and stream. Also provide an overall width and pointsPerLevel. -class ProgressBar extends StatelessWidget { +class ProgressBar extends StatefulWidget { final List levelBars; - final ProgressBarDetails progressBarDetails; const ProgressBar({ super.key, required this.levelBars, - required this.progressBarDetails, }); + @override + ProgressBarState createState() => ProgressBarState(); +} + +class ProgressBarState extends State { + double width = 0; + void setWidth(double newWidth) { + if (width != newWidth) { + setState(() => width = newWidth); + } + } + + get progressBarDetails => ProgressBarDetails( + totalWidth: width, + borderColor: Theme.of(context).colorScheme.primary.withOpacity(0.5), + ); + @override Widget build(BuildContext context) { - return Stack( - alignment: Alignment.centerLeft, - children: [ - ProgressBarBackground(details: progressBarDetails), - for (final levelBar in levelBars) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 2), - child: LevelBar( - details: levelBar, - progressBarDetails: progressBarDetails, - ), - ), - ], + return LayoutBuilder( + builder: (context, constraints) { + if (width != constraints.maxWidth) { + WidgetsBinding.instance.addPostFrameCallback( + (_) => setWidth(constraints.maxWidth), + ); + } + return Stack( + alignment: Alignment.centerLeft, + children: [ + ProgressBarBackground(details: progressBarDetails), + for (final levelBar in widget.levelBars) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 2), + child: LevelBar( + details: levelBar, + progressBarDetails: progressBarDetails, + ), + ), + ], + ); + }, ); } } diff --git a/lib/pangea/widgets/animations/progress_bar/progress_bar_details.dart b/lib/pangea/widgets/animations/progress_bar/progress_bar_details.dart index 9ff4df142..347a98a49 100644 --- a/lib/pangea/widgets/animations/progress_bar/progress_bar_details.dart +++ b/lib/pangea/widgets/animations/progress_bar/progress_bar_details.dart @@ -3,12 +3,12 @@ import 'dart:ui'; class LevelBarDetails { final Color fillColor; final int currentPoints; - final double width; + final double widthMultiplier; const LevelBarDetails({ required this.fillColor, required this.currentPoints, - required this.width, + required this.widthMultiplier, }); } 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 1509386e1..980ee1488 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 @@ -1,22 +1,21 @@ import 'dart:async'; -import 'package:fluffychat/config/themes.dart'; +import 'package:fluffychat/pages/chat_list/client_chooser_button.dart'; import 'package:fluffychat/pangea/controllers/get_analytics_controller.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.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/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/progress_indicator.dart'; -import 'package:fluffychat/widgets/avatar.dart'; +import 'package:fluffychat/pangea/widgets/flag.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:matrix/matrix.dart'; /// A summary of "My Analytics" shown at the top of the chat list /// It shows a variety of progress indicators such as @@ -120,7 +119,7 @@ class LearningProgressIndicatorsState } } - double get levelBarWidth => FluffyThemes.columnWidth - (32 * 2) - 25; + // double get levelBarWidth => FluffyThemes.columnWidth - (32 * 2) - 25; Color levelColor(int level) { final colors = [ @@ -147,19 +146,14 @@ class LearningProgressIndicatorsState ? const Color.fromARGB(255, 0, 190, 83) : Theme.of(context).colorScheme.primary, currentPoints: currentXP, - width: levelBarWidth * _pangeaController.analytics.levelProgress, + widthMultiplier: _pangeaController.analytics.levelProgress, ), LevelBarDetails( fillColor: Theme.of(context).colorScheme.primary, currentPoints: serverXP, - width: - levelBarWidth * _pangeaController.analytics.serverLevelProgress, + widthMultiplier: _pangeaController.analytics.serverLevelProgress, ), ], - progressBarDetails: ProgressBarDetails( - totalWidth: levelBarWidth, - borderColor: Theme.of(context).colorScheme.primary.withOpacity(0.5), - ), ); final levelBadge = Container( @@ -184,84 +178,67 @@ class LearningProgressIndicatorsState ), ); - return Stack( - alignment: Alignment.topCenter, + return Row( children: [ - // const Positioned( - // child: PointsGainedAnimation(), - // ), - Column( - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(46, 0, 32, 4), - child: Row( + const ClientChooserButton(), + const SizedBox(width: 6), + Expanded( + child: Column( + children: [ + Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - FutureBuilder( - future: _pangeaController.matrixState.client - .getProfileFromUserId( - _pangeaController.matrixState.client.userID!, - ), - builder: (context, snapshot) { - final mxid = Matrix.of(context).client.userID ?? - L10n.of(context)!.user; - return Avatar( - name: snapshot.data?.displayName ?? - mxid.localpart ?? - mxid, - mxContent: snapshot.data?.avatarUrl, - size: 40, - ); - }, - ), Row( - mainAxisAlignment: MainAxisAlignment.center, children: ProgressIndicatorEnum.values - .where( - (indicator) => - indicator != ProgressIndicatorEnum.level, - ) + .where((i) => i != ProgressIndicatorEnum.level) .map( - (indicator) => ProgressIndicatorBadge( - points: getProgressPoints(indicator), - onTap: () { - final model = getConstructsModel(indicator); - if (model == null) return; - showDialog( - context: context, - builder: (c) => AnalyticsPopup( - indicator: indicator, - constructsModel: model, - ), - ); - }, - progressIndicator: indicator, - loading: loading, + (indicator) => Padding( + padding: const EdgeInsets.only(right: 10), + child: ProgressIndicatorBadge( + points: getProgressPoints(indicator), + onTap: () { + final model = getConstructsModel(indicator); + if (model == null) return; + showDialog( + context: context, + builder: (c) => AnalyticsPopup( + indicator: indicator, + constructsModel: model, + ), + ); + }, + progressIndicator: indicator, + loading: loading, + ), ), ) .toList(), ), + InkWell( + onTap: () => showDialog( + context: context, + builder: (c) => const SettingsLearning(), + ), + child: LanguageFlag( + language: _pangeaController.languageController.userL2, + size: 24, + ), + ), ], ), - ), - Center( - child: SizedBox( + const SizedBox(height: 6), + SizedBox( height: 36, - child: SizedBox( - width: levelBarWidth + 16, - child: Stack( - alignment: Alignment.center, - children: [ - Positioned(left: 16, right: 0, child: progressBar), - Positioned(left: 0, child: levelBadge), - ], - ), + child: Stack( + alignment: Alignment.center, + children: [ + Positioned(left: 16, right: 0, child: progressBar), + Positioned(left: 0, child: levelBadge), + ], ), ), - ), - const SizedBox(height: 16), - ], + ], + ), ), ], ); diff --git a/lib/pangea/widgets/chat_list/analytics_summary/progress_indicator.dart b/lib/pangea/widgets/chat_list/analytics_summary/progress_indicator.dart index bd24b206f..f05ac634c 100644 --- a/lib/pangea/widgets/chat_list/analytics_summary/progress_indicator.dart +++ b/lib/pangea/widgets/chat_list/analytics_summary/progress_indicator.dart @@ -18,40 +18,34 @@ class ProgressIndicatorBadge extends StatelessWidget { @override Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5), - child: Tooltip( - message: progressIndicator.tooltip(context), - child: InkWell( - onTap: onTap, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - progressIndicator.icon, - color: progressIndicator.color(context), - ), - const SizedBox(width: 5), - !loading - ? Text( - points?.toString() ?? '0', - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ) - : const SizedBox( - height: 8, - width: 8, - child: CircularProgressIndicator.adaptive( - strokeWidth: 2, - ), - ), - ], + return Tooltip( + message: progressIndicator.tooltip(context), + child: InkWell( + onTap: onTap, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + progressIndicator.icon, + color: progressIndicator.color(context), ), - ), + const SizedBox(width: 5), + !loading + ? Text( + points?.toString() ?? '0', + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ) + : const SizedBox( + height: 8, + width: 8, + child: CircularProgressIndicator.adaptive( + strokeWidth: 2, + ), + ), + ], ), ), ); diff --git a/lib/pangea/widgets/chat_list/pangea_chat_list_header.dart b/lib/pangea/widgets/chat_list/pangea_chat_list_header.dart new file mode 100644 index 000000000..5fdec6839 --- /dev/null +++ b/lib/pangea/widgets/chat_list/pangea_chat_list_header.dart @@ -0,0 +1,82 @@ +import 'package:fluffychat/pages/chat_list/chat_list.dart'; +import 'package:fluffychat/pangea/widgets/chat_list/analytics_summary/learning_progress_indicators.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; + +class PangeaChatListHeader extends StatelessWidget + implements PreferredSizeWidget { + final ChatListController controller; + final bool globalSearch; + + const PangeaChatListHeader({ + super.key, + required this.controller, + this.globalSearch = true, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return SliverList( + delegate: SliverChildListDelegate( + [ + Padding( + padding: const EdgeInsets.only( + top: 16, + left: 16, + right: 16, + ), + child: Column( + children: [ + const LearningProgressIndicators(), + const SizedBox(height: 16), + TextField( + controller: controller.searchController, + focusNode: controller.searchFocusNode, + textInputAction: TextInputAction.search, + onChanged: (text) => controller.onSearchEnter( + text, + globalSearch: globalSearch, + ), + decoration: InputDecoration( + filled: true, + fillColor: theme.colorScheme.secondaryContainer, + border: OutlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(99), + ), + contentPadding: EdgeInsets.zero, + hintText: L10n.of(context)!.searchChatsRooms, + hintStyle: TextStyle( + color: theme.colorScheme.onPrimaryContainer, + fontWeight: FontWeight.normal, + ), + floatingLabelBehavior: FloatingLabelBehavior.never, + prefixIcon: controller.isSearchMode + ? IconButton( + tooltip: L10n.of(context)!.cancel, + icon: const Icon(Icons.close_outlined), + onPressed: controller.cancelSearch, + color: theme.colorScheme.onPrimaryContainer, + ) + : IconButton( + onPressed: controller.startSearch, + icon: Icon( + Icons.search_outlined, + color: theme.colorScheme.onPrimaryContainer, + ), + ), + ), + ), + ], + ), + ), + ], + ), + ); + } + + @override + Size get preferredSize => const Size.fromHeight(56); +}