From 4aac64e6b522c6e58262e7776f137c408025f9d3 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Thu, 10 Jul 2025 11:33:53 -0400 Subject: [PATCH] chore: show leaderboard on mobile (#3391) --- lib/pages/chat_list/space_view.dart | 10 ++ .../widgets/leaderboard_participant_list.dart | 152 ++++++++++++++++++ 2 files changed, 162 insertions(+) create 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 b53a64876..9de3a0f35 100644 --- a/lib/pages/chat_list/space_view.dart +++ b/lib/pages/chat_list/space_view.dart @@ -21,6 +21,7 @@ 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'; @@ -854,6 +855,15 @@ class _SpaceViewState extends State { // }, // ), KnockingUsersIndicator(room: room), + if (!FluffyThemes.isColumnMode(context)) + SliverList.builder( + itemCount: 1, + itemBuilder: (context, i) { + return LeaderboardParticipantList( + space: room, + ); + }, + ), // Pangea# SliverList.builder( itemCount: joinedRooms.length, diff --git a/lib/pangea/spaces/widgets/leaderboard_participant_list.dart b/lib/pangea/spaces/widgets/leaderboard_participant_list.dart new file mode 100644 index 000000000..a1bcdde8d --- /dev/null +++ b/lib/pangea/spaces/widgets/leaderboard_participant_list.dart @@ -0,0 +1,152 @@ +import 'package:flutter/material.dart'; + +import 'package:matrix/matrix.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, + ), + ), + ), + ), + ), + ); + }, + ); + }, + ), + ), + ), + ); + }, + ); + }, + ); + } +}