fluffychat/lib/pangea/course_plans/course_activities/activity_summaries_provider.dart
2025-12-03 10:04:43 -05:00

203 lines
6 KiB
Dart

import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/bot/utils/bot_name.dart';
import 'package:fluffychat/pangea/chat_settings/utils/room_summary_extension.dart';
import 'package:fluffychat/pangea/course_plans/course_topics/course_topic_model.dart';
import 'package:fluffychat/pangea/course_plans/courses/course_plan_model.dart';
import 'package:fluffychat/widgets/matrix.dart';
enum ActivitySummaryStatus {
notStarted,
inProgress,
completed;
String label(L10n l10n, int count) {
switch (this) {
case ActivitySummaryStatus.notStarted:
return l10n.notStartedActivitiesTitle(count);
case ActivitySummaryStatus.inProgress:
return l10n.inProgressActivitiesTitle(count);
case ActivitySummaryStatus.completed:
return l10n.completedActivitiesTitle(count);
}
}
}
mixin ActivitySummariesProvider<T extends StatefulWidget> on State<T> {
Map<String, RoomSummaryResponse>? roomSummaries;
Future<void> loadRoomSummaries(List<String> roomIds) async {
if (roomIds.isEmpty) {
roomSummaries = {};
return;
}
final resp = await Matrix.of(context).client.requestRoomSummaries(roomIds);
if (mounted) {
setState(() => roomSummaries = resp.summaries);
}
}
bool isActivityStarted(String roomId) {
if (isActivityFinished(roomId)) return true;
final roomSummary = roomSummaries?[roomId];
if (roomSummary == null) return false;
final activityPlan = roomSummary.activityPlan;
final assignedRoles = roomSummary.joinedUsersWithRoles;
return activityPlan.roles.length - assignedRoles.length <= 0;
}
bool isActivityFinished(String roomId) {
final roomSummary = roomSummaries?[roomId];
if (roomSummary == null) return false;
final activityRoles = roomSummary.activityRoles;
final roles = activityRoles.roles.values.where(
(r) => r.userId != BotName.byEnvironment,
);
if (roles.isEmpty) return false;
if (!roles.any((r) => r.isFinished)) return false;
return roles.every((r) {
if (r.isFinished) return true;
// if the user is in the chat (not null && membership is join),
// then the activity is not finished for them
final membership = roomSummary.getMembershipForUserId(r.userId);
return membership == null || membership != Membership.join;
});
}
Map<String, RoomSummaryResponse> activitySessions(String activityId) =>
Map.fromEntries(
roomSummaries?.entries
.where((v) => v.value.activityPlan.activityId == activityId) ??
[],
);
Map<ActivitySummaryStatus, Map<String, RoomSummaryResponse>>
activitySessionStatuses(
String activityId,
) {
final statuses = <ActivitySummaryStatus, Map<String, RoomSummaryResponse>>{
ActivitySummaryStatus.notStarted: {},
ActivitySummaryStatus.inProgress: {},
ActivitySummaryStatus.completed: {},
};
final sessions = activitySessions(activityId);
for (final entry in sessions.entries) {
final session = entry.value;
final roomId = entry.key;
if (isActivityFinished(roomId)) {
statuses[ActivitySummaryStatus.completed]![roomId] = session;
} else if (isActivityStarted(roomId)) {
statuses[ActivitySummaryStatus.inProgress]![roomId] = session;
} else {
statuses[ActivitySummaryStatus.notStarted]![roomId] = session;
}
}
return statuses;
}
Set<String> openSessions(String activityId) {
if (roomSummaries == null || roomSummaries!.isEmpty) return {};
final Set<String> sessions = {};
for (final entry in roomSummaries!.entries) {
final summary = entry.value;
final roomId = entry.key;
if (summary.activityPlan.activityId != activityId) {
continue;
}
if (!isActivityStarted(roomId)) {
sessions.add(roomId);
}
}
return sessions;
}
int numOpenSessions(String activityId) => openSessions(activityId).length;
Set<String> _completedActivities(String userID) {
if (roomSummaries == null || roomSummaries!.isEmpty) return {};
return roomSummaries!.values
.where(
(entry) => entry.activityRoles.roles.values.any(
(v) => v.userId == userID && v.isArchived,
),
)
.map((e) => e.activityPlan.activityId)
.whereType<String>()
.toSet();
}
bool hasCompletedActivity(
String userID,
String activityID,
) {
final completed = _completedActivities(userID);
return completed.contains(activityID);
}
bool _hasCompletedTopic(
String userID,
CourseTopicModel topic,
) {
final topicActivityIds = topic.activityIds.toSet();
final numTwoPersonActivities = topic.loadedActivities.values
.where((a) => a.req.numberOfParticipants <= 2)
.length;
final completedTopicActivities =
_completedActivities(userID).intersection(topicActivityIds);
return completedTopicActivities.length >= numTwoPersonActivities;
}
String? currentTopicId(
String userID,
CoursePlanModel course,
) {
if (course.loadedTopics.isEmpty) {
return null;
}
for (int i = 0; i < course.topicIds.length; i++) {
final topicId = course.topicIds[i];
final topic = course.loadedTopics[topicId];
if (topic == null) continue;
if (!topic.activityListComplete) continue;
if (!_hasCompletedTopic(userID, topic) && topic.activityIds.isNotEmpty) {
return topicId;
}
}
return course.topicIds.last;
}
Map<String, List<User>> topicsToUsers(
Room room,
CoursePlanModel course,
) {
final Map<String, List<User>> topicUserMap = {};
final users = room.getParticipants();
for (final user in users) {
if (user.id == BotName.byEnvironment) continue;
final topicId = currentTopicId(user.id, course);
if (topicId != null) {
topicUserMap.putIfAbsent(topicId, () => []).add(user);
}
}
return topicUserMap;
}
}