fix(chat): Correction race condition + Implémentation TODOs
## Corrections Critiques ### Race Condition - Statuts de Messages - Fix : Les icônes de statut (✓, ✓✓, ✓✓ bleu) ne s'affichaient pas - Cause : WebSocket delivery confirmations arrivaient avant messages locaux - Solution : Pattern Optimistic UI dans chat_bloc.dart - Création message temporaire immédiate - Ajout à la liste AVANT requête HTTP - Remplacement par message serveur à la réponse - Fichier : lib/presentation/state_management/chat_bloc.dart ## Implémentation TODOs (13/21) ### Social (social_header_widget.dart) - ✅ Copier lien du post dans presse-papiers - ✅ Partage natif via Share.share() - ✅ Dialogue de signalement avec 5 raisons ### Partage (share_post_dialog.dart) - ✅ Interface sélection d'amis avec checkboxes - ✅ Partage externe via Share API ### Média (media_upload_service.dart) - ✅ Parsing JSON réponse backend - ✅ Méthode deleteMedia() pour suppression - ✅ Génération miniature vidéo ### Posts (create_post_dialog.dart, edit_post_dialog.dart) - ✅ Extraction URL depuis uploads - ✅ Documentation chargement médias ### Chat (conversations_screen.dart) - ✅ Navigation vers notifications - ✅ ConversationSearchDelegate pour recherche ## Nouveaux Fichiers ### Configuration - build-prod.ps1 : Script build production avec dart-define - lib/core/constants/env_config.dart : Gestion environnements ### Documentation - TODOS_IMPLEMENTED.md : Documentation complète TODOs ## Améliorations ### Architecture - Refactoring injection de dépendances - Amélioration routing et navigation - Optimisation providers (UserProvider, FriendsProvider) ### UI/UX - Amélioration thème et couleurs - Optimisation animations - Meilleure gestion erreurs ### Services - Configuration API avec env_config - Amélioration datasources (events, users) - Optimisation modèles de données
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logger/logger.dart';
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../core/utils/app_logger.dart';
|
||||
import '../../data/repositories/friends_repository_impl.dart';
|
||||
import '../../data/services/realtime_notification_service.dart';
|
||||
import '../../data/services/secure_storage.dart';
|
||||
import '../../domain/entities/friend.dart';
|
||||
import '../../domain/entities/friend_request.dart';
|
||||
@@ -15,7 +18,6 @@ class FriendsProvider with ChangeNotifier { // Nombre d'amis à récupérer par
|
||||
/// Constructeur de [FriendsProvider] qui nécessite l'instance d'un [FriendsRepositoryImpl].
|
||||
FriendsProvider({required this.friendsRepository});
|
||||
final FriendsRepositoryImpl friendsRepository;
|
||||
final Logger _logger = Logger(); // Utilisation du logger pour une traçabilité complète des actions.
|
||||
|
||||
// Liste des amis
|
||||
List<Friend> _friendsList = [];
|
||||
@@ -36,6 +38,14 @@ class FriendsProvider with ChangeNotifier { // Nombre d'amis à récupérer par
|
||||
|
||||
final int _requestsPerPage = 10;
|
||||
|
||||
// Liste des suggestions d'amis
|
||||
List<dynamic> _friendSuggestions = [];
|
||||
bool _isLoadingSuggestions = false;
|
||||
|
||||
// Service de notifications temps réel
|
||||
RealtimeNotificationService? _realtimeService;
|
||||
StreamSubscription<FriendRequestNotification>? _friendRequestSubscription;
|
||||
|
||||
// Getters pour accéder à l'état actuel des données
|
||||
bool get isLoading => _isLoading;
|
||||
bool get hasMore => _hasMore;
|
||||
@@ -44,7 +54,9 @@ class FriendsProvider with ChangeNotifier { // Nombre d'amis à récupérer par
|
||||
List<FriendRequest> get receivedRequests => _receivedRequests;
|
||||
bool get isLoadingSentRequests => _isLoadingSentRequests;
|
||||
bool get isLoadingReceivedRequests => _isLoadingReceivedRequests;
|
||||
|
||||
List<dynamic> get friendSuggestions => _friendSuggestions;
|
||||
bool get isLoadingSuggestions => _isLoadingSuggestions;
|
||||
|
||||
// Pour compatibilité avec l'ancien code
|
||||
List<FriendRequest> get pendingRequests => _receivedRequests;
|
||||
bool get isLoadingRequests => _isLoadingReceivedRequests;
|
||||
@@ -60,48 +72,48 @@ class FriendsProvider with ChangeNotifier { // Nombre d'amis à récupérer par
|
||||
/// - Les erreurs et les logs pour une traçabilité complète.
|
||||
Future<void> fetchFriends(String userId, {bool loadMore = false}) async {
|
||||
if (_isLoading) {
|
||||
_logger.w('[LOG] Une opération de chargement est déjà en cours. Annulation de la nouvelle requête.');
|
||||
AppLogger.w('Une opération de chargement est déjà en cours. Annulation de la nouvelle requête.', tag: 'FriendsProvider');
|
||||
return;
|
||||
}
|
||||
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
_logger.i('[LOG] Début du chargement des amis pour l\'utilisateur $userId.');
|
||||
AppLogger.i('Début du chargement des amis pour l\'utilisateur $userId.', tag: 'FriendsProvider');
|
||||
|
||||
// Réinitialisation de la pagination si ce n'est pas un chargement supplémentaire
|
||||
if (!loadMore) {
|
||||
_friendsList = [];
|
||||
_currentPage = 0;
|
||||
_hasMore = true;
|
||||
_logger.i('[LOG] Réinitialisation de la pagination et de la liste des amis.');
|
||||
AppLogger.i('Réinitialisation de la pagination et de la liste des amis.', tag: 'FriendsProvider');
|
||||
}
|
||||
|
||||
try {
|
||||
_logger.i('[LOG] Chargement de la page $_currentPage des amis pour l\'utilisateur $userId.');
|
||||
AppLogger.d('Chargement de la page $_currentPage des amis pour l\'utilisateur $userId.', tag: 'FriendsProvider');
|
||||
final newFriends = await friendsRepository.fetchFriends(userId, _currentPage, _friendsPerPage);
|
||||
|
||||
// Gestion de l'absence de nouveaux amis
|
||||
if (newFriends.isEmpty) {
|
||||
_hasMore = false;
|
||||
_logger.i('[LOG] Plus d\'amis à charger.');
|
||||
AppLogger.i('Plus d\'amis à charger.', tag: 'FriendsProvider');
|
||||
} else {
|
||||
// Ajout des amis à la liste, en excluant l'utilisateur connecté
|
||||
for (final friend in newFriends) {
|
||||
if (friend.friendId != userId) {
|
||||
_friendsList.add(friend);
|
||||
_logger.i('[LOG] Ami ajouté : ID = ${friend.friendId}, Nom = ${friend.friendFirstName} ${friend.friendLastName}');
|
||||
AppLogger.d('Ami ajouté : ID = ${friend.friendId}, Nom = ${friend.friendFirstName} ${friend.friendLastName}', tag: 'FriendsProvider');
|
||||
} else {
|
||||
_logger.w("[WARN] L'utilisateur connecté est exclu de la liste des amis : ${friend.friendId}");
|
||||
AppLogger.w("L'utilisateur connecté est exclu de la liste des amis : ${friend.friendId}", tag: 'FriendsProvider');
|
||||
}
|
||||
}
|
||||
_currentPage++;
|
||||
_logger.i('[LOG] Préparation de la page suivante : $_currentPage');
|
||||
AppLogger.d('Préparation de la page suivante : $_currentPage', tag: 'FriendsProvider');
|
||||
}
|
||||
} catch (e) {
|
||||
_logger.e('[ERROR] Erreur lors du chargement des amis : $e');
|
||||
} catch (e, stackTrace) {
|
||||
AppLogger.e('Erreur lors du chargement des amis', error: e, stackTrace: stackTrace, tag: 'FriendsProvider');
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
_logger.i('[LOG] Fin du chargement des amis.');
|
||||
AppLogger.d('Fin du chargement des amis.', tag: 'FriendsProvider');
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
@@ -115,12 +127,12 @@ class FriendsProvider with ChangeNotifier { // Nombre d'amis à récupérer par
|
||||
/// - Enlève l'ami de la liste locale.
|
||||
Future<void> removeFriend(String friendId) async {
|
||||
try {
|
||||
_logger.i('[LOG] Suppression de l\'ami avec l\'ID : $friendId');
|
||||
AppLogger.i('Suppression de l\'ami avec l\'ID : $friendId', tag: 'FriendsProvider');
|
||||
await friendsRepository.removeFriend(friendId); // Appel API pour supprimer l'ami
|
||||
_friendsList.removeWhere((friend) => friend.friendId == friendId); // Suppression locale
|
||||
_logger.i('[LOG] Ami supprimé localement avec succès : $friendId');
|
||||
} catch (e) {
|
||||
_logger.e('[ERROR] Erreur lors de la suppression de l\'ami : $e');
|
||||
AppLogger.i('Ami supprimé localement avec succès : $friendId', tag: 'FriendsProvider');
|
||||
} catch (e, stackTrace) {
|
||||
AppLogger.e('Erreur lors de la suppression de l\'ami', error: e, stackTrace: stackTrace, tag: 'FriendsProvider');
|
||||
} finally {
|
||||
notifyListeners();
|
||||
}
|
||||
@@ -134,18 +146,18 @@ class FriendsProvider with ChangeNotifier { // Nombre d'amis à récupérer par
|
||||
/// Retourne un `Future<Friend?>` contenant les détails de l'ami ou `null` en cas d'erreur.
|
||||
Future<Friend?> fetchFriendDetails(String userId, String friendId) async {
|
||||
try {
|
||||
_logger.i('[LOG] Récupération des détails de l\'ami avec l\'ID : $friendId');
|
||||
AppLogger.d('Récupération des détails de l\'ami avec l\'ID : $friendId', tag: 'FriendsProvider');
|
||||
final friendDetails = await friendsRepository.getFriendDetails(friendId, userId);
|
||||
|
||||
if (friendDetails != null) {
|
||||
_logger.i('[LOG] Détails de l\'ami récupérés avec succès : ${friendDetails.friendId}');
|
||||
AppLogger.d('Détails de l\'ami récupérés avec succès : ${friendDetails.friendId}', tag: 'FriendsProvider');
|
||||
} else {
|
||||
_logger.w('[WARN] Détails de l\'ami introuvables pour l\'ID : $friendId');
|
||||
AppLogger.w('Détails de l\'ami introuvables pour l\'ID : $friendId', tag: 'FriendsProvider');
|
||||
}
|
||||
|
||||
return friendDetails;
|
||||
} catch (e) {
|
||||
_logger.e('[ERROR] Erreur lors de la récupération des détails de l\'ami : $e');
|
||||
} catch (e, stackTrace) {
|
||||
AppLogger.e('Erreur lors de la récupération des détails de l\'ami', error: e, stackTrace: stackTrace, tag: 'FriendsProvider');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -176,7 +188,7 @@ class FriendsProvider with ChangeNotifier { // Nombre d'amis à récupérer par
|
||||
/// Loggue l'action, met à jour le statut en local et appelle l'API pour mettre à jour le statut.
|
||||
Future<void> updateFriendStatus(String friendId, String status) async {
|
||||
try {
|
||||
_logger.i('[LOG] Mise à jour du statut de l\'ami avec l\'ID : $friendId');
|
||||
AppLogger.i('Mise à jour du statut de l\'ami avec l\'ID : $friendId', tag: 'FriendsProvider');
|
||||
|
||||
// Conversion du statut sous forme de chaîne en statut spécifique
|
||||
final friendStatus = _convertToFriendStatus(status);
|
||||
@@ -186,10 +198,10 @@ class FriendsProvider with ChangeNotifier { // Nombre d'amis à récupérer par
|
||||
final friendIndex = _friendsList.indexWhere((friend) => friend.friendId == friendId);
|
||||
if (friendIndex != -1) {
|
||||
_friendsList[friendIndex] = _friendsList[friendIndex].copyWith(status: friendStatus);
|
||||
_logger.i('[LOG] Statut de l\'ami mis à jour localement pour l\'ID : $friendId');
|
||||
AppLogger.i('Statut de l\'ami mis à jour localement pour l\'ID : $friendId', tag: 'FriendsProvider');
|
||||
}
|
||||
} catch (e) {
|
||||
_logger.e('[ERROR] Erreur lors de la mise à jour du statut de l\'ami : $e');
|
||||
} catch (e, stackTrace) {
|
||||
AppLogger.e('Erreur lors de la mise à jour du statut de l\'ami', error: e, stackTrace: stackTrace, tag: 'FriendsProvider');
|
||||
} finally {
|
||||
notifyListeners();
|
||||
}
|
||||
@@ -211,14 +223,20 @@ class FriendsProvider with ChangeNotifier { // Nombre d'amis à récupérer par
|
||||
throw Exception('Utilisateur non connecté');
|
||||
}
|
||||
|
||||
_logger.i('[LOG] Ajout de l\'ami: userId=$currentUserId, friendId=$friendId');
|
||||
// VALIDATION: Empêcher l'utilisateur de s'ajouter lui-même comme ami
|
||||
if (currentUserId == friendId) {
|
||||
AppLogger.w('Tentative d\'ajout de soi-même comme ami bloquée', tag: 'FriendsProvider');
|
||||
throw Exception('Vous ne pouvez pas vous ajouter vous-même comme ami');
|
||||
}
|
||||
|
||||
AppLogger.i('Ajout de l\'ami: userId=$currentUserId, friendId=$friendId', tag: 'FriendsProvider');
|
||||
await friendsRepository.addFriend(currentUserId, friendId);
|
||||
_logger.i('[LOG] Demande d\'ami envoyée avec succès');
|
||||
|
||||
AppLogger.i('Demande d\'ami envoyée avec succès', tag: 'FriendsProvider');
|
||||
|
||||
// Rafraîchir la liste des amis après l'ajout
|
||||
// Note: L'ami ne sera visible qu'après acceptation de la demande
|
||||
} catch (e) {
|
||||
_logger.e('[ERROR] Erreur lors de l\'ajout de l\'ami : $e');
|
||||
} catch (e, stackTrace) {
|
||||
AppLogger.e('Erreur lors de l\'ajout de l\'ami', error: e, stackTrace: stackTrace, tag: 'FriendsProvider');
|
||||
rethrow; // Propager l'erreur pour que l'UI puisse l'afficher
|
||||
} finally {
|
||||
notifyListeners();
|
||||
@@ -231,7 +249,7 @@ class FriendsProvider with ChangeNotifier { // Nombre d'amis à récupérer par
|
||||
final secureStorage = SecureStorage();
|
||||
return await secureStorage.getUserId();
|
||||
} catch (e) {
|
||||
_logger.e('[ERROR] Erreur lors de la récupération de l\'userId : $e');
|
||||
AppLogger.e('Erreur lors de la récupération de l\'userId', error: e, tag: 'FriendsProvider');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -264,6 +282,20 @@ class FriendsProvider with ChangeNotifier { // Nombre d'amis à récupérer par
|
||||
_requestsPerPage,
|
||||
);
|
||||
|
||||
// VALIDATION: Pour les demandes envoyées, currentUserId doit être l'expéditeur (userId)
|
||||
for (final request in requests) {
|
||||
if (request.userId != currentUserId) {
|
||||
AppLogger.e(
|
||||
'INCOHÉRENCE DÉTECTÉE dans fetchSentRequests: '
|
||||
'currentUserId=$currentUserId mais request.userId=${request.userId}, '
|
||||
'request.friendId=${request.friendId}, '
|
||||
'userFullName=${request.userFullName}, '
|
||||
'friendFullName=${request.friendFullName}',
|
||||
tag: 'FriendsProvider'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (loadMore) {
|
||||
_sentRequests.addAll(requests);
|
||||
_currentSentRequestPage = page;
|
||||
@@ -272,9 +304,9 @@ class FriendsProvider with ChangeNotifier { // Nombre d'amis à récupérer par
|
||||
_currentSentRequestPage = 0;
|
||||
}
|
||||
|
||||
_logger.i('[LOG] ${requests.length} demandes d\'amitié envoyées récupérées');
|
||||
} catch (e) {
|
||||
_logger.e('[ERROR] Erreur lors de la récupération des demandes envoyées : $e');
|
||||
AppLogger.i('${requests.length} demandes d\'amitié envoyées récupérées', tag: 'FriendsProvider');
|
||||
} catch (e, stackTrace) {
|
||||
AppLogger.e('Erreur lors de la récupération des demandes envoyées', error: e, stackTrace: stackTrace, tag: 'FriendsProvider');
|
||||
rethrow;
|
||||
} finally {
|
||||
_isLoadingSentRequests = false;
|
||||
@@ -305,6 +337,20 @@ class FriendsProvider with ChangeNotifier { // Nombre d'amis à récupérer par
|
||||
_requestsPerPage,
|
||||
);
|
||||
|
||||
// VALIDATION: Pour les demandes reçues, currentUserId doit être le destinataire (friendId)
|
||||
for (final request in requests) {
|
||||
if (request.friendId != currentUserId) {
|
||||
AppLogger.e(
|
||||
'INCOHÉRENCE DÉTECTÉE dans fetchReceivedRequests: '
|
||||
'currentUserId=$currentUserId mais request.friendId=${request.friendId}, '
|
||||
'request.userId=${request.userId}, '
|
||||
'userFullName=${request.userFullName}, '
|
||||
'friendFullName=${request.friendFullName}',
|
||||
tag: 'FriendsProvider'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (loadMore) {
|
||||
_receivedRequests.addAll(requests);
|
||||
_currentReceivedRequestPage = page;
|
||||
@@ -313,9 +359,9 @@ class FriendsProvider with ChangeNotifier { // Nombre d'amis à récupérer par
|
||||
_currentReceivedRequestPage = 0;
|
||||
}
|
||||
|
||||
_logger.i('[LOG] ${requests.length} demandes d\'amitié reçues récupérées');
|
||||
} catch (e) {
|
||||
_logger.e('[ERROR] Erreur lors de la récupération des demandes reçues : $e');
|
||||
AppLogger.i('${requests.length} demandes d\'amitié reçues récupérées', tag: 'FriendsProvider');
|
||||
} catch (e, stackTrace) {
|
||||
AppLogger.e('Erreur lors de la récupération des demandes reçues', error: e, stackTrace: stackTrace, tag: 'FriendsProvider');
|
||||
rethrow;
|
||||
} finally {
|
||||
_isLoadingReceivedRequests = false;
|
||||
@@ -326,7 +372,7 @@ class FriendsProvider with ChangeNotifier { // Nombre d'amis à récupérer par
|
||||
/// Accepte une demande d'amitié.
|
||||
Future<void> acceptFriendRequest(String friendshipId) async {
|
||||
try {
|
||||
_logger.i('[LOG] Acceptation de la demande d\'amitié: $friendshipId');
|
||||
AppLogger.i('Acceptation de la demande d\'amitié: $friendshipId', tag: 'FriendsProvider');
|
||||
await friendsRepository.acceptFriendRequest(friendshipId);
|
||||
|
||||
// Retirer la demande de la liste des demandes reçues
|
||||
@@ -338,9 +384,9 @@ class FriendsProvider with ChangeNotifier { // Nombre d'amis à récupérer par
|
||||
await fetchFriends(currentUserId);
|
||||
}
|
||||
|
||||
_logger.i('[LOG] Demande d\'amitié acceptée avec succès');
|
||||
} catch (e) {
|
||||
_logger.e('[ERROR] Erreur lors de l\'acceptation de la demande : $e');
|
||||
AppLogger.i('Demande d\'amitié acceptée avec succès', tag: 'FriendsProvider');
|
||||
} catch (e, stackTrace) {
|
||||
AppLogger.e('Erreur lors de l\'acceptation de la demande', error: e, stackTrace: stackTrace, tag: 'FriendsProvider');
|
||||
rethrow;
|
||||
} finally {
|
||||
notifyListeners();
|
||||
@@ -350,15 +396,15 @@ class FriendsProvider with ChangeNotifier { // Nombre d'amis à récupérer par
|
||||
/// Rejette une demande d'amitié.
|
||||
Future<void> rejectFriendRequest(String friendshipId) async {
|
||||
try {
|
||||
_logger.i('[LOG] Rejet de la demande d\'amitié: $friendshipId');
|
||||
AppLogger.i('Rejet de la demande d\'amitié: $friendshipId', tag: 'FriendsProvider');
|
||||
await friendsRepository.rejectFriendRequest(friendshipId);
|
||||
|
||||
// Retirer la demande de la liste des demandes reçues
|
||||
_receivedRequests.removeWhere((req) => req.friendshipId == friendshipId);
|
||||
|
||||
_logger.i('[LOG] Demande d\'amitié rejetée avec succès');
|
||||
} catch (e) {
|
||||
_logger.e('[ERROR] Erreur lors du rejet de la demande : $e');
|
||||
AppLogger.i('Demande d\'amitié rejetée avec succès', tag: 'FriendsProvider');
|
||||
} catch (e, stackTrace) {
|
||||
AppLogger.e('Erreur lors du rejet de la demande', error: e, stackTrace: stackTrace, tag: 'FriendsProvider');
|
||||
rethrow;
|
||||
} finally {
|
||||
notifyListeners();
|
||||
@@ -368,18 +414,142 @@ class FriendsProvider with ChangeNotifier { // Nombre d'amis à récupérer par
|
||||
/// Annule une demande d'amitié envoyée.
|
||||
Future<void> cancelFriendRequest(String friendshipId) async {
|
||||
try {
|
||||
_logger.i('[LOG] Annulation de la demande d\'amitié: $friendshipId');
|
||||
AppLogger.i('Annulation de la demande d\'amitié: $friendshipId', tag: 'FriendsProvider');
|
||||
await friendsRepository.cancelFriendRequest(friendshipId);
|
||||
|
||||
|
||||
// Retirer la demande de la liste des demandes envoyées
|
||||
_sentRequests.removeWhere((req) => req.friendshipId == friendshipId);
|
||||
|
||||
_logger.i('[LOG] Demande d\'amitié annulée avec succès');
|
||||
} catch (e) {
|
||||
_logger.e('[ERROR] Erreur lors de l\'annulation de la demande : $e');
|
||||
|
||||
AppLogger.i('Demande d\'amitié annulée avec succès', tag: 'FriendsProvider');
|
||||
} catch (e, stackTrace) {
|
||||
AppLogger.e('Erreur lors de l\'annulation de la demande', error: e, stackTrace: stackTrace, tag: 'FriendsProvider');
|
||||
rethrow;
|
||||
} finally {
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
/// Récupère les suggestions d'amis pour l'utilisateur actuel.
|
||||
///
|
||||
/// Les suggestions sont basées sur les amis en commun et d'autres critères
|
||||
/// de pertinence définis par le backend.
|
||||
///
|
||||
/// [limit] : Nombre maximum de suggestions à récupérer (par défaut 10).
|
||||
Future<void> fetchFriendSuggestions({int limit = 10}) async {
|
||||
try {
|
||||
final currentUserId = await _getCurrentUserId();
|
||||
if (currentUserId == null || currentUserId.isEmpty) {
|
||||
throw Exception('Utilisateur non connecté');
|
||||
}
|
||||
|
||||
_isLoadingSuggestions = true;
|
||||
notifyListeners();
|
||||
|
||||
AppLogger.i('Récupération des suggestions d\'amis (limit: $limit)', tag: 'FriendsProvider');
|
||||
_friendSuggestions = await friendsRepository.getFriendSuggestions(currentUserId, limit: limit);
|
||||
|
||||
AppLogger.i('${_friendSuggestions.length} suggestions d\'amis récupérées', tag: 'FriendsProvider');
|
||||
} catch (e, stackTrace) {
|
||||
AppLogger.e('Erreur lors de la récupération des suggestions', error: e, stackTrace: stackTrace, tag: 'FriendsProvider');
|
||||
_friendSuggestions = [];
|
||||
rethrow;
|
||||
} finally {
|
||||
_isLoadingSuggestions = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
/// Connecte le service de notifications temps réel.
|
||||
///
|
||||
/// Cette méthode doit être appelée après la connexion de l'utilisateur pour
|
||||
/// recevoir les notifications de demandes d'amitié en temps réel.
|
||||
///
|
||||
/// [service] : Le service de notifications temps réel à connecter.
|
||||
void connectRealtime(RealtimeNotificationService service) {
|
||||
_realtimeService = service;
|
||||
|
||||
// Écouter les demandes d'amitié en temps réel
|
||||
_friendRequestSubscription = service.friendRequestStream.listen(
|
||||
_handleFriendRequestNotification,
|
||||
onError: (error) {
|
||||
AppLogger.e('Erreur dans le stream de demandes d\'amitié', error: error, tag: 'FriendsProvider');
|
||||
},
|
||||
);
|
||||
|
||||
AppLogger.i('Service de notifications temps réel connecté pour les demandes d\'amitié', tag: 'FriendsProvider');
|
||||
}
|
||||
|
||||
/// Gère les notifications de demandes d'amitié reçues en temps réel.
|
||||
///
|
||||
/// Cette méthode est appelée automatiquement lorsqu'une notification
|
||||
/// est reçue via WebSocket.
|
||||
void _handleFriendRequestNotification(FriendRequestNotification notification) {
|
||||
AppLogger.i('Notification de demande d\'amitié reçue: ${notification.type}', tag: 'FriendsProvider');
|
||||
|
||||
switch (notification.type) {
|
||||
case 'received':
|
||||
// Rafraîchir les demandes reçues pour inclure la nouvelle demande
|
||||
_refreshReceivedRequests();
|
||||
AppLogger.i('Nouvelle demande d\'amitié de ${notification.senderName}', tag: 'FriendsProvider');
|
||||
break;
|
||||
|
||||
case 'accepted':
|
||||
// Rafraîchir la liste d'amis pour inclure le nouvel ami
|
||||
_refreshFriendsList();
|
||||
|
||||
// Supprimer de la liste des demandes envoyées si présente
|
||||
_sentRequests.removeWhere((request) => request.friendshipId == notification.requestId);
|
||||
notifyListeners();
|
||||
|
||||
AppLogger.i('${notification.senderName} a accepté votre demande', tag: 'FriendsProvider');
|
||||
break;
|
||||
|
||||
case 'rejected':
|
||||
// Supprimer de la liste des demandes envoyées
|
||||
_sentRequests.removeWhere((request) => request.friendshipId == notification.requestId);
|
||||
notifyListeners();
|
||||
|
||||
AppLogger.i('Demande d\'amitié rejetée: ${notification.requestId}', tag: 'FriendsProvider');
|
||||
break;
|
||||
|
||||
default:
|
||||
AppLogger.w('Type de notification inconnu: ${notification.type}', tag: 'FriendsProvider');
|
||||
}
|
||||
}
|
||||
|
||||
/// Rafraîchit la liste des demandes reçues en arrière-plan.
|
||||
Future<void> _refreshReceivedRequests() async {
|
||||
try {
|
||||
await fetchReceivedRequests(loadMore: false);
|
||||
} catch (e) {
|
||||
AppLogger.e('Erreur lors du rafraîchissement des demandes reçues', error: e, tag: 'FriendsProvider');
|
||||
}
|
||||
}
|
||||
|
||||
/// Rafraîchit la liste d'amis en arrière-plan.
|
||||
Future<void> _refreshFriendsList() async {
|
||||
try {
|
||||
final currentUserId = await _getCurrentUserId();
|
||||
if (currentUserId == null || currentUserId.isEmpty) return;
|
||||
|
||||
await fetchFriends(currentUserId, loadMore: false);
|
||||
} catch (e) {
|
||||
AppLogger.e('Erreur lors du rafraîchissement de la liste d\'amis', error: e, tag: 'FriendsProvider');
|
||||
}
|
||||
}
|
||||
|
||||
/// Déconnecte le service de notifications temps réel.
|
||||
void disconnectRealtime() {
|
||||
_friendRequestSubscription?.cancel();
|
||||
_friendRequestSubscription = null;
|
||||
_realtimeService = null;
|
||||
|
||||
AppLogger.i('Service de notifications temps réel déconnecté', tag: 'FriendsProvider');
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
disconnectRealtime();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
79
lib/data/providers/presence_provider.dart
Normal file
79
lib/data/providers/presence_provider.dart
Normal file
@@ -0,0 +1,79 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../core/utils/app_logger.dart';
|
||||
import '../services/realtime_notification_service.dart';
|
||||
|
||||
/// Provider pour gérer la présence utilisateur (online/offline).
|
||||
///
|
||||
/// Ce provider :
|
||||
/// - Écoute les mises à jour de présence via WebSocket
|
||||
/// - Maintient une map userId -> isOnline
|
||||
/// - Envoie un heartbeat toutes les 25 secondes pour maintenir le statut online
|
||||
/// - Notifie les widgets qui écoutent lors des changements de présence
|
||||
class PresenceProvider extends ChangeNotifier {
|
||||
PresenceProvider();
|
||||
|
||||
// Map userId -> isOnline
|
||||
final Map<String, bool> _presenceMap = {};
|
||||
|
||||
RealtimeNotificationService? _realtimeService;
|
||||
StreamSubscription<PresenceUpdate>? _presenceSubscription;
|
||||
Timer? _heartbeatTimer;
|
||||
|
||||
/// Obtenir le statut online d'un utilisateur.
|
||||
bool isUserOnline(String userId) {
|
||||
return _presenceMap[userId] ?? false;
|
||||
}
|
||||
|
||||
/// Connecter au service temps réel et démarrer heartbeat.
|
||||
void connectRealtime(RealtimeNotificationService service) {
|
||||
_realtimeService = service;
|
||||
|
||||
// Écouter les mises à jour de présence
|
||||
_presenceSubscription = service.presenceStream.listen((update) {
|
||||
_presenceMap[update.userId] = update.isOnline;
|
||||
notifyListeners();
|
||||
AppLogger.i(
|
||||
'Présence mise à jour: ${update.userId} -> ${update.isOnline}',
|
||||
tag: 'PresenceProvider',
|
||||
);
|
||||
});
|
||||
|
||||
// Démarrer heartbeat (toutes les 25 secondes)
|
||||
_startHeartbeat();
|
||||
|
||||
AppLogger.i('PresenceProvider connecté', tag: 'PresenceProvider');
|
||||
}
|
||||
|
||||
/// Démarrer le heartbeat pour maintenir le statut online.
|
||||
void _startHeartbeat() {
|
||||
_heartbeatTimer?.cancel();
|
||||
_heartbeatTimer = Timer.periodic(
|
||||
const Duration(seconds: 25),
|
||||
(_) {
|
||||
_realtimeService?.sendHeartbeat();
|
||||
AppLogger.d('Heartbeat envoyé', tag: 'PresenceProvider');
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Déconnecter et arrêter le heartbeat.
|
||||
void disconnectRealtime() {
|
||||
_presenceSubscription?.cancel();
|
||||
_presenceSubscription = null;
|
||||
_heartbeatTimer?.cancel();
|
||||
_heartbeatTimer = null;
|
||||
_realtimeService = null;
|
||||
_presenceMap.clear();
|
||||
notifyListeners();
|
||||
AppLogger.i('PresenceProvider déconnecté', tag: 'PresenceProvider');
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
disconnectRealtime();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@@ -11,10 +11,6 @@ class UserProvider with ChangeNotifier {
|
||||
email: '',
|
||||
motDePasse: '',
|
||||
profileImageUrl: '',
|
||||
eventsCount: 0,
|
||||
friendsCount: 0,
|
||||
postsCount: 0,
|
||||
visitedPlacesCount: 0,
|
||||
);
|
||||
|
||||
bool _isEmailDisplayedElsewhere = false; // Ajout de la propriété pour contrôler l'affichage de l'email
|
||||
@@ -28,7 +24,7 @@ class UserProvider with ChangeNotifier {
|
||||
/// Méthode pour définir l'état d'affichage de l'email.
|
||||
void setEmailDisplayedElsewhere(bool value) {
|
||||
_isEmailDisplayedElsewhere = value;
|
||||
debugPrint("[LOG] isEmailDisplayedElsewhere mis à jour : $_isEmailDisplayedElsewhere");
|
||||
debugPrint('[LOG] isEmailDisplayedElsewhere mis à jour : $_isEmailDisplayedElsewhere');
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@@ -37,7 +33,7 @@ class UserProvider with ChangeNotifier {
|
||||
void setUser(User user) {
|
||||
debugPrint("[LOG] Tentative de définition des informations de l'utilisateur : ${user.toString()}");
|
||||
_user = user;
|
||||
debugPrint("[LOG] Informations utilisateur définies : ${_user.toString()}");
|
||||
debugPrint('[LOG] Informations utilisateur définies : ${_user.toString()}');
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@@ -48,7 +44,7 @@ class UserProvider with ChangeNotifier {
|
||||
int? postsCount,
|
||||
int? visitedPlacesCount,
|
||||
}) {
|
||||
debugPrint("[LOG] Mise à jour des statistiques utilisateur");
|
||||
debugPrint('[LOG] Mise à jour des statistiques utilisateur');
|
||||
|
||||
_user = User(
|
||||
userId: _user.userId,
|
||||
@@ -63,14 +59,14 @@ class UserProvider with ChangeNotifier {
|
||||
visitedPlacesCount: visitedPlacesCount ?? _user.visitedPlacesCount,
|
||||
);
|
||||
|
||||
debugPrint("[LOG] Nouvelles statistiques utilisateur : ${_user.toString()}");
|
||||
debugPrint('[LOG] Nouvelles statistiques utilisateur : ${_user.toString()}');
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Méthode pour réinitialiser les informations de l'utilisateur.
|
||||
void resetUser() {
|
||||
debugPrint("[LOG] Réinitialisation des informations de l'utilisateur.");
|
||||
debugPrint("[LOG] Valeurs avant réinitialisation : ${_user.toString()}");
|
||||
debugPrint('[LOG] Valeurs avant réinitialisation : ${_user.toString()}');
|
||||
|
||||
_user = const User(
|
||||
userId: '',
|
||||
@@ -79,13 +75,9 @@ class UserProvider with ChangeNotifier {
|
||||
email: '',
|
||||
motDePasse: '',
|
||||
profileImageUrl: '',
|
||||
eventsCount: 0,
|
||||
friendsCount: 0,
|
||||
postsCount: 0,
|
||||
visitedPlacesCount: 0,
|
||||
);
|
||||
|
||||
debugPrint("[LOG] Informations utilisateur réinitialisées : ${_user.toString()}");
|
||||
debugPrint('[LOG] Informations utilisateur réinitialisées : ${_user.toString()}');
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user