chore: add activity summaries loading indicator (#3633)

This commit is contained in:
ggurdin 2025-08-05 13:26:44 -04:00 committed by GitHub
parent 31c6228ca4
commit c283f157da
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 145 additions and 42 deletions

View file

@ -5134,5 +5134,8 @@
},
"endActivityTitle": "I'm Done",
"endActivityDesc": "Did you complete the objectives?\nThis is your confirmation that you're stepping back from texting. But dont worry, the fun continues in the chat! Feel free to hang out and enjoy the show until everyone clicks 'Done'.",
"archiveToAnalytics": "Add to my Completed Activities"
"archiveToAnalytics": "Add to my Completed Activities",
"activitySummaryError": "Activity summaries unavailable",
"requestSummaries": "Request summaries",
"loadingActivitySummary": "Loading activity summary..."
}

View file

@ -110,10 +110,13 @@ class ActivityFinishedStatusMessageState
}
List<ActivityRoleModel> get rolesWithSummaries {
if (widget.room.activitySummary == null) return <ActivityRoleModel>[];
if (widget.room.activitySummary?.summary == null) {
return <ActivityRoleModel>[];
}
final roles = widget.room.activityRoles;
return roles.where((role) {
return widget.room.activitySummary!.participants.any(
return widget.room.activitySummary!.summary!.participants.any(
(p) => p.participantId == role.userId,
);
}).toList();
@ -132,7 +135,7 @@ class ActivityFinishedStatusMessageState
);
final userSummary =
widget.room.activitySummary?.participants.firstWhereOrNull(
widget.room.activitySummary?.summary?.participants.firstWhereOrNull(
(p) => p.participantId == _highlightedRole!.userId,
);
@ -143,7 +146,7 @@ class ActivityFinishedStatusMessageState
crossAxisAlignment: CrossAxisAlignment.center,
children: _expanded
? [
if (summary != null) ...[
if (summary?.summary != null) ...[
IconButton(
icon: Icon(
Icons.expand_more,
@ -186,7 +189,7 @@ class ActivityFinishedStatusMessageState
Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Text(
summary.summary,
summary!.summary!.summary,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: isColumnMode ? 16.0 : 12.0,
@ -219,7 +222,46 @@ class ActivityFinishedStatusMessageState
.toList(),
),
const SizedBox(height: 20.0),
],
] else if (summary?.isLoading ?? false)
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
spacing: 8.0,
children: [
const CircularProgressIndicator.adaptive(),
Text(L10n.of(context).loadingActivitySummary),
],
),
)
else if (summary?.hasError ?? false)
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
spacing: 8.0,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.school_outlined,
size: 24.0,
),
const SizedBox(width: 8),
Flexible(
child: Text(
L10n.of(context).activitySummaryError,
textAlign: TextAlign.center,
),
),
],
),
TextButton(
onPressed: () => widget.room.fetchSummaries(),
child: Text(L10n.of(context).requestSummaries),
),
],
),
),
if (!widget.room.isHiddenActivityRoom)
ElevatedButton(
style: ElevatedButton.styleFrom(

View file

@ -6,9 +6,9 @@ import 'package:matrix/matrix.dart';
import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
import 'package:fluffychat/pangea/activity_planner/activity_role_model.dart';
import 'package:fluffychat/pangea/activity_planner/bookmarked_activities_repo.dart';
import 'package:fluffychat/pangea/activity_summary/activity_summary_model.dart';
import 'package:fluffychat/pangea/activity_summary/activity_summary_repo.dart';
import 'package:fluffychat/pangea/activity_summary/activity_summary_request_model.dart';
import 'package:fluffychat/pangea/activity_summary/activity_summary_response_model.dart';
import 'package:fluffychat/pangea/chat_settings/utils/download_chat.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
@ -77,7 +77,7 @@ extension ActivityRoomExtension on Room {
}
Future<void> setActivitySummary(
ActivitySummaryResponseModel summary,
ActivitySummaryModel summary,
) async {
await client.setRoomStateWithKey(
id,
@ -88,7 +88,14 @@ extension ActivityRoomExtension on Room {
}
Future<void> fetchSummaries() async {
if (activitySummary != null) return;
if (activitySummary?.summary != null) return;
await setActivitySummary(
ActivitySummaryModel(
requestedAt: DateTime.now(),
summary: activitySummary?.summary,
),
);
final events = await getAllEvents(this);
final List<ActivitySummaryResultsMessage> messages = [];
@ -119,15 +126,37 @@ extension ActivityRoomExtension on Room {
messages.add(activityMessage);
}
final resp = await ActivitySummaryRepo.get(
ActivitySummaryRequestModel(
activity: activityPlan!,
activityResults: messages,
contentFeedback: [],
),
);
try {
final resp = await ActivitySummaryRepo.get(
ActivitySummaryRequestModel(
activity: activityPlan!,
activityResults: messages,
contentFeedback: [],
),
);
await setActivitySummary(resp);
await setActivitySummary(
ActivitySummaryModel(summary: resp),
);
} catch (e, s) {
ErrorHandler.logError(
e: e,
s: s,
data: {
"roomID": id,
"activityPlan": activityPlan?.toJson(),
"activityResults": messages.map((m) => m.toJson()).toList(),
},
);
if (activitySummary?.summary == null) {
await setActivitySummary(
ActivitySummaryModel(
errorAt: DateTime.now(),
),
);
}
}
}
Future<void> archiveActivity() async {
@ -182,12 +211,12 @@ extension ActivityRoomExtension on Room {
}
}
ActivitySummaryResponseModel? get activitySummary {
ActivitySummaryModel? get activitySummary {
final stateEvent = getState(PangeaEventTypes.activitySummary);
if (stateEvent == null) return null;
try {
return ActivitySummaryResponseModel.fromJson(stateEvent.content);
return ActivitySummaryModel.fromJson(stateEvent.content);
} catch (e, s) {
ErrorHandler.logError(
e: e,

View file

@ -7,7 +7,7 @@ import 'package:fluffychat/pangea/activity_planner/activity_finished_status_mess
import 'package:fluffychat/pangea/activity_planner/activity_room_extension.dart';
import 'package:fluffychat/pangea/activity_planner/activity_unfinished_status_message.dart';
class ActivityStatusMessage extends StatefulWidget {
class ActivityStatusMessage extends StatelessWidget {
final Room room;
const ActivityStatusMessage({
@ -15,32 +15,16 @@ class ActivityStatusMessage extends StatefulWidget {
required this.room,
});
@override
ActivityStatusMessageState createState() => ActivityStatusMessageState();
}
class ActivityStatusMessageState extends State<ActivityStatusMessage> {
@override
void initState() {
super.initState();
if (widget.room.activityIsFinished && widget.room.activitySummary == null) {
widget.room.fetchSummaries().then((_) {
if (mounted) setState(() {});
});
}
}
@override
Widget build(BuildContext context) {
if (!widget.room.showActivityChatUI) {
if (!room.showActivityChatUI) {
return const SizedBox.shrink();
}
return Material(
child: AnimatedSize(
duration: FluffyThemes.animationDuration,
child: widget.room.isInactiveInActivity
child: room.isInactiveInActivity
? Padding(
padding: EdgeInsets.only(
bottom: FluffyThemes.isColumnMode(context) ? 32.0 : 16.0,
@ -52,9 +36,9 @@ class ActivityStatusMessageState extends State<ActivityStatusMessage> {
maxHeight: MediaQuery.of(context).size.height * 0.8,
),
child: SingleChildScrollView(
child: widget.room.activityIsFinished
? ActivityFinishedStatusMessage(room: widget.room)
: ActivityUnfinishedStatusMessage(room: widget.room),
child: room.activityIsFinished
? ActivityFinishedStatusMessage(room: room)
: ActivityUnfinishedStatusMessage(room: room),
),
),
)

View file

@ -0,0 +1,45 @@
import 'package:fluffychat/pangea/activity_summary/activity_summary_response_model.dart';
class ActivitySummaryModel {
final ActivitySummaryResponseModel? summary;
final DateTime? requestedAt;
final DateTime? errorAt;
ActivitySummaryModel({
this.summary,
this.requestedAt,
this.errorAt,
});
Map<String, dynamic> toJson() {
return {
"summary": summary?.toJson(),
"requested_at": requestedAt?.toIso8601String(),
"error_at": errorAt?.toIso8601String(),
};
}
factory ActivitySummaryModel.fromJson(Map<String, dynamic> json) {
return ActivitySummaryModel(
summary: json['summary'] != null
? ActivitySummaryResponseModel.fromJson(json['summary'])
: null,
requestedAt: json['requested_at'] != null
? DateTime.parse(json['requested_at'])
: null,
errorAt:
json['error_at'] != null ? DateTime.parse(json['error_at']) : null,
);
}
bool get _hasTimeout =>
summary == null &&
requestedAt != null &&
requestedAt!.isBefore(
DateTime.now().subtract(const Duration(seconds: 30)),
);
bool get hasError => errorAt != null || _hasTimeout;
bool get isLoading => summary == null && requestedAt != null && !hasError;
}