update activity launch page buttons, sort activities in topics by number of participants (#3974)

This commit is contained in:
ggurdin 2025-09-12 14:53:26 -04:00 committed by GitHub
parent 7348e655f4
commit 94e55d9940
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 140 additions and 54 deletions

View file

@ -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"
}

View file

@ -177,6 +177,28 @@ class ActivitySessionStartController extends State<ActivitySessionStartPage> {
if (mounted) setState(() => _selectedRoleId = id);
}
Future<bool> 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<void> _loadActivity() async {
try {
setState(() {

View file

@ -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,
),
],
),
),
],
],
);
},
);
}
}

View file

@ -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(