From 94e55d9940726f766126bf8b82d01cdc4455f1f7 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Fri, 12 Sep 2025 14:53:26 -0400 Subject: [PATCH] update activity launch page buttons, sort activities in topics by number of participants (#3974) --- lib/l10n/intl_en.arb | 11 +- .../activity_session_start_page.dart | 22 +++ .../activity_sessions_start_view.dart | 147 ++++++++++++------ .../course_settings/course_settings.dart | 14 +- 4 files changed, 140 insertions(+), 54 deletions(-) diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 72e1ba83e..e3ee4ecbe 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -5234,5 +5234,14 @@ "moreLabel": "more", "promoCodeInfo": "Promo codes can be entered on the next page", "editsComingSoon": "The ability to edit cities and activities is coming soon.", - "editing": "Editing" + "editing": "Editing", + "activityNeedsMembers": "Uh oh! This activity needs {num} more people.", + "@activityNeedsMembers": { + "placeholders": { + "num": { + "type": "int" + } + } + }, + "inviteFriendsToCourse": "Invite friends to my course" } diff --git a/lib/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart b/lib/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart index 93a7b9609..76407d2c8 100644 --- a/lib/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart +++ b/lib/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart @@ -177,6 +177,28 @@ class ActivitySessionStartController extends State { if (mounted) setState(() => _selectedRoleId = id); } + Future courseHasEnoughParticipants() async { + final roomParticipants = widget.room?.getParticipants() ?? []; + final courseParticipants = await parent?.requestParticipants( + [Membership.join, Membership.invite, Membership.knock], + false, + true, + ) ?? + []; + + final botInRoom = roomParticipants.any( + (p) => p.id == BotName.byEnvironment, + ); + final botInCourse = courseParticipants.any( + (p) => p.id == BotName.byEnvironment, + ); + + final addBotToAvailableUsers = !botInCourse && !botInRoom; + final availableParticipants = + courseParticipants.length + (addBotToAvailableUsers ? 1 : 0); + return availableParticipants >= (activity?.req.numberOfParticipants ?? 0); + } + Future _loadActivity() async { try { setState(() { diff --git a/lib/pangea/activity_sessions/activity_session_start/activity_sessions_start_view.dart b/lib/pangea/activity_sessions/activity_session_start/activity_sessions_start_view.dart index 59cea6c42..5d7601645 100644 --- a/lib/pangea/activity_sessions/activity_session_start/activity_sessions_start_view.dart +++ b/lib/pangea/activity_sessions/activity_session_start/activity_sessions_start_view.dart @@ -127,54 +127,12 @@ class ActivitySessionStartView extends StatelessWidget { textAlign: TextAlign.center, ), if (controller.state == - SessionState.notStarted) ...[ - ElevatedButton( - style: buttonStyle, - onPressed: () => context.go( - "/rooms/spaces/${controller.widget.parentId}/activity/${controller.widget.activityId}?new=true", - ), - child: Row( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - Text( - L10n.of(context) - .startNewSession, - ), - ], - ), - ), - ElevatedButton( - style: buttonStyle, - onPressed: controller - .canJoinExistingSession - ? () async { - final resp = - await showFutureLoadingDialog( - context: context, - future: controller - .joinExistingSession, - ); - - if (!resp.isError) { - context.go( - "/rooms/spaces/${controller.widget.parentId}/${resp.result}", - ); - } - } - : null, - child: Row( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - Text( - L10n.of(context) - .joinOpenSession, - ), - ], - ), - ), - ] else if (controller.state == + SessionState.notStarted) + _ActivityStartButtons( + controller, + buttonStyle, + ) + else if (controller.state == SessionState.confirmedRole) ...[ if (controller.room!.courseParent != null) @@ -280,3 +238,96 @@ class ActivitySessionStartView extends StatelessWidget { ); } } + +class _ActivityStartButtons extends StatelessWidget { + final ActivitySessionStartController controller; + final ButtonStyle buttonStyle; + const _ActivityStartButtons( + this.controller, + this.buttonStyle, + ); + + @override + Widget build(BuildContext context) { + final hasActiveSession = controller.canJoinExistingSession; + return FutureBuilder( + future: controller.courseHasEnoughParticipants(), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const LinearProgressIndicator(); + } + + final bool hasEnoughParticipants = snapshot.data ?? true; + return Column( + spacing: 16.0, + children: [ + if (!hasEnoughParticipants) ...[ + Text( + L10n.of(context).activityNeedsMembers( + (controller.activity?.req.numberOfParticipants ?? 2) - 1, + ), + textAlign: TextAlign.center, + ), + if (controller.parent?.canInvite ?? false) + ElevatedButton( + style: buttonStyle, + onPressed: () => context.go( + "/rooms/spaces/${controller.parent!.id}/invite", + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + L10n.of(context).inviteFriendsToCourse, + ), + ], + ), + ), + ] else ...[ + ElevatedButton( + style: buttonStyle, + onPressed: () => context.go( + "/rooms/spaces/${controller.widget.parentId}/activity/${controller.widget.activityId}?new=true", + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + hasActiveSession + ? L10n.of(context).startNewSession + : L10n.of(context).start, + ), + ], + ), + ), + if (hasActiveSession) + ElevatedButton( + style: buttonStyle, + onPressed: () async { + final resp = await showFutureLoadingDialog( + context: context, + future: controller.joinExistingSession, + ); + + if (!resp.isError) { + context.go( + "/rooms/spaces/${controller.widget.parentId}/${resp.result}", + ); + } + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + L10n.of(context).joinOpenSession, + ), + ], + ), + ), + ], + ], + ); + }, + ); + } +} diff --git a/lib/pangea/course_settings/course_settings.dart b/lib/pangea/course_settings/course_settings.dart index 4e3c8ac78..2880acdce 100644 --- a/lib/pangea/course_settings/course_settings.dart +++ b/lib/pangea/course_settings/course_settings.dart @@ -65,6 +65,12 @@ class CourseSettings extends StatelessWidget { children: course.loadedTopics.mapIndexed((index, topic) { final unlocked = index <= currentTopicIndex; final usersInTopic = topicsToUsers[topic.uuid] ?? []; + final activities = topic.loadedActivities; + activities.sort( + (a, b) => a.req.numberOfParticipants.compareTo( + b.req.numberOfParticipants, + ), + ); return AbsorbPointer( absorbing: !unlocked, child: Opacity( @@ -168,10 +174,9 @@ class CourseSettings extends StatelessWidget { height: isColumnMode ? 290.0 : 210.0, child: ListView.builder( scrollDirection: Axis.horizontal, - itemCount: topic.loadedActivities.length, + itemCount: activities.length, itemBuilder: (context, index) { - final activityId = - topic.loadedActivities[index].activityId; + final activityId = activities[index].activityId; final complete = room.hasCompletedActivity( room.client.userID!, @@ -182,8 +187,7 @@ class CourseSettings extends StatelessWidget { activityId, ); - final activity = topic.loadedActivities[index]; - + final activity = activities[index]; return Padding( padding: const EdgeInsets.only(right: 24.0), child: MouseRegion(