From d8e28d7be95da206a0fabae58ab52e828ae38a35 Mon Sep 17 00:00:00 2001 From: Maxime Naturel Date: Thu, 10 Feb 2022 17:22:29 +0100 Subject: [PATCH] Adding autocomplete for @room (missing correct first letter of avatar) --- .../matrix/android/sdk/api/util/MatrixItem.kt | 43 +++++++++++++------ .../member/AutocompleteMemberController.kt | 37 ++++++++++++++-- .../member/AutocompleteMemberPresenter.kt | 21 +++++++-- .../home/room/detail/AutoCompleter.kt | 5 ++- vector/src/main/res/values/strings.xml | 3 ++ 5 files changed, 87 insertions(+), 22 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/MatrixItem.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/MatrixItem.kt index 31dcd007a2..9ad1e42513 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/MatrixItem.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/MatrixItem.kt @@ -27,7 +27,6 @@ import org.matrix.android.sdk.api.session.room.sender.SenderInfo import org.matrix.android.sdk.api.session.user.model.User import java.util.Locale -// TODO how to represent the notify everyone @room item? EveryoneItem ?? sealed class MatrixItem( open val id: String, open val displayName: String?, @@ -36,7 +35,18 @@ sealed class MatrixItem( data class UserItem(override val id: String, override val displayName: String? = null, override val avatarUrl: String? = null) : - MatrixItem(id, displayName?.removeSuffix(ircPattern), avatarUrl) { + MatrixItem(id, displayName?.removeSuffix(IRC_PATTERN), avatarUrl) { + init { + if (BuildConfig.DEBUG) checkId() + } + + override fun updateAvatar(newAvatar: String?) = copy(avatarUrl = newAvatar) + } + + data class EveryoneInRoomItem(override val id: String, + override val displayName: String? = null, + override val avatarUrl: String? = null) : + MatrixItem(id, displayName, avatarUrl) { init { if (BuildConfig.DEBUG) checkId() } @@ -47,7 +57,7 @@ sealed class MatrixItem( data class EventItem(override val id: String, override val displayName: String? = null, override val avatarUrl: String? = null) : - MatrixItem(id, displayName, avatarUrl) { + MatrixItem(id, displayName, avatarUrl) { init { if (BuildConfig.DEBUG) checkId() } @@ -58,7 +68,7 @@ sealed class MatrixItem( data class RoomItem(override val id: String, override val displayName: String? = null, override val avatarUrl: String? = null) : - MatrixItem(id, displayName, avatarUrl) { + MatrixItem(id, displayName, avatarUrl) { init { if (BuildConfig.DEBUG) checkId() } @@ -69,7 +79,7 @@ sealed class MatrixItem( data class SpaceItem(override val id: String, override val displayName: String? = null, override val avatarUrl: String? = null) : - MatrixItem(id, displayName, avatarUrl) { + MatrixItem(id, displayName, avatarUrl) { init { if (BuildConfig.DEBUG) checkId() } @@ -80,7 +90,7 @@ sealed class MatrixItem( data class RoomAliasItem(override val id: String, override val displayName: String? = null, override val avatarUrl: String? = null) : - MatrixItem(id, displayName, avatarUrl) { + MatrixItem(id, displayName, avatarUrl) { init { if (BuildConfig.DEBUG) checkId() } @@ -91,7 +101,7 @@ sealed class MatrixItem( data class GroupItem(override val id: String, override val displayName: String? = null, override val avatarUrl: String? = null) : - MatrixItem(id, displayName, avatarUrl) { + MatrixItem(id, displayName, avatarUrl) { init { if (BuildConfig.DEBUG) checkId() } @@ -110,16 +120,18 @@ sealed class MatrixItem( /** * Return the prefix as defined in the matrix spec (and not extracted from the id) */ - fun getIdPrefix() = when (this) { - is UserItem -> '@' - is EventItem -> '$' + private fun getIdPrefix() = when (this) { + is UserItem -> '@' + is EventItem -> '$' is SpaceItem, - is RoomItem -> '!' - is RoomAliasItem -> '#' - is GroupItem -> '+' + is RoomItem, + is EveryoneInRoomItem -> '!' + is RoomAliasItem -> '#' + is GroupItem -> '+' } fun firstLetterOfDisplayName(): String { + // TODO retrieve first letter of room name when EveryoneInRoomItem return (displayName?.takeIf { it.isNotBlank() } ?: id) .let { dn -> var startIndex = 0 @@ -152,7 +164,8 @@ sealed class MatrixItem( } companion object { - private const val ircPattern = " (IRC)" + private const val IRC_PATTERN = " (IRC)" + const val NOTIFY_EVERYONE = "@room" } } @@ -172,6 +185,8 @@ fun RoomSummary.toMatrixItem() = if (roomType == RoomType.SPACE) { fun RoomSummary.toRoomAliasMatrixItem() = MatrixItem.RoomAliasItem(canonicalAlias ?: roomId, displayName, avatarUrl) +fun RoomSummary.toEveryoneInRoomMatrixItem() = MatrixItem.EveryoneInRoomItem(roomId, MatrixItem.NOTIFY_EVERYONE, avatarUrl) + // If no name is available, use room alias as Riot-Web does fun PublicRoom.toMatrixItem() = MatrixItem.RoomItem(roomId, name ?: getPrimaryAlias() ?: "", avatarUrl) diff --git a/vector/src/main/java/im/vector/app/features/autocomplete/member/AutocompleteMemberController.kt b/vector/src/main/java/im/vector/app/features/autocomplete/member/AutocompleteMemberController.kt index 5bbd4a3996..0684067ba8 100644 --- a/vector/src/main/java/im/vector/app/features/autocomplete/member/AutocompleteMemberController.kt +++ b/vector/src/main/java/im/vector/app/features/autocomplete/member/AutocompleteMemberController.kt @@ -16,19 +16,35 @@ package im.vector.app.features.autocomplete.member +import android.content.Context import com.airbnb.epoxy.TypedEpoxyController +import im.vector.app.R import im.vector.app.features.autocomplete.AutocompleteClickListener import im.vector.app.features.autocomplete.autocompleteMatrixItem import im.vector.app.features.home.AvatarRenderer +import org.matrix.android.sdk.api.util.toEveryoneInRoomMatrixItem import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject -class AutocompleteMemberController @Inject constructor() : TypedEpoxyController>() { +class AutocompleteMemberController @Inject constructor(private val context: Context) + : TypedEpoxyController>() { + + /////////////////////////////////////////////////////////////////////////// + // FIELDS + /////////////////////////////////////////////////////////////////////////// var listener: AutocompleteClickListener? = null + /////////////////////////////////////////////////////////////////////////// + // DEPENDENCIES + /////////////////////////////////////////////////////////////////////////// + @Inject lateinit var avatarRenderer: AvatarRenderer + /////////////////////////////////////////////////////////////////////////// + // SPECIALIZATION + /////////////////////////////////////////////////////////////////////////// + override fun buildModels(data: List?) { if (data.isNullOrEmpty()) { return @@ -46,17 +62,30 @@ class AutocompleteMemberController @Inject constructor() : TypedEpoxyController< /////////////////////////////////////////////////////////////////////////// private fun buildRoomMemberItem(roomMember: AutocompleteMemberItem.RoomMember) { + val host = this autocompleteMatrixItem { roomMember.roomMemberSummary.let { user -> id(user.userId) matrixItem(user.toMatrixItem()) - avatarRenderer(this@AutocompleteMemberController.avatarRenderer) - clickListener { this@AutocompleteMemberController.listener?.onItemClick(roomMember) } + avatarRenderer(host.avatarRenderer) + clickListener { host.listener?.onItemClick(roomMember) } } } } private fun buildEveryoneItem(everyone: AutocompleteMemberItem.Everyone) { - // TODO + val host = this + + autocompleteMatrixItem { + everyone.roomSummary.let { room -> + id(room.roomId) + matrixItem(room.toEveryoneInRoomMatrixItem()) + subName(host.context.getString(R.string.room_message_notify_everyone)) + // TODO fix usage of first letter of room name when avatarUrl is empty + // TODO test avatar with a room which has a picture + avatarRenderer(host.avatarRenderer) + clickListener { host.listener?.onItemClick(everyone) } + } + } } } diff --git a/vector/src/main/java/im/vector/app/features/autocomplete/member/AutocompleteMemberPresenter.kt b/vector/src/main/java/im/vector/app/features/autocomplete/member/AutocompleteMemberPresenter.kt index bb7d41cc46..5640632ec2 100644 --- a/vector/src/main/java/im/vector/app/features/autocomplete/member/AutocompleteMemberPresenter.kt +++ b/vector/src/main/java/im/vector/app/features/autocomplete/member/AutocompleteMemberPresenter.kt @@ -28,6 +28,7 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary +import org.matrix.android.sdk.api.util.MatrixItem class AutocompleteMemberPresenter @AssistedInject constructor(context: Context, @Assisted val roomId: String, @@ -68,14 +69,28 @@ class AutocompleteMemberPresenter @AssistedInject constructor(context: Context, memberships = listOf(Membership.JOIN) excludeSelf = true } + val members = room.getRoomMembers(queryParams) .asSequence() .sortedBy { it.displayName } .disambiguate() + .map { AutocompleteMemberItem.RoomMember(it) } + .toList() + // TODO check if user can notify everyone => compare user role to room permission setting: PowerLevelsContent - // TODO if user can notify everyone, add entry AutocompleteMemberItem.Everyone - // TODO add header sections to separate members and notification - controller.setData(members.map { AutocompleteMemberItem.RoomMember(it) }.toList()) + val everyone = room.roomSummary() + ?.takeIf { query.isNullOrBlank() || MatrixItem.NOTIFY_EVERYONE.startsWith("@$query") } + ?.let { + AutocompleteMemberItem.Everyone(it) + } + + val items = mutableListOf().apply { + // TODO add header sections + addAll(members) + everyone?.let { add(it) } + } + + controller.setData(items) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/AutoCompleter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/AutoCompleter.kt index 88e49f7062..4bd38095a9 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/AutoCompleter.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/AutoCompleter.kt @@ -44,6 +44,7 @@ import im.vector.app.features.themes.ThemeUtils import org.matrix.android.sdk.api.session.group.model.GroupSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.util.MatrixItem +import org.matrix.android.sdk.api.util.toEveryoneInRoomMatrixItem import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.api.util.toRoomAliasMatrixItem @@ -135,7 +136,8 @@ class AutoCompleter @AssistedInject constructor( when (item) { is AutocompleteMemberItem.RoomMember -> insertMatrixItem(editText, editable, "@", item.roomMemberSummary.toMatrixItem()) - is AutocompleteMemberItem.Everyone -> Unit // TODO + is AutocompleteMemberItem.Everyone -> + insertMatrixItem(editText, editable, "@", item.roomSummary.toEveryoneInRoomMatrixItem()) } return true } @@ -253,6 +255,7 @@ class AutoCompleter @AssistedInject constructor( } companion object { + // TODO add consts for string trigger for autocomplete private const val ELEVATION = 6f } } diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index c155b6bb75..69a1c53d1c 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -3782,4 +3782,7 @@ Show less "%1$d more" + Notify the whole room + Users + Room notification