From ad1a84bc5cc0b4761e43d985ad9eae06d4a10209 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Thu, 10 Jul 2025 11:28:28 -0400 Subject: [PATCH] 3263 duplicate participant list causes confusion (#3390) * chore: remove participant list from space view * chore: don't change participant list rendering based on screen sizwe --- lib/pages/chat_list/space_view.dart | 9 - .../pages/pangea_chat_details.dart | 313 +++++++++--------- .../spaces/utils/load_participants_util.dart | 25 ++ .../widgets/leaderboard_participant_list.dart | 177 ---------- 4 files changed, 177 insertions(+), 347 deletions(-) delete mode 100644 lib/pangea/spaces/widgets/leaderboard_participant_list.dart diff --git a/lib/pages/chat_list/space_view.dart b/lib/pages/chat_list/space_view.dart index dacfcc956..b53a64876 100644 --- a/lib/pages/chat_list/space_view.dart +++ b/lib/pages/chat_list/space_view.dart @@ -21,7 +21,6 @@ import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/pangea/public_spaces/public_room_bottom_sheet.dart'; import 'package:fluffychat/pangea/spaces/constants/space_constants.dart'; import 'package:fluffychat/pangea/spaces/widgets/knocking_users_indicator.dart'; -import 'package:fluffychat/pangea/spaces/widgets/leaderboard_participant_list.dart'; import 'package:fluffychat/utils/localized_exception_extension.dart'; import 'package:fluffychat/utils/stream_extension.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; @@ -855,14 +854,6 @@ class _SpaceViewState extends State { // }, // ), KnockingUsersIndicator(room: room), - SliverList.builder( - itemCount: 1, - itemBuilder: (context, i) { - return LeaderboardParticipantList( - space: room, - ); - }, - ), // Pangea# SliverList.builder( itemCount: joinedRooms.length, diff --git a/lib/pangea/chat_settings/pages/pangea_chat_details.dart b/lib/pangea/chat_settings/pages/pangea_chat_details.dart index 76fbb3e99..b32b65ab2 100644 --- a/lib/pangea/chat_settings/pages/pangea_chat_details.dart +++ b/lib/pangea/chat_settings/pages/pangea_chat_details.dart @@ -11,7 +11,6 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pages/chat_details/chat_details.dart'; -import 'package:fluffychat/pages/chat_details/participant_list_item.dart'; import 'package:fluffychat/pangea/analytics_misc/level_display_name.dart'; import 'package:fluffychat/pangea/bot/utils/bot_name.dart'; import 'package:fluffychat/pangea/bot/widgets/bot_face_svg.dart'; @@ -671,117 +670,112 @@ class RoomParticipantsSection extends StatelessWidget { @override Widget build(BuildContext context) { final theme = Theme.of(context); - final List members = room.getParticipants().toList() - ..sort((b, a) => a.powerLevel.compareTo(b.powerLevel)); + return LoadParticipantsUtil( + space: room, + builder: (participantsLoader) { + final filteredParticipants = + participantsLoader.filteredParticipants(""); - final actualMembersCount = (room.summary.mInvitedMemberCount ?? 0) + - (room.summary.mJoinedMemberCount ?? 0); + final originalLeaders = filteredParticipants.take(3).toList(); + filteredParticipants.sort((a, b) { + // always sort bot to the end + final aIsBot = a.id == BotName.byEnvironment; + final bIsBot = b.id == BotName.byEnvironment; + if (aIsBot && !bIsBot) { + return 1; + } else if (bIsBot && !aIsBot) { + return -1; + } - return LayoutBuilder( - builder: (context, constraints) { - final availableWidth = constraints.maxWidth; - final capacity = (availableWidth / (_width + _spacing)).floor(); - return LoadParticipantsUtil( - space: room, - builder: (participantsLoader) { - final filteredParticipants = - participantsLoader.filteredParticipants(""); + // put knocking users at the front + if (a.membership == Membership.knock && + b.membership != Membership.knock) { + return -1; + } else if (b.membership == Membership.knock && + a.membership != Membership.knock) { + return 1; + } - filteredParticipants.sort((a, b) { - // always sort bot to the end - final aIsBot = a.id == BotName.byEnvironment; - final bIsBot = b.id == BotName.byEnvironment; - if (aIsBot && !bIsBot) { - return 1; - } else if (bIsBot && !aIsBot) { - return -1; - } + // then invited users + if (a.membership == Membership.invite && + b.membership != Membership.invite) { + return -1; + } else if (b.membership == Membership.invite && + a.membership != Membership.invite) { + return 1; + } - // put knocking users at the front - if (a.membership == Membership.knock && - b.membership != Membership.knock) { - return -1; - } else if (b.membership == Membership.knock && - a.membership != Membership.knock) { - return 1; - } + // then admins + if (a.powerLevel == 100 && b.powerLevel != 100) { + return -1; + } else if (b.powerLevel == 100 && a.powerLevel != 100) { + return 1; + } - // then invited users - if (a.membership == Membership.invite && - b.membership != Membership.invite) { - return -1; - } else if (b.membership == Membership.invite && - a.membership != Membership.invite) { - return 1; - } + return 0; + }); - // then admins - if (a.powerLevel == 100 && b.powerLevel != 100) { - return -1; - } else if (b.powerLevel == 100 && a.powerLevel != 100) { - return 1; - } + return Wrap( + spacing: _spacing, + runSpacing: _spacing, + alignment: WrapAlignment.center, + runAlignment: WrapAlignment.center, + children: [ + ...filteredParticipants.mapIndexed((index, user) { + final permissionBatch = user.powerLevel >= 100 + ? L10n.of(context).admin + : user.powerLevel >= 50 + ? L10n.of(context).moderator + : ''; - return 0; - }); + final membershipBatch = switch (user.membership) { + Membership.ban => null, + Membership.invite => L10n.of(context).invited, + Membership.join => null, + Membership.knock => L10n.of(context).knocking, + Membership.leave => null, + }; - if (capacity < 4) { - return Column( - children: [ - ...filteredParticipants.map( - (member) => ParticipantListItem(member), - ), - if (actualMembersCount - members.length > 0) - ListTile( - title: Text( - L10n.of(context).loadCountMoreParticipants( - (actualMembersCount - members.length), - ), - ), - leading: CircleAvatar( - backgroundColor: theme.scaffoldBackgroundColor, - child: const Icon( - Icons.group_outlined, - color: Colors.grey, - ), - ), - onTap: () => context.push( - '/rooms/${room.id}/details/members', - ), - trailing: const Icon(Icons.chevron_right_outlined), - ), - ], + final publicProfile = participantsLoader.getPublicProfile( + user.id, ); - } - return Wrap( - spacing: _spacing, - runSpacing: _spacing, - alignment: WrapAlignment.center, - runAlignment: WrapAlignment.center, - children: [ - ...filteredParticipants.mapIndexed((index, user) { - final permissionBatch = user.powerLevel >= 100 - ? L10n.of(context).admin - : user.powerLevel >= 50 - ? L10n.of(context).moderator - : ''; + final leaderIndex = originalLeaders.indexOf(user); + LinearGradient? gradient; + if (leaderIndex != -1) { + gradient = leaderIndex.leaderboardGradient; + if (user.id == BotName.byEnvironment || + publicProfile == null || + publicProfile.level == null) { + gradient = null; + } + } - final membershipBatch = switch (user.membership) { - Membership.ban => null, - Membership.invite => L10n.of(context).invited, - Membership.join => null, - Membership.knock => L10n.of(context).knocking, - Membership.leave => null, - }; - - return SizedBox( - width: _width, - child: Opacity( - opacity: user.membership == Membership.join ? 1.0 : 0.5, - child: Column( - spacing: 4.0, + return SizedBox( + width: _width, + child: Opacity( + opacity: user.membership == Membership.join ? 1.0 : 0.5, + child: Column( + spacing: 4.0, + children: [ + Stack( + alignment: Alignment.center, children: [ + if (gradient != null) + CircleAvatar( + radius: _width / 2, + child: Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + gradient: gradient, + ), + ), + ) + else + SizedBox( + height: _width, + width: _width, + ), Builder( builder: (context) { return MouseRegion( @@ -795,7 +789,7 @@ class RoomParticipantsSection extends StatelessWidget { child: Avatar( mxContent: user.avatarUrl, name: user.calcDisplayname(), - size: _width, + size: _width - 6.0, presenceUserId: user.id, presenceOffset: const Offset(0, 0), presenceSize: 18.0, @@ -805,83 +799,80 @@ class RoomParticipantsSection extends StatelessWidget { ); }, ), - Text( - user.calcDisplayname(), - style: theme.textTheme.labelLarge?.copyWith( - color: theme.colorScheme.primary, - fontWeight: FontWeight.bold, - ), - overflow: TextOverflow.ellipsis, - ), - Container( - height: 20.0, - alignment: Alignment.center, - child: LevelDisplayName( - userId: user.id, - textStyle: theme.textTheme.labelSmall, - ), - ), - Container( - height: 24.0, - alignment: Alignment.center, - child: membershipBatch != null + ], + ), + Text( + user.calcDisplayname(), + style: theme.textTheme.labelLarge?.copyWith( + color: theme.colorScheme.primary, + fontWeight: FontWeight.bold, + ), + overflow: TextOverflow.ellipsis, + ), + Container( + height: 20.0, + alignment: Alignment.center, + child: LevelDisplayName( + userId: user.id, + textStyle: theme.textTheme.labelSmall, + ), + ), + Container( + height: 24.0, + alignment: Alignment.center, + child: membershipBatch != null + ? Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 4, + ), + decoration: BoxDecoration( + color: theme.colorScheme.secondaryContainer, + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, + ), + ), + child: Text( + membershipBatch, + style: theme.textTheme.labelSmall?.copyWith( + color: + theme.colorScheme.onSecondaryContainer, + ), + ), + ) + : permissionBatch.isNotEmpty ? Container( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 4, ), decoration: BoxDecoration( - color: - theme.colorScheme.secondaryContainer, + color: user.powerLevel >= 100 + ? theme.colorScheme.tertiary + : theme.colorScheme.tertiaryContainer, borderRadius: BorderRadius.circular( AppConfig.borderRadius, ), ), child: Text( - membershipBatch, + permissionBatch, style: theme.textTheme.labelSmall?.copyWith( - color: theme - .colorScheme.onSecondaryContainer, + color: user.powerLevel >= 100 + ? theme.colorScheme.onTertiary + : theme.colorScheme + .onTertiaryContainer, ), ), ) - : permissionBatch.isNotEmpty - ? Container( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 4, - ), - decoration: BoxDecoration( - color: user.powerLevel >= 100 - ? theme.colorScheme.tertiary - : theme.colorScheme - .tertiaryContainer, - borderRadius: BorderRadius.circular( - AppConfig.borderRadius, - ), - ), - child: Text( - permissionBatch, - style: theme.textTheme.labelSmall - ?.copyWith( - color: user.powerLevel >= 100 - ? theme.colorScheme.onTertiary - : theme.colorScheme - .onTertiaryContainer, - ), - ), - ) - : null, - ), - ], + : null, ), - ), - ); - }), - ], - ); - }, + ], + ), + ), + ); + }), + ], ); }, ); diff --git a/lib/pangea/spaces/utils/load_participants_util.dart b/lib/pangea/spaces/utils/load_participants_util.dart index c6b176bd8..eb95f2f6f 100644 --- a/lib/pangea/spaces/utils/load_participants_util.dart +++ b/lib/pangea/spaces/utils/load_participants_util.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; +import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/bot/utils/bot_name.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/user/models/profile_model.dart'; @@ -125,3 +126,27 @@ class LoadParticipantsUtilState extends State { return widget.builder(this); } } + +extension LeaderboardGradient on int { + LinearGradient? get leaderboardGradient { + final Color? color = this == 0 + ? AppConfig.gold + : this == 1 + ? Colors.grey[400]! + : this == 2 + ? Colors.brown[400]! + : null; + + if (color == null) return null; + + return LinearGradient( + colors: [ + color, + Colors.white, + color, + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ); + } +} diff --git a/lib/pangea/spaces/widgets/leaderboard_participant_list.dart b/lib/pangea/spaces/widgets/leaderboard_participant_list.dart deleted file mode 100644 index c9906cea9..000000000 --- a/lib/pangea/spaces/widgets/leaderboard_participant_list.dart +++ /dev/null @@ -1,177 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:matrix/matrix.dart'; - -import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/config/themes.dart'; -import 'package:fluffychat/pages/chat_list/status_msg_list.dart'; -import 'package:fluffychat/pangea/bot/utils/bot_name.dart'; -import 'package:fluffychat/pangea/spaces/utils/load_participants_util.dart'; -import 'package:fluffychat/utils/stream_extension.dart'; -import 'package:fluffychat/widgets/adaptive_dialogs/user_dialog.dart'; -import 'package:fluffychat/widgets/matrix.dart'; -import 'package:fluffychat/widgets/presence_builder.dart'; - -class LeaderboardParticipantList extends StatefulWidget { - final Room space; - - const LeaderboardParticipantList({ - required this.space, - super.key, - }); - - static const double height = 116; - - @override - State createState() => - LeaderboardParticipantListState(); -} - -class LeaderboardParticipantListState - extends State { - final _scrollController = ScrollController(); - - @override - void dispose() { - _scrollController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final client = Matrix.of(context).client; - final theme = Theme.of(context); - - return StreamBuilder( - stream: client.onSync.stream.rateLimit(const Duration(seconds: 3)), - builder: (context, snapshot) { - return LoadParticipantsUtil( - space: widget.space, - builder: (participantsLoader) { - final participants = participantsLoader - .filteredParticipants("") - .where((p) => p.membership == Membership.join) - .toList(); - - return AnimatedSize( - duration: FluffyThemes.animationDuration, - curve: Curves.easeInOut, - child: SizedBox( - height: 130.0, - child: Scrollbar( - controller: _scrollController, - child: ListView.builder( - controller: _scrollController, - padding: const EdgeInsets.fromLTRB( - 8.0, - 8.0, - 8.0, - 16.0, - ), - scrollDirection: Axis.horizontal, - itemCount: participants.length, - itemBuilder: (context, i) { - final user = participants[i]; - final publicProfile = participantsLoader.getPublicProfile( - user.id, - ); - - LinearGradient? gradient = i.leaderboardGradient; - - if (user.id == BotName.byEnvironment || - publicProfile == null || - publicProfile.level == null) { - gradient = null; - } - - return PresenceBuilder( - userId: user.id, - builder: (context, presence) { - Color? dotColor; - if (presence != null) { - dotColor = presence.presence.isOnline - ? Colors.green - : presence.presence.isUnavailable - ? Colors.orange - : Colors.grey; - } - - return PresenceAvatar( - presence: presence ?? - CachedPresence( - PresenceType.unavailable, - null, - null, - null, - user.id, - ), - height: StatusMessageList.height, - onTap: (profile) => UserDialog.show( - context: context, - profile: profile, - ), - gradient: gradient, - showPresence: false, - floatingIndicator: Positioned( - bottom: 0, - right: 0, - child: Container( - width: 16, - height: 16, - decoration: BoxDecoration( - color: theme.colorScheme.surface, - borderRadius: BorderRadius.circular(32), - ), - alignment: Alignment.center, - child: Container( - width: 10, - height: 10, - decoration: BoxDecoration( - color: dotColor, - borderRadius: BorderRadius.circular(16), - border: Border.all( - width: 1, - color: theme.colorScheme.surface, - ), - ), - ), - ), - ), - ); - }, - ); - }, - ), - ), - ), - ); - }, - ); - }, - ); - } -} - -extension LeaderboardGradient on int { - LinearGradient? get leaderboardGradient { - final Color? color = this == 0 - ? AppConfig.gold - : this == 1 - ? Colors.grey[400]! - : this == 2 - ? Colors.brown[400]! - : null; - - if (color == null) return null; - - return LinearGradient( - colors: [ - color, - Colors.white, - color, - ], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - ); - } -}