Reimplement contact chips with a recyclerview.

This commit is contained in:
Alex Hart
2022-06-29 09:10:08 -03:00
committed by Cody Henthorne
parent 4215b0391d
commit 7bd34d2b99
6 changed files with 191 additions and 118 deletions

View File

@@ -0,0 +1,72 @@
package org.thoughtcrime.securesms.contacts
import androidx.lifecycle.ViewModel
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.BackpressureStrategy
import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.kotlin.plusAssign
import io.reactivex.rxjava3.schedulers.Schedulers
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.util.rx.RxStore
/**
* ViewModel expressly for displaying the current state of the contact chips
* in the contact selection fragment.
*/
class ContactChipViewModel : ViewModel() {
private val store = RxStore(emptyList<SelectedContacts.Model>())
val state: Flowable<List<SelectedContacts.Model>> = store.stateFlowable
.distinctUntilChanged()
.observeOn(AndroidSchedulers.mainThread())
val count = store.state.size
private val disposables = CompositeDisposable()
private val disposableMap: MutableMap<RecipientId, Disposable> = mutableMapOf()
override fun onCleared() {
disposables.clear()
disposableMap.values.forEach { it.dispose() }
disposableMap.clear()
}
fun add(selectedContact: SelectedContact) {
disposables += getOrCreateRecipientId(selectedContact).map { Recipient.resolved(it) }.observeOn(Schedulers.io()).subscribe { recipient ->
store.update { it + SelectedContacts.Model(selectedContact, recipient) }
disposableMap[recipient.id]?.dispose()
disposableMap[recipient.id] = store.update(recipient.live().asObservable().toFlowable(BackpressureStrategy.LATEST)) { changedRecipient, state ->
val index = state.indexOfFirst { it.selectedContact.matches(selectedContact) }
when {
index == 0 -> {
listOf(SelectedContacts.Model(selectedContact, changedRecipient)) + state.drop(index + 1)
}
index > 0 -> {
state.take(index) + SelectedContacts.Model(selectedContact, changedRecipient) + state.drop(index + 1)
}
else -> {
state
}
}
}
}
}
fun remove(selectedContact: SelectedContact) {
store.update { list ->
list.filterNot { it.selectedContact.matches(selectedContact) }
}
}
private fun getOrCreateRecipientId(selectedContact: SelectedContact): Single<RecipientId> {
return Single.fromCallable {
selectedContact.getOrCreateRecipientId(ApplicationDependencies.getApplication())
}
}
}

View File

@@ -0,0 +1,42 @@
package org.thoughtcrime.securesms.contacts
import android.view.View
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
import org.thoughtcrime.securesms.util.adapter.mapping.MappingModel
import org.thoughtcrime.securesms.util.adapter.mapping.MappingViewHolder
object SelectedContacts {
@JvmStatic
fun register(adapter: MappingAdapter, onCloseIconClicked: (Model) -> Unit) {
adapter.registerFactory(Model::class.java, LayoutFactory({ ViewHolder(it, onCloseIconClicked) }, R.layout.contact_selection_list_chip))
}
class Model(val selectedContact: SelectedContact, val recipient: Recipient) : MappingModel<Model> {
override fun areItemsTheSame(newItem: Model): Boolean {
return newItem.selectedContact.matches(selectedContact) && recipient == newItem.recipient
}
override fun areContentsTheSame(newItem: Model): Boolean {
return areItemsTheSame(newItem) && recipient.hasSameContent(newItem.recipient)
}
}
private class ViewHolder(itemView: View, private val onCloseIconClicked: (Model) -> Unit) : MappingViewHolder<Model>(itemView) {
private val chip: ContactChip = itemView.findViewById(R.id.contact_chip)
override fun bind(model: Model) {
chip.text = model.recipient.getShortDisplayName(context)
chip.setContact(model.selectedContact)
chip.isCloseIconVisible = true
chip.setOnCloseIconClickListener {
onCloseIconClicked(model)
}
chip.setAvatar(GlideApp.with(itemView), model.recipient, null)
}
}
}