mirror of
https://github.com/element-hq/element-android
synced 2024-11-24 10:25:35 +03:00
Add checkbox to filter contacts with MatrixId only
This commit is contained in:
parent
1c733e6661
commit
6ceac578a3
7 changed files with 108 additions and 57 deletions
|
@ -20,4 +20,5 @@ import im.vector.riotx.core.platform.VectorViewModelAction
|
|||
|
||||
sealed class PhoneBookAction : VectorViewModelAction {
|
||||
data class FilterWith(val filter: String) : PhoneBookAction()
|
||||
data class OnlyBoundContacts(val onlyBoundContacts: Boolean) : PhoneBookAction()
|
||||
}
|
||||
|
|
|
@ -52,11 +52,11 @@ class PhoneBookController @Inject constructor(
|
|||
|
||||
override fun buildModels() {
|
||||
val currentState = state ?: return
|
||||
val hasSearch = currentState.searchTerm.isNotBlank()
|
||||
val hasSearch = currentState.searchTerm.isNotEmpty()
|
||||
when (val asyncMappedContacts = currentState.mappedContacts) {
|
||||
is Uninitialized -> renderEmptyState(false)
|
||||
is Loading -> renderLoading()
|
||||
is Success -> renderSuccess(currentState.filteredMappedContacts, hasSearch)
|
||||
is Success -> renderSuccess(currentState.filteredMappedContacts, hasSearch, currentState.onlyBoundContacts)
|
||||
is Fail -> renderFailure(asyncMappedContacts.error)
|
||||
}
|
||||
}
|
||||
|
@ -75,49 +75,54 @@ class PhoneBookController @Inject constructor(
|
|||
}
|
||||
|
||||
private fun renderSuccess(mappedContacts: List<ContactModel>,
|
||||
hasSearch: Boolean) {
|
||||
hasSearch: Boolean,
|
||||
onlyBoundContacts: Boolean) {
|
||||
if (mappedContacts.isEmpty()) {
|
||||
renderEmptyState(hasSearch)
|
||||
} else {
|
||||
renderContacts(mappedContacts)
|
||||
renderContacts(mappedContacts, onlyBoundContacts)
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderContacts(mappedContacts: List<ContactModel>) {
|
||||
private fun renderContacts(mappedContacts: List<ContactModel>, onlyBoundContacts: Boolean) {
|
||||
for (mappedContact in mappedContacts) {
|
||||
contactItem {
|
||||
id(mappedContact.id)
|
||||
contact(mappedContact)
|
||||
avatarRenderer(avatarRenderer)
|
||||
}
|
||||
mappedContact.emails.forEach {
|
||||
contactDetailItem {
|
||||
id("$mappedContact.id${it.email}")
|
||||
threePid(it.email)
|
||||
matrixId(it.matrixId)
|
||||
clickListener {
|
||||
if (it.matrixId != null) {
|
||||
callback?.onMatrixIdClick(it.matrixId)
|
||||
} else {
|
||||
callback?.onThreePidClick(ThreePid.Email(it.email))
|
||||
mappedContact.emails
|
||||
.filter { !onlyBoundContacts || it.matrixId != null }
|
||||
.forEach {
|
||||
contactDetailItem {
|
||||
id("$mappedContact.id${it.email}")
|
||||
threePid(it.email)
|
||||
matrixId(it.matrixId)
|
||||
clickListener {
|
||||
if (it.matrixId != null) {
|
||||
callback?.onMatrixIdClick(it.matrixId)
|
||||
} else {
|
||||
callback?.onThreePidClick(ThreePid.Email(it.email))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mappedContact.msisdns.forEach {
|
||||
contactDetailItem {
|
||||
id("$mappedContact.id${it.phoneNumber}")
|
||||
threePid(it.phoneNumber)
|
||||
matrixId(it.matrixId)
|
||||
clickListener {
|
||||
if (it.matrixId != null) {
|
||||
callback?.onMatrixIdClick(it.matrixId)
|
||||
} else {
|
||||
callback?.onThreePidClick(ThreePid.Msisdn(it.phoneNumber))
|
||||
mappedContact.msisdns
|
||||
.filter { !onlyBoundContacts || it.matrixId != null }
|
||||
.forEach {
|
||||
contactDetailItem {
|
||||
id("$mappedContact.id${it.phoneNumber}")
|
||||
threePid(it.phoneNumber)
|
||||
matrixId(it.matrixId)
|
||||
clickListener {
|
||||
if (it.matrixId != null) {
|
||||
callback?.onMatrixIdClick(it.matrixId)
|
||||
} else {
|
||||
callback?.onThreePidClick(ThreePid.Msisdn(it.phoneNumber))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,10 @@ package im.vector.riotx.features.userdirectory
|
|||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import com.airbnb.mvrx.activityViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import com.jakewharton.rxbinding3.widget.checkedChanges
|
||||
import com.jakewharton.rxbinding3.widget.textChanges
|
||||
import im.vector.matrix.android.api.session.identity.ThreePid
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
|
@ -50,9 +52,18 @@ class PhoneBookFragment @Inject constructor(
|
|||
sharedActionViewModel = activityViewModelProvider.get(UserDirectorySharedActionViewModel::class.java)
|
||||
setupRecyclerView()
|
||||
setupFilterView()
|
||||
setupOnlyBoundContactsView()
|
||||
setupCloseView()
|
||||
}
|
||||
|
||||
private fun setupOnlyBoundContactsView() {
|
||||
phoneBookOnlyBoundContacts.checkedChanges()
|
||||
.subscribe {
|
||||
phoneBookViewModel.handle(PhoneBookAction.OnlyBoundContacts(it))
|
||||
}
|
||||
.disposeOnDestroyView()
|
||||
}
|
||||
|
||||
private fun setupFilterView() {
|
||||
phoneBookFilter
|
||||
.textChanges()
|
||||
|
@ -81,8 +92,9 @@ class PhoneBookFragment @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(phoneBookViewModel) {
|
||||
phoneBookController.setData(it)
|
||||
override fun invalidate() = withState(phoneBookViewModel) { state ->
|
||||
phoneBookOnlyBoundContacts.isVisible = state.isBoundRetrieved
|
||||
phoneBookController.setData(state)
|
||||
}
|
||||
|
||||
override fun onMatrixIdClick(matrixId: String) {
|
||||
|
|
|
@ -37,7 +37,9 @@ import im.vector.riotx.core.platform.EmptyViewEvents
|
|||
import im.vector.riotx.core.platform.VectorViewModel
|
||||
import im.vector.riotx.features.createdirect.CreateDirectRoomActivity
|
||||
import im.vector.riotx.features.invite.InviteUsersToRoomActivity
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
private typealias PhoneBookSearch = String
|
||||
|
||||
|
@ -71,13 +73,12 @@ class PhoneBookViewModel @AssistedInject constructor(@Assisted
|
|||
|
||||
private var allContacts: List<ContactModel> = emptyList()
|
||||
private var mappedContacts: List<ContactModel> = emptyList()
|
||||
private var foundThreePid: List<FoundThreePid> = emptyList()
|
||||
|
||||
init {
|
||||
loadContacts()
|
||||
|
||||
selectSubscribe(PhoneBookViewState::searchTerm) {
|
||||
updateState()
|
||||
selectSubscribe(PhoneBookViewState::searchTerm, PhoneBookViewState::onlyBoundContacts) { _, _ ->
|
||||
updateFilteredMappedContacts()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,7 +89,7 @@ class PhoneBookViewModel @AssistedInject constructor(@Assisted
|
|||
)
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
allContacts = contactsDataSource.getContacts()
|
||||
mappedContacts = allContacts
|
||||
|
||||
|
@ -99,7 +100,7 @@ class PhoneBookViewModel @AssistedInject constructor(@Assisted
|
|||
}
|
||||
|
||||
performLookup(allContacts)
|
||||
updateState()
|
||||
updateFilteredMappedContacts()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,24 +112,23 @@ class PhoneBookViewModel @AssistedInject constructor(@Assisted
|
|||
}
|
||||
session.identityService().lookUp(threePids, object : MatrixCallback<List<FoundThreePid>> {
|
||||
override fun onFailure(failure: Throwable) {
|
||||
// Ignore?
|
||||
// Ignore
|
||||
Timber.w(failure, "Unable to perform the lookup")
|
||||
}
|
||||
|
||||
override fun onSuccess(data: List<FoundThreePid>) {
|
||||
foundThreePid = data
|
||||
|
||||
mappedContacts = allContacts.map { contactModel ->
|
||||
contactModel.copy(
|
||||
emails = contactModel.emails.map { email ->
|
||||
email.copy(
|
||||
matrixId = foundThreePid
|
||||
matrixId = data
|
||||
.firstOrNull { foundThreePid -> foundThreePid.threePid.value == email.email }
|
||||
?.matrixId
|
||||
)
|
||||
},
|
||||
msisdns = contactModel.msisdns.map { msisdn ->
|
||||
msisdn.copy(
|
||||
matrixId = foundThreePid
|
||||
matrixId = data
|
||||
.firstOrNull { foundThreePid -> foundThreePid.threePid.value == msisdn.phoneNumber }
|
||||
?.matrixId
|
||||
)
|
||||
|
@ -136,15 +136,25 @@ class PhoneBookViewModel @AssistedInject constructor(@Assisted
|
|||
)
|
||||
}
|
||||
|
||||
updateState()
|
||||
setState {
|
||||
copy(
|
||||
isBoundRetrieved = true
|
||||
)
|
||||
}
|
||||
|
||||
updateFilteredMappedContacts()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateState() = withState { state ->
|
||||
private fun updateFilteredMappedContacts() = withState { state ->
|
||||
val filteredMappedContacts = mappedContacts
|
||||
.filter { it.displayName.contains(state.searchTerm, true) }
|
||||
.filter { contactModel ->
|
||||
!state.onlyBoundContacts
|
||||
|| contactModel.emails.any { it.matrixId != null } || contactModel.msisdns.any { it.matrixId != null }
|
||||
}
|
||||
|
||||
setState {
|
||||
copy(
|
||||
|
@ -155,10 +165,19 @@ class PhoneBookViewModel @AssistedInject constructor(@Assisted
|
|||
|
||||
override fun handle(action: PhoneBookAction) {
|
||||
when (action) {
|
||||
is PhoneBookAction.FilterWith -> handleFilterWith(action)
|
||||
is PhoneBookAction.FilterWith -> handleFilterWith(action)
|
||||
is PhoneBookAction.OnlyBoundContacts -> handleOnlyBoundContacts(action)
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
private fun handleOnlyBoundContacts(action: PhoneBookAction.OnlyBoundContacts) {
|
||||
setState {
|
||||
copy(
|
||||
onlyBoundContacts = action.onlyBoundContacts
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleFilterWith(action: PhoneBookAction.FilterWith) {
|
||||
setState {
|
||||
copy(
|
||||
|
|
|
@ -22,14 +22,14 @@ import com.airbnb.mvrx.MvRxState
|
|||
import im.vector.riotx.core.contacts.ContactModel
|
||||
|
||||
data class PhoneBookViewState(
|
||||
val searchTerm: String = "",
|
||||
// All the contacts on the phone
|
||||
val mappedContacts: Async<List<ContactModel>> = Loading(),
|
||||
val filteredMappedContacts: List<ContactModel> = emptyList()
|
||||
/*
|
||||
val knownUsers: Async<PagedList<User>> = Uninitialized,
|
||||
val directoryUsers: Async<List<User>> = Uninitialized,
|
||||
val selectedUsers: Set<User> = emptySet(),
|
||||
val createAndInviteState: Async<String> = Uninitialized,
|
||||
val filterKnownUsersValue: Option<String> = Option.empty()
|
||||
*/
|
||||
// Use to filter contacts by display name
|
||||
val searchTerm: String = "",
|
||||
// Tru to display only bound contacts with their bound 2pid
|
||||
val onlyBoundContacts: Boolean = false,
|
||||
// All contacts, filtered by searchTerm and onlyBoundContacts
|
||||
val filteredMappedContacts: List<ContactModel> = emptyList(),
|
||||
// True when the identity service has return some data
|
||||
val isBoundRetrieved: Boolean = false
|
||||
) : MvRxState
|
||||
|
|
|
@ -79,6 +79,20 @@
|
|||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/phoneBookOnlyBoundContacts"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/layout_horizontal_margin"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="@dimen/layout_horizontal_margin"
|
||||
android:text="@string/matrix_only_filter"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/phoneBookFilterContainer"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<View
|
||||
android:id="@+id/phoneBookFilterDivider"
|
||||
android:layout_width="0dp"
|
||||
|
@ -87,13 +101,12 @@
|
|||
android:background="?attr/vctr_list_divider_color"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/phoneBookFilterContainer" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/phoneBookOnlyBoundContacts" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/phoneBookRecyclerView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:fastScrollEnabled="true"
|
||||
android:overScrollMode="always"
|
||||
android:scrollbars="vertical"
|
||||
|
|
|
@ -6,8 +6,9 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:background="?riotx_background"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
android:minHeight="72dp"
|
||||
android:padding="8dp">
|
||||
android:paddingStart="8dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingEnd="8dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/contactAvatar"
|
||||
|
|
Loading…
Reference in a new issue