import 'package:flutter/material.dart'; import 'package:jiffy/jiffy.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'package:stream_chat_flutter/src/extension.dart'; /// It shows the current [Message] preview. /// /// Usually you don't use this widget as it's the default item used by /// [MessageSearchListView]. /// /// The widget renders the ui based on the first ancestor of type /// [StreamChatTheme]. /// Modify it to change the widget appearance. class MessageSearchItem extends StatelessWidget { /// Instantiate a new MessageSearchItem const MessageSearchItem({ Key? key, required this.getMessageResponse, this.onTap, this.showOnlineStatus = true, }) : super(key: key); /// [Message] displayed final GetMessageResponse getMessageResponse; /// Function called when tapping this widget final VoidCallback? onTap; /// If true the [MessageSearchItem] will show the current online Status final bool showOnlineStatus; @override Widget build(BuildContext context) { final message = getMessageResponse.message; final channel = getMessageResponse.channel; final channelName = channel?.extraData['name']; final user = message.user!; final channelPreviewTheme = ChannelPreviewTheme.of(context); return ListTile( onTap: onTap, leading: UserAvatar( user: user, showOnlineStatus: showOnlineStatus, constraints: const BoxConstraints.tightFor( height: 40, width: 40, ), ), title: Row( children: [ Text( user.id == StreamChat.of(context).currentUser?.id ? context.translations.youText : user.name, style: channelPreviewTheme.titleStyle, ), if (channelName != null) ...[ Text( ' ${context.translations.inText} ', style: channelPreviewTheme.titleStyle?.copyWith( fontWeight: FontWeight.normal, ), ), Text( channelName as String, style: channelPreviewTheme.titleStyle, ), ], ], ), subtitle: Row( children: [ Expanded(child: _buildSubtitle(context, message)), const SizedBox(width: 16), _buildDate(context, message), ], ), ); } Widget _buildDate(BuildContext context, Message message) { final createdAt = message.createdAt; String stringDate; final now = DateTime.now(); if (now.year != createdAt.year || now.month != createdAt.month || now.day != createdAt.day) { stringDate = Jiffy(createdAt.toLocal()).yMd; } else { stringDate = Jiffy(createdAt.toLocal()).jm; } return Text( stringDate, style: StreamChatTheme.of(context).channelPreviewTheme.lastMessageAtStyle, ); } Widget _buildSubtitle(BuildContext context, Message message) { var text = message.text; if (message.isDeleted) { text = context.translations.messageDeletedText; } else if (message.attachments.isNotEmpty) { final parts = [ ...message.attachments.map((e) { if (e.type == 'image') { return '📷'; } else if (e.type == 'video') { return '🎬'; } else if (e.type == 'giphy') { return '[GIF]'; } return e == message.attachments.last ? (e.title ?? 'File') : '${e.title ?? 'File'} , '; }), message.text ?? '', ]; text = parts.join(' '); } final channelPreviewTheme = ChannelPreviewTheme.of(context); return Text.rich( _getDisplayText( text!, message.mentionedUsers, message.attachments, channelPreviewTheme.subtitleStyle?.copyWith( fontStyle: (message.isSystem || message.isDeleted) ? FontStyle.italic : FontStyle.normal, ), channelPreviewTheme.subtitleStyle?.copyWith( fontStyle: (message.isSystem || message.isDeleted) ? FontStyle.italic : FontStyle.normal, fontWeight: FontWeight.bold, ), ), maxLines: 1, overflow: TextOverflow.ellipsis, ); } TextSpan _getDisplayText( String text, List mentions, List attachments, TextStyle? normalTextStyle, TextStyle? mentionsTextStyle) { final textList = text.split(' '); final resList = []; for (final e in textList) { if (mentions.isNotEmpty && mentions.any((element) => '@${element.name}' == e)) { resList.add(TextSpan( text: '$e ', style: mentionsTextStyle, )); } else if (attachments.isNotEmpty && attachments .where((e) => e.title != null) .any((element) => element.title == e)) { resList.add(TextSpan( text: '$e ', style: normalTextStyle?.copyWith(fontStyle: FontStyle.italic), )); } else { resList.add(TextSpan( text: e == textList.last ? e : '$e ', style: normalTextStyle, )); } } return TextSpan(children: resList); } }