import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:intl/intl.dart'; import 'package:share_plus/share_plus.dart'; import '../../../core/constants/design_system.dart'; import '../../../domain/entities/social_post.dart'; import '../widgets/custom_snackbar.dart'; /// Widget d'en-tête pour les posts sociaux avec design moderne. /// /// Affiche l'avatar, le nom de l'utilisateur, le timestamp et les options. class SocialHeaderWidget extends StatelessWidget { const SocialHeaderWidget({ required this.post, required this.onEditPost, required this.onClosePost, required this.menuKey, required this.menuContext, this.showVerifiedBadge = false, super.key, }); final SocialPost post; final VoidCallback onEditPost; final VoidCallback onClosePost; final GlobalKey menuKey; final BuildContext menuContext; final bool showVerifiedBadge; @override Widget build(BuildContext context) { final theme = Theme.of(context); return Row( children: [ // Avatar avec bordure gradient (style Instagram) _buildAvatar(theme), const SizedBox(width: DesignSystem.spacingMd), // Informations utilisateur Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ // Nom avec badge de vérification Row( children: [ Flexible( child: Text( post.authorFullName, style: theme.textTheme.bodyMedium?.copyWith( fontWeight: FontWeight.w600, fontSize: 14, letterSpacing: -0.2, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), if (showVerifiedBadge) ...[ const SizedBox(width: 4), Icon( Icons.verified, size: 16, color: theme.colorScheme.primary, ), ], ], ), // Timestamp const SizedBox(height: 2), Text( _formatTimestamp(post.timestamp), style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.onSurface.withOpacity(0.5), fontSize: 12, height: 1.2, ), ), ], ), ), // Menu options IconButton( key: menuKey, icon: const Icon(Icons.more_horiz_rounded), iconSize: 22, onPressed: () => _showOptionsMenu(menuContext), color: theme.colorScheme.onSurface.withOpacity(0.6), padding: EdgeInsets.zero, constraints: const BoxConstraints(minWidth: 36, minHeight: 36), ), ], ); } /// Construit l'avatar avec bordure dégradée Widget _buildAvatar(ThemeData theme) { return Container( decoration: BoxDecoration( shape: BoxShape.circle, gradient: LinearGradient( colors: [ theme.colorScheme.primary.withOpacity(0.3), theme.colorScheme.secondary.withOpacity(0.3), ], begin: Alignment.topLeft, end: Alignment.bottomRight, ), boxShadow: [ BoxShadow( color: theme.colorScheme.primary.withOpacity(0.1), blurRadius: 8, offset: const Offset(0, 2), ), ], ), padding: const EdgeInsets.all(2), child: Container( decoration: BoxDecoration( shape: BoxShape.circle, color: theme.scaffoldBackgroundColor, ), padding: const EdgeInsets.all(2), child: CircleAvatar( radius: 18, backgroundColor: theme.colorScheme.surfaceVariant, backgroundImage: _getProfileImage(), child: _getProfileImage() == null ? Icon( Icons.person_rounded, size: 20, color: theme.colorScheme.onSurfaceVariant, ) : null, ), ), ); } /// Récupère l'image de profil de l'utilisateur ImageProvider? _getProfileImage() { if (post.userProfileImageUrl.isEmpty) { return null; } if (post.userProfileImageUrl.startsWith('http://') || post.userProfileImageUrl.startsWith('https://')) { return NetworkImage(post.userProfileImageUrl); } return AssetImage(post.userProfileImageUrl); } /// Formate le timestamp de manière relative String _formatTimestamp(DateTime timestamp) { final now = DateTime.now(); final difference = now.difference(timestamp); if (difference.inSeconds < 60) { return 'À l\'instant'; } else if (difference.inMinutes < 60) { return 'Il y a ${difference.inMinutes} min'; } else if (difference.inHours < 24) { return 'Il y a ${difference.inHours}h'; } else if (difference.inDays < 7) { return 'Il y a ${difference.inDays}j'; } else if (difference.inDays < 30) { final weeks = (difference.inDays / 7).floor(); return 'Il y a ${weeks}sem'; } else { return DateFormat('d MMM', 'fr_FR').format(timestamp); } } /// Affiche le menu des options void _showOptionsMenu(BuildContext context) { showModalBottomSheet( context: context, backgroundColor: Colors.transparent, builder: (context) { final theme = Theme.of(context); return Container( decoration: BoxDecoration( color: theme.scaffoldBackgroundColor, borderRadius: const BorderRadius.vertical( top: Radius.circular(DesignSystem.radiusLg), ), ), child: SafeArea( child: Column( mainAxisSize: MainAxisSize.min, children: [ // Handle Container( margin: const EdgeInsets.only( top: DesignSystem.spacingMd, bottom: DesignSystem.spacingSm, ), width: 40, height: 4, decoration: BoxDecoration( color: theme.colorScheme.onSurface.withOpacity(0.2), borderRadius: BorderRadius.circular(2), ), ), // Options _buildMenuOption( context, theme, Icons.edit_outlined, 'Modifier', onEditPost, ), _buildMenuOption( context, theme, Icons.link_rounded, 'Copier le lien', () { _copyPostLink(context); }, ), _buildMenuOption( context, theme, Icons.share_outlined, 'Partager', () { _sharePost(context); }, ), Divider( height: 1, thickness: 1, color: theme.dividerColor.withOpacity(0.5), ), _buildMenuOption( context, theme, Icons.report_outlined, 'Signaler', () { _reportPost(context); }, isDestructive: false, textColor: theme.colorScheme.error, ), const SizedBox(height: DesignSystem.spacingSm), ], ), ), ); }, ); } /// Construit une option du menu Widget _buildMenuOption( BuildContext context, ThemeData theme, IconData icon, String title, VoidCallback onTap, { bool isDestructive = false, Color? textColor, }) { final color = textColor ?? (isDestructive ? theme.colorScheme.error : null); return ListTile( leading: Icon( icon, color: color, size: 22, ), title: Text( title, style: theme.textTheme.bodyMedium?.copyWith( color: color, fontSize: 14, ), ), onTap: () { Navigator.of(context).pop(); onTap(); }, contentPadding: const EdgeInsets.symmetric( horizontal: DesignSystem.spacingLg, vertical: DesignSystem.spacingXs, ), ); } /// Copie le lien du post dans le presse-papiers void _copyPostLink(BuildContext context) { final postUrl = 'https://afterwork.app/post/${post.id}'; Clipboard.setData(ClipboardData(text: postUrl)); context.showSuccess('Lien copié dans le presse-papiers'); } /// Partage le post via la fonctionnalité native de partage Future _sharePost(BuildContext context) async { try { final postUrl = 'https://afterwork.app/post/${post.id}'; final shareText = '${post.content}\n\n$postUrl'; await Share.share( shareText, subject: 'Publication AfterWork', ); } catch (e) { if (context.mounted) { context.showError('Erreur lors du partage'); } } } /// Affiche le dialog de signalement void _reportPost(BuildContext context) { showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Signaler ce post'), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Pourquoi signalez-vous ce post ?', style: TextStyle(fontWeight: FontWeight.w600), ), const SizedBox(height: DesignSystem.spacingMd), _buildReportOption(context, 'Contenu inapproprié'), _buildReportOption(context, 'Spam ou arnaque'), _buildReportOption(context, 'Harcèlement'), _buildReportOption(context, 'Fausses informations'), _buildReportOption(context, 'Autre'), ], ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Annuler'), ), ], ), ); } /// Construit une option de signalement Widget _buildReportOption(BuildContext context, String reason) { return InkWell( onTap: () { Navigator.of(context).pop(); _submitReport(context, reason); }, child: Padding( padding: const EdgeInsets.symmetric( vertical: DesignSystem.spacingSm, horizontal: DesignSystem.spacingXs, ), child: Row( children: [ Icon( Icons.report_outlined, size: 20, color: Theme.of(context).colorScheme.error.withOpacity(0.7), ), const SizedBox(width: DesignSystem.spacingMd), Text( reason, style: Theme.of(context).textTheme.bodyMedium, ), ], ), ), ); } /// Soumet le signalement Future _submitReport(BuildContext context, String reason) async { // Simuler l'envoi du signalement // Dans une vraie application, cela ferait un appel API await Future.delayed(const Duration(milliseconds: 500)); if (context.mounted) { context.showSuccess('Signalement envoyé. Merci pour votre contribution.'); } } }