150 lines
4.5 KiB
Dart
150 lines
4.5 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:intl/intl.dart';
|
|
import '../models/conversation_model.dart';
|
|
import '../theme/app_theme.dart';
|
|
|
|
class ConversationTile extends StatelessWidget {
|
|
final ConversationModel conversation;
|
|
final VoidCallback onTap;
|
|
|
|
const ConversationTile({
|
|
super.key,
|
|
required this.conversation,
|
|
required this.onTap,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final lastMsg = conversation.lastMessage;
|
|
final hasUnread = conversation.unreadCount > 0;
|
|
|
|
return ListTile(
|
|
onTap: onTap,
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
|
leading: _buildAvatar(),
|
|
title: Row(
|
|
children: [
|
|
Expanded(
|
|
child: Text(
|
|
conversation.name,
|
|
style: const TextStyle(
|
|
color: AppTheme.textPrimary,
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
Text(
|
|
_formatTime(conversation.timestamp),
|
|
style: TextStyle(
|
|
color: hasUnread ? AppTheme.primary : AppTheme.textSecondary,
|
|
fontSize: 12,
|
|
fontWeight: hasUnread ? FontWeight.bold : FontWeight.normal,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
subtitle: Padding(
|
|
padding: const EdgeInsets.only(top: 4.0),
|
|
child: Row(
|
|
children: [
|
|
if (lastMsg != null && lastMsg.fromMe) ...[
|
|
const Icon(Icons.done_all, size: 16, color: AppTheme.primary), // Or proper ACK double tick
|
|
const SizedBox(width: 4),
|
|
],
|
|
Expanded(
|
|
child: Text(
|
|
_getSubtitleText(lastMsg),
|
|
style: const TextStyle(
|
|
color: AppTheme.textSecondary,
|
|
fontSize: 14,
|
|
),
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
if (conversation.isMuted) ...[
|
|
const SizedBox(width: 8),
|
|
const Icon(Icons.volume_off, size: 16, color: AppTheme.textSecondary),
|
|
],
|
|
if (conversation.pinned) ...[
|
|
const SizedBox(width: 8),
|
|
const Icon(Icons.push_pin, size: 16, color: AppTheme.textSecondary),
|
|
],
|
|
if (hasUnread) ...[
|
|
const SizedBox(width: 8),
|
|
Container(
|
|
padding: const EdgeInsets.all(6),
|
|
decoration: const BoxDecoration(
|
|
color: AppTheme.primary,
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Text(
|
|
conversation.unreadCount.toString(),
|
|
style: const TextStyle(
|
|
color: Colors.black,
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildAvatar() {
|
|
if (conversation.avatar != null) {
|
|
return CircleAvatar(
|
|
radius: 26,
|
|
backgroundImage: NetworkImage(conversation.avatar!),
|
|
backgroundColor: AppTheme.surfaceLight,
|
|
);
|
|
}
|
|
return CircleAvatar(
|
|
radius: 26,
|
|
backgroundColor: AppTheme.primaryDark,
|
|
child: Text(
|
|
conversation.name.isNotEmpty ? conversation.name[0].toUpperCase() : '?',
|
|
style: const TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
String _getSubtitleText(LastMessageModel? lastMsg) {
|
|
if (lastMsg == null) return '';
|
|
if (lastMsg.hasMedia) {
|
|
return '📷 Photo'; // or other media indicator
|
|
}
|
|
return lastMsg.body;
|
|
}
|
|
|
|
String _formatTime(int timestamp) {
|
|
if (timestamp == 0) return '';
|
|
final dt = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
|
|
final now = DateTime.now();
|
|
final today = DateTime(now.year, now.month, now.day);
|
|
final yesterday = today.subtract(const Duration(days: 1));
|
|
final msgDate = DateTime(dt.year, dt.month, dt.day);
|
|
|
|
if (msgDate == today) {
|
|
return DateFormat('hh:mm a').format(dt);
|
|
} else if (msgDate == yesterday) {
|
|
return 'Yesterday';
|
|
} else if (now.difference(dt).inDays < 7) {
|
|
return DateFormat('EEEE').format(dt); // e.g. "Monday"
|
|
} else {
|
|
return DateFormat('MM/dd/yy').format(dt);
|
|
}
|
|
}
|
|
}
|