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", "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'.", "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 { List<ActivityRoleModel> get rolesWithSummaries {
if (widget.room.activitySummary == null) return <ActivityRoleModel>[]; if (widget.room.activitySummary?.summary == null) {
return <ActivityRoleModel>[];
}
final roles = widget.room.activityRoles; final roles = widget.room.activityRoles;
return roles.where((role) { return roles.where((role) {
return widget.room.activitySummary!.participants.any( return widget.room.activitySummary!.summary!.participants.any(
(p) => p.participantId == role.userId, (p) => p.participantId == role.userId,
); );
}).toList(); }).toList();
@ -132,7 +135,7 @@ class ActivityFinishedStatusMessageState
); );
final userSummary = final userSummary =
widget.room.activitySummary?.participants.firstWhereOrNull( widget.room.activitySummary?.summary?.participants.firstWhereOrNull(
(p) => p.participantId == _highlightedRole!.userId, (p) => p.participantId == _highlightedRole!.userId,
); );
@ -143,7 +146,7 @@ class ActivityFinishedStatusMessageState
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: _expanded children: _expanded
? [ ? [
if (summary != null) ...[ if (summary?.summary != null) ...[
IconButton( IconButton(
icon: Icon( icon: Icon(
Icons.expand_more, Icons.expand_more,
@ -186,7 +189,7 @@ class ActivityFinishedStatusMessageState
Padding( Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0), padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Text( child: Text(
summary.summary, summary!.summary!.summary,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
fontSize: isColumnMode ? 16.0 : 12.0, fontSize: isColumnMode ? 16.0 : 12.0,
@ -219,7 +222,46 @@ class ActivityFinishedStatusMessageState
.toList(), .toList(),
), ),
const SizedBox(height: 20.0), 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) if (!widget.room.isHiddenActivityRoom)
ElevatedButton( ElevatedButton(
style: ElevatedButton.styleFrom( 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_plan_model.dart';
import 'package:fluffychat/pangea/activity_planner/activity_role_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_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_repo.dart';
import 'package:fluffychat/pangea/activity_summary/activity_summary_request_model.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/chat_settings/utils/download_chat.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
@ -77,7 +77,7 @@ extension ActivityRoomExtension on Room {
} }
Future<void> setActivitySummary( Future<void> setActivitySummary(
ActivitySummaryResponseModel summary, ActivitySummaryModel summary,
) async { ) async {
await client.setRoomStateWithKey( await client.setRoomStateWithKey(
id, id,
@ -88,7 +88,14 @@ extension ActivityRoomExtension on Room {
} }
Future<void> fetchSummaries() async { 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 events = await getAllEvents(this);
final List<ActivitySummaryResultsMessage> messages = []; final List<ActivitySummaryResultsMessage> messages = [];
@ -119,15 +126,37 @@ extension ActivityRoomExtension on Room {
messages.add(activityMessage); messages.add(activityMessage);
} }
final resp = await ActivitySummaryRepo.get( try {
ActivitySummaryRequestModel( final resp = await ActivitySummaryRepo.get(
activity: activityPlan!, ActivitySummaryRequestModel(
activityResults: messages, activity: activityPlan!,
contentFeedback: [], 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 { Future<void> archiveActivity() async {
@ -182,12 +211,12 @@ extension ActivityRoomExtension on Room {
} }
} }
ActivitySummaryResponseModel? get activitySummary { ActivitySummaryModel? get activitySummary {
final stateEvent = getState(PangeaEventTypes.activitySummary); final stateEvent = getState(PangeaEventTypes.activitySummary);
if (stateEvent == null) return null; if (stateEvent == null) return null;
try { try {
return ActivitySummaryResponseModel.fromJson(stateEvent.content); return ActivitySummaryModel.fromJson(stateEvent.content);
} catch (e, s) { } catch (e, s) {
ErrorHandler.logError( ErrorHandler.logError(
e: e, 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_room_extension.dart';
import 'package:fluffychat/pangea/activity_planner/activity_unfinished_status_message.dart'; import 'package:fluffychat/pangea/activity_planner/activity_unfinished_status_message.dart';
class ActivityStatusMessage extends StatefulWidget { class ActivityStatusMessage extends StatelessWidget {
final Room room; final Room room;
const ActivityStatusMessage({ const ActivityStatusMessage({
@ -15,32 +15,16 @@ class ActivityStatusMessage extends StatefulWidget {
required this.room, 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (!widget.room.showActivityChatUI) { if (!room.showActivityChatUI) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
return Material( return Material(
child: AnimatedSize( child: AnimatedSize(
duration: FluffyThemes.animationDuration, duration: FluffyThemes.animationDuration,
child: widget.room.isInactiveInActivity child: room.isInactiveInActivity
? Padding( ? Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(
bottom: FluffyThemes.isColumnMode(context) ? 32.0 : 16.0, 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, maxHeight: MediaQuery.of(context).size.height * 0.8,
), ),
child: SingleChildScrollView( child: SingleChildScrollView(
child: widget.room.activityIsFinished child: room.activityIsFinished
? ActivityFinishedStatusMessage(room: widget.room) ? ActivityFinishedStatusMessage(room: room)
: ActivityUnfinishedStatusMessage(room: widget.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;
}