fix: disable ping course participants button if there are no no-bot users in course to ping
This commit is contained in:
parent
570d5e511d
commit
341c3ec125
10 changed files with 194 additions and 172 deletions
|
|
@ -2245,7 +2245,7 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
);
|
||||
}
|
||||
|
||||
if (room.isActivitySession == true && !room.activityHasStarted) {
|
||||
if (room.isActivitySession && !room.isActivityStarted) {
|
||||
return ActivitySessionStartPage(
|
||||
activityId: room.activityId!,
|
||||
roomId: room.id,
|
||||
|
|
|
|||
|
|
@ -455,7 +455,7 @@ class ChatView extends StatelessWidget {
|
|||
bottomSheetPadding,
|
||||
),
|
||||
),
|
||||
if (controller.room.activityIsFinished)
|
||||
if (controller.room.isActivityFinished)
|
||||
LoadActivitySummaryWidget(
|
||||
room: controller.room,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -34,6 +34,65 @@ class RoleException implements Exception {
|
|||
}
|
||||
|
||||
extension ActivityRoomExtension on Room {
|
||||
ActivityPlanModel? get activityPlan {
|
||||
final stateEvent = getState(PangeaEventTypes.activityPlan);
|
||||
if (stateEvent == null) return null;
|
||||
|
||||
try {
|
||||
return ActivityPlanModel.fromJson(stateEvent.content);
|
||||
} catch (e, s) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
"roomID": id,
|
||||
"stateEvent": stateEvent.content,
|
||||
},
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
ActivitySummaryModel? get activitySummary {
|
||||
final stateEvent = getState(PangeaEventTypes.activitySummary);
|
||||
if (stateEvent == null) return null;
|
||||
|
||||
try {
|
||||
return ActivitySummaryModel.fromJson(stateEvent.content);
|
||||
} catch (e, s) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
"roomID": id,
|
||||
"stateEvent": stateEvent.content,
|
||||
},
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
ActivityRolesModel? get activityRoles {
|
||||
final content = getState(PangeaEventTypes.activityRole)?.content;
|
||||
if (content == null) return null;
|
||||
|
||||
try {
|
||||
return ActivityRolesModel.fromJson(content);
|
||||
} catch (e, s) {
|
||||
if (!kDebugMode && !Environment.isStagingEnvironment) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
"roomID": id,
|
||||
"stateEvent": content,
|
||||
},
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> joinActivity(ActivityRole role) async {
|
||||
final assigned = assignedRoles?.values ?? [];
|
||||
if (assigned.any((r) => r.userId != client.userID && r.role == role.name)) {
|
||||
|
|
@ -252,162 +311,6 @@ extension ActivityRoomExtension on Room {
|
|||
}
|
||||
}
|
||||
|
||||
ActivityPlanModel? get activityPlan {
|
||||
final stateEvent = getState(PangeaEventTypes.activityPlan);
|
||||
if (stateEvent == null) return null;
|
||||
|
||||
try {
|
||||
return ActivityPlanModel.fromJson(stateEvent.content);
|
||||
} catch (e, s) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
"roomID": id,
|
||||
"stateEvent": stateEvent.content,
|
||||
},
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
ActivitySummaryModel? get activitySummary {
|
||||
final stateEvent = getState(PangeaEventTypes.activitySummary);
|
||||
if (stateEvent == null) return null;
|
||||
|
||||
try {
|
||||
return ActivitySummaryModel.fromJson(stateEvent.content);
|
||||
} catch (e, s) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
"roomID": id,
|
||||
"stateEvent": stateEvent.content,
|
||||
},
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
ActivityRolesModel? get activityRoles {
|
||||
final content = getState(PangeaEventTypes.activityRole)?.content;
|
||||
if (content == null) return null;
|
||||
|
||||
try {
|
||||
return ActivityRolesModel.fromJson(content);
|
||||
} catch (e, s) {
|
||||
if (!kDebugMode && !Environment.isStagingEnvironment) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
"roomID": id,
|
||||
"stateEvent": content,
|
||||
},
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, ActivityRoleModel>? get assignedRoles {
|
||||
final roles = activityRoles?.roles;
|
||||
if (roles == null) return null;
|
||||
|
||||
final participants = getParticipants();
|
||||
return Map.fromEntries(
|
||||
roles.entries.where(
|
||||
(r) => participants.any(
|
||||
(p) => p.id == r.value.userId && p.membership == Membership.join,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
ActivityRole? get ownRole {
|
||||
final role = ownRoleState;
|
||||
if (role == null || activityPlan == null) return null;
|
||||
|
||||
return activityPlan!.roles[role.id];
|
||||
}
|
||||
|
||||
ActivityRoleModel? get ownRoleState => activityRoles?.role(client.userID!);
|
||||
|
||||
int get remainingRoles {
|
||||
final availableRoles = activityPlan?.roles;
|
||||
return max(0, (availableRoles?.length ?? 0) - (assignedRoles?.length ?? 0));
|
||||
}
|
||||
|
||||
bool get showActivityChatUI {
|
||||
return activityPlan != null &&
|
||||
powerForChangingStateEvent(PangeaEventTypes.activityRole) == 0 &&
|
||||
powerForChangingStateEvent(PangeaEventTypes.activitySummary) == 0;
|
||||
}
|
||||
|
||||
bool get activityHasStarted =>
|
||||
(activityPlan?.roles.length ?? 0) - (activityRoles?.roles.length ?? 0) <=
|
||||
0;
|
||||
|
||||
bool get isActiveInActivity {
|
||||
if (!showActivityChatUI) return false;
|
||||
final role = ownRoleState;
|
||||
return role != null && !role.isFinished;
|
||||
}
|
||||
|
||||
bool get isInactiveInActivity {
|
||||
if (!showActivityChatUI) return false;
|
||||
final role = ownRoleState;
|
||||
return role == null || role.isFinished;
|
||||
}
|
||||
|
||||
bool get hasCompletedActivity => ownRoleState?.isFinished ?? false;
|
||||
|
||||
bool get activityIsFinished {
|
||||
final roles = activityRoles?.roles.values.where(
|
||||
(r) => r.userId != BotName.byEnvironment,
|
||||
);
|
||||
|
||||
if (roles == null || 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 user = getParticipants().firstWhereOrNull(
|
||||
(u) => u.id == r.userId,
|
||||
);
|
||||
return user == null || user.membership != Membership.join;
|
||||
});
|
||||
}
|
||||
|
||||
bool get isHiddenActivityRoom => ownRoleState?.isArchived ?? false;
|
||||
|
||||
bool get hasDismissedGoalTooltip =>
|
||||
ownRoleState?.dismissedGoalTooltip ?? false;
|
||||
|
||||
Room? get courseParent => pangeaSpaceParents.firstWhereOrNull(
|
||||
(parent) => parent.coursePlan != null,
|
||||
);
|
||||
|
||||
bool get isActivityRoomType =>
|
||||
roomType?.startsWith(PangeaRoomTypes.activitySession) == true;
|
||||
|
||||
bool get isActivitySession => isActivityRoomType || activityPlan != null;
|
||||
|
||||
bool get showActivityFinished =>
|
||||
showActivityChatUI && ownRoleState != null && hasCompletedActivity;
|
||||
|
||||
String? get activityId {
|
||||
if (!isActivitySession) return null;
|
||||
if (isActivityRoomType) {
|
||||
return roomType!.split(":").last;
|
||||
}
|
||||
return activityPlan?.activityId;
|
||||
}
|
||||
|
||||
Future<ActivitySummaryAnalyticsModel> getActivityAnalytics() async {
|
||||
// wait for local storage box to init in getAnalytics initialization
|
||||
if (!MatrixState.pangeaController.getAnalytics.initCompleter.isCompleted) {
|
||||
|
|
@ -449,4 +352,101 @@ extension ActivityRoomExtension on Room {
|
|||
|
||||
return analytics;
|
||||
}
|
||||
|
||||
// UI-related helper functions
|
||||
|
||||
bool get showActivityChatUI {
|
||||
return activityPlan != null &&
|
||||
powerForChangingStateEvent(PangeaEventTypes.activityRole) == 0 &&
|
||||
powerForChangingStateEvent(PangeaEventTypes.activitySummary) == 0;
|
||||
}
|
||||
|
||||
// helper functions for activity role state in overall activity
|
||||
|
||||
Map<String, ActivityRoleModel>? get assignedRoles {
|
||||
final roles = activityRoles?.roles;
|
||||
if (roles == null) return null;
|
||||
|
||||
final participants = getParticipants();
|
||||
return Map.fromEntries(
|
||||
roles.entries.where(
|
||||
(r) => participants.any(
|
||||
(p) => p.id == r.value.userId && p.membership == Membership.join,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
int get numRemainingRoles {
|
||||
final availableRoles = activityPlan?.roles;
|
||||
return max(0, (availableRoles?.length ?? 0) - (assignedRoles?.length ?? 0));
|
||||
}
|
||||
|
||||
// helper functions for activity role state for specific users
|
||||
|
||||
ActivityRole? get ownRole {
|
||||
final role = ownRoleState;
|
||||
if (role == null || activityPlan == null) return null;
|
||||
|
||||
return activityPlan!.roles[role.id];
|
||||
}
|
||||
|
||||
ActivityRoleModel? get ownRoleState => activityRoles?.role(client.userID!);
|
||||
|
||||
// helper functions for activity state for overall activity
|
||||
|
||||
bool get isActivitySession =>
|
||||
roomType?.startsWith(PangeaRoomTypes.activitySession) == true ||
|
||||
activityPlan != null;
|
||||
|
||||
String? get activityId {
|
||||
if (!isActivitySession) return null;
|
||||
if (roomType?.startsWith(PangeaRoomTypes.activitySession) == true) {
|
||||
return roomType!.split(":").last;
|
||||
}
|
||||
return activityPlan?.activityId;
|
||||
}
|
||||
|
||||
bool get isActivityStarted =>
|
||||
(activityPlan?.roles.length ?? 0) - (activityRoles?.roles.length ?? 0) <=
|
||||
0;
|
||||
|
||||
bool get isActivityFinished {
|
||||
final roles = activityRoles?.roles.values.where(
|
||||
(r) => r.userId != BotName.byEnvironment,
|
||||
);
|
||||
|
||||
if (roles == null || 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 user = getParticipants().firstWhereOrNull(
|
||||
(u) => u.id == r.userId,
|
||||
);
|
||||
return user == null || user.membership != Membership.join;
|
||||
});
|
||||
}
|
||||
|
||||
// helper functions for activity state for specific users
|
||||
|
||||
bool get hasPickedRole => ownRoleState != null;
|
||||
|
||||
bool get hasCompletedRole => ownRoleState?.isFinished ?? false;
|
||||
|
||||
bool get hasArchivedActivity => ownRoleState?.isArchived ?? false;
|
||||
|
||||
bool get hasDismissedGoalTooltip =>
|
||||
ownRoleState?.dismissedGoalTooltip ?? false;
|
||||
|
||||
bool get isActiveInActivity => hasPickedRole && !hasCompletedRole;
|
||||
|
||||
// helper functions for activity course context
|
||||
|
||||
Room? get courseParent => pangeaSpaceParents.firstWhereOrNull(
|
||||
(parent) => parent.coursePlan != null,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ class ActivityFinishedStatusMessage extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!controller.room.showActivityFinished) {
|
||||
if (!controller.room.hasCompletedRole) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
|
|
@ -84,7 +84,7 @@ class ActivityFinishedStatusMessage extends StatelessWidget {
|
|||
spacing: 12.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: controller.room.activityIsFinished
|
||||
children: controller.room.isActivityFinished
|
||||
? [
|
||||
if (summary?.isLoading ?? false) ...[
|
||||
Text(
|
||||
|
|
@ -129,7 +129,7 @@ class ActivityFinishedStatusMessage extends StatelessWidget {
|
|||
child: Text(L10n.of(context).requestSummaries),
|
||||
),
|
||||
],
|
||||
if (!controller.room.isHiddenActivityRoom)
|
||||
if (!controller.room.hasArchivedActivity)
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
|
|
|
|||
|
|
@ -114,8 +114,8 @@ class ActivityStatsMenuState extends State<ActivityStatsMenu> {
|
|||
final isColumnMode = FluffyThemes.isColumnMode(context);
|
||||
|
||||
// Completion status variables
|
||||
final bool userComplete = room.hasCompletedActivity;
|
||||
final bool activityComplete = room.activityIsFinished;
|
||||
final bool userComplete = room.hasCompletedRole;
|
||||
final bool activityComplete = room.isActivityFinished;
|
||||
bool shouldShowEndForAll = true;
|
||||
bool shouldShowImDone = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ class ActivitySessionStartController extends State<ActivitySessionStartPage>
|
|||
false;
|
||||
|
||||
SessionState get state {
|
||||
if (activityRoom?.ownRoleState != null) return SessionState.confirmedRole;
|
||||
if (activityRoom?.hasPickedRole == true) return SessionState.confirmedRole;
|
||||
if (_selectedRoleId != null) return SessionState.selectedRole;
|
||||
if (activityRoom == null) {
|
||||
return widget.roomId != null || widget.launch
|
||||
|
|
@ -127,7 +127,8 @@ class ActivitySessionStartController extends State<ActivitySessionStartPage>
|
|||
String? get descriptionText {
|
||||
switch (state) {
|
||||
case SessionState.confirmedRole:
|
||||
return L10n.of(context).waitingToFillRole(activityRoom!.remainingRoles);
|
||||
return L10n.of(context)
|
||||
.waitingToFillRole(activityRoom!.numRemainingRoles);
|
||||
case SessionState.selectedRole:
|
||||
return activity!.roles[_selectedRoleId!]!.goal;
|
||||
case SessionState.notStarted:
|
||||
|
|
@ -175,7 +176,16 @@ class ActivitySessionStartController extends State<ActivitySessionStartPage>
|
|||
|
||||
bool get canPingParticipants {
|
||||
if (activityRoom == null || courseParent == null) return false;
|
||||
return _pingCooldown == null || !_pingCooldown!.isActive;
|
||||
if (_pingCooldown != null && _pingCooldown!.isActive) return false;
|
||||
|
||||
final courseParticipants = courseParent!.getParticipants();
|
||||
final roomParticipants = activityRoom!.getParticipants();
|
||||
for (final p in courseParticipants) {
|
||||
if (p.id == BotName.byEnvironment) continue;
|
||||
if (roomParticipants.any((rp) => rp.id == p.id)) continue;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void toggleInstructions() {
|
||||
|
|
@ -220,6 +230,18 @@ class ActivitySessionStartController extends State<ActivitySessionStartPage>
|
|||
final futures = <Future>[];
|
||||
futures.add(_loadSummary());
|
||||
futures.add(_loadActivity());
|
||||
|
||||
// load the course participants, since we will need that
|
||||
// info to determine if course pinging is enabled
|
||||
if (courseParent != null) {
|
||||
futures.add(
|
||||
courseParent!.requestParticipants(
|
||||
[Membership.join, Membership.invite, Membership.knock],
|
||||
false,
|
||||
true,
|
||||
),
|
||||
);
|
||||
}
|
||||
await Future.wait(futures);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
|
|
|
|||
|
|
@ -57,14 +57,14 @@ class ChatListItemSubtitle extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (room.showActivityChatUI) {
|
||||
if (room.isHiddenActivityRoom) {
|
||||
if (room.hasArchivedActivity) {
|
||||
return Text(
|
||||
room.activityPlan!.learningObjective,
|
||||
style: style,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
);
|
||||
} else if (!room.activityHasStarted) {
|
||||
} else if (!room.isActivityStarted) {
|
||||
return OpenRolesIndicator(
|
||||
totalSlots: room.activityPlan!.req.numberOfParticipants,
|
||||
userIds:
|
||||
|
|
@ -73,7 +73,7 @@ class ChatListItemSubtitle extends StatelessWidget {
|
|||
room: room,
|
||||
space: room.courseParent,
|
||||
);
|
||||
} else if (room.activityIsFinished) {
|
||||
} else if (room.isActivityFinished) {
|
||||
return Text(
|
||||
L10n.of(context).activityDone,
|
||||
style: style,
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ void chatContextMenuAction(
|
|||
),
|
||||
),
|
||||
],
|
||||
if (room.isActiveInActivity && room.activityHasStarted)
|
||||
if (room.isActiveInActivity && room.isActivityStarted)
|
||||
PopupMenuItem(
|
||||
value: ChatContextAction.endActivity,
|
||||
child: Row(
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ extension CoursePlanRoomExtension on Room {
|
|||
final room = client.getRoomById(child.roomId!);
|
||||
if (room?.membership == Membership.join &&
|
||||
room?.activityId == activityId &&
|
||||
!room!.isHiddenActivityRoom) {
|
||||
!room!.hasArchivedActivity) {
|
||||
return room.id;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,5 +41,5 @@ extension RoomInformationRoomExtension on Room {
|
|||
|
||||
bool get isAnalyticsRoom => roomType == PangeaRoomTypes.analytics;
|
||||
|
||||
bool get isHiddenRoom => isAnalyticsRoom || isHiddenActivityRoom;
|
||||
bool get isHiddenRoom => isAnalyticsRoom || hasArchivedActivity;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue