chore: normalize activity role list into separate widget and use it in activity state event and activity role selection widgets (#3747)

This commit is contained in:
ggurdin 2025-08-14 15:30:36 -04:00 committed by GitHub
parent bd303a5796
commit 3fcf3845d8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 135 additions and 88 deletions

View file

@ -0,0 +1,115 @@
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_participant_indicator.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_role_model.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart';
import 'package:fluffychat/widgets/avatar.dart';
class ActivityParticipantList extends StatelessWidget {
final Room room;
final Function(String)? onTap;
final bool Function(String)? canSelect;
final bool Function(String)? isSelected;
final double Function(ActivityRoleModel?)? getOpacity;
const ActivityParticipantList({
super.key,
required this.room,
this.onTap,
this.canSelect,
this.isSelected,
this.getOpacity,
});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final availableRoles = room.activityPlan!.roles;
final assignedRoles = room.assignedRoles ?? {};
final remainingMembers = room.getParticipants().where(
(p) => !assignedRoles.values.any((r) => r.userId == p.id),
);
return Column(
spacing: 12.0,
mainAxisSize: MainAxisSize.min,
children: [
Wrap(
alignment: WrapAlignment.center,
spacing: 12.0,
runSpacing: 12.0,
children: availableRoles.values.map((availableRole) {
final assignedRole = assignedRoles[availableRole.id];
final user = room.getParticipants().firstWhereOrNull(
(u) => u.id == assignedRole?.userId,
);
final selectable =
canSelect != null ? canSelect!(availableRole.id) : true;
return ActivityParticipantIndicator(
availableRole: availableRole,
assignedRole: assignedRole,
opacity: getOpacity != null ? getOpacity!(assignedRole) : 1.0,
avatarUrl: availableRole.avatarUrl ?? user?.avatarUrl?.toString(),
onTap: onTap != null && selectable
? () => onTap!(availableRole.id)
: null,
selected:
isSelected != null ? isSelected!(availableRole.id) : false,
);
}).toList(),
),
Wrap(
alignment: WrapAlignment.center,
spacing: 12.0,
runSpacing: 12.0,
children: remainingMembers.map((member) {
return Container(
decoration: BoxDecoration(
color: theme.colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(18.0),
),
padding: const EdgeInsets.all(4.0),
child: Opacity(
opacity: 0.5,
child: Row(
spacing: 4.0,
mainAxisSize: MainAxisSize.min,
children: [
Avatar(
size: 18.0,
mxContent: member.avatarUrl,
name: member.calcDisplayname(),
userId: member.id,
),
ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 80.0,
),
child: Text(
member.calcDisplayname(),
style: TextStyle(
fontSize: 12.0,
color: theme.colorScheme.onPrimaryContainer,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
],
),
),
);
}).toList(),
),
],
);
}
}

View file

@ -1,14 +1,12 @@
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/chat/events/state_message.dart';
import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_participant_indicator.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_participant_list.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart';
import 'package:fluffychat/widgets/avatar.dart';
class ActivityStateEvent extends StatelessWidget {
final Event event;
@ -22,15 +20,6 @@ class ActivityStateEvent extends StatelessWidget {
try {
final activity = ActivityPlanModel.fromJson(event.content);
final availableRoles = event.room.activityPlan!.roles;
final assignedRoles = event.room.assignedRoles ?? {};
final remainingMembers = event.room.getParticipants().where(
(p) => !assignedRoles.values.any((r) => r.userId == p.id),
);
final theme = Theme.of(context);
return Container(
padding: const EdgeInsets.symmetric(
horizontal: 24.0,
@ -48,69 +37,10 @@ class ActivityStateEvent extends StatelessWidget {
),
if (event.room.ownRole != null ||
event.room.remainingRoles == 0) ...[
Wrap(
alignment: WrapAlignment.center,
spacing: 12.0,
runSpacing: 12.0,
children: availableRoles.values.map((availableRole) {
final assignedRole = assignedRoles[availableRole.id];
final user = event.room.getParticipants().firstWhereOrNull(
(u) => u.id == assignedRole?.userId,
);
return ActivityParticipantIndicator(
availableRole: availableRole,
assignedRole: assignedRole,
opacity: assignedRole == null || assignedRole.isFinished
? 0.5
: 1.0,
avatarUrl:
availableRole.avatarUrl ?? user?.avatarUrl?.toString(),
);
}).toList(),
),
Wrap(
alignment: WrapAlignment.center,
spacing: 12.0,
runSpacing: 12.0,
children: remainingMembers.map((member) {
return Container(
decoration: BoxDecoration(
color: theme.colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(18.0),
),
padding: const EdgeInsets.all(4.0),
child: Opacity(
opacity: 0.5,
child: Row(
spacing: 4.0,
mainAxisSize: MainAxisSize.min,
children: [
Avatar(
size: 18.0,
mxContent: member.avatarUrl,
name: member.calcDisplayname(),
userId: member.id,
),
ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 80.0,
),
child: Text(
member.calcDisplayname(),
style: TextStyle(
fontSize: 12.0,
color: theme.colorScheme.onPrimaryContainer,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
],
),
),
);
}).toList(),
ActivityParticipantList(
room: event.room,
getOpacity: (role) =>
role == null || role.isFinished ? 0.5 : 1.0,
),
],
],

View file

@ -4,7 +4,7 @@ import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_participant_indicator.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_participant_list.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
@ -47,18 +47,11 @@ class ActivityUnfinishedStatusMessageState
children: [
if (!completed) ...[
if (unassignedIds.isNotEmpty)
Wrap(
alignment: WrapAlignment.center,
spacing: 12.0,
runSpacing: 12.0,
children: unassignedIds.map((id) {
return ActivityParticipantIndicator(
availableRole: availableRoles[id]!,
selected: _selectedRoleId == id,
onTap: () => _selectRole(id),
avatarUrl: availableRoles[id]?.avatarUrl,
);
}).toList(),
ActivityParticipantList(
room: widget.room,
onTap: _selectRole,
isSelected: (id) => _selectedRoleId == id,
canSelect: (id) => unassignedIds.contains(id),
),
const SizedBox(height: 16.0),
Text(

View file

@ -173,22 +173,27 @@ class SpaceAnalyticsView extends StatelessWidget {
controller.downloads.length,
),
icon: Icons.group_outlined,
mini: mini,
),
_TableHeaderCell(
text: L10n.of(context).level,
icon: Icons.star,
mini: mini,
),
_TableHeaderCell(
text: L10n.of(context).vocab,
icon: Symbols.dictionary,
mini: mini,
),
_TableHeaderCell(
text: L10n.of(context).grammar,
icon: Symbols.toys_and_games,
mini: mini,
),
_TableHeaderCell(
text: L10n.of(context).activities,
icon: Icons.radar,
mini: mini,
),
],
),
@ -263,18 +268,21 @@ class SpaceAnalyticsView extends StatelessWidget {
text: download.summary?.level?.toString(),
downloadStatus: download.downloadStatus,
requestStatus: download.requestStatus,
mini: mini,
),
_TableContentCell(
text: download.summary?.numLemmas
.toString(),
downloadStatus: download.downloadStatus,
requestStatus: download.requestStatus,
mini: mini,
),
_TableContentCell(
text: download.summary?.numMorphConstructs
.toString(),
downloadStatus: download.downloadStatus,
requestStatus: download.requestStatus,
mini: mini,
),
_TableContentCell(
text: download
@ -282,6 +290,7 @@ class SpaceAnalyticsView extends StatelessWidget {
.toString(),
downloadStatus: download.downloadStatus,
requestStatus: download.requestStatus,
mini: mini,
),
],
);