fix: update room summary model (#5543)

This commit is contained in:
ggurdin 2026-01-30 16:06:40 -05:00 committed by GitHub
parent 995c8a6e71
commit b66aa5f9db
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 119 additions and 34 deletions

View file

@ -191,7 +191,7 @@ class ActivitySessionStartController extends State<ActivitySessionStartPage>
final availableRoles = activity!.roles;
final assignedRoles = activityRoom?.assignedRoles ??
roomSummaries?[widget.roomId]?.activityRoles.roles ??
roomSummaries?[widget.roomId]?.activityRoles?.roles ??
{};
final unassignedIds = availableRoles.keys
.where((id) => !assignedRoles.containsKey(id))

View file

@ -518,7 +518,7 @@ class _ActivityStatuses extends StatelessWidget {
// room (like the bot). Otherwise, show only joined users with roles
Map<String, ActivityRoleModel> activityRoles =
status == ActivitySummaryStatus.completed
? e.value.activityRoles.roles
? (e.value.activityRoles?.roles ?? {})
: e.value.joinedUsersWithRoles;
// If the user is in the activity room and it's not completed, use the room's
@ -530,7 +530,7 @@ class _ActivityStatuses extends StatelessWidget {
return ListTile(
title: OpenRolesIndicator(
roles: activityPlan.roles.values
roles: (activityPlan?.roles.values ?? [])
.sorted((a, b) => a.id.compareTo(b.id))
.toList(),
assignedRoles: activityRoles.values.toList(),

View file

@ -1,5 +1,6 @@
import 'dart:convert';
import 'package:collection/collection.dart';
import 'package:http/http.dart' hide Client;
import 'package:matrix/matrix.dart';
import 'package:matrix/matrix_api_lite/generated/api.dart';
@ -7,6 +8,8 @@ import 'package:matrix/matrix_api_lite/generated/api.dart';
import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_role_model.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_roles_model.dart';
import 'package:fluffychat/pangea/activity_summary/activity_summary_model.dart';
import 'package:fluffychat/pangea/course_plans/courses/course_plan_event.dart';
import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
extension RoomSummaryExtension on Api {
@ -52,27 +55,40 @@ class RoomSummariesResponse {
});
return RoomSummariesResponse(summaries: summaries);
}
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
summaries.forEach((key, value) {
json[key] = value.toJson();
});
return json;
}
}
class RoomSummaryResponse {
final ActivityPlanModel activityPlan;
final ActivityRolesModel activityRoles;
final ActivityPlanModel? activityPlan;
final ActivityRolesModel? activityRoles;
final ActivitySummaryModel? activitySummary;
final CoursePlanEvent? coursePlan;
final JoinRules? joinRule;
final Map<String, int>? powerLevels;
final Map<String, String> membershipSummary;
final String? displayName;
final String? avatarUrl;
RoomSummaryResponse({
required this.activityPlan,
required this.activityRoles,
required this.membershipSummary,
this.activityPlan,
this.activityRoles,
this.activitySummary,
this.coursePlan,
this.joinRule,
this.powerLevels,
this.displayName,
this.avatarUrl,
});
List<String> get adminUserIDs {
if (powerLevels == null) return [];
return powerLevels!.entries
.where((entry) => entry.value >= 100)
.map((entry) => entry.key)
.toList();
}
Membership? getMembershipForUserId(String userId) {
final membershipString = membershipSummary[userId];
if (membershipString == null) return null;
@ -83,32 +99,93 @@ class RoomSummaryResponse {
}
Map<String, ActivityRoleModel> get joinedUsersWithRoles {
if (activityRoles == null) return {};
return Map.fromEntries(
activityRoles.roles.entries.where(
activityRoles!.roles.entries.where(
(role) => getMembershipForUserId(role.value.userId) == Membership.join,
),
);
}
factory RoomSummaryResponse.fromJson(Map<String, dynamic> json) {
final planEntry =
json[PangeaEventTypes.activityPlan]?["default"]?["content"];
ActivityPlanModel? plan;
if (planEntry != null && planEntry is Map<String, dynamic>) {
plan = ActivityPlanModel.fromJson(planEntry);
}
final rolesEntry =
json[PangeaEventTypes.activityRole]?["default"]?["content"];
ActivityRolesModel? roles;
if (rolesEntry != null && rolesEntry is Map<String, dynamic>) {
roles = ActivityRolesModel.fromJson(rolesEntry);
}
final summaryEntry =
json[PangeaEventTypes.activitySummary]?["default"]?["content"];
ActivitySummaryModel? summary;
if (summaryEntry != null && summaryEntry is Map<String, dynamic>) {
summary = ActivitySummaryModel.fromJson(summaryEntry);
}
final coursePlanEntry =
json[PangeaEventTypes.coursePlan]?["default"]?["content"];
CoursePlanEvent? coursePlan;
if (coursePlanEntry != null && coursePlanEntry is Map<String, dynamic>) {
coursePlan = CoursePlanEvent.fromJson(coursePlanEntry);
}
final powerLevelsEntry =
json[EventTypes.RoomPowerLevels]?['default']?['content']?['users'];
Map<String, int>? powerLevels;
if (powerLevelsEntry != null) {
powerLevels = Map<String, int>.from(powerLevelsEntry);
}
final joinRulesString =
json[EventTypes.RoomJoinRules]?['default']?['content']?['join_rule'];
JoinRules? joinRule;
if (joinRulesString != null && joinRulesString is String) {
joinRule = JoinRules.values
.singleWhereOrNull((element) => element.text == joinRulesString);
}
final displayName =
json[EventTypes.RoomName]?['default']?['content']?['name'] as String?;
String? avatarUrl =
json[EventTypes.RoomAvatar]?['default']?['content']?['url'] as String?;
if (avatarUrl != null && Uri.tryParse(avatarUrl) == null) {
avatarUrl = null;
}
return RoomSummaryResponse(
activityPlan: ActivityPlanModel.fromJson(
json[PangeaEventTypes.activityPlan]?["default"]?["content"] ?? {},
),
activityRoles: ActivityRolesModel.fromJson(
json[PangeaEventTypes.activityRole]?["default"]?["content"] ?? {},
),
activityPlan: plan,
activityRoles: roles,
activitySummary: summary,
coursePlan: coursePlan,
powerLevels: powerLevels,
joinRule: joinRule,
membershipSummary: Map<String, String>.from(
json['membership_summary'] ?? {},
),
displayName: displayName,
avatarUrl: avatarUrl,
);
}
Map<String, dynamic> toJson() {
return {
PangeaEventTypes.activityPlan: activityPlan.toJson(),
PangeaEventTypes.activityRole: activityRoles.toJson(),
'membership_summary': membershipSummary,
'activityPlan': activityPlan?.toJson(),
'activityRoles': activityRoles?.toJson(),
'activitySummary': activitySummary?.toJson(),
'coursePlan': coursePlan?.toJson(),
'joinRule': joinRule?.text,
'powerLevels': powerLevels,
'membershipSummary': membershipSummary,
'displayName': displayName,
'avatarUrl': avatarUrl,
};
}
}

View file

@ -133,9 +133,13 @@ class CourseChatsController extends State<CourseChats>
}
final activity = summary.activityPlan;
final roles = summary.activityRoles;
final users = summary.joinedUsersWithRoles;
if (users.isEmpty || !validIDs.contains(activity.activityId)) {
if (activity == null ||
roles == null ||
users.isEmpty ||
!validIDs.contains(activity.activityId)) {
continue;
}
@ -148,7 +152,7 @@ class CourseChatsController extends State<CourseChats>
// It's possible for users to finish an activity and then for some of the
// users to leave, but if the activity was archived by anyone, that means
// it was full at some point.
if (summary.activityRoles.roles.values.any((role) => role.isArchived)) {
if (roles.roles.values.any((role) => role.isArchived)) {
continue;
}

View file

@ -48,6 +48,7 @@ mixin ActivitySummariesProvider<T extends StatefulWidget> on State<T> {
final activityPlan = roomSummary.activityPlan;
final assignedRoles = roomSummary.joinedUsersWithRoles;
if (activityPlan == null) return false;
return activityPlan.roles.length - assignedRoles.length <= 0;
}
@ -56,6 +57,7 @@ mixin ActivitySummariesProvider<T extends StatefulWidget> on State<T> {
if (roomSummary == null) return false;
final activityRoles = roomSummary.activityRoles;
if (activityRoles == null) return false;
final roles = activityRoles.roles.values.where(
(r) => r.userId != BotName.byEnvironment,
);
@ -76,7 +78,7 @@ mixin ActivitySummariesProvider<T extends StatefulWidget> on State<T> {
Map<String, RoomSummaryResponse> activitySessions(String activityId) =>
Map.fromEntries(
roomSummaries?.entries
.where((v) => v.value.activityPlan.activityId == activityId) ??
.where((v) => v.value.activityPlan?.activityId == activityId) ??
[],
);
@ -115,7 +117,7 @@ mixin ActivitySummariesProvider<T extends StatefulWidget> on State<T> {
final summary = entry.value;
final roomId = entry.key;
if (summary.activityPlan.activityId != activityId) {
if (summary.activityPlan?.activityId != activityId) {
continue;
}
@ -132,11 +134,13 @@ mixin ActivitySummariesProvider<T extends StatefulWidget> on State<T> {
if (roomSummaries == null || roomSummaries!.isEmpty) return {};
return roomSummaries!.values
.where(
(entry) => entry.activityRoles.roles.values.any(
(v) => v.userId == userID && v.isArchived,
),
(entry) =>
entry.activityRoles != null &&
entry.activityRoles!.roles.values.any(
(v) => v.userId == userID && v.isArchived,
),
)
.map((e) => e.activityPlan.activityId)
.map((e) => e.activityPlan?.activityId)
.whereType<String>()
.toSet();
}

View file

@ -6,7 +6,7 @@ description: Learn a language while texting your friends.
# Pangea#
publish_to: none
# On version bump also increase the build number for F-Droid
version: 4.1.16+5
version: 4.1.16+6
environment:
sdk: ">=3.0.0 <4.0.0"