import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; import 'package:pyramid/widgets/mxc_image.dart'; class RoomListItem extends StatelessWidget { final Room room; final VoidCallback onTap; const RoomListItem({super.key, required this.room, required this.onTap}); @override Widget build(BuildContext context) { final unread = room.notificationCount; final lastEvent = room.lastEvent; final theme = Theme.of(context); return ListTile( onTap: onTap, leading: _RoomAvatar(room: room), title: Text( room.getLocalizedDisplayname(), maxLines: 1, overflow: TextOverflow.ellipsis, style: unread > 0 ? theme.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.bold) : theme.textTheme.bodyLarge, ), subtitle: lastEvent != null ? Text( _previewText(lastEvent), maxLines: 1, overflow: TextOverflow.ellipsis, style: theme.textTheme.bodySmall, ) : null, trailing: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.end, children: [ if (lastEvent != null) Text( _formatTime(lastEvent.originServerTs), style: theme.textTheme.labelSmall?.copyWith( color: unread > 0 ? theme.colorScheme.primary : theme.colorScheme.onSurfaceVariant, ), ), if (unread > 0) ...[ const SizedBox(height: 4), _UnreadBadge(count: unread), ], ], ), ); } String _previewText(Event event) { if (event.type == EventTypes.Message) { final body = event.body; if (body.isNotEmpty) return body; } return event.type; } String _formatTime(DateTime time) { final now = DateTime.now(); if (time.day == now.day && time.month == now.month && time.year == now.year) { return '${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}'; } return '${time.day.toString().padLeft(2, '0')}.${time.month.toString().padLeft(2, '0')}'; } } class _RoomAvatar extends StatelessWidget { final Room room; const _RoomAvatar({required this.room}); @override Widget build(BuildContext context) { final avatar = room.avatar; final name = room.getLocalizedDisplayname(); final initials = name.isNotEmpty ? name[0].toUpperCase() : '?'; final color = _colorForName(name, Theme.of(context).colorScheme); return CircleAvatar( radius: 24, backgroundColor: color, child: ClipOval( child: avatar != null ? MxcImage( mxcUri: avatar, width: 48, height: 48, placeholder: (_) => Text( initials, style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold), ), ) : Text( initials, style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold), ), ), ); } Color _colorForName(String name, ColorScheme scheme) { final colors = [ scheme.primary, scheme.secondary, scheme.tertiary, Colors.teal, Colors.indigo, Colors.orange, ]; final hash = name.codeUnits.fold(0, (a, b) => a + b); return colors[hash % colors.length]; } } class _UnreadBadge extends StatelessWidget { final int count; const _UnreadBadge({required this.count}); @override Widget build(BuildContext context) { final theme = Theme.of(context); return Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( color: theme.colorScheme.primary, borderRadius: BorderRadius.circular(10), ), child: Text( count > 99 ? '99+' : '$count', style: theme.textTheme.labelSmall?.copyWith( color: theme.colorScheme.onPrimary, fontWeight: FontWeight.bold, ), ), ); } }