## 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
512 lines
18 KiB
Dart
512 lines
18 KiB
Dart
import 'env_config.dart';
|
|
|
|
/// Classe utilitaire pour gérer toutes les URLs de l'API backend.
|
|
///
|
|
/// Cette classe centralise toutes les URLs de l'API pour faciliter
|
|
/// la maintenance et éviter la duplication de code.
|
|
///
|
|
/// **Usage:**
|
|
/// ```dart
|
|
/// // URL simple
|
|
/// final url = Urls.authenticateUser;
|
|
///
|
|
/// // URL avec paramètres dynamiques
|
|
/// final userUrl = Urls.getUserByIdWithId('123');
|
|
/// final eventUrl = Urls.getEventByIdWithId('456');
|
|
/// ```
|
|
class Urls {
|
|
/// Constructeur privé pour empêcher l'instanciation
|
|
Urls._();
|
|
|
|
/// URL de base de l'API
|
|
static String get baseUrl => EnvConfig.apiBaseUrl;
|
|
|
|
// ============================================================================
|
|
// AUTHENTIFICATION ET UTILISATEURS
|
|
// ============================================================================
|
|
|
|
/// Endpoint pour authentifier un utilisateur
|
|
static String get authenticateUser => '$baseUrl/users/authenticate';
|
|
|
|
/// Endpoint pour créer un nouvel utilisateur
|
|
static String get createUser => '$baseUrl/users';
|
|
|
|
/// Endpoint de base pour les opérations sur les utilisateurs
|
|
static String get usersBase => '$baseUrl/users';
|
|
|
|
/// Retourne l'URL pour obtenir un utilisateur par son ID
|
|
///
|
|
/// [userId] L'ID de l'utilisateur
|
|
static String getUserByIdWithId(String userId) => '$usersBase/$userId';
|
|
|
|
/// Retourne l'URL pour rechercher un utilisateur par email
|
|
///
|
|
/// [email] L'email de l'utilisateur à rechercher
|
|
static String searchUserByEmail(String email) =>
|
|
'$usersBase/search?email=${Uri.encodeComponent(email)}';
|
|
|
|
/// Retourne l'URL pour supprimer un utilisateur par son ID
|
|
///
|
|
/// [userId] L'ID de l'utilisateur
|
|
static String deleteUserWithId(String userId) => '$usersBase/$userId';
|
|
|
|
/// Retourne l'URL pour mettre à jour l'image de profil d'un utilisateur
|
|
///
|
|
/// [userId] L'ID de l'utilisateur
|
|
static String updateUserProfileImageWithId(String userId) =>
|
|
'$usersBase/$userId/profile-image';
|
|
|
|
/// Retourne l'URL pour mettre à jour un utilisateur
|
|
///
|
|
/// [userId] L'ID de l'utilisateur
|
|
static String updateUserWithId(String userId) => '$usersBase/$userId';
|
|
|
|
// ============================================================================
|
|
// ÉVÉNEMENTS
|
|
// ============================================================================
|
|
|
|
/// Endpoint de base pour les opérations sur les événements
|
|
static String get eventsBase => '$baseUrl/events';
|
|
|
|
/// Endpoint pour créer un nouvel événement
|
|
static String get createEvent => eventsBase;
|
|
|
|
/// Endpoint pour obtenir tous les événements
|
|
static String get getAllEvents => eventsBase;
|
|
|
|
/// Endpoint pour obtenir les événements créés par un utilisateur et ses amis
|
|
static String get getEventsCreatedByUserAndFriends =>
|
|
'$eventsBase/created-by-user-and-friends';
|
|
|
|
/// Retourne l'URL pour obtenir un événement par son ID
|
|
///
|
|
/// [eventId] L'ID de l'événement
|
|
static String getEventByIdWithId(String eventId) => '$eventsBase/$eventId';
|
|
|
|
/// Retourne l'URL pour supprimer un événement par son ID
|
|
///
|
|
/// [eventId] L'ID de l'événement
|
|
static String deleteEventWithId(String eventId) => '$eventsBase/$eventId';
|
|
|
|
/// Retourne l'URL pour mettre à jour un événement
|
|
///
|
|
/// [eventId] L'ID de l'événement
|
|
static String updateEventWithId(String eventId) => '$eventsBase/$eventId';
|
|
|
|
/// Retourne l'URL pour mettre à jour l'image d'un événement
|
|
///
|
|
/// [eventId] L'ID de l'événement
|
|
static String updateEventImageWithId(String eventId) =>
|
|
'$eventsBase/$eventId/image';
|
|
|
|
/// Retourne l'URL pour fermer un événement
|
|
///
|
|
/// [eventId] L'ID de l'événement
|
|
static String closeEventWithId(String eventId) => '$eventsBase/$eventId/close';
|
|
|
|
/// Retourne l'URL pour rouvrir un événement
|
|
///
|
|
/// [eventId] L'ID de l'événement
|
|
static String reopenEventWithId(String eventId) =>
|
|
'$eventsBase/$eventId/reopen';
|
|
|
|
/// Retourne l'URL pour mettre à jour le statut d'un événement
|
|
///
|
|
/// [eventId] L'ID de l'événement
|
|
static String updateEventStatusWithId(String eventId) =>
|
|
'$eventsBase/$eventId/status';
|
|
|
|
/// Endpoint pour obtenir les événements après une date
|
|
static String get getEventsAfterDate => '$eventsBase/after-date';
|
|
|
|
/// Endpoint pour obtenir les événements entre deux dates
|
|
static String get getEventsBetweenDates => '$eventsBase/between-dates';
|
|
|
|
/// Retourne l'URL pour obtenir les événements par catégorie
|
|
///
|
|
/// [category] La catégorie des événements
|
|
static String getEventsByCategoryWithCategory(String category) =>
|
|
'$eventsBase/category/$category';
|
|
|
|
/// Retourne l'URL pour obtenir les événements par statut
|
|
///
|
|
/// [status] Le statut des événements (ouvert, fermé, etc.)
|
|
static String getEventsByStatusWithStatus(String status) =>
|
|
'$eventsBase/status/$status';
|
|
|
|
/// Retourne l'URL pour obtenir les événements d'un utilisateur
|
|
///
|
|
/// [userId] L'ID de l'utilisateur
|
|
static String getEventsByUserWithUserId(String userId) =>
|
|
'$eventsBase/user/$userId';
|
|
|
|
/// Retourne l'URL pour obtenir les événements de l'utilisateur et de ses amis
|
|
///
|
|
/// [userId] L'ID de l'utilisateur
|
|
static String getEventsByFriends(String userId) =>
|
|
'$eventsBase/friends/$userId';
|
|
|
|
/// Endpoint pour rechercher des événements
|
|
///
|
|
/// **Note:** Utilisez des paramètres de requête pour le mot-clé
|
|
static String get searchEvents => '$eventsBase/search';
|
|
|
|
// ============================================================================
|
|
// PARTICIPANTS AUX ÉVÉNEMENTS
|
|
// ============================================================================
|
|
|
|
/// Retourne l'URL pour ajouter un participant à un événement
|
|
///
|
|
/// [eventId] L'ID de l'événement
|
|
static String addParticipantWithEventId(String eventId) =>
|
|
'$eventsBase/$eventId/participants';
|
|
|
|
/// Retourne l'URL pour retirer un participant d'un événement
|
|
///
|
|
/// [eventId] L'ID de l'événement
|
|
/// [userId] L'ID de l'utilisateur à retirer
|
|
static String removeParticipantWithIds(String eventId, String userId) =>
|
|
'$eventsBase/$eventId/participants/$userId';
|
|
|
|
/// Retourne l'URL pour obtenir le nombre de participants d'un événement
|
|
///
|
|
/// [eventId] L'ID de l'événement
|
|
static String getNumberOfParticipantsWithEventId(String eventId) =>
|
|
'$eventsBase/$eventId/participants/count';
|
|
|
|
/// Retourne l'URL pour réagir à un événement (utilise favorite)
|
|
///
|
|
/// [eventId] L'ID de l'événement
|
|
/// [userId] L'ID de l'utilisateur
|
|
static String reactToEventWithId(String eventId, String userId) =>
|
|
'$eventsBase/$eventId/favorite?userId=$userId';
|
|
|
|
/// Retourne l'URL pour participer à un événement (utilise participants)
|
|
///
|
|
/// [eventId] L'ID de l'événement
|
|
static String participateInEventWithId(String eventId) =>
|
|
'$eventsBase/$eventId/participants';
|
|
|
|
// ============================================================================
|
|
// AMIS ET RELATIONS SOCIALES
|
|
// ============================================================================
|
|
|
|
/// Endpoint de base pour les opérations sur les amis
|
|
static String get friendsBase => '$baseUrl/friends';
|
|
|
|
/// Retourne l'URL pour obtenir les amis d'un utilisateur
|
|
///
|
|
/// [userId] L'ID de l'utilisateur
|
|
static String getFriendsWithUserId(String userId) =>
|
|
'$friendsBase/user/$userId';
|
|
|
|
/// Retourne l'URL pour ajouter un ami
|
|
///
|
|
/// [userId] L'ID de l'utilisateur
|
|
/// [friendId] L'ID de l'ami à ajouter
|
|
static String addFriendWithIds(String userId, String friendId) =>
|
|
'$friendsBase/$userId/$friendId';
|
|
|
|
/// Retourne l'URL pour supprimer un ami
|
|
///
|
|
/// [userId] L'ID de l'utilisateur
|
|
/// [friendId] L'ID de l'ami à supprimer
|
|
static String removeFriendWithIds(String userId, String friendId) =>
|
|
'$friendsBase/$userId/$friendId';
|
|
|
|
/// Retourne l'URL pour récupérer les demandes d'amitié en attente
|
|
///
|
|
/// [userId] L'ID de l'utilisateur
|
|
/// [page] Le numéro de la page (optionnel, par défaut 0)
|
|
/// [size] La taille de la page (optionnel, par défaut 10)
|
|
static String getPendingFriendRequestsWithUserId(String userId, {int page = 0, int size = 10}) =>
|
|
'$friendsBase/pending/$userId?page=$page&size=$size';
|
|
|
|
/// Retourne l'URL pour récupérer les demandes d'amitié envoyées
|
|
///
|
|
/// [userId] L'ID de l'utilisateur
|
|
/// [page] Le numéro de la page (optionnel, par défaut 0)
|
|
/// [size] La taille de la page (optionnel, par défaut 10)
|
|
static String getSentFriendRequestsWithUserId(String userId, {int page = 0, int size = 10}) =>
|
|
'$friendsBase/sent/$userId?page=$page&size=$size';
|
|
|
|
/// Retourne l'URL pour récupérer les demandes d'amitié reçues
|
|
///
|
|
/// [userId] L'ID de l'utilisateur
|
|
/// [page] Le numéro de la page (optionnel, par défaut 0)
|
|
/// [size] La taille de la page (optionnel, par défaut 10)
|
|
static String getReceivedFriendRequestsWithUserId(String userId, {int page = 0, int size = 10}) =>
|
|
'$friendsBase/received/$userId?page=$page&size=$size';
|
|
|
|
/// Retourne l'URL pour accepter une demande d'amitié
|
|
///
|
|
/// [friendshipId] L'ID de la relation d'amitié
|
|
static String acceptFriendRequestWithId(String friendshipId) =>
|
|
'$friendsBase/$friendshipId/accept';
|
|
|
|
/// Retourne l'URL pour rejeter une demande d'amitié
|
|
///
|
|
/// [friendshipId] L'ID de la relation d'amitié
|
|
static String rejectFriendRequestWithId(String friendshipId) =>
|
|
'$friendsBase/$friendshipId/reject';
|
|
|
|
/// Retourne l'URL pour récupérer les suggestions d'amis
|
|
///
|
|
/// [userId] L'ID de l'utilisateur
|
|
/// [limit] Nombre maximum de suggestions (optionnel, par défaut 10)
|
|
static String getFriendSuggestionsWithUserId(String userId, {int limit = 10}) =>
|
|
'$friendsBase/suggestions/$userId?limit=$limit';
|
|
|
|
// ============================================================================
|
|
// NOTIFICATIONS
|
|
// ============================================================================
|
|
|
|
/// Endpoint de base pour les opérations sur les notifications
|
|
static String get notificationsBase => '$baseUrl/notifications';
|
|
|
|
/// Retourne l'URL pour obtenir les notifications d'un utilisateur
|
|
///
|
|
/// [userId] L'ID de l'utilisateur
|
|
static String getNotificationsWithUserId(String userId) =>
|
|
'$notificationsBase/user/$userId';
|
|
|
|
/// Retourne l'URL pour marquer une notification comme lue
|
|
///
|
|
/// [notificationId] L'ID de la notification
|
|
static String markNotificationAsReadWithId(String notificationId) =>
|
|
'$notificationsBase/$notificationId/read';
|
|
|
|
/// Retourne l'URL pour marquer toutes les notifications comme lues
|
|
///
|
|
/// [userId] L'ID de l'utilisateur
|
|
static String markAllNotificationsAsReadWithUserId(String userId) =>
|
|
'$notificationsBase/user/$userId/mark-all-read';
|
|
|
|
/// Retourne l'URL pour supprimer une notification
|
|
///
|
|
/// [notificationId] L'ID de la notification
|
|
static String deleteNotificationWithId(String notificationId) =>
|
|
'$notificationsBase/$notificationId';
|
|
|
|
// ============================================================================
|
|
// POSTS SOCIAUX
|
|
// ============================================================================
|
|
|
|
/// Endpoint de base pour les opérations sur les posts sociaux
|
|
static String get postsBase => '$baseUrl/posts';
|
|
|
|
/// Retourne l'URL pour obtenir tous les posts (avec pagination)
|
|
static String get getAllPosts => postsBase;
|
|
|
|
/// Retourne l'URL pour créer un nouveau post
|
|
static String get createSocialPost => postsBase;
|
|
|
|
/// Retourne l'URL pour obtenir un post par son ID
|
|
///
|
|
/// [postId] L'ID du post
|
|
static String getSocialPostByIdWithId(String postId) => '$postsBase/$postId';
|
|
|
|
/// Retourne l'URL pour mettre à jour un post
|
|
///
|
|
/// [postId] L'ID du post
|
|
static String updateSocialPostWithId(String postId) => '$postsBase/$postId';
|
|
|
|
/// Retourne l'URL pour supprimer un post
|
|
///
|
|
/// [postId] L'ID du post
|
|
static String deleteSocialPostWithId(String postId) => '$postsBase/$postId';
|
|
|
|
/// Retourne l'URL pour rechercher des posts
|
|
///
|
|
/// [query] Le terme de recherche
|
|
static String searchSocialPostsWithQuery(String query) =>
|
|
'$postsBase/search?q=${Uri.encodeComponent(query)}';
|
|
|
|
/// Retourne l'URL pour liker un post
|
|
///
|
|
/// [postId] L'ID du post
|
|
static String likeSocialPostWithId(String postId) => '$postsBase/$postId/like';
|
|
|
|
/// Retourne l'URL pour commenter un post
|
|
///
|
|
/// [postId] L'ID du post
|
|
static String commentSocialPostWithId(String postId) =>
|
|
'$postsBase/$postId/comment';
|
|
|
|
/// Retourne l'URL pour obtenir tous les commentaires d'un post
|
|
///
|
|
/// [postId] L'ID du post
|
|
static String getCommentsForPost(String postId) =>
|
|
'$postsBase/$postId/comments';
|
|
|
|
/// Retourne l'URL pour partager un post
|
|
///
|
|
/// [postId] L'ID du post
|
|
static String shareSocialPostWithId(String postId) => '$postsBase/$postId/share';
|
|
|
|
/// Retourne l'URL pour obtenir les posts d'un utilisateur
|
|
///
|
|
/// [userId] L'ID de l'utilisateur
|
|
static String getSocialPostsByUserId(String userId) =>
|
|
'$postsBase/user/$userId';
|
|
|
|
/// Retourne l'URL pour obtenir les posts de l'utilisateur et de ses amis
|
|
///
|
|
/// [userId] L'ID de l'utilisateur
|
|
static String getSocialPostsByFriends(String userId) =>
|
|
'$postsBase/friends/$userId';
|
|
|
|
// ============================================================================
|
|
// STORIES
|
|
// ============================================================================
|
|
|
|
/// Endpoint de base pour les opérations sur les stories
|
|
static String get storiesBase => '$baseUrl/stories';
|
|
|
|
/// Retourne l'URL pour obtenir toutes les stories (actives)
|
|
static String get getAllStories => storiesBase;
|
|
|
|
/// Retourne l'URL pour obtenir les stories d'un utilisateur
|
|
///
|
|
/// [userId] L'ID de l'utilisateur
|
|
static String getStoriesByUserId(String userId) => '$storiesBase/user/$userId';
|
|
|
|
/// Retourne l'URL pour créer une nouvelle story
|
|
static String get createStory => storiesBase;
|
|
|
|
/// Retourne l'URL pour obtenir une story par son ID
|
|
///
|
|
/// [storyId] L'ID de la story
|
|
static String getStoryByIdWithId(String storyId) => '$storiesBase/$storyId';
|
|
|
|
/// Retourne l'URL pour supprimer une story
|
|
///
|
|
/// [storyId] L'ID de la story
|
|
static String deleteStoryWithId(String storyId) => '$storiesBase/$storyId';
|
|
|
|
/// Retourne l'URL pour marquer une story comme vue
|
|
///
|
|
/// [storyId] L'ID de la story
|
|
/// [userId] L'ID de l'utilisateur qui voit la story
|
|
static String markStoryAsViewedWithId(String storyId, String userId) =>
|
|
'$storiesBase/$storyId/view?userId=$userId';
|
|
|
|
/// Retourne l'URL pour obtenir les vues d'une story
|
|
///
|
|
/// [storyId] L'ID de la story
|
|
static String getStoryViewsWithId(String storyId) => '$storiesBase/$storyId/views';
|
|
|
|
// ============================================================================
|
|
// MESSAGERIE
|
|
// ============================================================================
|
|
|
|
/// Endpoint de base pour les opérations sur les messages
|
|
static String get messagesBase => '$baseUrl/messages';
|
|
|
|
/// Retourne l'URL pour envoyer un message
|
|
static String get sendMessage => messagesBase;
|
|
|
|
/// Retourne l'URL pour obtenir les conversations d'un utilisateur
|
|
///
|
|
/// [userId] L'ID de l'utilisateur
|
|
static String getUserConversations(String userId) =>
|
|
'$messagesBase/conversations/$userId';
|
|
|
|
/// Retourne l'URL pour obtenir les messages d'une conversation
|
|
///
|
|
/// [conversationId] L'ID de la conversation
|
|
/// [page] Le numéro de la page (optionnel)
|
|
/// [size] La taille de la page (optionnel)
|
|
static String getConversationMessages(String conversationId,
|
|
{int page = 0, int size = 50}) =>
|
|
'$messagesBase/conversation/$conversationId?page=$page&size=$size';
|
|
|
|
/// Retourne l'URL pour obtenir une conversation entre deux utilisateurs
|
|
///
|
|
/// [user1Id] L'ID du premier utilisateur
|
|
/// [user2Id] L'ID du deuxième utilisateur
|
|
static String getConversationBetweenUsers(String user1Id, String user2Id) =>
|
|
'$messagesBase/conversation/between/$user1Id/$user2Id';
|
|
|
|
/// Retourne l'URL pour marquer un message comme lu
|
|
///
|
|
/// [messageId] L'ID du message
|
|
static String markMessageAsRead(String messageId) =>
|
|
'$messagesBase/$messageId/read';
|
|
|
|
/// Retourne l'URL pour marquer tous les messages d'une conversation comme lus
|
|
///
|
|
/// [conversationId] L'ID de la conversation
|
|
/// [userId] L'ID de l'utilisateur
|
|
static String markAllMessagesAsRead(String conversationId, String userId) =>
|
|
'$messagesBase/conversation/$conversationId/read/$userId';
|
|
|
|
/// Retourne l'URL pour obtenir le nombre de messages non lus
|
|
///
|
|
/// [userId] L'ID de l'utilisateur
|
|
static String getUnreadMessagesCount(String userId) =>
|
|
'$messagesBase/unread/count/$userId';
|
|
|
|
/// Retourne l'URL pour supprimer un message
|
|
///
|
|
/// [messageId] L'ID du message
|
|
static String deleteMessage(String messageId) => '$messagesBase/$messageId';
|
|
|
|
/// Retourne l'URL pour supprimer une conversation
|
|
///
|
|
/// [conversationId] L'ID de la conversation
|
|
static String deleteConversation(String conversationId) =>
|
|
'$messagesBase/conversation/$conversationId';
|
|
|
|
/// Retourne l'URL WebSocket pour le chat en temps réel
|
|
///
|
|
/// [userId] L'ID de l'utilisateur
|
|
static String getChatWebSocketUrl(String userId) {
|
|
final wsUrl = baseUrl
|
|
.replaceFirst('http://', 'ws://')
|
|
.replaceFirst('https://', 'wss://');
|
|
return '$wsUrl/chat/ws/$userId';
|
|
}
|
|
|
|
// ============================================================================
|
|
// MÉTHODES UTILITAIRES
|
|
// ============================================================================
|
|
|
|
/// Construit une URL avec des paramètres de requête
|
|
///
|
|
/// [baseUrl] L'URL de base
|
|
/// [params] Les paramètres de requête (clé-valeur)
|
|
///
|
|
/// **Exemple:**
|
|
/// ```dart
|
|
/// final url = Urls.buildUrlWithParams(
|
|
/// Urls.searchEvents,
|
|
/// {'keyword': 'concert', 'category': 'music'},
|
|
/// );
|
|
/// // Résultat: 'http://api.com/events/search?keyword=concert&category=music'
|
|
/// ```
|
|
static String buildUrlWithParams(String baseUrl, Map<String, String> params) {
|
|
if (params.isEmpty) return baseUrl;
|
|
|
|
final queryString = params.entries
|
|
.map((e) => '${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}')
|
|
.join('&');
|
|
|
|
return '$baseUrl?$queryString';
|
|
}
|
|
|
|
/// Valide qu'une URL est bien formée
|
|
///
|
|
/// [url] L'URL à valider
|
|
///
|
|
/// Returns `true` si l'URL est valide, `false` sinon
|
|
static bool isValidUrl(String url) {
|
|
try {
|
|
final uri = Uri.parse(url);
|
|
return uri.hasScheme && uri.hasAuthority;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|