Enhance group member management: add last name support in GroupMember model, update member display in chat and trip details, and implement pseudo change functionality in chat group.
This commit is contained in:
@@ -1,12 +1,15 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'dart:async';
|
||||
import '../../blocs/user/user_bloc.dart';
|
||||
import '../../blocs/user/user_state.dart' as user_state;
|
||||
import '../../blocs/message/message_bloc.dart';
|
||||
import '../../blocs/message/message_event.dart';
|
||||
import '../../blocs/message/message_state.dart';
|
||||
import '../../models/group.dart';
|
||||
import '../../models/group_member.dart';
|
||||
import '../../models/message.dart';
|
||||
import '../../repositories/group_repository.dart';
|
||||
|
||||
/// Chat group content widget for group messaging functionality.
|
||||
///
|
||||
@@ -47,17 +50,34 @@ class _ChatGroupContentState extends State<ChatGroupContent> {
|
||||
/// Currently selected message for editing (null if not editing)
|
||||
Message? _editingMessage;
|
||||
|
||||
/// Repository pour gérer les groupes
|
||||
final _groupRepository = GroupRepository();
|
||||
|
||||
/// Subscription pour écouter les changements des membres du groupe
|
||||
late StreamSubscription<List<GroupMember>> _membersSubscription;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Load messages when the widget initializes
|
||||
context.read<MessageBloc>().add(LoadMessages(widget.group.id));
|
||||
|
||||
// Écouter les changements des membres du groupe
|
||||
_membersSubscription = _groupRepository.watchGroupMembers(widget.group.id).listen((updatedMembers) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
widget.group.members.clear();
|
||||
widget.group.members.addAll(updatedMembers);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_messageController.dispose();
|
||||
_scrollController.dispose();
|
||||
_membersSubscription.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -381,12 +401,15 @@ class _ChatGroupContentState extends State<ChatGroupContent> {
|
||||
textColor = isDark ? Colors.white : Colors.black87;
|
||||
}
|
||||
|
||||
// Trouver le membre qui a envoyé le message pour récupérer sa photo
|
||||
// Trouver le membre qui a envoyé le message pour récupérer son pseudo actuel
|
||||
final senderMember = widget.group.members.firstWhere(
|
||||
(m) => m.userId == message.senderId,
|
||||
orElse: () => null as dynamic,
|
||||
) as dynamic;
|
||||
|
||||
// Utiliser le pseudo actuel du membre, ou le senderName en fallback
|
||||
final displayName = senderMember != null ? senderMember.pseudo : message.senderName;
|
||||
|
||||
return Align(
|
||||
alignment: isMe ? Alignment.centerRight : Alignment.centerLeft,
|
||||
child: GestureDetector(
|
||||
@@ -413,8 +436,8 @@ class _ChatGroupContentState extends State<ChatGroupContent> {
|
||||
senderMember.profilePictureUrl == null ||
|
||||
senderMember.profilePictureUrl!.isEmpty)
|
||||
? Text(
|
||||
message.senderName.isNotEmpty
|
||||
? message.senderName[0].toUpperCase()
|
||||
displayName.isNotEmpty
|
||||
? displayName[0].toUpperCase()
|
||||
: '?',
|
||||
style: const TextStyle(fontSize: 12),
|
||||
)
|
||||
@@ -443,7 +466,7 @@ class _ChatGroupContentState extends State<ChatGroupContent> {
|
||||
children: [
|
||||
if (!isMe) ...[
|
||||
Text(
|
||||
message.senderName,
|
||||
displayName,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
@@ -689,6 +712,9 @@ class _ChatGroupContentState extends State<ChatGroupContent> {
|
||||
? member.firstName[0].toUpperCase()
|
||||
: '?');
|
||||
|
||||
// Construire le nom complet
|
||||
final fullName = '${member.firstName} ${member.lastName}'.trim();
|
||||
|
||||
return ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundImage: (member.profilePictureUrl != null &&
|
||||
@@ -699,10 +725,30 @@ class _ChatGroupContentState extends State<ChatGroupContent> {
|
||||
? Text(initials)
|
||||
: null,
|
||||
),
|
||||
title: Text(member.pseudo),
|
||||
title: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(member.pseudo),
|
||||
if (fullName.isNotEmpty)
|
||||
Text(
|
||||
fullName,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: member.role == 'admin'
|
||||
? const Text('Administrateur', style: TextStyle(fontSize: 12))
|
||||
: null,
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Icons.edit, size: 18),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
_showChangePseudoDialog(member);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
@@ -716,4 +762,86 @@ class _ChatGroupContentState extends State<ChatGroupContent> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showChangePseudoDialog(dynamic member) {
|
||||
final pseudoController = TextEditingController(text: member.pseudo);
|
||||
final theme = Theme.of(context);
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
backgroundColor: theme.dialogBackgroundColor,
|
||||
title: Text(
|
||||
'Changer le pseudo',
|
||||
style: theme.textTheme.titleLarge?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
content: TextField(
|
||||
controller: pseudoController,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Nouveau pseudo',
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 12,
|
||||
),
|
||||
),
|
||||
style: TextStyle(color: theme.colorScheme.onSurface),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text(
|
||||
'Annuler',
|
||||
style: TextStyle(color: theme.colorScheme.primary),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
final newPseudo = pseudoController.text.trim();
|
||||
if (newPseudo.isNotEmpty) {
|
||||
_updateMemberPseudo(member, newPseudo);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
'Valider',
|
||||
style: TextStyle(color: theme.colorScheme.primary),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _updateMemberPseudo(dynamic member, String newPseudo) async {
|
||||
try {
|
||||
final updatedMember = member.copyWith(pseudo: newPseudo);
|
||||
await _groupRepository.addMember(widget.group.id, updatedMember);
|
||||
|
||||
if (mounted) {
|
||||
// Le stream listener va automatiquement mettre à jour les membres
|
||||
// Pas besoin de fermer le dialog ou de faire un refresh manuel
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Pseudo modifié en "$newPseudo"'),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Erreur lors de la modification du pseudo: $e'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user