3871 activity session issues feedback (#3874)

* fix: add header when fetching image from CMS

* fix: only show activity start page is all roles have never been full

* chore: disable archive button until summary loads

* chore: still save activity analytics summary even if there's a choreo error when fetching summary
This commit is contained in:
ggurdin 2025-09-04 13:48:26 -04:00 committed by GitHub
parent 720f50bc8c
commit 916da50bd4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 116 additions and 66 deletions

View file

@ -9,7 +9,6 @@ import 'package:fluffychat/pages/chat/events/message.dart';
import 'package:fluffychat/pages/chat/seen_by_row.dart';
import 'package:fluffychat/pages/chat/typing_indicators.dart';
import 'package:fluffychat/pangea/activity_planner/activity_plan_message.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_session_chat/activity_finished_status_message.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_user_summaries_widget.dart';
import 'package:fluffychat/pangea/events/extensions/pangea_event_extension.dart';
import 'package:fluffychat/utils/account_config.dart';
@ -95,7 +94,7 @@ class ChatEventList extends StatelessWidget {
// Request history button or progress indicator:
// #Pangea
// if (i == events.length + 1) {
if (i == events.length + 3) {
if (i == events.length + 2) {
// Pangea#
if (timeline.isRequestingHistory) {
return const Center(
@ -126,17 +125,13 @@ class ChatEventList extends StatelessWidget {
// #Pangea
if (i == 1) {
return ActivityFinishedStatusMessage(controller: controller);
}
if (i == 2) {
return ActivityUserSummaries(controller: controller);
}
// Pangea#
// #Pangea
// i--;
i = i - 3;
i = i - 2;
// Pangea#
// The message at this index:
@ -214,7 +209,7 @@ class ChatEventList extends StatelessWidget {
},
// #Pangea
// childCount: events.length + 2,
childCount: events.length + 4,
childCount: events.length + 3,
// Pangea#
findChildIndexCallback: (key) =>
controller.findChildIndexCallback(key, thisEventsKeyMap),

View file

@ -14,6 +14,7 @@ import 'package:fluffychat/pages/chat/chat_app_bar_title.dart';
import 'package:fluffychat/pages/chat/chat_event_list.dart';
import 'package:fluffychat/pages/chat/pinned_events.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_session_chat/activity_finished_status_message.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_session_chat/activity_pinned_message.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_session_chat/load_activity_summary_widget.dart';
import 'package:fluffychat/pangea/chat/widgets/chat_input_bar.dart';
@ -437,6 +438,9 @@ class ChatView extends StatelessWidget {
),
if (controller.room.activityIsFinished)
LoadActivitySummaryWidget(room: controller.room),
ActivityFinishedStatusMessage(
controller: controller,
),
// Pangea#
],
),

View file

@ -237,7 +237,18 @@ class ActivityPlannerBuilderState extends State<ActivityPlannerBuilder> {
mxcUri.pathSegments.last,
);
} else {
final Response response = await http.get(Uri.parse(url));
final Response response = await http.get(
Uri.parse(url),
headers: {
'Authorization':
'Bearer ${MatrixState.pangeaController.userController.accessToken}',
},
);
if (response.statusCode != 200) {
throw Exception(
"Failed to download image from URL: ${response.statusCode}",
);
}
avatar = response.bodyBytes;
filename = Uri.encodeComponent(
Uri.parse(url).pathSegments.last,

View file

@ -121,7 +121,7 @@ extension ActivityRoomExtension on Room {
final events = await getAllEvents();
final List<ActivitySummaryResultsMessage> messages = [];
final ActivitySummaryAnalyticsModel analytics =
ActivitySummaryAnalyticsModel();
activitySummary?.analytics ?? ActivitySummaryAnalyticsModel();
final timeline = this.timeline ?? await getTimeline();
for (final event in events) {
@ -148,7 +148,10 @@ extension ActivityRoomExtension on Room {
);
messages.add(activityMessage);
analytics.addConstructs(pangeaMessage);
if (activitySummary?.analytics == null) {
analytics.addConstructs(pangeaMessage);
}
}
try {
@ -182,6 +185,7 @@ extension ActivityRoomExtension on Room {
await setActivitySummary(
ActivitySummaryModel(
errorAt: DateTime.now(),
analytics: analytics,
),
);
}
@ -274,7 +278,9 @@ extension ActivityRoomExtension on Room {
powerForChangingStateEvent(PangeaEventTypes.activitySummary) == 0;
}
bool get activityHasStarted => remainingRoles == 0;
bool get activityHasStarted =>
(activityPlan?.roles.length ?? 0) - (activityRoles?.roles.length ?? 0) <=
0;
bool get isActiveInActivity {
if (!showActivityChatUI) return false;

View file

@ -7,6 +7,7 @@ import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_session_chat/saved_activity_analytics_dialog.dart';
import 'package:fluffychat/pangea/activity_summary/activity_summary_model.dart';
import 'package:fluffychat/pangea/course_plans/course_plan_room_extension.dart';
import 'package:fluffychat/pangea/course_plans/course_plans_repo.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
@ -20,6 +21,30 @@ class ActivityFinishedStatusMessage extends StatelessWidget {
required this.controller,
});
Future<void> _onArchive(BuildContext context) async {
{
final resp = await showFutureLoadingDialog(
context: context,
future: () => _archiveToAnalytics(context),
);
if (!resp.isError) {
final navigate = await showDialog(
context: context,
builder: (context) {
return const SavedActivityAnalyticsDialog();
},
);
if (navigate == true) {
context.go(
"/rooms/analytics?mode=activities",
);
}
}
}
}
Future<void> _archiveToAnalytics(BuildContext context) async {
await controller.room.archiveActivity();
await MatrixState.pangeaController.putAnalytics
@ -44,6 +69,11 @@ class ActivityFinishedStatusMessage extends StatelessWidget {
await courseParent.finishCourseActivity(activityId, topicId);
}
ActivitySummaryModel? get summary => controller.room.activitySummary;
bool get _enableArchive =>
summary?.summary != null || summary?.hasError == true;
@override
Widget build(BuildContext context) {
if (!controller.room.showActivityChatUI ||
@ -53,18 +83,13 @@ class ActivityFinishedStatusMessage extends StatelessWidget {
}
final theme = Theme.of(context);
final summary = controller.room.activitySummary;
return AnimatedSize(
duration: FluffyThemes.animationDuration,
child: Container(
margin: const EdgeInsets.only(top: 20.0),
padding: const EdgeInsets.only(
top: 12.0,
left: 12.0,
right: 12.0,
),
padding: const EdgeInsets.all(12.0),
decoration: BoxDecoration(
color: theme.colorScheme.surface,
border: Border(
top: BorderSide(color: theme.dividerColor),
),
@ -123,27 +148,8 @@ class ActivityFinishedStatusMessage extends StatelessWidget {
theme.colorScheme.onPrimaryContainer,
backgroundColor: theme.colorScheme.primaryContainer,
),
onPressed: () async {
final resp = await showFutureLoadingDialog(
context: context,
future: () => _archiveToAnalytics(context),
);
if (!resp.isError) {
final navigate = await showDialog(
context: context,
builder: (context) {
return const SavedActivityAnalyticsDialog();
},
);
if (navigate == true) {
context.go(
"/rooms/analytics?mode=activities",
);
}
}
},
onPressed:
_enableArchive ? () => _onArchive(context) : null,
child: Row(
spacing: 12.0,
mainAxisAlignment: MainAxisAlignment.center,

View file

@ -156,7 +156,7 @@ class ButtonControlledCarouselView extends StatelessWidget {
child: SingleChildScrollView(
child: Text(
p.feedback,
style: const TextStyle(fontSize: 8.0),
style: const TextStyle(fontSize: 12.0),
),
),
),

View file

@ -65,6 +65,10 @@ class _MxcImageState extends State<MxcImage> {
: _imageDataCache[cacheKey] = data;
}
// #Pangea
Object? _error;
// Pangea#
Future<void> _load() async {
if (!mounted) return;
final client =
@ -112,12 +116,22 @@ class _MxcImageState extends State<MxcImage> {
return;
}
try {
// #Pangea
setState(() => _error = null);
// Pangea#
await _load();
} on IOException catch (_) {
if (!mounted) return;
await Future.delayed(widget.retryDuration);
_tryLoad();
}
// #Pangea
catch (e) {
if (mounted) {
setState(() => _error = e);
}
}
// Pangea#
}
@override
@ -153,37 +167,51 @@ class _MxcImageState extends State<MxcImage> {
return AnimatedCrossFade(
crossFadeState:
hasData ? CrossFadeState.showSecond : CrossFadeState.showFirst,
// #Pangea
// hasData ? CrossFadeState.showSecond : CrossFadeState.showFirst,
hasData || _error != null
? CrossFadeState.showSecond
: CrossFadeState.showFirst,
// Pangea#
duration: const Duration(milliseconds: 128),
firstChild: placeholder(context),
secondChild: hasData
? Image.memory(
data,
// #Pangea
// secondChild: hasData
secondChild: _error != null
? SizedBox(
width: widget.width,
height: widget.height,
fit: widget.fit,
filterQuality:
widget.isThumbnail ? FilterQuality.low : FilterQuality.medium,
errorBuilder: (context, e, s) {
Logs().d('Unable to render mxc image', e, s);
return SizedBox(
)
: hasData
// Pangea#
? Image.memory(
data,
width: widget.width,
height: widget.height,
child: Material(
color: Theme.of(context).colorScheme.surfaceContainer,
child: Icon(
Icons.broken_image_outlined,
size: min(widget.height ?? 64, 64),
color: Theme.of(context).colorScheme.onSurface,
),
),
);
},
)
: SizedBox(
width: widget.width,
height: widget.height,
),
fit: widget.fit,
filterQuality: widget.isThumbnail
? FilterQuality.low
: FilterQuality.medium,
errorBuilder: (context, e, s) {
Logs().d('Unable to render mxc image', e, s);
return SizedBox(
width: widget.width,
height: widget.height,
child: Material(
color: Theme.of(context).colorScheme.surfaceContainer,
child: Icon(
Icons.broken_image_outlined,
size: min(widget.height ?? 64, 64),
color: Theme.of(context).colorScheme.onSurface,
),
),
);
},
)
: SizedBox(
width: widget.width,
height: widget.height,
),
);
}
}