Autocompletion for room canonical alias
This commit is contained in:
parent
3a829bdfe8
commit
92f43a591a
|
@ -62,6 +62,9 @@ sealed class MatrixItem(
|
||||||
init {
|
init {
|
||||||
if (BuildConfig.DEBUG) checkId()
|
if (BuildConfig.DEBUG) checkId()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Best name is the id, and we keep the displayName of the room for the case we need the first letter
|
||||||
|
override fun getBestName() = id
|
||||||
}
|
}
|
||||||
|
|
||||||
data class GroupItem(override val id: String,
|
data class GroupItem(override val id: String,
|
||||||
|
@ -73,7 +76,7 @@ sealed class MatrixItem(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getBestName(): String {
|
open fun getBestName(): String {
|
||||||
return displayName?.takeIf { it.isNotBlank() } ?: id
|
return displayName?.takeIf { it.isNotBlank() } ?: id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +98,7 @@ sealed class MatrixItem(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun firstLetterOfDisplayName(): String {
|
fun firstLetterOfDisplayName(): String {
|
||||||
return getBestName()
|
return (displayName?.takeIf { it.isNotBlank() } ?: id)
|
||||||
.let { dn ->
|
.let { dn ->
|
||||||
var startIndex = 0
|
var startIndex = 0
|
||||||
val initial = dn[startIndex]
|
val initial = dn[startIndex]
|
||||||
|
@ -138,4 +141,5 @@ sealed class MatrixItem(
|
||||||
fun User.toMatrixItem() = MatrixItem.UserItem(userId, displayName, avatarUrl)
|
fun User.toMatrixItem() = MatrixItem.UserItem(userId, displayName, avatarUrl)
|
||||||
fun GroupSummary.toMatrixItem() = MatrixItem.GroupItem(groupId, displayName, avatarUrl)
|
fun GroupSummary.toMatrixItem() = MatrixItem.GroupItem(groupId, displayName, avatarUrl)
|
||||||
fun RoomSummary.toMatrixItem() = MatrixItem.RoomItem(roomId, displayName, avatarUrl)
|
fun RoomSummary.toMatrixItem() = MatrixItem.RoomItem(roomId, displayName, avatarUrl)
|
||||||
|
fun RoomSummary.toRoomAliasMatrixItem() = MatrixItem.RoomAliasItem(canonicalAlias ?: roomId, displayName, avatarUrl)
|
||||||
fun PublicRoom.toMatrixItem() = MatrixItem.RoomItem(roomId, name, avatarUrl)
|
fun PublicRoom.toMatrixItem() = MatrixItem.RoomItem(roomId, name, avatarUrl)
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package im.vector.riotx.features.autocomplete.user
|
package im.vector.riotx.features.autocomplete
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
|
@ -25,23 +25,27 @@ import im.vector.matrix.android.api.util.MatrixItem
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
||||||
import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
||||||
|
import im.vector.riotx.core.extensions.setTextOrHide
|
||||||
import im.vector.riotx.features.home.AvatarRenderer
|
import im.vector.riotx.features.home.AvatarRenderer
|
||||||
|
|
||||||
@EpoxyModelClass(layout = R.layout.item_autocomplete_user)
|
@EpoxyModelClass(layout = R.layout.item_autocomplete_matrix_item)
|
||||||
abstract class AutocompleteUserItem : VectorEpoxyModel<AutocompleteUserItem.Holder>() {
|
abstract class AutocompleteMatrixItem : VectorEpoxyModel<AutocompleteMatrixItem.Holder>() {
|
||||||
|
|
||||||
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
|
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
|
||||||
@EpoxyAttribute lateinit var matrixItem: MatrixItem
|
@EpoxyAttribute lateinit var matrixItem: MatrixItem
|
||||||
|
@EpoxyAttribute var subName: String? = null
|
||||||
@EpoxyAttribute var clickListener: View.OnClickListener? = null
|
@EpoxyAttribute var clickListener: View.OnClickListener? = null
|
||||||
|
|
||||||
override fun bind(holder: Holder) {
|
override fun bind(holder: Holder) {
|
||||||
holder.view.setOnClickListener(clickListener)
|
holder.view.setOnClickListener(clickListener)
|
||||||
holder.nameView.text = matrixItem.getBestName()
|
holder.nameView.text = matrixItem.getBestName()
|
||||||
|
holder.subNameView.setTextOrHide(subName)
|
||||||
avatarRenderer.render(matrixItem, holder.avatarImageView)
|
avatarRenderer.render(matrixItem, holder.avatarImageView)
|
||||||
}
|
}
|
||||||
|
|
||||||
class Holder : VectorEpoxyHolder() {
|
class Holder : VectorEpoxyHolder() {
|
||||||
val nameView by bind<TextView>(R.id.userAutocompleteName)
|
val nameView by bind<TextView>(R.id.matrixItemAutocompleteName)
|
||||||
val avatarImageView by bind<ImageView>(R.id.userAutocompleteAvatar)
|
val subNameView by bind<TextView>(R.id.matrixItemAutocompleteSubname)
|
||||||
|
val avatarImageView by bind<ImageView>(R.id.matrixItemAutocompleteAvatar)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.riotx.features.autocomplete.room
|
||||||
|
|
||||||
|
import com.airbnb.epoxy.TypedEpoxyController
|
||||||
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
|
import im.vector.matrix.android.api.util.toMatrixItem
|
||||||
|
import im.vector.riotx.features.autocomplete.AutocompleteClickListener
|
||||||
|
import im.vector.riotx.features.autocomplete.autocompleteMatrixItem
|
||||||
|
import im.vector.riotx.features.home.AvatarRenderer
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class AutocompleteRoomController @Inject constructor() : TypedEpoxyController<List<RoomSummary>>() {
|
||||||
|
|
||||||
|
var listener: AutocompleteClickListener<RoomSummary>? = null
|
||||||
|
|
||||||
|
@Inject lateinit var avatarRenderer: AvatarRenderer
|
||||||
|
|
||||||
|
override fun buildModels(data: List<RoomSummary>?) {
|
||||||
|
if (data.isNullOrEmpty()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data.forEach { roomSummary ->
|
||||||
|
autocompleteMatrixItem {
|
||||||
|
id(roomSummary.roomId)
|
||||||
|
matrixItem(roomSummary.toMatrixItem())
|
||||||
|
subName(roomSummary.canonicalAlias)
|
||||||
|
avatarRenderer(avatarRenderer)
|
||||||
|
clickListener { _ ->
|
||||||
|
listener?.onItemClick(roomSummary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.riotx.features.autocomplete.room
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.airbnb.mvrx.Async
|
||||||
|
import com.airbnb.mvrx.Success
|
||||||
|
import com.otaliastudios.autocomplete.RecyclerViewPresenter
|
||||||
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
|
import im.vector.riotx.features.autocomplete.AutocompleteClickListener
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class AutocompleteRoomPresenter @Inject constructor(context: Context,
|
||||||
|
private val controller: AutocompleteRoomController
|
||||||
|
) : RecyclerViewPresenter<RoomSummary>(context), AutocompleteClickListener<RoomSummary> {
|
||||||
|
|
||||||
|
var callback: Callback? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
controller.listener = this
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun instantiateAdapter(): RecyclerView.Adapter<*> {
|
||||||
|
return controller.adapter
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onItemClick(t: RoomSummary) {
|
||||||
|
dispatchClick(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onQuery(query: CharSequence?) {
|
||||||
|
callback?.onQueryRooms(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun render(rooms: Async<List<RoomSummary>>) {
|
||||||
|
if (rooms is Success) {
|
||||||
|
controller.setData(rooms())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Callback {
|
||||||
|
fun onQueryRooms(query: CharSequence?)
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ import com.airbnb.epoxy.TypedEpoxyController
|
||||||
import im.vector.matrix.android.api.session.user.model.User
|
import im.vector.matrix.android.api.session.user.model.User
|
||||||
import im.vector.matrix.android.api.util.toMatrixItem
|
import im.vector.matrix.android.api.util.toMatrixItem
|
||||||
import im.vector.riotx.features.autocomplete.AutocompleteClickListener
|
import im.vector.riotx.features.autocomplete.AutocompleteClickListener
|
||||||
|
import im.vector.riotx.features.autocomplete.autocompleteMatrixItem
|
||||||
import im.vector.riotx.features.home.AvatarRenderer
|
import im.vector.riotx.features.home.AvatarRenderer
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -34,7 +35,7 @@ class AutocompleteUserController @Inject constructor() : TypedEpoxyController<Li
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data.forEach { user ->
|
data.forEach { user ->
|
||||||
autocompleteUserItem {
|
autocompleteMatrixItem {
|
||||||
id(user.userId)
|
id(user.userId)
|
||||||
matrixItem(user.toMatrixItem())
|
matrixItem(user.toMatrixItem())
|
||||||
avatarRenderer(avatarRenderer)
|
avatarRenderer(avatarRenderer)
|
||||||
|
|
|
@ -60,6 +60,7 @@ import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.room.model.Membership
|
import im.vector.matrix.android.api.session.room.model.Membership
|
||||||
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
import im.vector.matrix.android.api.session.room.model.message.*
|
import im.vector.matrix.android.api.session.room.model.message.*
|
||||||
import im.vector.matrix.android.api.session.room.send.SendState
|
import im.vector.matrix.android.api.session.room.send.SendState
|
||||||
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
||||||
|
@ -68,6 +69,7 @@ import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent
|
||||||
import im.vector.matrix.android.api.session.user.model.User
|
import im.vector.matrix.android.api.session.user.model.User
|
||||||
import im.vector.matrix.android.api.util.MatrixItem
|
import im.vector.matrix.android.api.util.MatrixItem
|
||||||
import im.vector.matrix.android.api.util.toMatrixItem
|
import im.vector.matrix.android.api.util.toMatrixItem
|
||||||
|
import im.vector.matrix.android.api.util.toRoomAliasMatrixItem
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.dialogs.withColoredButton
|
import im.vector.riotx.core.dialogs.withColoredButton
|
||||||
import im.vector.riotx.core.epoxy.LayoutManagerStateRestorer
|
import im.vector.riotx.core.epoxy.LayoutManagerStateRestorer
|
||||||
|
@ -83,6 +85,7 @@ import im.vector.riotx.features.attachments.AttachmentsHelper
|
||||||
import im.vector.riotx.features.attachments.ContactAttachment
|
import im.vector.riotx.features.attachments.ContactAttachment
|
||||||
import im.vector.riotx.features.autocomplete.command.AutocompleteCommandPresenter
|
import im.vector.riotx.features.autocomplete.command.AutocompleteCommandPresenter
|
||||||
import im.vector.riotx.features.autocomplete.command.CommandAutocompletePolicy
|
import im.vector.riotx.features.autocomplete.command.CommandAutocompletePolicy
|
||||||
|
import im.vector.riotx.features.autocomplete.room.AutocompleteRoomPresenter
|
||||||
import im.vector.riotx.features.autocomplete.user.AutocompleteUserPresenter
|
import im.vector.riotx.features.autocomplete.user.AutocompleteUserPresenter
|
||||||
import im.vector.riotx.features.command.Command
|
import im.vector.riotx.features.command.Command
|
||||||
import im.vector.riotx.features.home.AvatarRenderer
|
import im.vector.riotx.features.home.AvatarRenderer
|
||||||
|
@ -140,6 +143,7 @@ class RoomDetailFragment @Inject constructor(
|
||||||
private val commandAutocompletePolicy: CommandAutocompletePolicy,
|
private val commandAutocompletePolicy: CommandAutocompletePolicy,
|
||||||
private val autocompleteCommandPresenter: AutocompleteCommandPresenter,
|
private val autocompleteCommandPresenter: AutocompleteCommandPresenter,
|
||||||
private val autocompleteUserPresenter: AutocompleteUserPresenter,
|
private val autocompleteUserPresenter: AutocompleteUserPresenter,
|
||||||
|
private val autocompleteRoomPresenter: AutocompleteRoomPresenter,
|
||||||
private val permalinkHandler: PermalinkHandler,
|
private val permalinkHandler: PermalinkHandler,
|
||||||
private val notificationDrawerManager: NotificationDrawerManager,
|
private val notificationDrawerManager: NotificationDrawerManager,
|
||||||
val roomDetailViewModelFactory: RoomDetailViewModel.Factory,
|
val roomDetailViewModelFactory: RoomDetailViewModel.Factory,
|
||||||
|
@ -150,6 +154,7 @@ class RoomDetailFragment @Inject constructor(
|
||||||
VectorBaseFragment(),
|
VectorBaseFragment(),
|
||||||
TimelineEventController.Callback,
|
TimelineEventController.Callback,
|
||||||
AutocompleteUserPresenter.Callback,
|
AutocompleteUserPresenter.Callback,
|
||||||
|
AutocompleteRoomPresenter.Callback,
|
||||||
VectorInviteView.Callback,
|
VectorInviteView.Callback,
|
||||||
JumpToReadMarkerView.Callback,
|
JumpToReadMarkerView.Callback,
|
||||||
AttachmentTypeSelectorView.Callback,
|
AttachmentTypeSelectorView.Callback,
|
||||||
|
@ -582,6 +587,52 @@ class RoomDetailFragment @Inject constructor(
|
||||||
})
|
})
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
autocompleteRoomPresenter.callback = this
|
||||||
|
Autocomplete.on<RoomSummary>(composerLayout.composerEditText)
|
||||||
|
.with(CharPolicy('#', true))
|
||||||
|
.with(autocompleteRoomPresenter)
|
||||||
|
.with(elevation)
|
||||||
|
.with(backgroundDrawable)
|
||||||
|
.with(object : AutocompleteCallback<RoomSummary> {
|
||||||
|
override fun onPopupItemClicked(editable: Editable, item: RoomSummary): Boolean {
|
||||||
|
// Detect last '#' and remove it
|
||||||
|
var startIndex = editable.lastIndexOf("#")
|
||||||
|
if (startIndex == -1) {
|
||||||
|
startIndex = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect next word separator
|
||||||
|
var endIndex = editable.indexOf(" ", startIndex)
|
||||||
|
if (endIndex == -1) {
|
||||||
|
endIndex = editable.length
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace the word by its completion
|
||||||
|
val matrixItem = item.toRoomAliasMatrixItem()
|
||||||
|
val displayName = matrixItem.getBestName()
|
||||||
|
|
||||||
|
// with a trailing space
|
||||||
|
editable.replace(startIndex, endIndex, "$displayName ")
|
||||||
|
|
||||||
|
// Add the span
|
||||||
|
val span = PillImageSpan(
|
||||||
|
glideRequests,
|
||||||
|
avatarRenderer,
|
||||||
|
requireContext(),
|
||||||
|
matrixItem
|
||||||
|
)
|
||||||
|
span.bind(composerLayout.composerEditText)
|
||||||
|
|
||||||
|
editable.setSpan(span, startIndex, startIndex + displayName.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPopupVisibilityChanged(shown: Boolean) {
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
|
||||||
autocompleteUserPresenter.callback = this
|
autocompleteUserPresenter.callback = this
|
||||||
Autocomplete.on<User>(composerLayout.composerEditText)
|
Autocomplete.on<User>(composerLayout.composerEditText)
|
||||||
.with(CharPolicy('@', true))
|
.with(CharPolicy('@', true))
|
||||||
|
@ -724,6 +775,7 @@ class RoomDetailFragment @Inject constructor(
|
||||||
|
|
||||||
private fun renderTextComposerState(state: TextComposerViewState) {
|
private fun renderTextComposerState(state: TextComposerViewState) {
|
||||||
autocompleteUserPresenter.render(state.asyncUsers)
|
autocompleteUserPresenter.render(state.asyncUsers)
|
||||||
|
autocompleteRoomPresenter.render(state.asyncRooms)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderTombstoneEventHandling(async: Async<String>) {
|
private fun renderTombstoneEventHandling(async: Async<String>) {
|
||||||
|
@ -1056,6 +1108,12 @@ class RoomDetailFragment @Inject constructor(
|
||||||
textComposerViewModel.handle(TextComposerAction.QueryUsers(query))
|
textComposerViewModel.handle(TextComposerAction.QueryUsers(query))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AutocompleteRoomPresenter.Callback
|
||||||
|
|
||||||
|
override fun onQueryRooms(query: CharSequence?) {
|
||||||
|
textComposerViewModel.handle(TextComposerAction.QueryRooms(query))
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleActions(action: EventSharedAction) {
|
private fun handleActions(action: EventSharedAction) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is EventSharedAction.AddReaction -> {
|
is EventSharedAction.AddReaction -> {
|
||||||
|
|
|
@ -20,4 +20,5 @@ import im.vector.riotx.core.platform.VectorViewModelAction
|
||||||
|
|
||||||
sealed class TextComposerAction : VectorViewModelAction {
|
sealed class TextComposerAction : VectorViewModelAction {
|
||||||
data class QueryUsers(val query: CharSequence?) : TextComposerAction()
|
data class QueryUsers(val query: CharSequence?) : TextComposerAction()
|
||||||
|
data class QueryRooms(val query: CharSequence?) : TextComposerAction()
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import com.jakewharton.rxrelay2.BehaviorRelay
|
||||||
import com.squareup.inject.assisted.Assisted
|
import com.squareup.inject.assisted.Assisted
|
||||||
import com.squareup.inject.assisted.AssistedInject
|
import com.squareup.inject.assisted.AssistedInject
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
import im.vector.matrix.android.api.session.user.model.User
|
import im.vector.matrix.android.api.session.user.model.User
|
||||||
import im.vector.matrix.rx.rx
|
import im.vector.matrix.rx.rx
|
||||||
import im.vector.riotx.core.platform.VectorViewModel
|
import im.vector.riotx.core.platform.VectorViewModel
|
||||||
|
@ -32,16 +33,16 @@ import io.reactivex.Observable
|
||||||
import io.reactivex.functions.BiFunction
|
import io.reactivex.functions.BiFunction
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
typealias AutocompleteUserQuery = CharSequence
|
typealias AutocompleteQuery = CharSequence
|
||||||
|
|
||||||
class TextComposerViewModel @AssistedInject constructor(@Assisted initialState: TextComposerViewState,
|
class TextComposerViewModel @AssistedInject constructor(@Assisted initialState: TextComposerViewState,
|
||||||
private val session: Session
|
private val session: Session
|
||||||
) : VectorViewModel<TextComposerViewState, TextComposerAction>(initialState) {
|
) : VectorViewModel<TextComposerViewState, TextComposerAction>(initialState) {
|
||||||
|
|
||||||
private val room = session.getRoom(initialState.roomId)!!
|
private val room = session.getRoom(initialState.roomId)!!
|
||||||
private val roomId = initialState.roomId
|
|
||||||
|
|
||||||
private val usersQueryObservable = BehaviorRelay.create<Option<AutocompleteUserQuery>>()
|
private val usersQueryObservable = BehaviorRelay.create<Option<AutocompleteQuery>>()
|
||||||
|
private val roomsQueryObservable = BehaviorRelay.create<Option<AutocompleteQuery>>()
|
||||||
|
|
||||||
@AssistedInject.Factory
|
@AssistedInject.Factory
|
||||||
interface Factory {
|
interface Factory {
|
||||||
|
@ -59,11 +60,13 @@ class TextComposerViewModel @AssistedInject constructor(@Assisted initialState:
|
||||||
|
|
||||||
init {
|
init {
|
||||||
observeUsersQuery()
|
observeUsersQuery()
|
||||||
|
observeRoomsQuery()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handle(action: TextComposerAction) {
|
override fun handle(action: TextComposerAction) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is TextComposerAction.QueryUsers -> handleQueryUsers(action)
|
is TextComposerAction.QueryUsers -> handleQueryUsers(action)
|
||||||
|
is TextComposerAction.QueryRooms -> handleQueryRooms(action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,8 +75,14 @@ class TextComposerViewModel @AssistedInject constructor(@Assisted initialState:
|
||||||
usersQueryObservable.accept(query)
|
usersQueryObservable.accept(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun handleQueryRooms(action: TextComposerAction.QueryRooms) {
|
||||||
|
val query = Option.fromNullable(action.query)
|
||||||
|
roomsQueryObservable.accept(query)
|
||||||
|
}
|
||||||
|
|
||||||
private fun observeUsersQuery() {
|
private fun observeUsersQuery() {
|
||||||
Observable.combineLatest<List<String>, Option<AutocompleteUserQuery>, List<User>>(
|
Observable.combineLatest<List<String>, Option<AutocompleteQuery>, List<User>>(
|
||||||
room.rx().liveRoomMemberIds(),
|
room.rx().liveRoomMemberIds(),
|
||||||
usersQueryObservable.throttleLast(300, TimeUnit.MILLISECONDS),
|
usersQueryObservable.throttleLast(300, TimeUnit.MILLISECONDS),
|
||||||
BiFunction { roomMemberIds, query ->
|
BiFunction { roomMemberIds, query ->
|
||||||
|
@ -87,6 +96,7 @@ class TextComposerViewModel @AssistedInject constructor(@Assisted initialState:
|
||||||
it.displayName?.startsWith(prefix = filter, ignoreCase = true) ?: false
|
it.displayName?.startsWith(prefix = filter, ignoreCase = true) ?: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.sortedBy { it.displayName }
|
||||||
}
|
}
|
||||||
).execute { async ->
|
).execute { async ->
|
||||||
copy(
|
copy(
|
||||||
|
@ -94,4 +104,24 @@ class TextComposerViewModel @AssistedInject constructor(@Assisted initialState:
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun observeRoomsQuery() {
|
||||||
|
Observable.combineLatest<List<RoomSummary>, Option<AutocompleteQuery>, List<RoomSummary>>(
|
||||||
|
session.rx().liveRoomSummaries(),
|
||||||
|
roomsQueryObservable.throttleLast(300, TimeUnit.MILLISECONDS),
|
||||||
|
BiFunction { roomSummaries, query ->
|
||||||
|
val filter = query.orNull() ?: ""
|
||||||
|
// Keep only room with a canonical alias
|
||||||
|
roomSummaries
|
||||||
|
.filter {
|
||||||
|
it.canonicalAlias?.contains(filter, ignoreCase = true) == true
|
||||||
|
}
|
||||||
|
.sortedBy { it.displayName }
|
||||||
|
}
|
||||||
|
).execute { async ->
|
||||||
|
copy(
|
||||||
|
asyncRooms = async
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,11 +19,13 @@ package im.vector.riotx.features.home.room.detail.composer
|
||||||
import com.airbnb.mvrx.Async
|
import com.airbnb.mvrx.Async
|
||||||
import com.airbnb.mvrx.MvRxState
|
import com.airbnb.mvrx.MvRxState
|
||||||
import com.airbnb.mvrx.Uninitialized
|
import com.airbnb.mvrx.Uninitialized
|
||||||
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
import im.vector.matrix.android.api.session.user.model.User
|
import im.vector.matrix.android.api.session.user.model.User
|
||||||
import im.vector.riotx.features.home.room.detail.RoomDetailArgs
|
import im.vector.riotx.features.home.room.detail.RoomDetailArgs
|
||||||
|
|
||||||
data class TextComposerViewState(val roomId: String,
|
data class TextComposerViewState(val roomId: String,
|
||||||
val asyncUsers: Async<List<User>> = Uninitialized
|
val asyncUsers: Async<List<User>> = Uninitialized,
|
||||||
|
val asyncRooms: Async<List<RoomSummary>> = Uninitialized
|
||||||
) : MvRxState {
|
) : MvRxState {
|
||||||
|
|
||||||
constructor(args: RoomDetailArgs) : this(roomId = args.roomId)
|
constructor(args: RoomDetailArgs) : this(roomId = args.roomId)
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?riotx_background"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="8dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/matrixItemAutocompleteAvatar"
|
||||||
|
android:layout_width="28dp"
|
||||||
|
android:layout_height="28dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
tools:src="@tools:sample/avatars" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:layout_marginLeft="12dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/matrixItemAutocompleteName"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="?riotx_text_primary"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
tools:text="name" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/matrixItemAutocompleteSubname"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="?riotx_text_secondary"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:text="name"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -1,30 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="?riotx_background"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:padding="8dp">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/userAutocompleteAvatar"
|
|
||||||
android:layout_width="28dp"
|
|
||||||
android:layout_height="28dp"
|
|
||||||
tools:src="@tools:sample/avatars" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/userAutocompleteName"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:layout_marginStart="12dp"
|
|
||||||
android:layout_marginLeft="12dp"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:textColor="?riotx_text_primary"
|
|
||||||
android:textSize="12sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
tools:text="name" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
Loading…
Reference in New Issue