diff --git a/app/src/main/java/chat/revolt/api/internals/Members.kt b/app/src/main/java/chat/revolt/api/internals/Members.kt index 1e2a6f0b..35903094 100644 --- a/app/src/main/java/chat/revolt/api/internals/Members.kt +++ b/app/src/main/java/chat/revolt/api/internals/Members.kt @@ -38,4 +38,10 @@ class Members { member.nickname?.let { userId to member.nickname } }?.toMap() ?: emptyMap() } + + fun filterNamesFor(serverId: String, query: String): List { + return memberCache[serverId]?.values?.filter { member -> + member.nickname?.contains(query, ignoreCase = true) ?: false + } ?: emptyList() + } } diff --git a/app/src/main/java/chat/revolt/components/chat/NativeMessageField.kt b/app/src/main/java/chat/revolt/components/chat/NativeMessageField.kt index 42eb3de2..482e0b53 100644 --- a/app/src/main/java/chat/revolt/components/chat/NativeMessageField.kt +++ b/app/src/main/java/chat/revolt/components/chat/NativeMessageField.kt @@ -79,6 +79,8 @@ import chat.revolt.api.REVOLT_FILES import chat.revolt.api.schemas.ChannelType import chat.revolt.api.schemas.Member import chat.revolt.components.generic.RemoteImage +import chat.revolt.components.generic.UserAvatar +import chat.revolt.components.screens.chat.ChannelIcon import chat.revolt.internals.Autocomplete import kotlinx.coroutines.launch @@ -155,9 +157,9 @@ fun NativeMessageField( modifier: Modifier = Modifier, forceSendButton: Boolean = false, disabled: Boolean = false, - editMode: Boolean = false, serverId: String? = null, channelId: String? = null, + editMode: Boolean = false, cancelEdit: () -> Unit = {}, onFocusChange: (Boolean) -> Unit = {}, onSelectionChange: (Pair) -> Unit = {} @@ -247,10 +249,15 @@ fun NativeMessageField( }, label = { Text("@${item.user.username}#${item.user.discriminator}") }, icon = { - Icon( - painter = painterResource(R.drawable.ic_human_greeting_variant_24dp), - contentDescription = null, - modifier = Modifier.size(SuggestionChipDefaults.IconSize) + UserAvatar( + username = item.user.username + ?: stringResource(R.string.unknown), + userId = item.user.id ?: "", + avatar = item.user.avatar, + rawUrl = item.member?.avatar?.id?.let { + "$REVOLT_FILES/avatars/$it?max_side=64" + }, + size = SuggestionChipDefaults.IconSize, ) }, modifier = Modifier @@ -270,11 +277,7 @@ fun NativeMessageField( }, label = { Text("#${item.channel.name}") }, icon = { - Icon( - painter = painterResource(R.drawable.ic_pound_24dp), - contentDescription = null, - modifier = Modifier.size(SuggestionChipDefaults.IconSize) - ) + item.channel.channelType?.let { type -> ChannelIcon(channelType = type) } }, modifier = Modifier .animateItemPlacement() @@ -360,6 +363,8 @@ fun NativeMessageField( AndroidView( factory = { context -> object : androidx.appcompat.widget.AppCompatEditText(context) { + var serverId: String? = null + override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection? { var ic = super.onCreateInputConnection(outAttrs) EditorInfoCompat.setContentMimeTypes( @@ -402,6 +407,17 @@ fun NativeMessageField( Autocomplete.emoji(lastWord.substring(1)) ) } + + lastWord.startsWith('@') -> { + if (channelId == null) return + autocompleteSuggestions.addAll( + Autocomplete.user( + channelId, + this.serverId, + lastWord.substring(1) + ) + ) + } } } }.apply { @@ -489,6 +505,7 @@ fun NativeMessageField( it.setSelection(value.length) } it.hint = it.context.getString(placeholderResource, channelName) + it.serverId = serverId }, modifier = Modifier .weight(1f) diff --git a/app/src/main/java/chat/revolt/internals/Autocomplete.kt b/app/src/main/java/chat/revolt/internals/Autocomplete.kt index 66f92a71..418552e7 100644 --- a/app/src/main/java/chat/revolt/internals/Autocomplete.kt +++ b/app/src/main/java/chat/revolt/internals/Autocomplete.kt @@ -1,6 +1,7 @@ package chat.revolt.internals import chat.revolt.api.RevoltAPI +import chat.revolt.api.schemas.ChannelType import chat.revolt.components.chat.AutocompleteSuggestion object Autocomplete { @@ -33,4 +34,104 @@ object Autocomplete { return (unicodeResults + customResults) } + + fun user( + channelId: String, + serverId: String? = null, + query: String + ): List { + val channel = RevoltAPI.channelCache[channelId] ?: return emptyList() + + return when (channel.channelType) { + ChannelType.DirectMessage -> { + val otherUser = channel.recipients?.find { it != RevoltAPI.selfId } + if (otherUser != null) { + val user = RevoltAPI.userCache[otherUser] + if (user != null && user.username?.contains(query) == true) { + listOf( + AutocompleteSuggestion.User( + user, + null, + query + ) + ) + } else { + emptyList() + } + } else { + emptyList() + } + } + + ChannelType.Group -> { + val users = + channel.recipients?.mapNotNull { RevoltAPI.userCache[it] } ?: emptyList() + users + .filter { it.username?.contains(query) ?: false } + .map { + AutocompleteSuggestion.User( + it, + null, + query + ) + } + } + + ChannelType.SavedMessages -> { + val user = RevoltAPI.userCache[RevoltAPI.selfId] + return if (user != null && user.username?.contains(query) == true) { + listOf( + AutocompleteSuggestion.User( + user, + null, + query + ) + ) + } else { + emptyList() + } + } + + ChannelType.TextChannel, ChannelType.VoiceChannel -> { + if (serverId == null) return emptyList() + if (query.length < 2) return emptyList() + + val byNickname = RevoltAPI.members.filterNamesFor(serverId, query) + .map { m -> m to RevoltAPI.userCache[m.id?.user] }.filter { (_, u) -> + u != null + }.map { (m, u) -> + m to u!! + } + val byUsername = RevoltAPI.userCache.values.filter { + it.username?.contains( + query, + ignoreCase = true + ) ?: false + }.mapNotNull { + it.id?.let { _ -> + RevoltAPI.members.getMember( + serverId, + it.id + ) to it + } + }.filter { (member, _) -> + member != null + }.map { (member, user) -> + member!! to user + } + + val all = (byNickname + byUsername).distinctBy { it.first.id } + + all.map { + AutocompleteSuggestion.User( + it.second, + it.first, + query + ) + } + } + + null -> emptyList() + } + } } \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreen.kt b/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreen.kt index 52bab3c1..d9fbabcd 100644 --- a/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreen.kt +++ b/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreen.kt @@ -519,6 +519,8 @@ fun ChannelScreen( ), forceSendButton = viewModel.pendingAttachments.isNotEmpty(), disabled = viewModel.pendingAttachments.isNotEmpty() && viewModel.isSendingMessage, + channelId = channelId, + serverId = channel?.server, editMode = viewModel.editingMessage != null, cancelEdit = viewModel::cancelEditingMessage, onFocusChange = { nowFocused ->