import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import '../../../core/constants/design_system.dart'; import '../../../domain/entities/social_post.dart'; import '../../widgets/animated_widgets.dart'; import '../../widgets/fullscreen_image_viewer.dart'; import '../../widgets/social_header_widget.dart'; import '../../widgets/social_interaction_row.dart'; /// Card moderne et élaborée pour afficher un post social. /// /// Design inspiré d'Instagram avec hiérarchie visuelle claire, /// support de contenu riche (hashtags, mentions) et animations fluides. class SocialCard extends StatefulWidget { const SocialCard({ required this.post, required this.onLike, required this.onComment, required this.onShare, required this.onDeletePost, required this.onEditPost, this.showVerifiedBadge = false, super.key, }); final SocialPost post; final VoidCallback onLike; final VoidCallback onComment; final VoidCallback onShare; final VoidCallback onDeletePost; final VoidCallback onEditPost; final bool showVerifiedBadge; @override State createState() => _SocialCardState(); } class _SocialCardState extends State { bool _showFullContent = false; @override Widget build(BuildContext context) { final theme = Theme.of(context); return AnimatedCard( margin: const EdgeInsets.only(bottom: DesignSystem.spacingMd), borderRadius: DesignSystem.borderRadiusMd, elevation: 0.5, hoverElevation: 1.5, padding: EdgeInsets.zero, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header avec avatar, nom, timestamp Padding( padding: const EdgeInsets.fromLTRB( DesignSystem.spacingLg, DesignSystem.spacingMd, DesignSystem.spacingSm, DesignSystem.spacingMd, ), child: SocialHeaderWidget( post: widget.post, onEditPost: widget.onEditPost, menuKey: GlobalKey(), menuContext: context, onClosePost: () {}, showVerifiedBadge: widget.showVerifiedBadge, ), ), // Image du post avec Hero animation if (widget.post.imageUrl != null && widget.post.imageUrl!.isNotEmpty) _buildPostImage(context, theme), // Contenu et interactions Padding( padding: const EdgeInsets.all(DesignSystem.spacingLg), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Barre d'interactions (like, comment, share) SocialInteractionRow( post: widget.post, onLike: widget.onLike, onComment: widget.onComment, onShare: widget.onShare, ), const SizedBox(height: DesignSystem.spacingMd), // Nombre de likes if (widget.post.likesCount > 0) _buildLikesCount(theme), // Contenu du post avec texte enrichi _buildPostContent(theme), // Nombre de commentaires if (widget.post.commentsCount > 0) _buildCommentsCount(theme), ], ), ), ], ), ); } /// Construit l'image du post avec Hero animation et double-tap to like Widget _buildPostImage(BuildContext context, ThemeData theme) { return GestureDetector( onDoubleTap: () { if (!widget.post.isLikedByCurrentUser) { widget.onLike(); _showLikeAnimation(); } }, onTap: () => _openFullscreenImage(context), child: AspectRatio( aspectRatio: 1.0, child: Container( width: double.infinity, decoration: BoxDecoration( color: theme.colorScheme.surfaceVariant.withOpacity(0.3), ), child: Hero( tag: 'social_post_image_${widget.post.id}', child: Image.network( widget.post.imageUrl!, fit: BoxFit.cover, loadingBuilder: (context, child, loadingProgress) { if (loadingProgress == null) return child; return Center( child: CircularProgressIndicator( value: loadingProgress.expectedTotalBytes != null ? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes! : null, strokeWidth: 2.5, ), ); }, errorBuilder: (context, error, stackTrace) { return Container( color: theme.colorScheme.surfaceVariant.withOpacity(0.5), child: Center( child: Icon( Icons.broken_image_rounded, size: 48, color: theme.colorScheme.onSurfaceVariant.withOpacity(0.5), ), ), ); }, ), ), ), ), ); } /// Construit le compteur de likes Widget _buildLikesCount(ThemeData theme) { return Padding( padding: const EdgeInsets.only(bottom: DesignSystem.spacingMd), child: Text( _formatLikesCount(widget.post.likesCount), style: theme.textTheme.labelMedium?.copyWith( fontWeight: FontWeight.w600, fontSize: 13, letterSpacing: -0.1, ), ), ); } /// Construit le contenu du post avec texte enrichi Widget _buildPostContent(ThemeData theme) { final content = widget.post.content; final shouldTruncate = content.length > 150 && !_showFullContent; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ RichText( text: TextSpan( style: theme.textTheme.bodyMedium?.copyWith( fontSize: 14, height: 1.4, letterSpacing: -0.1, ), children: [ // Nom de l'auteur en gras TextSpan( text: '${widget.post.authorFullName} ', style: const TextStyle(fontWeight: FontWeight.w600), ), // Contenu avec support des hashtags et mentions ..._buildEnrichedContent( shouldTruncate ? '${content.substring(0, 150)}...' : content, theme, ), ], ), ), // Bouton "Voir plus" / "Voir moins" if (content.length > 150) GestureDetector( onTap: () { setState(() { _showFullContent = !_showFullContent; }); }, child: Padding( padding: const EdgeInsets.only(top: DesignSystem.spacingXs), child: Text( _showFullContent ? 'Voir moins' : 'Voir plus', style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.onSurface.withOpacity(0.5), fontSize: 13, fontWeight: FontWeight.w500, ), ), ), ), ], ); } /// Construit le compteur de commentaires Widget _buildCommentsCount(ThemeData theme) { return GestureDetector( onTap: widget.onComment, child: Padding( padding: const EdgeInsets.only(top: DesignSystem.spacingMd), child: Text( _formatCommentsCount(widget.post.commentsCount), style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.onSurface.withOpacity(0.5), fontSize: 13, ), ), ), ); } /// Construit le contenu enrichi avec hashtags et mentions cliquables List _buildEnrichedContent(String content, ThemeData theme) { final spans = []; final words = content.split(' '); for (var i = 0; i < words.length; i++) { final word = words[i]; if (word.startsWith('#')) { // Hashtag spans.add( TextSpan( text: word, style: TextStyle( color: theme.colorScheme.primary, fontWeight: FontWeight.w600, ), recognizer: TapGestureRecognizer() ..onTap = () { _handleHashtagTap(word.substring(1)); }, ), ); } else if (word.startsWith('@')) { // Mention spans.add( TextSpan( text: word, style: TextStyle( color: theme.colorScheme.primary, fontWeight: FontWeight.w600, ), recognizer: TapGestureRecognizer() ..onTap = () { _handleMentionTap(word.substring(1)); }, ), ); } else { // Texte normal spans.add(TextSpan(text: word)); } // Ajouter un espace sauf pour le dernier mot if (i < words.length - 1) { spans.add(const TextSpan(text: ' ')); } } return spans; } /// Ouvre l'image en plein écran avec Hero animation void _openFullscreenImage(BuildContext context) { Navigator.push( context, PageRouteBuilder( opaque: false, barrierColor: Colors.black, pageBuilder: (context, animation, secondaryAnimation) { return FadeTransition( opacity: animation, child: FullscreenImageViewer( imageUrl: widget.post.imageUrl!, heroTag: 'social_post_image_${widget.post.id}', title: widget.post.content, ), ); }, ), ); } /// Affiche l'animation de like void _showLikeAnimation() { // TODO: Implémenter animation de coeur qui apparaît au centre if (mounted) { // Animation visuelle rapide } } /// Gère le tap sur un hashtag void _handleHashtagTap(String hashtag) { debugPrint('[SocialCard] Hashtag cliqué: #$hashtag'); // TODO: Naviguer vers la page des posts avec ce hashtag } /// Gère le tap sur une mention void _handleMentionTap(String username) { debugPrint('[SocialCard] Mention cliquée: @$username'); // TODO: Naviguer vers le profil de l'utilisateur } /// Formate le nombre de likes String _formatLikesCount(int count) { if (count == 1) { return '1 j\'aime'; } return '${_formatCount(count)} j\'aime'; } /// Formate le nombre de commentaires String _formatCommentsCount(int count) { if (count == 1) { return 'Voir le commentaire'; } return 'Voir les $count commentaires'; } /// Formate les compteurs (1K, 1M, etc.) String _formatCount(int count) { if (count >= 1000000) { final value = count / 1000000; return value % 1 == 0 ? '${value.toInt()} M' : '${value.toStringAsFixed(1)} M'; } else if (count >= 1000) { final value = count / 1000; return value % 1 == 0 ? '${value.toInt()} K' : '${value.toStringAsFixed(1)} K'; } return count.toString(); } }