# Widgets Sociaux - Architecture Modulaire Architecture de composants réutilisables pour les posts sociaux avec support d'images et vidéos. ## 📐 Architecture ### 🔹 Widgets Atomiques (Niveau 1) Les plus petits composants réutilisables dans toute l'application. #### `SocialActionButton` **Bouton d'action tout petit et uniforme** - Taille par défaut: 22px - Supporte tooltip - Animation scale: 0.85 - Couleur personnalisable ```dart SocialActionButton( icon: Icons.favorite_rounded, onTap: () => handleLike(), color: Colors.red, tooltip: 'J\'aime', ) ``` #### `SocialActionButtonWithCount` **Bouton d'action avec compteur** - Affichage du nombre (1K, 1M formaté) - Support état actif/inactif - Couleur différente si actif ```dart SocialActionButtonWithCount( icon: Icons.favorite_rounded, count: 245, onTap: () => handleLike(), isActive: true, activeColor: Colors.red, ) ``` #### `SocialBadge` **Badge réutilisable générique** - Icône optionnelle - Couleurs personnalisables - Taille de police ajustable - Padding personnalisable ```dart SocialBadge( label: 'Nouveau', icon: Icons.fiber_new, fontSize: 11, ) ``` #### `VerifiedBadge` **Badge de compte vérifié** - Icône verified avec tooltip - Taille personnalisable ```dart VerifiedBadge(size: 16) ``` #### `CategoryBadge` **Badge de catégorie** - Couleur secondaire - Icône optionnelle ```dart CategoryBadge( category: 'Sport', icon: Icons.sports_soccer, ) ``` #### `StatusBadge` **Badge de statut dynamique** - Couleurs automatiques selon le statut - "nouveau" → primaryContainer - "tendance" → errorContainer - "populaire" → tertiaryContainer ```dart StatusBadge( status: 'tendance', icon: Icons.trending_up, ) ``` #### `MediaCountBadge` **Badge de nombre de médias** - Pour images/vidéos - Affichage sur fond noir semi-transparent - Icône différente selon le type ```dart MediaCountBadge( count: 5, isVideo: false, ) ``` ### 🔹 Widgets de Médias (Niveau 2) Composants spécialisés pour la gestion des médias. #### `PostMedia` (Model) **Modèle de données pour un média** ```dart class PostMedia { final String url; final MediaType type; // image ou video final String? thumbnailUrl; final Duration? duration; } ``` #### `PostMediaViewer` **Affichage de médias avec dispositions multiples** - 1 média: Aspect ratio 1:1 - 2 médias: Côte à côte - 3 médias: 1 grand + 2 petits - 4+ médias: Grille 2x2 avec compteur "+N" **Fonctionnalités:** - Hero animation - Loading progressif - Gestion d'erreurs - Tap pour plein écran - Badge de durée pour vidéos ```dart PostMediaViewer( medias: [ PostMedia(url: 'image1.jpg', type: MediaType.image), PostMedia(url: 'video1.mp4', type: MediaType.video, duration: Duration(minutes: 2, seconds: 30)), ], postId: post.id, onTap: () => handleMediaTap(), ) ``` #### `MediaPicker` **Sélecteur de médias pour création de post** - Sélection depuis galerie (multi) - Capture photo depuis caméra - Sélection vidéo - Limite: 10 médias max (personnalisable) - Prévisualisation avec bouton supprimer - Indicateur vidéo sur thumbnails **Fonctionnalités:** - Compteur médias sélectionnés - Boutons désactivés si limite atteinte - Grille horizontale scrollable - Suppression individuelle ```dart MediaPicker( onMediasChanged: (medias) { setState(() => selectedMedias = medias); }, maxMedias: 10, initialMedias: [], ) ``` ### 🔹 Dialogues et Composants Complexes (Niveau 3) #### `CreatePostDialog` **Dialogue de création de post avec médias** **Fonctionnalités:** - Avatar et nom utilisateur - Champ texte (3-8 lignes, max 500 caractères) - MediaPicker intégré - Validation formulaire - État de chargement pendant création - Compteur caractères - Visibilité (Public par défaut) - **Compression automatique** des images avant upload - **Upload progressif** avec indicateur de progression - **Nettoyage automatique** des fichiers temporaires **Processus d'upload en 3 étapes:** 1. Compression des images (0-50%) 2. Upload des médias (50-100%) 3. Création du post **Présentation:** - Modal bottom sheet - Auto-focus sur le champ texte - Padding adapté au clavier - Actions: Annuler / Publier - Barre de progression avec statut textuel ```dart await CreatePostDialog.show( context: context, onPostCreated: (content, medias) async { await createPost(content, medias); }, userName: 'Jean Dupont', userAvatarUrl: 'avatar.jpg', ); ``` #### `EditPostDialog` **Dialogue d'édition de post existant** **Fonctionnalités:** - Pré-remplissage avec contenu existant - Détection automatique des changements - MediaPicker pour modifier les médias - Validation formulaire - Bouton "Enregistrer" désactivé si aucun changement - État de chargement pendant mise à jour **Présentation:** - Modal bottom sheet - Auto-focus sur le champ texte - Icône d'édition dans l'en-tête - Actions: Annuler / Enregistrer ```dart await EditPostDialog.show( context: context, post: existingPost, onPostUpdated: (content, medias) async { await updatePost(existingPost.id, content, medias); }, ); ``` #### `FullscreenVideoPlayer` **Lecteur vidéo plein écran avec contrôles** **Fonctionnalités:** - Lecture automatique au démarrage - Orientation paysage forcée - Mode immersif (barre système cachée) - Contrôles tactiles : - Tap pour afficher/masquer contrôles - Play/Pause - Reculer de 10s - Avancer de 10s - Barre de progression interactive (scrubbing) - Affichage durée actuelle / totale - Hero animation pour transition fluide **Présentation:** - Fond noir - Header avec bouton fermer et titre - Contrôles centraux (reculer/play/avancer) - Footer avec barre de progression - Gradient overlay sur header/footer ```dart await FullscreenVideoPlayer.show( context: context, videoUrl: 'https://example.com/video.mp4', heroTag: 'post_media_123_0', title: 'Ma vidéo', ); ``` #### `SocialCardRefactored` **Card modulaire de post social** **Composants internes:** - `_PostHeader`: Avatar gradient, nom, badge vérifié, timestamp, menu - `_UserAvatar`: Avatar avec bordure gradient - `_PostTimestamp`: Formatage relatif (Il y a X min/h/j) - `_PostMenu`: PopupMenu (Modifier, Supprimer) - `_PostActions`: Like, Comment, Share, Bookmark - `_PostStats`: Nombre de likes - `_PostContent`: Texte enrichi (hashtags #, mentions @) - `_CommentsLink`: Lien vers commentaires **Fonctionnalités:** - Support médias via PostMediaViewer - Hashtags et mentions cliquables (couleur primaire) - "Voir plus/Voir moins" pour contenu long (>150 caractères) - Badge de catégorie optionnel - Badge vérifié optionnel - AnimatedCard avec elevation ```dart SocialCardRefactored( post: post, onLike: () => handleLike(), onComment: () => handleComment(), onShare: () => handleShare(), onDeletePost: () => handleDelete(), onEditPost: () => handleEdit(), showVerifiedBadge: true, showCategory: true, category: 'Sport', ) ``` ## 🔧 Services (lib/data/services/) ### `ImageCompressionService` **Service de compression d'images avant upload** **Configurations prédéfinies:** - `CompressionConfig.post` : 85% qualité, 1920x1920px - `CompressionConfig.thumbnail` : 70% qualité, 400x400px - `CompressionConfig.story` : 90% qualité, 1080x1920px - `CompressionConfig.avatar` : 80% qualité, 500x500px **Fonctionnalités:** - Compression avec qualité ajustable (0-100) - Redimensionnement automatique - Support formats: JPEG, PNG, WebP - Compression parallèle pour plusieurs images - Callback de progression - Statistiques de réduction de taille - Nettoyage automatique des fichiers temporaires **Utilisation:** ```dart final compressionService = ImageCompressionService(); // Compression d'une image final compressed = await compressionService.compressImage( imageFile, config: CompressionConfig.post, ); // Compression multiple avec progression final compressedList = await compressionService.compressMultipleImages( imageFiles, config: CompressionConfig.thumbnail, onProgress: (processed, total) { print('Compression: $processed/$total'); }, ); // Créer un thumbnail final thumbnail = await compressionService.createThumbnail(imageFile); // Nettoyer les fichiers temporaires await compressionService.cleanupTempFiles(); ``` **Logs (si EnvConfig.enableDetailedLogs = true):** ``` [ImageCompression] Compression de: photo.jpg [ImageCompression] Taille originale: 4.2 MB [ImageCompression] Taille compressée: 1.8 MB [ImageCompression] Réduction: 57.1% ``` ### `MediaUploadService` **Service d'upload de médias vers le backend** **Fonctionnalités:** - Upload d'images et vidéos - Upload parallèle de plusieurs médias - Callback de progression - Génération automatique de thumbnails pour vidéos - Support vidéos locales et réseau - Suppression de médias **Modèle de résultat:** ```dart class MediaUploadResult { final String url; // URL du média uploadé final String? thumbnailUrl; // URL du thumbnail (vidéo) final String type; // 'image' ou 'video' final Duration? duration; // Durée (vidéo seulement) } ``` **Utilisation:** ```dart final uploadService = MediaUploadService(http.Client()); // Upload d'un média final result = await uploadService.uploadMedia(imageFile); print('URL: ${result.url}'); // Upload multiple avec progression final results = await uploadService.uploadMultipleMedias( mediaFiles, onProgress: (uploaded, total) { print('Upload: $uploaded/$total'); }, ); // Générer thumbnail pour vidéo final thumbnailUrl = await uploadService.generateVideoThumbnail(videoUrl); // Supprimer un média await uploadService.deleteMedia(mediaUrl); ``` **Configuration:** Le endpoint d'upload est configuré dans `EnvConfig.mediaUploadEndpoint`. ## 📦 Utilisation ### Import Simple ```dart import 'package:afterwork/presentation/widgets/social/social_widgets.dart'; ``` Ceci importe tous les widgets nécessaires : - Boutons d'action - Badges - Media picker - Post media viewer - Create post dialog - Social card ### Exemple Complet ```dart // 1. Créer un post avec compression et upload automatiques await CreatePostDialog.show( context: context, onPostCreated: (content, medias) async { // Les médias sont déjà compressés et uploadés par le dialogue await apiService.createPost( content: content, mediaFiles: medias, ); }, userName: currentUser.name, userAvatarUrl: currentUser.avatar, ); // 2. Créer un post manuellement avec services final compressionService = ImageCompressionService(); final uploadService = MediaUploadService(http.Client()); // Compresser les images final compressedMedias = await compressionService.compressMultipleImages( selectedMedias, config: CompressionConfig.post, onProgress: (processed, total) { print('Compression: $processed/$total'); }, ); // Uploader les médias final uploadResults = await uploadService.uploadMultipleMedias( compressedMedias, onProgress: (uploaded, total) { print('Upload: $uploaded/$total'); }, ); // Créer le post avec les URLs await apiService.createPost( content: contentController.text, mediaUrls: uploadResults.map((r) => r.url).toList(), ); // Nettoyer les fichiers temporaires await compressionService.cleanupTempFiles(); // 3. Afficher un post avec médias SocialCardRefactored( post: SocialPost( id: '123', content: 'Super soirée avec les amis ! #afterwork @john', imageUrl: '', // Géré par medias maintenant likesCount: 42, commentsCount: 5, sharesCount: 2, isLikedByCurrentUser: false, // ... ), medias: [ PostMedia( url: 'https://example.com/photo1.jpg', type: MediaType.image, ), PostMedia( url: 'https://example.com/video1.mp4', type: MediaType.video, thumbnailUrl: 'https://example.com/video1_thumb.jpg', duration: Duration(minutes: 2, seconds: 30), ), ], onLike: () { setState(() { post = post.copyWith( isLikedByCurrentUser: !post.isLikedByCurrentUser, likesCount: post.isLikedByCurrentUser ? post.likesCount - 1 : post.likesCount + 1, ); }); }, onComment: () { Navigator.push(/* CommentsScreen */); }, onEditPost: () async { await EditPostDialog.show( context: context, post: post, onPostUpdated: (content, medias) async { await apiService.updatePost(post.id, content, medias); }, ); }, onDeletePost: () async { await apiService.deletePost(post.id); }, showVerifiedBadge: user.isVerified, showCategory: true, category: post.category, ); // 4. Afficher une vidéo en plein écran GestureDetector( onTap: () { FullscreenVideoPlayer.show( context: context, videoUrl: 'https://example.com/video.mp4', heroTag: 'post_media_${post.id}_0', title: 'Ma vidéo', ); }, child: Stack( children: [ Image.network(videoThumbnail), Center( child: Icon(Icons.play_circle_outline, size: 64), ), ], ), ); ``` ## 🎨 Principes de Design ### Uniformité - Tous les boutons icônes: 20-22px - Tous les badges: fontSize 10-11px - Spacing: DesignSystem constants (4, 8, 12, 16px) - Border radius: DesignSystem (4, 10, 16px) ### Réutilisabilité - Chaque composant fait UNE chose - Props claires et typées - Valeurs par défaut sensées - Documentation inline ### Performances - Widgets const quand possible - Keys pour listes - Lazy loading des images - AnimatedCard pour hover effects ### Accessibilité - Tooltips sur tous les boutons - Couleurs avec contraste suffisant - Tailles tactiles minimales respectées (44x44) ## 🔧 Personnalisation ### Thème Tous les widgets utilisent le ThemeData du contexte : - `theme.colorScheme.primary` - `theme.colorScheme.onSurface` - `theme.textTheme.bodyMedium` ### DesignSystem Les constantes sont centralisées : - `DesignSystem.spacingSm` (8px) - `DesignSystem.spacingMd` (12px) - `DesignSystem.spacingLg` (16px) - `DesignSystem.radiusSm` (4px) - `DesignSystem.radiusMd` (10px) - `DesignSystem.radiusLg` (16px) ## 📝 Statut des Fonctionnalités ### ✅ Complété - [x] **Support de plusieurs médias par post** - PostMediaViewer supporte 1-10+ médias avec dispositions adaptatives - [x] **Upload des médias vers le backend** - MediaUploadService avec progression et support image/vidéo - [x] **Lecteur vidéo en plein écran** - FullscreenVideoPlayer avec contrôles complets - [x] **Édition de post avec médias** - EditPostDialog avec détection de changements - [x] **Compression d'images avant upload** - ImageCompressionService avec 4 configs prédéfinies - [x] **Architecture modulaire** - Widgets atomiques, réutilisables et bien documentés - [x] **Animations et interactions** - Like, bookmark, hero transitions, scale effects - [x] **Hashtags et mentions cliquables** - Parsing et styling automatiques - [x] **Progress tracking** - Indicateurs de progression pour compression et upload ### 🚧 À Venir - [ ] **Stories avec médias** - Système de stories éphémères (24h) avec images/vidéos - [ ] **Filtres sur les photos** - Filtres Instagram-style pour édition d'images - [ ] **GIF support** - Support des GIFs animés dans les posts - [ ] **Commentaires imbriqués** - Système de réponses aux commentaires - [ ] **Réactions étendues** - Plus de réactions au-delà du simple like (❤️ 😂 😮 😢 😡) - [ ] **Partage vers stories** - Partager un post dans sa story - [ ] **Brouillons** - Sauvegarder des posts en brouillon - [ ] **Planification** - Programmer la publication d'un post ### 📊 Métriques - **13 widgets** créés (atomiques + composites) - **2 services** (compression + upload) - **5 types de badges** réutilisables - **4 layouts** pour affichage médias (1, 2, 3, 4+) - **3 étapes** de processus d'upload (compression, upload, création) - **10 médias max** par post (configurable)