import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import '../models/message_model.dart'; import '../theme/app_theme.dart'; class MessageBubble extends StatelessWidget { final MessageModel message; const MessageBubble({super.key, required this.message}); @override Widget build(BuildContext context) { final isMe = message.fromMe; final align = isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start; final bg = isMe ? AppTheme.outgoingMsg : AppTheme.incomingMsg; final radius = isMe ? const BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(0), bottomLeft: Radius.circular(12), bottomRight: Radius.circular(12), ) : const BorderRadius.only( topLeft: Radius.circular(0), topRight: Radius.circular(12), bottomLeft: Radius.circular(12), bottomRight: Radius.circular(12), ); return Container( margin: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), child: Column( crossAxisAlignment: align, children: [ Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width * 0.75, ), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: bg, borderRadius: radius, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ // Show sender name in group chats if not from me if (!isMe && message.author != null) ...[ Text( message.author!, style: const TextStyle( color: AppTheme.primary, fontSize: 12, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 4), ], // Media placeholder if it is media if (message.hasMedia) ...[ _buildMediaPlaceholder(), const SizedBox(height: 6), ], // Message text & time row Row( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.end, children: [ Flexible( child: Text( message.body, style: const TextStyle( color: AppTheme.textPrimary, fontSize: 15, ), ), ), const SizedBox(width: 8), Padding( padding: const EdgeInsets.only(top: 4), child: Row( mainAxisSize: MainAxisSize.min, children: [ Text( _formatTime(message.timestamp), style: const TextStyle( color: AppTheme.textSecondary, fontSize: 10, ), ), if (isMe) ...[ const SizedBox(width: 4), _buildAckIcon(message.ack), ], ], ), ), ], ), ], ), ), ], ), ); } Widget _buildMediaPlaceholder() { IconData iconData = Icons.insert_drive_file; String label = "File Attachment"; switch (message.type) { case "image": iconData = Icons.photo; label = "Image"; break; case "video": iconData = Icons.videocam; label = "Video"; break; case "audio": iconData = Icons.audiotrack; label = "Audio File"; break; case "sticker": iconData = Icons.emoji_emotions; label = "Sticker"; break; } return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.black.withOpacity(0.15), borderRadius: BorderRadius.circular(8), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(iconData, color: AppTheme.textSecondary, size: 32), const SizedBox(width: 12), Text( label, style: const TextStyle(color: AppTheme.textPrimary, fontWeight: FontWeight.w500), ), ], ), ); } Widget _buildAckIcon(int ack) { switch (ack) { case 1: // Pending return const Icon(Icons.access_time, size: 13, color: AppTheme.textSecondary); case 2: // Sent return const Icon(Icons.done, size: 15, color: AppTheme.textSecondary); case 3: // Delivered return const Icon(Icons.done_all, size: 15, color: AppTheme.textSecondary); case 4: // Read return const Icon(Icons.done_all, size: 15, color: Colors.blue); default: return const SizedBox.shrink(); } } String _formatTime(int timestamp) { if (timestamp == 0) return ''; final dt = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000); return DateFormat('h:mm a').format(dt); } }