From 5d9420b808ce8731f0a50965a90597eee76233b5 Mon Sep 17 00:00:00 2001 From: Joshua Owolabi Date: Mon, 21 Oct 2024 12:46:30 +0100 Subject: [PATCH] Highlighting and tapping on user mentions (#5408) * Support tapping User mentions * reformat code * reformat code * fix apiDump * rename onTapUserMention to onUserMentionCLick * fix issues with build --------- Co-authored-by: Joshua Owolabi <109959715+IsaacNewton111@users.noreply.github.com> Co-authored-by: Aleksandar Apostolov Co-authored-by: Kanat Kiialbaev --- .../compose/sample/ui/MessagesActivity.kt | 117 ++++++++++-------- .../api/stream-chat-android-compose.api | 14 +-- .../ui/components/composer/InputField.kt | 1 + .../ui/components/messages/MessageContent.kt | 4 + .../ui/components/messages/MessageText.kt | 32 ++--- .../ui/components/poll/PollOptionInput.kt | 1 + .../compose/ui/messages/MessagesScreen.kt | 2 + .../ui/messages/list/MessageContainer.kt | 4 + .../compose/ui/messages/list/MessageItem.kt | 71 ++++++++--- .../compose/ui/messages/list/MessageList.kt | 6 + .../compose/ui/util/MessageTextFormatter.kt | 3 + .../ui/util/QuotedMessageTextFormatter.kt | 1 + .../chat/android/compose/ui/util/TextUtils.kt | 42 +++++++ .../api/stream-chat-android-ui-common.api | 1 + .../ui/common/utils/extensions/User.kt | 4 + .../compose/messages/MessageItemTest.kt | 26 ++++ .../chat/android/uitests/util/TestData.kt | 23 ++++ 17 files changed, 259 insertions(+), 93 deletions(-) diff --git a/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/MessagesActivity.kt b/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/MessagesActivity.kt index 087b716c830..3e4010c8faf 100644 --- a/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/MessagesActivity.kt +++ b/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/MessagesActivity.kt @@ -139,63 +139,74 @@ class MessagesActivity : BaseConnectedActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContent { - val isInDarkMode = isSystemInDarkTheme() - val colors = if (isInDarkMode) StreamColors.defaultDarkColors() else StreamColors.defaultColors() - val typography = StreamTypography.defaultTypography() - val shapes = StreamShapes.defaultShapes() - val messageComposerTheme = MessageComposerTheme.defaultTheme(isInDarkMode, typography, shapes, colors) - ChatTheme( - isInDarkMode = isInDarkMode, - colors = colors, - shapes = shapes, - typography = typography, - dateFormatter = ChatApp.dateFormatter, - autoTranslationEnabled = ChatApp.autoTranslationEnabled, - isComposerLinkPreviewEnabled = ChatApp.isComposerLinkPreviewEnabled, - allowUIAutomationTest = true, - messageComposerTheme = messageComposerTheme.let { - it.copy( - attachmentCancelIcon = it.attachmentCancelIcon.copy( - painter = painterResource(id = R.drawable.stream_compose_ic_clear), - tint = colors.overlayDark, - backgroundColor = colors.appBackground, - ), - audioRecording = it.audioRecording.copy( - enabled = true, - showRecordButtonOverSend = false, - ), - ) - }, - attachmentPickerTheme = AttachmentPickerTheme.defaultTheme(colors).copy( - backgroundOverlay = colors.overlayDark, - backgroundSecondary = colors.inputBackground, - backgroundPrimary = colors.barsBackground, - ), - reactionOptionsTheme = ReactionOptionsTheme.defaultTheme(), - messageOptionsTheme = MessageOptionsTheme.defaultTheme( - optionVisibility = MessageOptionItemVisibility(), - ), - ) { - Column { - if (BuildConfig.DEBUG) { - MembersList(viewModel = membersViewModel) - } - MessagesScreen( - viewModelFactory = factory, - reactionSorting = ReactionSortingByLastReactionAt, - onBackPressed = { finish() }, - onHeaderTitleClick = ::openChannelInfo, - onUserAvatarClick = { user -> - Log.i("MessagesActivity", "user avatar clicked: ${user.id}") - }, - ) - } + SetupChatTheme() + } + } + + @Composable + private fun SetupChatTheme() { + val isInDarkMode = isSystemInDarkTheme() + val colors = if (isInDarkMode) StreamColors.defaultDarkColors() else StreamColors.defaultColors() + val typography = StreamTypography.defaultTypography() + val shapes = StreamShapes.defaultShapes() + val messageComposerTheme = MessageComposerTheme.defaultTheme(isInDarkMode, typography, shapes, colors) + ChatTheme( + isInDarkMode = isInDarkMode, + colors = colors, + shapes = shapes, + typography = typography, + dateFormatter = ChatApp.dateFormatter, + autoTranslationEnabled = ChatApp.autoTranslationEnabled, + isComposerLinkPreviewEnabled = ChatApp.isComposerLinkPreviewEnabled, + allowUIAutomationTest = true, + messageComposerTheme = messageComposerTheme.let { + it.copy( + attachmentCancelIcon = it.attachmentCancelIcon.copy( + painter = painterResource(id = R.drawable.stream_compose_ic_clear), + tint = colors.overlayDark, + backgroundColor = colors.appBackground, + ), + audioRecording = it.audioRecording.copy( + enabled = true, + showRecordButtonOverSend = false, + ), + ) + }, + attachmentPickerTheme = AttachmentPickerTheme.defaultTheme(colors).copy( + backgroundOverlay = colors.overlayDark, + backgroundSecondary = colors.inputBackground, + backgroundPrimary = colors.barsBackground, + ), + reactionOptionsTheme = ReactionOptionsTheme.defaultTheme(), + messageOptionsTheme = MessageOptionsTheme.defaultTheme( + optionVisibility = MessageOptionItemVisibility(), + ), + ) { + SetupContent() + } + } - // MyCustomUi() + @Composable + private fun SetupContent() { + Column { + if (BuildConfig.DEBUG) { + MembersList(viewModel = membersViewModel) } + MessagesScreen( + viewModelFactory = factory, + reactionSorting = ReactionSortingByLastReactionAt, + onBackPressed = { finish() }, + onHeaderTitleClick = ::openChannelInfo, + onUserAvatarClick = { user -> + Log.i("MessagesActivity", "user avatar clicked: ${user.id}") + }, + onUserMentionClick = { user -> + Log.i("MessagesActivity", "user mention tapped: ${user.id}") + }, + ) } + // MyCustomUi() } private fun openChannelInfo(channel: Channel) { diff --git a/stream-chat-android-compose/api/stream-chat-android-compose.api b/stream-chat-android-compose/api/stream-chat-android-compose.api index 7c0c0c51544..a360f6fa354 100644 --- a/stream-chat-android-compose/api/stream-chat-android-compose.api +++ b/stream-chat-android-compose/api/stream-chat-android-compose.api @@ -1166,7 +1166,7 @@ public final class io/getstream/chat/android/compose/ui/components/messages/Mess } public final class io/getstream/chat/android/compose/ui/components/messages/MessageContentKt { - public static final fun MessageContent (Lio/getstream/chat/android/models/Message;Lio/getstream/chat/android/models/User;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;III)V + public static final fun MessageContent (Lio/getstream/chat/android/models/Message;Lio/getstream/chat/android/models/User;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;III)V } public final class io/getstream/chat/android/compose/ui/components/messages/MessageFooterKt { @@ -1188,7 +1188,7 @@ public final class io/getstream/chat/android/compose/ui/components/messages/Mess } public final class io/getstream/chat/android/compose/ui/components/messages/MessageTextKt { - public static final fun MessageText (Lio/getstream/chat/android/models/Message;Lio/getstream/chat/android/models/User;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V + public static final fun MessageText (Lio/getstream/chat/android/models/Message;Lio/getstream/chat/android/models/User;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)V } public final class io/getstream/chat/android/compose/ui/components/messages/MessageThreadFooterKt { @@ -1481,7 +1481,7 @@ public final class io/getstream/chat/android/compose/ui/messages/MessagesScreenK public static final fun MessageDialogs (Lio/getstream/chat/android/compose/viewmodel/messages/MessageListViewModel;Landroidx/compose/runtime/Composer;I)V public static final fun MessageMenus (Landroidx/compose/foundation/layout/BoxScope;Lio/getstream/chat/android/compose/viewmodel/messages/MessageListViewModel;Lio/getstream/chat/android/compose/viewmodel/messages/MessageComposerViewModel;ZZLandroidx/compose/runtime/Composer;I)V public static final fun MessageModerationDialog (Lio/getstream/chat/android/compose/viewmodel/messages/MessageListViewModel;Lio/getstream/chat/android/compose/viewmodel/messages/MessageComposerViewModel;ZZLandroidx/compose/runtime/Composer;I)V - public static final fun MessagesScreen (Lio/getstream/chat/android/compose/viewmodel/messages/MessagesViewModelFactory;ZLio/getstream/chat/android/models/ReactionSorting;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;ZZLio/getstream/chat/android/compose/ui/messages/list/ThreadMessagesStart;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;III)V + public static final fun MessagesScreen (Lio/getstream/chat/android/compose/viewmodel/messages/MessagesViewModelFactory;ZLio/getstream/chat/android/models/ReactionSorting;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ZZLio/getstream/chat/android/compose/ui/messages/list/ThreadMessagesStart;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;III)V public static final fun PollDialogs (Lio/getstream/chat/android/compose/viewmodel/messages/MessageListViewModel;Landroidx/compose/runtime/Composer;I)V } @@ -1880,17 +1880,17 @@ public final class io/getstream/chat/android/compose/ui/messages/list/Composable } public final class io/getstream/chat/android/compose/ui/messages/list/MessageContainerKt { - public static final fun MessageContainer (Lio/getstream/chat/android/ui/common/state/messages/list/MessageListItemState;Lio/getstream/chat/android/models/ReactionSorting;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;IIII)V + public static final fun MessageContainer (Lio/getstream/chat/android/ui/common/state/messages/list/MessageListItemState;Lio/getstream/chat/android/models/ReactionSorting;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;IIII)V } public final class io/getstream/chat/android/compose/ui/messages/list/MessageItemKt { public static final field HighlightFadeOutDurationMillis I - public static final fun MessageItem (Lio/getstream/chat/android/ui/common/state/messages/list/MessageItemState;Lio/getstream/chat/android/models/ReactionSorting;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Landroidx/compose/runtime/Composer;IIII)V + public static final fun MessageItem (Lio/getstream/chat/android/ui/common/state/messages/list/MessageItemState;Lio/getstream/chat/android/models/ReactionSorting;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Landroidx/compose/runtime/Composer;IIII)V } public final class io/getstream/chat/android/compose/ui/messages/list/MessageListKt { - public static final fun MessageList (Lio/getstream/chat/android/compose/viewmodel/messages/MessageListViewModel;Lio/getstream/chat/android/models/ReactionSorting;Landroidx/compose/ui/Modifier;Landroidx/compose/foundation/layout/PaddingValues;Lio/getstream/chat/android/compose/ui/messages/list/MessagesLazyListState;Lio/getstream/chat/android/compose/ui/messages/list/ThreadMessagesStart;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;IIII)V - public static final fun MessageList (Lio/getstream/chat/android/ui/common/state/messages/list/MessageListState;Lio/getstream/chat/android/compose/ui/messages/list/ThreadMessagesStart;Lio/getstream/chat/android/models/ReactionSorting;Landroidx/compose/ui/Modifier;Landroidx/compose/foundation/layout/PaddingValues;Lio/getstream/chat/android/compose/ui/messages/list/MessagesLazyListState;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;IIIII)V + public static final fun MessageList (Lio/getstream/chat/android/compose/viewmodel/messages/MessageListViewModel;Lio/getstream/chat/android/models/ReactionSorting;Landroidx/compose/ui/Modifier;Landroidx/compose/foundation/layout/PaddingValues;Lio/getstream/chat/android/compose/ui/messages/list/MessagesLazyListState;Lio/getstream/chat/android/compose/ui/messages/list/ThreadMessagesStart;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;IIIII)V + public static final fun MessageList (Lio/getstream/chat/android/ui/common/state/messages/list/MessageListState;Lio/getstream/chat/android/compose/ui/messages/list/ThreadMessagesStart;Lio/getstream/chat/android/models/ReactionSorting;Landroidx/compose/ui/Modifier;Landroidx/compose/foundation/layout/PaddingValues;Lio/getstream/chat/android/compose/ui/messages/list/MessagesLazyListState;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;IIIIII)V } public final class io/getstream/chat/android/compose/ui/messages/list/MessagesKt { diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/composer/InputField.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/composer/InputField.kt index 87def86a9bc..af5107dee08 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/composer/InputField.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/composer/InputField.kt @@ -115,6 +115,7 @@ public fun InputField( textColor = theme.textStyle.color, textFontStyle = typography.body.fontStyle, linkColor = colors.primaryAccent, + mentionsColor = colors.primaryAccent, ) TransformedText(styledText, OffsetMapping.Identity) }, diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/MessageContent.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/MessageContent.kt index c1937bcbd3e..4682ceb9a77 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/MessageContent.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/MessageContent.kt @@ -58,6 +58,7 @@ public fun MessageContent( onLongItemClick: (Message) -> Unit = {}, onGiphyActionClick: (GiphyAction) -> Unit = {}, onQuotedMessageClick: (Message) -> Unit = {}, + onUserMentionClick: (User) -> Unit = {}, onLinkClick: ((Message, String) -> Unit)? = null, onMediaGalleryPreviewResult: (MediaGalleryPreviewResult?) -> Unit = {}, giphyEphemeralContent: @Composable () -> Unit = { @@ -77,6 +78,7 @@ public fun MessageContent( onMediaGalleryPreviewResult = onMediaGalleryPreviewResult, onQuotedMessageClick = onQuotedMessageClick, onLinkClick = onLinkClick, + onUserMentionClick = onUserMentionClick, ) }, ) { @@ -143,6 +145,7 @@ internal fun DefaultMessageContent( onLongItemClick: (Message) -> Unit, onMediaGalleryPreviewResult: (MediaGalleryPreviewResult?) -> Unit = {}, onQuotedMessageClick: (Message) -> Unit, + onUserMentionClick: (User) -> Unit = {}, onLinkClick: ((Message, String) -> Unit)? = null, ) { Column { @@ -159,6 +162,7 @@ internal fun DefaultMessageContent( onLongItemClick = onLongItemClick, onQuotedMessageClick = onQuotedMessageClick, onLinkClick = onLinkClick, + onUserMentionClick = onUserMentionClick, ) } } diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/MessageText.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/MessageText.kt index 1f5a6f558fc..455a4447b8c 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/MessageText.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/MessageText.kt @@ -38,12 +38,14 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.util.fastAny import io.getstream.chat.android.compose.ui.theme.ChatTheme import io.getstream.chat.android.compose.ui.util.AnnotationTagEmail +import io.getstream.chat.android.compose.ui.util.AnnotationTagMention import io.getstream.chat.android.compose.ui.util.AnnotationTagUrl import io.getstream.chat.android.compose.ui.util.isEmojiOnlyWithoutBubble import io.getstream.chat.android.compose.ui.util.isFewEmoji import io.getstream.chat.android.compose.ui.util.isSingleEmoji import io.getstream.chat.android.models.Message import io.getstream.chat.android.models.User +import io.getstream.chat.android.ui.common.utils.extensions.getUserByNameOrId import io.getstream.chat.android.ui.common.utils.extensions.isMine /** @@ -67,6 +69,7 @@ public fun MessageText( modifier: Modifier = Modifier, onLongItemClick: (Message) -> Unit, onLinkClick: ((Message, String) -> Unit)? = null, + onUserMentionClick: (User) -> Unit = {}, ) { val context = LocalContext.current @@ -84,8 +87,10 @@ public fun MessageText( ChatTheme.otherMessageTheme.textStyle } } - - if (annotations.fastAny { it.tag == AnnotationTagUrl || it.tag == AnnotationTagEmail }) { + if (annotations.fastAny { + it.tag == AnnotationTagUrl || it.tag == AnnotationTagEmail || it.tag == AnnotationTagMention + } + ) { ClickableText( modifier = modifier .padding( @@ -98,18 +103,17 @@ public fun MessageText( style = style, onLongPress = { onLongItemClick(message) }, ) { position -> - val targetUrl = annotations.firstOrNull { - position in it.start..it.end - }?.item - - if (!targetUrl.isNullOrEmpty()) { - onLinkClick?.invoke(message, targetUrl) ?: run { - context.startActivity( - Intent( - Intent.ACTION_VIEW, - Uri.parse(targetUrl), - ), - ) + val annotation = annotations.firstOrNull { position in it.start..it.end } + if (annotation?.tag == AnnotationTagMention) { + message.mentionedUsers.getUserByNameOrId(annotation.item)?.let { onUserMentionClick.invoke(it) } + } else { + val targetUrl = annotation?.item + if (!targetUrl.isNullOrEmpty()) { + onLinkClick?.invoke(message, targetUrl) ?: run { + context.startActivity( + Intent(Intent.ACTION_VIEW, Uri.parse(targetUrl)), + ) + } } } } diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionInput.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionInput.kt index d3abc594cb8..02d1210e2c9 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionInput.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionInput.kt @@ -107,6 +107,7 @@ public fun PollOptionInput( textColor = textColor, textFontStyle = typography.body.fontStyle, linkColor = colors.primaryAccent, + mentionsColor = colors.primaryAccent, ) TransformedText(styledText, OffsetMapping.Identity) }, diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/MessagesScreen.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/MessagesScreen.kt index 804543a5e63..0ce0e480797 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/MessagesScreen.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/MessagesScreen.kt @@ -142,6 +142,7 @@ public fun MessagesScreen( onComposerLinkPreviewClick: ((LinkPreview) -> Unit)? = null, onMessageLinkClick: ((Message, String) -> Unit)? = null, onUserAvatarClick: (User) -> Unit = {}, + onUserMentionClick: (User) -> Unit = {}, skipPushNotification: Boolean = false, skipEnrichUrl: Boolean = false, threadMessagesStart: ThreadMessagesStart = ThreadMessagesStart.BOTTOM, @@ -238,6 +239,7 @@ public fun MessagesScreen( }, onUserAvatarClick = onUserAvatarClick, onMessageLinkClick = onMessageLinkClick, + onUserMentionClick = onUserMentionClick, onMediaGalleryPreviewResult = remember(listViewModel, composerViewModel) { { result -> diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/list/MessageContainer.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/list/MessageContainer.kt index a6b63a4d4f2..fd7bdfb6110 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/list/MessageContainer.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/list/MessageContainer.kt @@ -97,6 +97,7 @@ public fun MessageContainer( onUserAvatarClick: ((User) -> Unit)? = null, onLinkClick: ((Message, String) -> Unit)? = null, onMediaGalleryPreviewResult: (MediaGalleryPreviewResult?) -> Unit = {}, + onUserMentionClick: (User) -> Unit = {}, dateSeparatorContent: @Composable (DateSeparatorItemState) -> Unit = { DefaultMessageDateSeparatorContent(dateSeparator = it) }, @@ -129,6 +130,7 @@ public fun MessageContainer( onUserAvatarClick?.invoke(it.message.user) }, onLinkClick = onLinkClick, + onUserMentionClick = onUserMentionClick, ) }, typingIndicatorContent: @Composable (TypingItemState) -> Unit = { }, @@ -290,6 +292,7 @@ internal fun DefaultMessageItem( onQuotedMessageClick: (Message) -> Unit, onUserAvatarClick: () -> Unit, onMediaGalleryPreviewResult: (MediaGalleryPreviewResult?) -> Unit = {}, + onUserMentionClick: (User) -> Unit = {}, ) { MessageItem( messageItem = messageItem, @@ -308,5 +311,6 @@ internal fun DefaultMessageItem( onUserAvatarClick = onUserAvatarClick, onLinkClick = onLinkClick, onMediaGalleryPreviewResult = onMediaGalleryPreviewResult, + onUserMentionClick = onUserMentionClick, ) } diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/list/MessageItem.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/list/MessageItem.kt index f1dca89b4c2..c96668028b2 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/list/MessageItem.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/list/MessageItem.kt @@ -48,6 +48,7 @@ import androidx.compose.ui.Alignment.Companion.BottomEnd import androidx.compose.ui.Alignment.Companion.End import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -147,6 +148,7 @@ public fun MessageItem( onQuotedMessageClick: (Message) -> Unit = {}, onUserAvatarClick: (() -> Unit)? = null, onLinkClick: ((Message, String) -> Unit)? = null, + onUserMentionClick: (User) -> Unit = {}, onMediaGalleryPreviewResult: (MediaGalleryPreviewResult?) -> Unit = {}, leadingContent: @Composable RowScope.(MessageItemState) -> Unit = { DefaultMessageItemLeadingContent( @@ -169,6 +171,7 @@ public fun MessageItem( onGiphyActionClick = onGiphyActionClick, onQuotedMessageClick = onQuotedMessageClick, onLinkClick = onLinkClick, + onUserMentionClick = onUserMentionClick, onPollUpdated = onPollUpdated, onCastVote = onCastVote, onRemoveVote = onRemoveVote, @@ -436,14 +439,15 @@ internal fun DefaultMessageItemTrailingContent( * @param onClosePoll Handler when a user close a poll. * @param onAddPollOption Handler when a user add a poll option. */ -@Composable @Suppress("LongParameterList") +@Composable internal fun DefaultMessageItemCenterContent( messageItem: MessageItemState, onLongItemClick: (Message) -> Unit = {}, onGiphyActionClick: (GiphyAction) -> Unit = {}, onQuotedMessageClick: (Message) -> Unit = {}, onLinkClick: ((Message, String) -> Unit)? = null, + onUserMentionClick: (User) -> Unit = {}, onMediaGalleryPreviewResult: (MediaGalleryPreviewResult?) -> Unit = {}, onPollUpdated: (Message, Poll) -> Unit, onCastVote: (Message, Poll, Option) -> Unit, @@ -490,6 +494,7 @@ internal fun DefaultMessageItemCenterContent( onMediaGalleryPreviewResult = onMediaGalleryPreviewResult, onQuotedMessageClick = onQuotedMessageClick, onLinkClick = onLinkClick, + onUserMentionClick = onUserMentionClick, ) } } @@ -568,31 +573,16 @@ internal fun RegularMessageContent( onGiphyActionClick: (GiphyAction) -> Unit = {}, onQuotedMessageClick: (Message) -> Unit = {}, onLinkClick: ((Message, String) -> Unit)? = null, + onUserMentionClick: (User) -> Unit = {}, onMediaGalleryPreviewResult: (MediaGalleryPreviewResult?) -> Unit = {}, ) { val message = messageItem.message val position = messageItem.groupPosition val ownsMessage = messageItem.isMine - val messageBubbleShape = when { - position.contains(MessagePosition.TOP) || position.contains(MessagePosition.MIDDLE) -> RoundedCornerShape(16.dp) - else -> { - if (ownsMessage) ChatTheme.shapes.myMessageBubble else ChatTheme.shapes.otherMessageBubble - } - } - - val messageBubbleColor = when { - message.isGiphyEphemeral() -> ChatTheme.colors.giphyMessageBackground - message.isDeleted() -> when (ownsMessage) { - true -> ChatTheme.ownMessageTheme.deletedBackgroundColor - else -> ChatTheme.otherMessageTheme.deletedBackgroundColor - } + val messageBubbleShape = getMessageBubbleShape(position = position, ownsMessage = ownsMessage) - else -> when (ownsMessage) { - true -> ChatTheme.ownMessageTheme.backgroundColor - else -> ChatTheme.otherMessageTheme.backgroundColor - } - } + val messageBubbleColor = getMessageBubbleColor(message = message, ownsMessage = ownsMessage) if (!messageItem.isErrorOrFailed()) { MessageBubble( @@ -609,6 +599,7 @@ internal fun RegularMessageContent( onMediaGalleryPreviewResult = onMediaGalleryPreviewResult, onQuotedMessageClick = onQuotedMessageClick, onLinkClick = onLinkClick, + onUserMentionClick = onUserMentionClick, ) }, ) @@ -643,6 +634,46 @@ internal fun RegularMessageContent( } } +/** + * Determines the shape of the message bubble based on the message position and ownership. + * + * @param position The position of the message in the group (top, middle, etc.). + * @param ownsMessage Indicates if the current user owns the message. + * @return A shape for the message bubble. + */ +@Composable +private fun getMessageBubbleShape(position: List, ownsMessage: Boolean): Shape { + return when { + position.contains(MessagePosition.TOP) || position.contains(MessagePosition.MIDDLE) -> RoundedCornerShape(16.dp) + else -> if (ownsMessage) ChatTheme.shapes.myMessageBubble else ChatTheme.shapes.otherMessageBubble + } +} + +/** + * Determines the background color of the message bubble based on the message content and ownership. + * + * @param message The message data. + * @param ownsMessage Indicates if the current user owns the message. + * @return A color for the message bubble. + */ +@Composable +private fun getMessageBubbleColor(message: Message, ownsMessage: Boolean): Color { + return when { + message.isGiphyEphemeral() -> ChatTheme.colors.giphyMessageBackground + message.isDeleted() -> if (ownsMessage) { + ChatTheme.ownMessageTheme.deletedBackgroundColor + } else { + ChatTheme.otherMessageTheme.deletedBackgroundColor + } + + else -> if (ownsMessage) { + ChatTheme.ownMessageTheme.backgroundColor + } else { + ChatTheme.otherMessageTheme.backgroundColor + } + } +} + /** * The default text message content. It holds the quoted message in case there is one. * @@ -657,6 +688,7 @@ internal fun DefaultMessageTextContent( currentUser: User?, onLongItemClick: (Message) -> Unit, onQuotedMessageClick: (Message) -> Unit, + onUserMentionClick: (User) -> Unit = {}, onLinkClick: ((Message, String) -> Unit)? = null, ) { val quotedMessage = message.replyTo @@ -677,6 +709,7 @@ internal fun DefaultMessageTextContent( currentUser = currentUser, onLongItemClick = onLongItemClick, onLinkClick = onLinkClick, + onUserMentionClick = onUserMentionClick, ) } } diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/list/MessageList.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/list/MessageList.kt index 483a8797a04..875365ffa52 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/list/MessageList.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/list/MessageList.kt @@ -140,6 +140,7 @@ public fun MessageList( }, onUserAvatarClick: ((User) -> Unit)? = null, onMessageLinkClick: ((Message, String) -> Unit)? = null, + onUserMentionClick: (User) -> Unit = {}, onMediaGalleryPreviewResult: (MediaGalleryPreviewResult?) -> Unit = { if (it?.resultType == MediaGalleryPreviewResultType.SHOW_IN_CHAT) { viewModel.scrollToMessage( @@ -178,6 +179,7 @@ public fun MessageList( onQuotedMessageClick = onQuotedMessageClick, onUserAvatarClick = onUserAvatarClick, onLinkClick = onMessageLinkClick, + onUserMentionClick = onUserMentionClick, ) }, ) { @@ -244,6 +246,7 @@ internal fun DefaultMessageContainer( onQuotedMessageClick: (Message) -> Unit, onUserAvatarClick: ((User) -> Unit)? = null, onLinkClick: ((Message, String) -> Unit)? = null, + onUserMentionClick: (User) -> Unit = {}, ) { MessageContainer( messageListItemState = messageListItemState, @@ -262,6 +265,7 @@ internal fun DefaultMessageContainer( onQuotedMessageClick = onQuotedMessageClick, onUserAvatarClick = onUserAvatarClick, onLinkClick = onLinkClick, + onUserMentionClick = onUserMentionClick, ) } @@ -357,6 +361,7 @@ public fun MessageList( onScrollToBottom: (() -> Unit) -> Unit = {}, onUserAvatarClick: ((User) -> Unit)? = null, onMessageLinkClick: ((Message, String) -> Unit)? = null, + onUserMentionClick: (User) -> Unit = { _ -> }, loadingContent: @Composable () -> Unit = { DefaultMessageListLoadingIndicator(modifier) }, emptyContent: @Composable () -> Unit = { DefaultMessageListEmptyContent(modifier) }, helperContent: @Composable BoxScope.() -> Unit = { @@ -388,6 +393,7 @@ public fun MessageList( onQuotedMessageClick = onQuotedMessageClick, onUserAvatarClick = onUserAvatarClick, onLinkClick = onMessageLinkClick, + onUserMentionClick = onUserMentionClick, ) }, ) { diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/MessageTextFormatter.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/MessageTextFormatter.kt index 5b93af8d373..10c6c382016 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/MessageTextFormatter.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/MessageTextFormatter.kt @@ -192,12 +192,15 @@ private class DefaultMessageTextFormatter( } ?: message.text else -> message.text } + val mentionedUserNames = message.mentionedUsers.map { it.name.ifEmpty { it.id } } val textColor = textStyle(message.isMine(currentUser)).color return buildAnnotatedMessageText( text = displayedText, textColor = textColor, textFontStyle = typography.body.fontStyle, linkColor = colors.primaryAccent, + mentionsColor = colors.primaryAccent, + mentionedUserNames = mentionedUserNames, builder = { builder?.invoke(this, message, currentUser) }, diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/QuotedMessageTextFormatter.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/QuotedMessageTextFormatter.kt index 531345b211b..e0d6000219c 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/QuotedMessageTextFormatter.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/QuotedMessageTextFormatter.kt @@ -233,6 +233,7 @@ private class DefaultQuotedMessageTextFormatter( textColor = textColor, textFontStyle = typography.body.fontStyle, linkColor = colors.primaryAccent, + mentionsColor = colors.primaryAccent, builder = { builder?.invoke(this, message, replyMessage, currentUser) }, diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/TextUtils.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/TextUtils.kt index 410cb555968..e7251f4dc9c 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/TextUtils.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/TextUtils.kt @@ -43,6 +43,11 @@ internal const val AnnotationTagUrl: AnnotationTag = "URL" */ internal const val AnnotationTagEmail: AnnotationTag = "EMAIL" +/** + * The tag used to annotate mentions in the message text. + */ +internal const val AnnotationTagMention: AnnotationTag = "MENTION" + /** * Takes the given message text and builds an annotated message text that shows links and allows for clicks, * if there are any links available. @@ -63,6 +68,7 @@ internal fun buildAnnotatedMessageText( textColor = color, textFontStyle = ChatTheme.typography.body.fontStyle, linkColor = ChatTheme.colors.primaryAccent, + mentionsColor = ChatTheme.colors.primaryAccent, ) } @@ -72,6 +78,8 @@ internal fun buildAnnotatedMessageText( textColor: Color, textFontStyle: FontStyle?, linkColor: Color, + mentionsColor: Color, + mentionedUserNames: List = emptyList(), builder: (AnnotatedString.Builder).() -> Unit = {}, ): AnnotatedString { return buildAnnotatedString { @@ -103,6 +111,11 @@ internal fun buildAnnotatedMessageText( schemes = EMAIL_SCHEMES, linkColor = linkColor, ) + tagUser( + text = text, + mentionsColor = mentionsColor, + mentionedUserNames = mentionedUserNames, + ) // Finally, we apply any additional styling that was passed in. builder(this) @@ -170,6 +183,35 @@ private fun AnnotatedString.Builder.linkify( } } +private fun AnnotatedString.Builder.tagUser( + text: String, + mentionsColor: Color, + mentionedUserNames: List, +) { + mentionedUserNames.forEach { userName -> + val start = text.indexOf(userName) + val end = start + userName.length + + if (start < 0) return@forEach + + addStyle( + style = SpanStyle( + color = mentionsColor, + fontWeight = FontWeight.Bold, + ), + start = start - 1, // -1 to include the @ symbol + end = end, + ) + + addStringAnnotation( + tag = AnnotationTagMention, + annotation = userName, + start = start - 1, // -1 to include the @ symbol + end = end, + ) + } +} + internal fun String.ensureLowercaseScheme(schemes: List): String = schemes.fold(this) { acc, scheme -> acc.replace(scheme, scheme.lowercase(), ignoreCase = true) diff --git a/stream-chat-android-ui-common/api/stream-chat-android-ui-common.api b/stream-chat-android-ui-common/api/stream-chat-android-ui-common.api index 207f61da4a9..3ba493817f7 100644 --- a/stream-chat-android-ui-common/api/stream-chat-android-ui-common.api +++ b/stream-chat-android-ui-common/api/stream-chat-android-ui-common.api @@ -1703,6 +1703,7 @@ public final class io/getstream/chat/android/ui/common/utils/extensions/MessageK public final class io/getstream/chat/android/ui/common/utils/extensions/UserKt { public static final fun getInitials (Lio/getstream/chat/android/models/User;)Ljava/lang/String; + public static final fun getUserByNameOrId (Ljava/util/List;Ljava/lang/String;)Lio/getstream/chat/android/models/User; } public abstract interface class io/getstream/chat/android/ui/common/utils/typing/TypingUpdatesBuffer { diff --git a/stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/utils/extensions/User.kt b/stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/utils/extensions/User.kt index 0e8819f7d1c..506b481dc12 100644 --- a/stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/utils/extensions/User.kt +++ b/stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/utils/extensions/User.kt @@ -20,3 +20,7 @@ import io.getstream.chat.android.models.User public val User.initials: String get() = name.initials() + +public fun List.getUserByNameOrId(nameOrId: String): User? { + return firstOrNull { it.name == nameOrId } ?: firstOrNull { it.id == nameOrId } +} diff --git a/stream-chat-android-ui-uitests/src/androidTest/java/io/getstream/chat/android/uitests/snapshot/compose/messages/MessageItemTest.kt b/stream-chat-android-ui-uitests/src/androidTest/java/io/getstream/chat/android/uitests/snapshot/compose/messages/MessageItemTest.kt index 027b0ada910..7a58d336593 100644 --- a/stream-chat-android-ui-uitests/src/androidTest/java/io/getstream/chat/android/uitests/snapshot/compose/messages/MessageItemTest.kt +++ b/stream-chat-android-ui-uitests/src/androidTest/java/io/getstream/chat/android/uitests/snapshot/compose/messages/MessageItemTest.kt @@ -64,4 +64,30 @@ class MessageItemTest : ComposeScreenshotTest() { reactionSorting = ReactionSortingByCount, ) } + + @Test + fun messageItemForUserMentions() = runScreenshotTest { + MessageItem( + messageItem = MessageItemState( + message = TestData.message5(), + isMine = true, + showMessageFooter = true, + ), + onLongItemClick = {}, + reactionSorting = ReactionSortingByCount, + ) + } + + @Test + fun messageItemForUserMentionsWithoutUsername() = runScreenshotTest { + MessageItem( + messageItem = MessageItemState( + message = TestData.message6(), + isMine = true, + showMessageFooter = true, + ), + onLongItemClick = {}, + reactionSorting = ReactionSortingByCount, + ) + } } diff --git a/stream-chat-android-ui-uitests/src/androidTest/java/io/getstream/chat/android/uitests/util/TestData.kt b/stream-chat-android-ui-uitests/src/androidTest/java/io/getstream/chat/android/uitests/util/TestData.kt index 3e8ecf2e41d..01c2072feef 100644 --- a/stream-chat-android-ui-uitests/src/androidTest/java/io/getstream/chat/android/uitests/util/TestData.kt +++ b/stream-chat-android-ui-uitests/src/androidTest/java/io/getstream/chat/android/uitests/util/TestData.kt @@ -64,6 +64,11 @@ object TestData { image = FakeImageLoader.AVATAR_BELAL, ) + fun user6(): User = User().copy( + id = "jake", + image = FakeImageLoader.AVATAR_AMIT, + ) + fun member1() = Member( user = user1(), isInvited = false, @@ -121,6 +126,24 @@ object TestData { user = user4(), ) + fun message5(): Message = Message().copy( + id = "message5", + text = "tagged @Belal Khan ", + createdAt = date1(), + type = MessageType.REGULAR, + user = user4(), + mentionedUsers = listOf(user5()), + ) + + fun message6(): Message = Message().copy( + id = "message6", + text = "tagged @jake ", + createdAt = date1(), + type = MessageType.REGULAR, + user = user5(), + mentionedUsers = listOf(user6()), + ) + fun channel1() = Channel().copy( type = "messaging", id = "channel1",