Skip to content

Commit dcb3e99

Browse files
committed
Add ui-components composer autocomplete for enhanced mentions
1 parent ab96b71 commit dcb3e99

20 files changed

Lines changed: 478 additions & 61 deletions

File tree

stream-chat-android-docs/src/main/kotlin/io/getstream/chat/docs/kotlin/ui/messages/MessageComposer.kt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ private object MessageComposer : Fragment() {
132132
messageComposerView.attachmentRemovalListener = { attachment ->
133133
// Handle attachment removal
134134
}
135-
messageComposerView.mentionSelectionListener = { user ->
135+
messageComposerView.suggestedMentionSelectionListener = { mention ->
136136
// Handle mention selection
137137
}
138138
messageComposerView.commandSelectionListener = { command ->
@@ -198,8 +198,8 @@ private object MessageComposer : Fragment() {
198198
messageComposerView.attachmentRemovalListener = { attachment ->
199199
messageComposerViewModel.removeAttachment(attachment)
200200
}
201-
messageComposerView.mentionSelectionListener = { user ->
202-
messageComposerViewModel.selectMention(user)
201+
messageComposerView.suggestedMentionSelectionListener = { mention ->
202+
messageComposerViewModel.selectMention(mention)
203203
}
204204
messageComposerView.commandSelectionListener = { command ->
205205
messageComposerViewModel.selectCommand(command)
@@ -279,7 +279,8 @@ private object MessageComposer : Fragment() {
279279
)
280280
messageComposerView.setMentionSuggestionsContent(
281281
DefaultMessageComposerMentionSuggestionsContent(context).also {
282-
it.mentionSelectionListener = { user -> messageComposerView.mentionSelectionListener(user) }
282+
it.suggestedMentionSelectionListener =
283+
{ mention -> messageComposerView.suggestedMentionSelectionListener(mention) }
283284
}
284285
)
285286
}
@@ -323,7 +324,7 @@ private object MessageComposer : Fragment() {
323324
)
324325
messageComposerView.setMentionSuggestionsContent(
325326
DefaultMessageComposerMentionSuggestionsContent(context).also {
326-
it.mentionSelectionListener = { user -> messageComposerViewModel.selectMention(user) }
327+
it.suggestedMentionSelectionListener = { mention -> messageComposerViewModel.selectMention(mention) }
327328
}
328329
)
329330
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
Copyright (c) 2014-2026 Stream.io Inc. All rights reserved.
4+
5+
Licensed under the Stream License;
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
https://github.com/GetStream/stream-chat-android/blob/main/LICENSE
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
-->
17+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
18+
android:width="20dp"
19+
android:height="20dp"
20+
android:viewportWidth="20"
21+
android:viewportHeight="20">
22+
<path
23+
android:strokeColor="#FF000000"
24+
android:strokeWidth="1.5"
25+
android:strokeLineCap="round"
26+
android:strokeLineJoin="round"
27+
android:pathData="M0.8 15.63C1.42 14.67 2.28 13.88 3.28 13.33 4.29 12.78 5.42 12.5 6.56 12.5 7.71 12.5 8.83 12.78 9.84 13.33 10.85 13.88 11.7 14.67 12.33 15.63M19.2 15.63C18.58 14.67 17.72 13.88 16.72 13.33 15.71 12.78 14.58 12.5 13.44 12.5 14.04 12.5 14.63 12.37 15.17 12.11 15.71 11.86 16.19 11.48 16.57 11.02 16.95 10.56 17.22 10.02 17.37 9.44 17.52 8.86 17.54 8.26 17.42 7.67 17.31 7.08 17.07 6.53 16.72 6.04 16.36 5.56 15.91 5.16 15.38 4.87 14.86 4.59 14.28 4.42 13.68 4.38 13.08 4.35 12.48 4.44 11.93 4.66M10.63 8.44C10.63 10.68 8.81 12.5 6.56 12.5 4.32 12.5 2.5 10.68 2.5 8.44 2.5 6.19 4.32 4.38 6.56 4.38 8.81 4.38 10.63 6.19 10.63 8.44Z"/>
28+
</vector>

stream-chat-android-ui-components/api/stream-chat-android-ui-components.api

Lines changed: 19 additions & 2 deletions
Large diffs are not rendered by default.

stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/MessageComposerView.kt

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import io.getstream.chat.android.models.ChannelCapabilities
3232
import io.getstream.chat.android.models.Command
3333
import io.getstream.chat.android.models.CreatePollParams
3434
import io.getstream.chat.android.models.User
35+
import io.getstream.chat.android.ui.common.feature.messages.composer.mention.Mention
3536
import io.getstream.chat.android.ui.common.state.messages.MessageMode
3637
import io.getstream.chat.android.ui.common.state.messages.composer.MessageComposerState
3738
import io.getstream.chat.android.ui.databinding.StreamUiMessageComposerBinding
@@ -116,10 +117,26 @@ public class MessageComposerView : ConstraintLayout {
116117
public var attachmentRemovalListener: (Attachment) -> Unit = {}
117118

118119
/**
119-
* Selection listener invoked when a mention suggestion item is selected.
120-
*/
120+
* Selection listener invoked when a user mention suggestion item is selected.
121+
*
122+
* Kept for backward compatibility; only fires for [Mention.User] rows. New code should
123+
* prefer [suggestedMentionSelectionListener], which fires for every mention type. Note both
124+
* listeners fire on a user tap: a custom [mentionSelectionListener] runs in addition to
125+
* [suggestedMentionSelectionListener], so the default selection still inserts the mention. To
126+
* replace the default selection behavior, override [suggestedMentionSelectionListener].
127+
*/
128+
@Deprecated(
129+
message = "Use suggestedMentionSelectionListener; it also fires for other mention types.",
130+
replaceWith = ReplaceWith("suggestedMentionSelectionListener"),
131+
level = DeprecationLevel.WARNING,
132+
)
121133
public var mentionSelectionListener: (User) -> Unit = {}
122134

135+
/**
136+
* Selection listener invoked when any mention suggestion item is selected.
137+
*/
138+
public var suggestedMentionSelectionListener: (Mention) -> Unit = {}
139+
123140
/**
124141
* Selection listener invoked when a command suggestion item is selected.
125142
*/
@@ -256,14 +273,15 @@ public class MessageComposerView : ConstraintLayout {
256273
/**
257274
* The current list of mention suggestions.
258275
*/
259-
private var mentionSuggestions: List<User>? = null
276+
private var mentionSuggestions: List<Mention>? = null
260277

261278
/**
262279
* Default implementation of [mentionSuggestionsContent].
263280
*/
264281
private val defaultMentionSuggestionsView: View by lazy {
265282
DefaultMessageComposerMentionSuggestionsContent(context).also {
266283
it.mentionSelectionListener = { user -> mentionSelectionListener(user) }
284+
it.suggestedMentionSelectionListener = { mention -> suggestedMentionSelectionListener(mention) }
267285
}.attachContext()
268286
}
269287

@@ -606,6 +624,9 @@ public class MessageComposerView : ConstraintLayout {
606624
if (contentView.mentionSelectionListener == null) {
607625
contentView.mentionSelectionListener = { mentionSelectionListener(it) }
608626
}
627+
if (contentView.suggestedMentionSelectionListener == null) {
628+
contentView.suggestedMentionSelectionListener = { suggestedMentionSelectionListener(it) }
629+
}
609630
}
610631
}
611632

@@ -643,12 +664,12 @@ public class MessageComposerView : ConstraintLayout {
643664
*/
644665
private fun renderSuggestion(state: MessageComposerState) {
645666
when {
646-
state.mentionSuggestions.isNotEmpty() -> renderMentionSuggestions(state)
667+
state.suggestedMentions.isNotEmpty() -> renderMentionSuggestions(state)
647668
state.commandSuggestions.isNotEmpty() -> renderCommandsSuggestions(state)
648669
else -> suggestionsPopup?.dismiss()
649670
}
650671
this.commandSuggestions = state.commandSuggestions
651-
this.mentionSuggestions = state.mentionSuggestions
672+
this.mentionSuggestions = state.suggestedMentions
652673
}
653674

654675
/**
@@ -683,7 +704,7 @@ public class MessageComposerView : ConstraintLayout {
683704
*/
684705
private fun renderMentionSuggestions(state: MessageComposerState) {
685706
// Do not do anything if the list hasn't changed
686-
if (this.mentionSuggestions == state.mentionSuggestions) return
707+
if (this.mentionSuggestions == state.suggestedMentions) return
687708
if (!messageComposerContext.style.messageInputMentionsHandlingEnabled) return
688709

689710
(mentionSuggestionsContent as? MessageComposerContent)?.renderState(state)

stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/content/DefaultMessageComposerLeadingContent.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ public open class DefaultMessageComposerLeadingContent : FrameLayout, MessageCom
130130
val hasAttachments = state.attachments.isNotEmpty()
131131
val hasCommandInput = state.inputValue.startsWith("/")
132132
val hasCommandSuggestions = state.commandSuggestions.isNotEmpty()
133-
val hasMentionSuggestions = state.mentionSuggestions.isNotEmpty()
133+
val hasMentionSuggestions = state.suggestedMentions.isNotEmpty()
134134
val isInEditMode = state.action is Edit
135135
val hasCommands = state.hasCommands
136136
val noRecording = state.recording is RecordingState.Idle

0 commit comments

Comments
 (0)