diff --git a/packages/stream_chat_flutter/lib/src/channel_list_view.dart b/packages/stream_chat_flutter/lib/src/channel_list_view.dart index 1113fda2..f7c39e8e 100644 --- a/packages/stream_chat_flutter/lib/src/channel_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/channel_list_view.dart @@ -591,7 +591,7 @@ class _ChannelListViewState extends State { decoration: BoxDecoration( color: chatThemeData.channelListViewTheme.backgroundColor, ), - child: StreamChannelPreview( + child: ChannelPreview( onLongPress: widget.onChannelLongPress, channel: channel, onImageTap: widget.onImageTap != null diff --git a/packages/stream_chat_flutter/lib/src/channel_preview.dart b/packages/stream_chat_flutter/lib/src/channel_preview.dart index 1eb28bd2..ed5fe9f8 100644 --- a/packages/stream_chat_flutter/lib/src/channel_preview.dart +++ b/packages/stream_chat_flutter/lib/src/channel_preview.dart @@ -4,10 +4,6 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@macro channel_preview} -@Deprecated("Use 'StreamChannelPreview' instead") -typedef ChannelPreview = StreamChannelPreview; - /// {@template channel_preview} /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/channel_preview.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/channel_preview_paint.png) @@ -24,9 +20,10 @@ typedef ChannelPreview = StreamChannelPreview; /// [StreamChatTheme]. /// Modify it to change the widget appearance. /// {@endtemplate} -class StreamChannelPreview extends StatelessWidget { - /// Constructor for creating [StreamChannelPreview] - const StreamChannelPreview({ +@Deprecated("Use 'StreamChannelListTile' instead") +class ChannelPreview extends StatelessWidget { + /// Constructor for creating [ChannelPreview] + const ChannelPreview({ required this.channel, Key? key, this.onTap, @@ -71,7 +68,7 @@ class StreamChannelPreview extends StatelessWidget { @override Widget build(BuildContext context) { - final channelPreviewTheme = StreamChannelPreviewTheme.of(context); + final channelPreviewTheme = ChannelPreviewTheme.of(context); final streamChatState = StreamChat.of(context); return BetterStreamBuilder( stream: channel.isMutedStream, @@ -194,13 +191,13 @@ class StreamChannelPreview extends StatelessWidget { return Text( stringDate, - style: StreamChannelPreviewTheme.of(context).lastMessageAtStyle, + style: ChannelPreviewTheme.of(context).lastMessageAtStyle, ); }, ); Widget _buildSubtitle(BuildContext context) { - final channelPreviewTheme = StreamChannelPreviewTheme.of(context); + final channelPreviewTheme = ChannelPreviewTheme.of(context); if (channel.isMuted) { return Row( crossAxisAlignment: CrossAxisAlignment.end, @@ -253,7 +250,7 @@ class StreamChannelPreview extends StatelessWidget { text = parts.join(' '); - final channelPreviewTheme = StreamChannelPreviewTheme.of(context); + final channelPreviewTheme = ChannelPreviewTheme.of(context); return Text.rich( _getDisplayText( text, diff --git a/packages/stream_chat_flutter/lib/src/extension.dart b/packages/stream_chat_flutter/lib/src/extension.dart index 0feaf185..a0c9d288 100644 --- a/packages/stream_chat_flutter/lib/src/extension.dart +++ b/packages/stream_chat_flutter/lib/src/extension.dart @@ -225,3 +225,50 @@ extension UserListX on List { return entries.map((e) => e.key).toList(growable: false); } } + +/// Extensions on Message +extension MessageX on Message { + /// It replaces the user mentions with the actual user names. + Message replaceMentions({bool linkify = true}) { + var messageTextToRender = text; + for (final user in mentionedUsers.toSet()) { + final userId = user.id; + final userName = user.name; + if (linkify) { + messageTextToRender = messageTextToRender?.replaceAll( + '@$userId', + '[@$userName](@${userName.replaceAll(' ', '')})', + ); + } else { + messageTextToRender = messageTextToRender?.replaceAll( + '@$userId', + '@$userName', + ); + } + } + return copyWith(text: messageTextToRender); + } + + /// It returns the message with the translated text if available locally + Message translate(String language) => + copyWith(text: i18n?['${language}_text'] ?? text); + + /// It returns the message replacing the mentioned user names with + /// the respective user ids + Message replaceMentionsWithId() { + if (mentionedUsers.isEmpty) return this; + + var messageTextToSend = text; + if (messageTextToSend == null) return this; + + for (final user in mentionedUsers.toSet()) { + final userName = user.name; + messageTextToSend = messageTextToSend!.replaceAll( + '@$userName', + '@${user.id}', + ); + } + + return copyWith(text: messageTextToSend); + } +} diff --git a/packages/stream_chat_flutter/lib/src/message_input/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input/message_input.dart index 21d5775d..0ffa472c 100644 --- a/packages/stream_chat_flutter/lib/src/message_input/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input/message_input.dart @@ -1171,10 +1171,10 @@ class StreamMessageInputState extends State splits[splits.length - 1] = user.name; final rejoin = splits.join('@'); - _effectiveController.text = rejoin + - _effectiveController.text.substring( - _effectiveController.selectionStart, - ); + _effectiveController.text = + '$rejoin${_effectiveController.text.substring( + _effectiveController.selectionStart, + )}'; _onChangedDebounced.cancel(); setState(() => _showMentionsOverlay = false); @@ -1679,6 +1679,8 @@ class StreamMessageInputState extends State await streamChannel.reloadChannel(); } + message = message.replaceMentionsWithId(); + try { Future sendingFuture; if (_isEditing) { diff --git a/packages/stream_chat_flutter/lib/src/message_text.dart b/packages/stream_chat_flutter/lib/src/message_text.dart index 012aa5c7..b6fee801 100644 --- a/packages/stream_chat_flutter/lib/src/message_text.dart +++ b/packages/stream_chat_flutter/lib/src/message_text.dart @@ -1,6 +1,7 @@ import 'package:collection/collection.dart' show IterableExtension; import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; +import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; /// {@macro message_text} @@ -40,13 +41,14 @@ class StreamMessageText extends StatelessWidget { stream: streamChat.currentUserStream.map((it) => it!.language ?? 'en'), initialData: streamChat.currentUser!.language ?? 'en', builder: (context, language) { - final translatedText = - message.i18n?['${language}_text'] ?? message.text; - final messageText = - _replaceMentions(translatedText ?? '').replaceAll('\n', '\n\n'); + final messageText = message + .translate(language) + .replaceMentions() + .text + ?.replaceAll('\n', '\n\n'); final themeData = Theme.of(context); return MarkdownBody( - data: messageText, + data: messageText ?? '', onTapLink: ( String link, String? href, @@ -86,17 +88,4 @@ class StreamMessageText extends StatelessWidget { }, ); } - - String _replaceMentions(String text) { - var messageTextToRender = text; - for (final user in message.mentionedUsers.toSet()) { - final userId = user.id; - final userName = user.name; - messageTextToRender = messageTextToRender.replaceAll( - '@$userId', - '[@$userName](@${userName.replaceAll(' ', '')})', - ); - } - return messageTextToRender; - } } diff --git a/packages/stream_chat_flutter/lib/src/v4/channel_list_view/stream_channel_list_tile.dart b/packages/stream_chat_flutter/lib/src/v4/channel_list_view/stream_channel_list_tile.dart index 3b254817..0c78b926 100644 --- a/packages/stream_chat_flutter/lib/src/v4/channel_list_view/stream_channel_list_tile.dart +++ b/packages/stream_chat_flutter/lib/src/v4/channel_list_view/stream_channel_list_tile.dart @@ -9,6 +9,7 @@ import 'package:stream_chat_flutter/src/typing_indicator.dart'; import 'package:stream_chat_flutter/src/unread_indicator.dart'; import 'package:stream_chat_flutter/src/v4/stream_channel_avatar.dart'; import 'package:stream_chat_flutter/src/v4/stream_channel_name.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; /// A widget that displays a channel preview. @@ -359,7 +360,10 @@ class ChannelLastMessageText extends StatelessWidget { if (lastMessage == null) return const Offstage(); - final lastMessageText = lastMessage.text; + final lastMessageText = lastMessage + .translate(channel.client.state.currentUser?.language ?? 'en') + .replaceMentions(linkify: false) + .text; final lastMessageAttachments = lastMessage.attachments; final lastMessageMentionedUsers = lastMessage.mentionedUsers;