feat(frontend): Séparation des demandes d'amitié envoyées et reçues

- Ajout de deux endpoints distincts dans Urls: getSentFriendRequestsWithUserId et getReceivedFriendRequestsWithUserId
- Ajout de méthodes dans FriendsRepository et FriendsRepositoryImpl pour récupérer séparément les demandes envoyées et reçues
- Ajout de la méthode cancelFriendRequest pour annuler une demande envoyée
- Modification de FriendsProvider pour gérer deux listes distinctes: sentRequests et receivedRequests
- Mise à jour de FriendsScreen pour afficher deux sections:
  - Demandes reçues: avec boutons Accepter/Rejeter
  - Demandes envoyées: avec bouton Annuler uniquement
- Correction du mapping JSON dans FriendRequest.fromJson (userNom/userPrenoms correctement mappés)
- Amélioration de FriendRequestCard pour gérer les deux types de demandes
- Correction de la validation d'URL d'image dans FriendDetailScreen
- Support du champ uuid dans UserModel.fromJson pour compatibilité backend
This commit is contained in:
dahoud
2026-01-07 16:33:27 +00:00
parent 69c8c21591
commit 06031b01f2
10 changed files with 2599 additions and 373 deletions

View File

@@ -7,6 +7,11 @@ import '../../domain/entities/friend.dart';
/// et une option pour envoyer un message.
/// Utilisé lorsque l'utilisateur clique sur un ami pour voir plus de détails.
class FriendDetailScreen extends StatelessWidget {
/// Constructeur de la classe [FriendDetailScreen].
FriendDetailScreen({
required this.friendFirstName, required this.friendLastName, required this.imageUrl, required this.friendId, required this.status, required this.lastInteraction, required this.dateAdded, super.key,
});
final String friendFirstName; // Nom de l'ami
final String friendLastName;
final String imageUrl; // URL de l'image de profil de l'ami
@@ -16,18 +21,6 @@ class FriendDetailScreen extends StatelessWidget {
final String lastInteraction;
final String dateAdded;
/// Constructeur de la classe [FriendDetailScreen].
FriendDetailScreen({
Key? key,
required this.friendFirstName,
required this.friendLastName,
required this.imageUrl,
required this.friendId,
required this.status,
required this.lastInteraction,
required this.dateAdded,
}) : super(key: key);
/// Méthode statique pour lancer l'écran des détails d'un ami.
static void open(
BuildContext context,
@@ -37,7 +30,7 @@ class FriendDetailScreen extends StatelessWidget {
String imageUrl,
FriendStatus status,
String lastInteraction,
String dateAdded) {
String dateAdded,) {
Navigator.push(
context,
MaterialPageRoute(
@@ -54,16 +47,44 @@ class FriendDetailScreen extends StatelessWidget {
);
}
/// Vérifie si une URL est valide pour le chargement d'image réseau.
bool _isValidImageUrl(String? url) {
if (url == null || url.isEmpty) {
return false;
}
try {
final uri = Uri.tryParse(url);
if (uri == null) {
return false;
}
// Vérifier que c'est une URL HTTP/HTTPS valide
if (!uri.hasScheme || (!uri.scheme.startsWith('http'))) {
return false;
}
// Vérifier qu'il y a un host
if (uri.host.isEmpty) {
return false;
}
return true;
} catch (e) {
return false;
}
}
@override
Widget build(BuildContext context) {
_logger.i('[LOG] Affichage des détails de l\'ami : $friendFirstName (ID: $friendId)');
// Utilise `AssetImage` si `imageUrl` est vide ou ne contient pas d'URL valide.
final imageProvider =
imageUrl.isNotEmpty && Uri.tryParse(imageUrl)?.hasAbsolutePath == true
// Vérifier si l'URL est valide avant de créer le NetworkImage
final bool hasValidImageUrl = _isValidImageUrl(imageUrl);
final ImageProvider imageProvider = hasValidImageUrl
? NetworkImage(imageUrl)
: const AssetImage('lib/assets/images/default_avatar.png')
as ImageProvider;
as ImageProvider;
return Scaffold(
appBar: AppBar(
@@ -72,10 +93,9 @@ class FriendDetailScreen extends StatelessWidget {
elevation: 6, // Ombre sous l'app bar pour plus de profondeur
),
body: Padding(
padding: const EdgeInsets.all(16.0), // Espacement autour du contenu
padding: const EdgeInsets.all(16), // Espacement autour du contenu
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Animation Hero pour une transition fluide lors de la navigation
Hero(
@@ -85,12 +105,12 @@ class FriendDetailScreen extends StatelessWidget {
curve: Curves.easeInOut,
child: CircleAvatar(
radius: 80,
backgroundImage: imageProvider,
backgroundImage: hasValidImageUrl ? imageProvider : null,
backgroundColor: Colors.grey.shade800,
onBackgroundImageError: (error, stackTrace) {
_logger.e('[ERROR] Erreur lors du chargement de l\'image pour $friendFirstName (ID: $friendId): $error');
},
child: imageUrl.isEmpty
child: !hasValidImageUrl
? const Icon(Icons.person, size: 60, color: Colors.white)
: null,
),
@@ -156,9 +176,8 @@ class FriendDetailScreen extends StatelessWidget {
/// Widget réutilisable pour afficher une ligne d'information avec un texte d'introduction et une valeur.
Widget _buildInfoRow(String label, String value) {
return Padding(
padding: const EdgeInsets.only(bottom: 12.0),
padding: const EdgeInsets.only(bottom: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
label,