mirror of
https://github.com/nextcloud/talk-android
synced 2025-07-13 15:54:59 +01:00
Fix up search with contacts
Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
parent
ee570224d9
commit
6c73b6f70c
@ -20,24 +20,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.newarch.features.contactsflow.source
|
||||
package com.nextcloud.talk.newarch.features.contactsflow
|
||||
|
||||
import com.nextcloud.talk.newarch.features.contactsflow.contacts.ContactsViewSource
|
||||
import com.otaliastudios.elements.Source
|
||||
import com.otaliastudios.elements.extensions.ListSource
|
||||
import com.nextcloud.talk.models.json.participants.Participant
|
||||
|
||||
class FixedListSource(list: List<Any>, elementType: Int) : ListSource<Any>(list, elementType) {
|
||||
override fun areContentsTheSame(first: Any, second: Any): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun <E : Any> areItemsTheSame(own: Any, dependency: Source<E>, other: E?): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun dependsOn(source: Source<*>): Boolean {
|
||||
return source is ContactsViewSource
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
data class ParticipantElement(
|
||||
val data: Any,
|
||||
val elementType: Int
|
||||
)
|
@ -28,6 +28,7 @@ import androidx.core.view.isVisible
|
||||
import coil.api.load
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.models.json.participants.Participant
|
||||
import com.nextcloud.talk.newarch.features.contactsflow.ParticipantElement
|
||||
import com.nextcloud.talk.newarch.local.models.getCredentials
|
||||
import com.nextcloud.talk.newarch.services.GlobalService
|
||||
import com.nextcloud.talk.newarch.utils.ElementPayload
|
||||
@ -78,13 +79,14 @@ open class ContactPresenter<T : Any>(context: Context, onElementClick: ((Page, H
|
||||
super.onBind(page, holder, element, payloads)
|
||||
|
||||
if (element.type == ParticipantElementType.PARTICIPANT.ordinal || element.type == ParticipantElementType.PARTICIPANT_SELECTED.ordinal) {
|
||||
val participant = element.data as Participant?
|
||||
val participantElement = element.data as ParticipantElement
|
||||
val participant = participantElement.data as Participant
|
||||
val user = globalService.currentUserLiveData.value
|
||||
|
||||
holder.itemView.checkedImageView?.isVisible = participant?.selected == true
|
||||
holder.itemView.checkedImageView?.isVisible = participant.selected == true
|
||||
|
||||
if (!payloads.contains(ElementPayload.SELECTION_TOGGLE)) {
|
||||
participant?.displayName?.let {
|
||||
participant.displayName?.let {
|
||||
if (element.type == ParticipantElementType.PARTICIPANT_SELECTED.ordinal) {
|
||||
holder.itemView.participantNameTextView.text = it.substringBefore(" ", it)
|
||||
} else {
|
||||
@ -97,7 +99,7 @@ open class ContactPresenter<T : Any>(context: Context, onElementClick: ((Page, H
|
||||
|
||||
holder.itemView.clearImageView?.load(Images().getImageWithBackground(context, R.drawable.ic_baseline_clear_24, R.color.bg_selected_participant_clear_icon, R.color.white))
|
||||
|
||||
when (participant?.source) {
|
||||
when (participant.source) {
|
||||
"users" -> {
|
||||
when (participant.type) {
|
||||
Participant.ParticipantType.GUEST, Participant.ParticipantType.GUEST_AS_MODERATOR, Participant.ParticipantType.USER_FOLLOWING_LINK -> {
|
||||
@ -127,7 +129,7 @@ open class ContactPresenter<T : Any>(context: Context, onElementClick: ((Page, H
|
||||
} else if (element.type == ParticipantElementType.PARTICIPANT_FOOTER.ordinal) {
|
||||
holder.itemView.messageTextView.text = (element.data as FooterSource.Data<*, *>).footer.toString()
|
||||
} else if (element.type == ParticipantElementType.PARTICIPANT_NEW_GROUP.ordinal) {
|
||||
val pairData = element.data as Pair<*, *>
|
||||
val pairData = (element.data as ParticipantElement).data as Pair<*, *>
|
||||
holder.itemView.participantNameTextView.text = pairData.first as CharSequence
|
||||
holder.itemView.avatarImageView.load(Images().getImageWithBackground(context, pairData.second as Int))
|
||||
} else {
|
||||
|
@ -39,7 +39,6 @@ import com.nextcloud.talk.controllers.ChatController
|
||||
import com.nextcloud.talk.models.json.participants.Participant
|
||||
import com.nextcloud.talk.newarch.features.contactsflow.ContactsViewOperationState
|
||||
import com.nextcloud.talk.newarch.features.contactsflow.groupconversation.GroupConversationView
|
||||
import com.nextcloud.talk.newarch.features.contactsflow.source.FixedListSource
|
||||
import com.nextcloud.talk.newarch.features.search.DebouncingTextWatcher
|
||||
import com.nextcloud.talk.newarch.mvvm.BaseView
|
||||
import com.nextcloud.talk.newarch.mvvm.ext.initRecyclerView
|
||||
@ -84,8 +83,7 @@ class ContactsView(private val bundle: Bundle? = null) : BaseView() {
|
||||
|
||||
// todo - change empty state magic
|
||||
val participantsAdapterBuilder = Adapter.builder(this)
|
||||
//.addSource(FixedListSource(listOf(Pair(context.getString(R.string.nc_join_via_link), R.drawable.ic_link_white_24px)), ParticipantElementType.PARTICIPANT_JOIN_VIA_LINK.ordinal))
|
||||
.addSource(ContactsViewSource(data = viewModel.contactsLiveData, elementType = ParticipantElementType.PARTICIPANT.ordinal))
|
||||
.addSource(ContactsViewSource(data = viewModel.contactsLiveData))
|
||||
.addSource(ContactsHeaderSource(activity as Context, ParticipantElementType.PARTICIPANT_HEADER.ordinal))
|
||||
.addSource(ContactsViewFooterSource(activity as Context, ParticipantElementType.PARTICIPANT_FOOTER.ordinal))
|
||||
.addPresenter(ContactPresenter(activity as Context, ::onElementClick))
|
||||
@ -97,14 +95,10 @@ class ContactsView(private val bundle: Bundle? = null) : BaseView() {
|
||||
})
|
||||
.setAutoScrollMode(Adapter.AUTOSCROLL_POSITION_0, true)
|
||||
|
||||
if (!hasToken) {
|
||||
participantsAdapterBuilder.addSource(FixedListSource(listOf(Pair(context.getString(R.string.nc_new_group), R.drawable.ic_people_group_white_24px)), ParticipantElementType.PARTICIPANT_NEW_GROUP.ordinal))
|
||||
}
|
||||
|
||||
participantsAdapter = participantsAdapterBuilder.into(view.selectedParticipantsRecyclerView)
|
||||
|
||||
selectedParticipantsAdapter = Adapter.builder(this)
|
||||
.addSource(ContactsViewSource(data = viewModel.selectedParticipantsLiveData, elementType = ParticipantElementType.PARTICIPANT_SELECTED.ordinal, loadingIndicatorsEnabled = false, errorIndicatorEnabled = false, emptyIndicatorEnabled = false))
|
||||
.addSource(ContactsViewSource(data = viewModel.selectedParticipantsLiveData, loadingIndicatorsEnabled = false, errorIndicatorEnabled = false, emptyIndicatorEnabled = false))
|
||||
.addPresenter(ContactPresenter(activity as Context, ::onElementClick))
|
||||
.setAutoScrollMode(Adapter.AUTOSCROLL_POSITION_ANY, true)
|
||||
.into(view.selectedParticipantsRecyclerView)
|
||||
@ -241,8 +235,10 @@ class ContactsView(private val bundle: Bundle? = null) : BaseView() {
|
||||
override fun onFloatingActionButtonClick() {
|
||||
if (hasToken) {
|
||||
val conversationToken = bundle?.getString(BundleKeys.KEY_CONVERSATION_TOKEN)
|
||||
conversationToken?.let {
|
||||
viewModel.selectedParticipantsLiveData.value?.let { participants -> viewModel.addParticipants(it, participants) }
|
||||
conversationToken?.let { conversationToken ->
|
||||
viewModel.selectedParticipantsLiveData.value?.let { participantElements ->
|
||||
viewModel.addParticipants(conversationToken, participantElements.map { it.data as Participant })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,34 +25,35 @@ package com.nextcloud.talk.newarch.features.contactsflow.contacts
|
||||
import android.content.Context
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.models.json.participants.Participant
|
||||
import com.nextcloud.talk.newarch.features.contactsflow.ParticipantElement
|
||||
import com.otaliastudios.elements.Page
|
||||
import com.otaliastudios.elements.Source
|
||||
import com.otaliastudios.elements.extensions.FooterSource
|
||||
|
||||
class ContactsViewFooterSource(private val context: Context, private val elementType: Int) : FooterSource<Participant, String>() {
|
||||
private var lastAnchor: Participant? = null
|
||||
class ContactsViewFooterSource(private val context: Context, private val elementType: Int) : FooterSource<ParticipantElement, String>() {
|
||||
private var lastAnchor: ParticipantElement? = null
|
||||
|
||||
override fun dependsOn(source: Source<*>): Boolean {
|
||||
return source is ContactsViewSource
|
||||
}
|
||||
|
||||
override fun areItemsTheSame(first: Data<Participant, String>, second: Data<Participant, String>): Boolean {
|
||||
override fun areItemsTheSame(first: Data<ParticipantElement, String>, second: Data<ParticipantElement, String>): Boolean {
|
||||
return first == second
|
||||
}
|
||||
|
||||
override fun getElementType(data: Data<Participant, String>) = elementType
|
||||
override fun getElementType(data: Data<ParticipantElement, String>) = elementType
|
||||
|
||||
override fun computeFooters(page: Page, list: List<Participant>): List<Data<Participant, String>> {
|
||||
override fun computeFooters(page: Page, list: List<ParticipantElement>): List<Data<ParticipantElement, String>> {
|
||||
lastAnchor = null
|
||||
val results = arrayListOf<Data<Participant, String>>()
|
||||
val results = arrayListOf<Data<ParticipantElement, String>>()
|
||||
lastAnchor = if (list.isNotEmpty()) {
|
||||
val participant = list.takeLast(1)[0]
|
||||
val participantElement = list.takeLast(1)[0]
|
||||
|
||||
if (lastAnchor == null || lastAnchor != participant) {
|
||||
results.add(Data(participant, context.getString(R.string.nc_search_for_more)))
|
||||
if (lastAnchor == null || lastAnchor != participantElement) {
|
||||
results.add(Data(participantElement, context.getString(R.string.nc_search_for_more)))
|
||||
}
|
||||
|
||||
participant
|
||||
participantElement
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
@ -25,53 +25,58 @@ package com.nextcloud.talk.newarch.features.contactsflow.contacts
|
||||
import android.content.Context
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.models.json.participants.Participant
|
||||
import com.nextcloud.talk.newarch.features.contactsflow.ParticipantElement
|
||||
import com.otaliastudios.elements.Page
|
||||
import com.otaliastudios.elements.Source
|
||||
import com.otaliastudios.elements.extensions.HeaderSource
|
||||
|
||||
class ContactsHeaderSource(private val context: Context, private val elementType: Int) : HeaderSource<Participant, String>() {
|
||||
class ContactsHeaderSource(private val context: Context, private val elementType: Int) : HeaderSource<ParticipantElement, String>() {
|
||||
|
||||
// Store the last header that was added, even if it belongs to a previous page.
|
||||
private var headersAlreadyAdded = mutableListOf<String>()
|
||||
|
||||
override fun dependsOn(source: Source<*>) = source is ContactsViewSource
|
||||
|
||||
override fun computeHeaders(page: Page, list: List<Participant>): List<Data<Participant, String>> {
|
||||
val results = arrayListOf<Data<Participant, String>>()
|
||||
override fun computeHeaders(page: Page, list: List<ParticipantElement>): List<Data<ParticipantElement, String>> {
|
||||
val results = arrayListOf<Data<ParticipantElement, String>>()
|
||||
headersAlreadyAdded = mutableListOf()
|
||||
for (participant in list) {
|
||||
val header = when (participant.source) {
|
||||
"users" -> {
|
||||
context.getString(R.string.nc_contacts)
|
||||
}
|
||||
"groups" -> {
|
||||
context.getString(R.string.nc_groups)
|
||||
}
|
||||
"emails" -> {
|
||||
context.getString(R.string.nc_emails)
|
||||
}
|
||||
"circles" -> {
|
||||
context.getString(R.string.nc_circles)
|
||||
}
|
||||
else -> {
|
||||
context.getString(R.string.nc_others)
|
||||
}
|
||||
}
|
||||
for (participantElement in list) {
|
||||
if (participantElement.data is Participant) {
|
||||
val participant = participantElement.data
|
||||
val header = when (participant.source) {
|
||||
"users" -> {
|
||||
context.getString(R.string.nc_contacts)
|
||||
}
|
||||
"groups" -> {
|
||||
context.getString(R.string.nc_groups)
|
||||
}
|
||||
"emails" -> {
|
||||
context.getString(R.string.nc_emails)
|
||||
}
|
||||
"circles" -> {
|
||||
context.getString(R.string.nc_circles)
|
||||
}
|
||||
else -> {
|
||||
context.getString(R.string.nc_others)
|
||||
|
||||
if (!headersAlreadyAdded.contains(header)) {
|
||||
results.add(Data(participant, header))
|
||||
headersAlreadyAdded.add(header)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!headersAlreadyAdded.contains(header)) {
|
||||
results.add(Data(participantElement, header))
|
||||
headersAlreadyAdded.add(header)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
override fun getElementType(data: Data<Participant, String>): Int {
|
||||
override fun getElementType(data: Data<ParticipantElement, String>): Int {
|
||||
return elementType
|
||||
}
|
||||
|
||||
override fun areItemsTheSame(first: Data<Participant, String>, second: Data<Participant, String>): Boolean {
|
||||
override fun areItemsTheSame(first: Data<ParticipantElement, String>, second: Data<ParticipantElement, String>): Boolean {
|
||||
return first == second
|
||||
}
|
||||
}
|
@ -27,6 +27,7 @@ import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.distinctUntilChanged
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.models.json.conversations.ConversationOverall
|
||||
import com.nextcloud.talk.models.json.participants.AddParticipantOverall
|
||||
@ -39,6 +40,7 @@ import com.nextcloud.talk.newarch.domain.usecases.GetContactsUseCase
|
||||
import com.nextcloud.talk.newarch.domain.usecases.base.UseCaseResponse
|
||||
import com.nextcloud.talk.newarch.features.contactsflow.ContactsViewOperationState
|
||||
import com.nextcloud.talk.newarch.features.contactsflow.ContactsViewOperationStateWrapper
|
||||
import com.nextcloud.talk.newarch.features.contactsflow.ParticipantElement
|
||||
import com.nextcloud.talk.newarch.features.conversationslist.ConversationsListView
|
||||
import com.nextcloud.talk.newarch.services.GlobalService
|
||||
import kotlinx.coroutines.runBlocking
|
||||
@ -51,10 +53,10 @@ class ContactsViewModel constructor(
|
||||
private val addParticipantToConversationUseCase: AddParticipantToConversationUseCase,
|
||||
val globalService: GlobalService
|
||||
) : BaseViewModel<ConversationsListView>(application) {
|
||||
private val selectedParticipants = mutableListOf<Participant>()
|
||||
val selectedParticipantsLiveData: MutableLiveData<List<Participant>> = MutableLiveData()
|
||||
private val _contacts: MutableLiveData<List<Participant>> = MutableLiveData()
|
||||
val contactsLiveData: LiveData<List<Participant>> = _contacts
|
||||
private val selectedParticipants = mutableListOf<ParticipantElement>()
|
||||
val selectedParticipantsLiveData: MutableLiveData<List<ParticipantElement>> = MutableLiveData()
|
||||
private val _contacts: MutableLiveData<List<ParticipantElement>> = MutableLiveData()
|
||||
val contactsLiveData: LiveData<List<ParticipantElement>> = _contacts
|
||||
private val _operationState = MutableLiveData(ContactsViewOperationStateWrapper(ContactsViewOperationState.WAITING, null, null))
|
||||
val operationState: LiveData<ContactsViewOperationStateWrapper> = _operationState.distinctUntilChanged()
|
||||
|
||||
@ -79,12 +81,12 @@ class ContactsViewModel constructor(
|
||||
}
|
||||
|
||||
fun selectParticipant(participant: Participant) {
|
||||
selectedParticipants.add(participant)
|
||||
selectedParticipants.add(ParticipantElement(participant, ParticipantElementType.PARTICIPANT_SELECTED.ordinal))
|
||||
selectedParticipantsLiveData.postValue(selectedParticipants)
|
||||
}
|
||||
|
||||
fun unselectParticipant(participant: Participant) {
|
||||
selectedParticipants.remove(participant)
|
||||
selectedParticipants.remove(ParticipantElement(participant, ParticipantElementType.PARTICIPANT_SELECTED.ordinal))
|
||||
selectedParticipantsLiveData.postValue(selectedParticipants)
|
||||
}
|
||||
|
||||
@ -146,14 +148,24 @@ class ContactsViewModel constructor(
|
||||
it.displayName!!.toLowerCase()
|
||||
}))
|
||||
|
||||
val selectedUserIds = selectedParticipants.map { it.userId }
|
||||
for (participant in sortedList) {
|
||||
if (participant.userId in selectedUserIds) {
|
||||
participant.selected = true
|
||||
val selectedUserIds = selectedParticipants.map { (it.data as Participant).userId }
|
||||
|
||||
|
||||
val participantElementsList: MutableList<ParticipantElement> = sortedList.map {
|
||||
if (it.userId in selectedUserIds) {
|
||||
it.selected = true
|
||||
}
|
||||
|
||||
ParticipantElement(it, ParticipantElementType.PARTICIPANT.ordinal)
|
||||
} as MutableList<ParticipantElement>
|
||||
|
||||
|
||||
if (conversationToken.isNullOrEmpty() && searchQuery.isNullOrEmpty()) {
|
||||
val newGroupElement = ParticipantElement(Pair(context.getString(R.string.nc_new_group), R.drawable.ic_people_group_white_24px), ParticipantElementType.PARTICIPANT_NEW_GROUP.ordinal)
|
||||
participantElementsList.add(0, newGroupElement)
|
||||
}
|
||||
|
||||
_contacts.postValue(sortedList)
|
||||
_contacts.postValue(participantElementsList)
|
||||
}
|
||||
|
||||
override suspend fun onError(errorModel: ErrorModel?) {
|
||||
|
@ -24,12 +24,13 @@ package com.nextcloud.talk.newarch.features.contactsflow.contacts
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import com.nextcloud.talk.models.json.participants.Participant
|
||||
import com.nextcloud.talk.newarch.features.contactsflow.ParticipantElement
|
||||
import com.otaliastudios.elements.Element
|
||||
import com.otaliastudios.elements.Page
|
||||
import com.otaliastudios.elements.Source
|
||||
import com.otaliastudios.elements.extensions.MainSource
|
||||
|
||||
class ContactsViewSource<T : Participant>(private val data: LiveData<List<T>>, private val elementType: Int = 0, loadingIndicatorsEnabled: Boolean = true, errorIndicatorEnabled: Boolean = true, emptyIndicatorEnabled: Boolean = true) : MainSource<T>(loadingIndicatorsEnabled, errorIndicatorEnabled, emptyIndicatorEnabled) {
|
||||
class ContactsViewSource<T : ParticipantElement>(private val data: LiveData<List<T>>, loadingIndicatorsEnabled: Boolean = true, errorIndicatorEnabled: Boolean = true, emptyIndicatorEnabled: Boolean = true) : MainSource<T>(loadingIndicatorsEnabled, errorIndicatorEnabled, emptyIndicatorEnabled) {
|
||||
|
||||
override fun onPageOpened(page: Page, dependencies: List<Element<*>>) {
|
||||
super.onPageOpened(page, dependencies)
|
||||
@ -39,7 +40,7 @@ class ContactsViewSource<T : Participant>(private val data: LiveData<List<T>>, p
|
||||
}
|
||||
|
||||
override fun getElementType(data: T): Int {
|
||||
return elementType
|
||||
return data.elementType
|
||||
}
|
||||
|
||||
override fun dependsOn(source: Source<*>) = false
|
||||
@ -49,6 +50,14 @@ class ContactsViewSource<T : Participant>(private val data: LiveData<List<T>>, p
|
||||
}
|
||||
|
||||
override fun areItemsTheSame(first: T, second: T): Boolean {
|
||||
return first.userId == second.userId
|
||||
if (first.elementType != second.elementType) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (first.data is Participant && second.data is Participant) {
|
||||
return first.data.userId == second.data.userId
|
||||
}
|
||||
|
||||
return first.data == second.data
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user