@@ -49,8 +49,8 @@ import io.getstream.chat.android.compose.R
4949import io.getstream.chat.android.compose.ui.components.composer.MessageInput
5050import io.getstream.chat.android.compose.ui.messages.composer.actions.AudioRecordingActions
5151import io.getstream.chat.android.compose.ui.messages.composer.internal.suggestions.CommandSuggestionList
52+ import io.getstream.chat.android.compose.ui.messages.composer.internal.suggestions.MentionSuggestionList
5253import io.getstream.chat.android.compose.ui.messages.composer.internal.suggestions.SuggestionsMenu
53- import io.getstream.chat.android.compose.ui.messages.composer.internal.suggestions.UserSuggestionList
5454import io.getstream.chat.android.compose.ui.theme.ChatTheme
5555import io.getstream.chat.android.compose.ui.theme.ComposerConfig
5656import io.getstream.chat.android.compose.ui.theme.LocalChatUiConfig
@@ -73,6 +73,7 @@ import io.getstream.chat.android.models.Message
7373import io.getstream.chat.android.models.User
7474import io.getstream.chat.android.previewdata.PreviewCommandData
7575import io.getstream.chat.android.previewdata.PreviewUserData
76+ import io.getstream.chat.android.ui.common.feature.messages.composer.mention.Mention
7677import io.getstream.chat.android.ui.common.state.messages.Edit
7778import io.getstream.chat.android.ui.common.state.messages.Reply
7879import io.getstream.chat.android.ui.common.state.messages.composer.MessageComposerState
@@ -99,14 +100,20 @@ import io.getstream.chat.android.ui.common.utils.MediaStringUtil
99100 * @param onCancelAction Handler for the cancel button on Message actions, such as Edit and Reply.
100101 * @param onLinkPreviewClick Handler when the user taps on a link preview.
101102 * @param onCancelLinkPreviewClick Handler when the user taps on the cancel link preview.
102- * @param onUserSelected Handler when the user taps on a user suggestion item.
103+ * @param onUserSelected Legacy handler that fires only when the user taps a user suggestion item.
104+ * Kept for backward compatibility; new callers should use [onMentionSelected], which receives every
105+ * mention type including users. Note both callbacks fire on a user tap: a custom [onUserSelected]
106+ * runs in addition to [onMentionSelected], so the default [onMentionSelected] still inserts the
107+ * mention. To replace the default selection behavior, override [onMentionSelected].
103108 * @param onCommandSelected Handler for every tap on a command suggestion item, including taps on
104109 * disabled items. The default emits [MessageComposerViewEvent.CommandUnavailable] on
105110 * [MessageComposerViewModel.events] when the command is not available for the current action;
106111 * overrides replace that behavior and become responsible for their own filtering and feedback.
107112 * @param onAlsoSendToChannelChange Handler when the "Also send to channel" checkbox is changed.
108113 * @param onActiveCommandDismiss Called when the user taps the dismiss button on the active command chip.
109114 * @param recordingActions The actions that can be performed on an audio recording.
115+ * @param onMentionSelected Handler when the user taps any mention suggestion item. This is the
116+ * canonical callback for all mention selections.
110117 * @param input Customizable composable that represents the input field for the composer, [MessageInput] by default.
111118 */
112119@Composable
@@ -124,14 +131,15 @@ public fun MessageComposer(
124131 onCancelAction : () -> Unit = { viewModel.dismissMessageActions() },
125132 onLinkPreviewClick : ((LinkPreview ) -> Unit )? = null,
126133 onCancelLinkPreviewClick : (() -> Unit )? = { viewModel.cancelLinkPreview() },
127- onUserSelected : (User ) -> Unit = { viewModel.selectMention(it) },
134+ onUserSelected : (User ) -> Unit = {},
128135 onCommandSelected : (Command ) -> Unit = viewModel : :selectCommand,
129136 onAlsoSendToChannelChange : (Boolean ) -> Unit = viewModel : :setAlsoSendToChannel,
130137 onActiveCommandDismiss : () -> Unit = viewModel : :clearActiveCommand,
131138 recordingActions : AudioRecordingActions = AudioRecordingActions .defaultActions(
132139 viewModel = viewModel,
133140 sendOnComplete = ChatTheme .config.composer.audioRecordingSendOnComplete,
134141 ),
142+ onMentionSelected : (Mention ) -> Unit = viewModel : :selectMention,
135143 input : @Composable RowScope .(MessageComposerState ) -> Unit = { state ->
136144 val inputFocusRequester = remember { FocusRequester () }
137145 LaunchedEffect (Unit ) {
@@ -189,6 +197,7 @@ public fun MessageComposer(
189197 onSendMessage(messageWithData)
190198 },
191199 onUserSelected = onUserSelected,
200+ onMentionSelected = onMentionSelected,
192201 onCommandSelected = onCommandSelected,
193202 onAlsoSendToChannelSelected = onAlsoSendToChannelChange,
194203 onActiveCommandDismiss = onActiveCommandDismiss,
@@ -235,9 +244,16 @@ internal val LocalMessageComposerSnackbarHostState =
235244 * @param onCancelAction Handler for the cancel button on Message actions, such as Edit and Reply.
236245 * @param onLinkPreviewClick Handler when the user taps on a link preview.
237246 * @param onCancelLinkPreviewClick Handler when the user taps on the cancel link preview.
238- * @param onUserSelected Handler when the user taps on a user suggestion item.
247+ * @param onUserSelected Legacy handler that fires only when the user taps a user suggestion item.
248+ * Kept for backward compatibility; new callers should use [onMentionSelected], which receives every
249+ * mention type including users. Note both callbacks fire on a user tap: a custom [onUserSelected]
250+ * runs in addition to [onMentionSelected], so the default [onMentionSelected] still inserts the
251+ * mention. To replace the default selection behavior, override [onMentionSelected].
239252 * @param onCommandSelected Handler when the user taps on a command suggestion item, including taps
240253 * on disabled items.
254+ * @param onMentionSelected Handler when the user taps any mention suggestion item. Canonical
255+ * callback for all mention selections. Must be wired when any non-user mention kind is enabled —
256+ * otherwise non-user mentions silently no-op on tap.
241257 * @param onAlsoSendToChannelChange Handler when the "Also send to channel" checkbox is changed.
242258 * @param onActiveCommandDismiss Called when the user taps the dismiss button on the active command chip.
243259 * @param recordingActions The actions that can be performed on an audio recording.
@@ -264,6 +280,7 @@ public fun MessageComposer(
264280 onAlsoSendToChannelChange : (Boolean ) -> Unit = {},
265281 onActiveCommandDismiss : () -> Unit = {},
266282 recordingActions : AudioRecordingActions = AudioRecordingActions .None ,
283+ onMentionSelected : (Mention ) -> Unit = {},
267284 input : @Composable RowScope .(MessageComposerState ) -> Unit = { state ->
268285 ChatTheme .componentFactory.MessageComposerInput (
269286 params = MessageComposerInputParams (
@@ -285,7 +302,13 @@ public fun MessageComposer(
285302 },
286303) {
287304 val validationErrors = messageComposerState.validationErrors
288- val userSuggestions = messageComposerState.mentionSuggestions
305+ // Prefer the list with all mentions but fall back to the legacy user-only field for
306+ // callers that construct MessageComposerState directly without going through the controller.
307+ val suggestedMentions = messageComposerState.suggestedMentions
308+ val legacyMentionSuggestions = messageComposerState.mentionSuggestions
309+ val mentionSuggestions = remember(suggestedMentions, legacyMentionSuggestions) {
310+ suggestedMentions.ifEmpty { legacyMentionSuggestions.map(Mention ::User ) }
311+ }
289312 val commandSuggestions = messageComposerState.commandSuggestions
290313 val snackbarHostState = LocalMessageComposerSnackbarHostState .current ? : remember { SnackbarHostState () }
291314
@@ -295,12 +318,13 @@ public fun MessageComposer(
295318 )
296319
297320 MessageComposerSurface (modifier = modifier) {
298- if (userSuggestions .isNotEmpty()) {
299- SuggestionsMenu (contentMaxHeight = UserSuggestionsMaxHeight ) {
300- UserSuggestionList (
301- users = userSuggestions ,
321+ if (mentionSuggestions .isNotEmpty()) {
322+ SuggestionsMenu (contentMaxHeight = MentionSuggestionsMaxHeight ) {
323+ MentionSuggestionList (
324+ mentions = mentionSuggestions ,
302325 currentUser = messageComposerState.currentUser,
303326 onUserSelected = onUserSelected,
327+ onMentionSelected = onMentionSelected,
304328 )
305329 }
306330 }
@@ -358,7 +382,7 @@ public fun MessageComposer(
358382 }
359383}
360384
361- private val UserSuggestionsMaxHeight = 176 .dp
385+ private val MentionSuggestionsMaxHeight = 176 .dp
362386private val CommandSuggestionsMaxHeight = 208 .dp
363387
364388@StringRes
@@ -368,18 +392,21 @@ private fun MessageComposerViewEvent.messageResOrNull(): Int? = when (this) {
368392 is Reply -> R .string.stream_compose_message_composer_command_unavailable_in_reply
369393 else -> null
370394 }
395+
371396 is MessageComposerViewEvent .CancelCommandRequired -> when (action) {
372397 is Edit -> R .string.stream_compose_message_composer_cancel_command_to_edit
373398 is Reply -> R .string.stream_compose_message_composer_cancel_command_to_reply
374399 else -> null
375400 }
401+
376402 else -> null
377403}
378404
379405private fun MessageComposerViewEvent.snackbarVariant (): StreamSnackbarVariant = when (this ) {
380406 is MessageComposerViewEvent .CommandUnavailable ,
381407 is MessageComposerViewEvent .CancelCommandRequired ,
382408 -> StreamSnackbarVariant .Error
409+
383410 else -> StreamSnackbarVariant .Default
384411}
385412
@@ -516,6 +543,14 @@ internal fun MessageComposerFixedStyleWithUserSuggestions() {
516543 PreviewUserData .user1,
517544 PreviewUserData .user5,
518545 ),
546+ suggestedMentions = listOf (
547+ Mention .Channel ,
548+ Mention .Here ,
549+ Mention .User (PreviewUserData .userWithOnlineStatus),
550+ Mention .User (PreviewUserData .user1),
551+ Mention .User (PreviewUserData .user5),
552+ Mention .Role (" admin" ),
553+ ),
519554 ),
520555 onSendMessage = { _, _ -> },
521556 )
@@ -622,6 +657,14 @@ internal fun MessageComposerFloatingStyleWithUserSuggestions() {
622657 PreviewUserData .user1,
623658 PreviewUserData .user5,
624659 ),
660+ suggestedMentions = listOf (
661+ Mention .Channel ,
662+ Mention .Here ,
663+ Mention .User (PreviewUserData .userWithOnlineStatus),
664+ Mention .User (PreviewUserData .user1),
665+ Mention .User (PreviewUserData .user5),
666+ Mention .Role (" admin" ),
667+ ),
625668 ),
626669 onSendMessage = { _, _ -> },
627670 )
0 commit comments