Files
afterwork/lib/presentation/screens/event/event_card.dart
2024-09-03 14:24:46 +00:00

430 lines
15 KiB
Dart

import 'package:flutter/material.dart';
import 'package:afterwork/data/datasources/event_remote_data_source.dart';
import '../../../core/utils/date_formatter.dart';
/// Widget pour afficher une carte d'événement.
/// Cette classe est utilisée pour afficher les détails d'un événement,
/// incluant son titre, sa description, son image, et des actions possibles
/// telles que réagir, commenter, partager, participer, et fermer ou rouvrir l'événement.
class EventCard extends StatelessWidget {
// Identifiant unique de l'événement
final String eventId;
// Source de données distante pour les opérations sur l'événement
final EventRemoteDataSource eventRemoteDataSource;
// Identifiant de l'utilisateur
final String userId;
// Nom de l'utilisateur
final String userName;
// Prénom de l'utilisateur
final String userLastName;
// URL de l'image de profil de l'utilisateur
final String profileImage;
// Nom complet de l'utilisateur (nom + prénom)
final String name;
// Date de publication de l'événement
final String datePosted;
// Titre de l'événement
final String eventTitle;
// Description de l'événement
final String eventDescription;
// URL de l'image de l'événement
final String eventImageUrl;
// Statut de l'événement (e.g., "OPEN", "CLOSED")
final String eventStatus;
// Catégorie de l'événement
final String eventCategory;
// Nombre de réactions à l'événement
final int reactionsCount;
// Nombre de commentaires sur l'événement
final int commentsCount;
// Nombre de partages de l'événement
final int sharesCount;
// Callback pour l'action "Réagir"
final VoidCallback onReact;
// Callback pour l'action "Commenter"
final VoidCallback onComment;
// Callback pour l'action "Partager"
final VoidCallback onShare;
// Callback pour l'action "Participer"
final VoidCallback onParticipate;
// Callback pour afficher plus d'options
final VoidCallback onMoreOptions;
// Callback pour fermer l'événement
final VoidCallback onCloseEvent;
// Callback pour rouvrir l'événement
final VoidCallback onReopenEvent;
const EventCard({
Key? key,
required this.eventId,
required this.eventRemoteDataSource,
required this.userId,
required this.userName,
required this.userLastName,
required this.profileImage,
required this.name,
required this.datePosted,
required this.eventTitle,
required this.eventDescription,
required this.eventImageUrl,
required this.eventStatus,
required this.eventCategory,
required this.reactionsCount,
required this.commentsCount,
required this.sharesCount,
required this.onReact,
required this.onComment,
required this.onShare,
required this.onParticipate,
required this.onMoreOptions,
required this.onCloseEvent,
required this.onReopenEvent,
}) : super(key: key);
@override
Widget build(BuildContext context) {
// Log du rendu de la carte d'événement
print('Rendu de l\'EventCard pour l\'événement $eventId avec statut $eventStatus');
return AnimatedOpacity(
opacity: 1.0,
duration: const Duration(milliseconds: 300),
child: Dismissible(
key: ValueKey(eventId),
direction: eventStatus == 'CLOSED'
? DismissDirection.endToStart // Permet de rouvrir avec un swipe à gauche
: DismissDirection.startToEnd, // Permet de fermer avec un swipe à droite
onDismissed: (direction) {
if (direction == DismissDirection.startToEnd) {
// Log du déclenchement de la fermeture de l'événement
print('Tentative de fermeture de l\'événement $eventId');
onCloseEvent();
} else if (direction == DismissDirection.endToStart && eventStatus == 'CLOSED') {
// Log du déclenchement de la réouverture de l'événement
print('Tentative de réouverture de l\'événement $eventId');
onReopenEvent();
}
},
background: Container(
color: Colors.red,
alignment: Alignment.centerLeft,
padding: const EdgeInsets.only(left: 20.0),
child: const Icon(Icons.delete, color: Colors.white),
),
secondaryBackground: Container(
color: Colors.green,
alignment: Alignment.centerRight,
padding: const EdgeInsets.only(right: 20.0),
child: const Icon(Icons.replay, color: Colors.white),
),
child: Stack(
children: [
Card(
color: const Color(0xFF2C2C3E),
margin: const EdgeInsets.symmetric(vertical: 10.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildHeader(context),
const SizedBox(height: 10),
_buildEventCategory(),
const SizedBox(height: 5),
_buildEventDetails(),
const SizedBox(height: 10),
_buildEventImage(),
const SizedBox(height: 10),
Divider(color: Colors.white.withOpacity(0.2)),
_buildInteractionRow(),
const SizedBox(height: 5),
_buildParticipateButton(),
],
),
),
),
],
),
),
);
}
/// Construire l'en-tête de la carte avec les informations de l'utilisateur.
/// Cette méthode affiche l'image de profil, le nom de l'utilisateur, la date
/// de publication de l'événement, et le statut de l'événement.
Widget _buildHeader(BuildContext context) {
// Log du rendu de l'en-tête de la carte
print('Rendu de l\'en-tête pour l\'événement $eventId');
// Convertir la date `datePosted` en DateTime si ce n'est pas déjà fait
DateTime dateTimePosted = DateTime.parse(datePosted);
// Utiliser le DateFormatter pour formater la date
String formattedDate = DateFormatter.formatDate(dateTimePosted);
return Row(
children: [
CircleAvatar(
backgroundImage: AssetImage(profileImage),
radius: 25,
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
name,
style: const TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
Row(
children: [
Text(
formattedDate,
style: const TextStyle(color: Colors.white70, fontSize: 14),
),
const SizedBox(width: 10),
_buildStatusBadge(), // Badge de statut aligné sur la même ligne que la date du post
],
),
],
),
),
IconButton(
icon: const Icon(Icons.more_vert, color: Colors.white),
onPressed: () {
// Log du déclenchement du bouton "Plus d'options"
print('Plus d\'options déclenché pour l\'événement $eventId');
onMoreOptions();
},
),
if (eventStatus != 'CLOSED') // Masquer le bouton de fermeture si l'événement est fermé
IconButton(
icon: const Icon(Icons.close, color: Colors.white),
onPressed: () {
// Log du déclenchement du bouton de fermeture de l'événement
print('Tentative de fermeture de l\'événement $eventId');
onCloseEvent();
},
),
],
);
}
/// Afficher la catégorie de l'événement au-dessus du titre.
/// Cette méthode affiche la catégorie en italique pour distinguer le type d'événement.
Widget _buildEventCategory() {
// Log du rendu de la catégorie de l'événement
print('Affichage de la catégorie pour l\'événement $eventId: $eventCategory');
return Text(
eventCategory,
style: const TextStyle(
color: Colors.blueAccent,
fontSize: 14,
fontStyle: FontStyle.italic, // Style en italique
fontWeight: FontWeight.w400, // Titre fin
),
);
}
/// Afficher les détails de l'événement.
/// Cette méthode affiche le titre et la description de l'événement.
Widget _buildEventDetails() {
// Log du rendu des détails de l'événement
print('Affichage des détails pour l\'événement $eventId');
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
eventTitle,
style: const TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 5),
Text(
eventDescription,
style: const TextStyle(color: Colors.white70, fontSize: 14),
),
],
);
}
/// Afficher l'image de l'événement.
/// Cette méthode affiche l'image associée à l'événement.
Widget _buildEventImage() {
// Log du rendu de l'image de l'événement
print('Affichage de l\'image pour l\'événement $eventId');
return ClipRRect(
borderRadius: BorderRadius.circular(10.0),
child: Image.network(
eventImageUrl,
height: 180,
width: double.infinity,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
// Log de l'erreur lors du chargement de l'image
print('Erreur de chargement de l\'image pour l\'événement $eventId: $error');
return Image.asset(
'lib/assets/images/placeholder.png',
height: 180,
width: double.infinity,
fit: BoxFit.cover,
);
},
),
);
}
/// Afficher les icônes d'interaction (réagir, commenter, partager).
/// Cette méthode affiche les boutons pour réagir, commenter, et partager l'événement.
Widget _buildInteractionRow() {
// Log du rendu de la ligne d'interaction de l'événement
print('Affichage des interactions pour l\'événement $eventId');
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0), // Réduire le padding vertical
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround, // Utiliser spaceAround pour réduire l'espace
children: [
Expanded(
child: _buildIconButton(
icon: Icons.thumb_up_alt_outlined,
label: 'Réagir',
count: reactionsCount,
onPressed: () {
// Log de l'action "Réagir"
print('Réaction à l\'événement $eventId');
onReact();
},
),
),
Expanded(
child: _buildIconButton(
icon: Icons.comment_outlined,
label: 'Commenter',
count: commentsCount,
onPressed: () {
// Log de l'action "Commenter"
print('Commentaire sur l\'événement $eventId');
onComment();
},
),
),
Expanded(
child: _buildIconButton(
icon: Icons.share_outlined,
label: 'Partager',
count: sharesCount,
onPressed: () {
// Log de l'action "Partager"
print('Partage de l\'événement $eventId');
onShare();
},
),
),
],
),
);
}
/// Bouton d'interaction personnalisé.
/// Cette méthode construit un bouton avec une icône et un label pour l'interaction.
Widget _buildIconButton({
required IconData icon,
required String label,
required int count,
required VoidCallback onPressed,
}) {
// Log de la construction du bouton d'interaction
print('Construction du bouton $label pour l\'événement $eventId');
return TextButton.icon(
onPressed: onPressed,
icon: Icon(icon, color: const Color(0xFF1DBF73), size: 20),
label: Text(
'$label ($count)',
style: const TextStyle(color: Colors.white70, fontSize: 12),
overflow: TextOverflow.ellipsis,
),
);
}
/// Bouton pour participer à l'événement.
/// Cette méthode construit un bouton qui permet de participer à l'événement.
/// Si l'événement est fermé, le bouton est caché.
Widget _buildParticipateButton() {
// Log de la construction du bouton "Participer"
print('Construction du bouton "Participer" pour l\'événement $eventId avec statut $eventStatus');
// Si l'événement est fermé, ne rien retourner (pas de bouton)
if (eventStatus == 'CLOSED') {
print('L\'événement $eventId est fermé, le bouton "Participer" est caché.');
return SizedBox.shrink(); // Retourne un widget vide pour ne pas occuper d'espace
}
// Sinon, retourner le bouton "Participer"
return ElevatedButton(
onPressed: onParticipate,
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF1DBF73),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
padding: const EdgeInsets.symmetric(vertical: 12.0),
minimumSize: const Size(double.infinity, 40),
),
child: const Text(
'Participer',
style: TextStyle(color: Colors.white),
),
);
}
/// Construire un badge pour afficher le statut de l'événement.
/// Cette méthode affiche un badge avec le statut de l'événement ("OPEN" ou "CLOSED").
Widget _buildStatusBadge() {
// Log de la construction du badge de statut
print('Construction du badge de statut pour l\'événement $eventId: $eventStatus');
Color badgeColor;
switch (eventStatus) {
case 'CLOSED':
badgeColor = Colors.redAccent;
break;
case 'OPEN':
default:
badgeColor = Colors.greenAccent;
break;
}
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
decoration: BoxDecoration(
color: badgeColor.withOpacity(0.7),
borderRadius: BorderRadius.circular(8.0),
),
child: Text(
eventStatus.toUpperCase(),
style: const TextStyle(
color: Colors.white,
fontSize: 10, // Réduction de la taille du texte
fontWeight: FontWeight.bold,
),
),
);
}
}