From 6c73b6f70c9a16d1ce99bb908332c35da4ac1661 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Sat, 1 Feb 2020 23:02:14 +0100 Subject: [PATCH] Fix up search with contacts Signed-off-by: Mario Danic --- ...xedListSource.kt => ParticipantElement.kt} | 25 ++------ .../contactsflow/contacts/ContactPresenter.kt | 12 ++-- .../contactsflow/contacts/ContactsView.kt | 16 ++--- .../contacts/ContactsViewFooterSource.kt | 21 +++---- .../contacts/ContactsViewHeaderSource.kt | 59 ++++++++++--------- .../contacts/ContactsViewModel.kt | 34 +++++++---- .../contacts/ContactsViewSource.kt | 15 ++++- 7 files changed, 97 insertions(+), 85 deletions(-) rename app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/{source/FixedListSource.kt => ParticipantElement.kt} (55%) diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/source/FixedListSource.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/ParticipantElement.kt similarity index 55% rename from app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/source/FixedListSource.kt rename to app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/ParticipantElement.kt index e2f95406e..fde654f5e 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/source/FixedListSource.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/ParticipantElement.kt @@ -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, elementType: Int) : ListSource(list, elementType) { - override fun areContentsTheSame(first: Any, second: Any): Boolean { - return true - } - - override fun areItemsTheSame(own: Any, dependency: Source, other: E?): Boolean { - return true - } - - override fun dependsOn(source: Source<*>): Boolean { - return source is ContactsViewSource - } - - -} \ No newline at end of file +data class ParticipantElement( + val data: Any, + val elementType: Int +) \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/contacts/ContactPresenter.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/contacts/ContactPresenter.kt index 0e57f5eb2..f09cc222e 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/contacts/ContactPresenter.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/contacts/ContactPresenter.kt @@ -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(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(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(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 { diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/contacts/ContactsView.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/contacts/ContactsView.kt index 602b87bea..00cdcd9dc 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/contacts/ContactsView.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/contacts/ContactsView.kt @@ -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 }) + } } } } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/contacts/ContactsViewFooterSource.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/contacts/ContactsViewFooterSource.kt index 7272006be..111c1bcf6 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/contacts/ContactsViewFooterSource.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/contacts/ContactsViewFooterSource.kt @@ -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() { - private var lastAnchor: Participant? = null +class ContactsViewFooterSource(private val context: Context, private val elementType: Int) : FooterSource() { + private var lastAnchor: ParticipantElement? = null override fun dependsOn(source: Source<*>): Boolean { return source is ContactsViewSource } - override fun areItemsTheSame(first: Data, second: Data): Boolean { + override fun areItemsTheSame(first: Data, second: Data): Boolean { return first == second } - override fun getElementType(data: Data) = elementType + override fun getElementType(data: Data) = elementType - override fun computeFooters(page: Page, list: List): List> { + override fun computeFooters(page: Page, list: List): List> { lastAnchor = null - val results = arrayListOf>() + val results = arrayListOf>() 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 } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/contacts/ContactsViewHeaderSource.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/contacts/ContactsViewHeaderSource.kt index c8295b251..d97078491 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/contacts/ContactsViewHeaderSource.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/contacts/ContactsViewHeaderSource.kt @@ -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() { +class ContactsHeaderSource(private val context: Context, private val elementType: Int) : HeaderSource() { // Store the last header that was added, even if it belongs to a previous page. private var headersAlreadyAdded = mutableListOf() override fun dependsOn(source: Source<*>) = source is ContactsViewSource - override fun computeHeaders(page: Page, list: List): List> { - val results = arrayListOf>() + override fun computeHeaders(page: Page, list: List): List> { + val results = arrayListOf>() 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): Int { + override fun getElementType(data: Data): Int { return elementType } - override fun areItemsTheSame(first: Data, second: Data): Boolean { + override fun areItemsTheSame(first: Data, second: Data): Boolean { return first == second } } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/contacts/ContactsViewModel.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/contacts/ContactsViewModel.kt index 86a3b9bf3..3af39b941 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/contacts/ContactsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/contacts/ContactsViewModel.kt @@ -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(application) { - private val selectedParticipants = mutableListOf() - val selectedParticipantsLiveData: MutableLiveData> = MutableLiveData() - private val _contacts: MutableLiveData> = MutableLiveData() - val contactsLiveData: LiveData> = _contacts + private val selectedParticipants = mutableListOf() + val selectedParticipantsLiveData: MutableLiveData> = MutableLiveData() + private val _contacts: MutableLiveData> = MutableLiveData() + val contactsLiveData: LiveData> = _contacts private val _operationState = MutableLiveData(ContactsViewOperationStateWrapper(ContactsViewOperationState.WAITING, null, null)) val operationState: LiveData = _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 = sortedList.map { + if (it.userId in selectedUserIds) { + it.selected = true } + + ParticipantElement(it, ParticipantElementType.PARTICIPANT.ordinal) + } as MutableList + + + 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?) { diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/contacts/ContactsViewSource.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/contacts/ContactsViewSource.kt index 6be96c740..aeb76bad0 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/contacts/ContactsViewSource.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/contacts/ContactsViewSource.kt @@ -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(private val data: LiveData>, private val elementType: Int = 0, loadingIndicatorsEnabled: Boolean = true, errorIndicatorEnabled: Boolean = true, emptyIndicatorEnabled: Boolean = true) : MainSource(loadingIndicatorsEnabled, errorIndicatorEnabled, emptyIndicatorEnabled) { +class ContactsViewSource(private val data: LiveData>, loadingIndicatorsEnabled: Boolean = true, errorIndicatorEnabled: Boolean = true, emptyIndicatorEnabled: Boolean = true) : MainSource(loadingIndicatorsEnabled, errorIndicatorEnabled, emptyIndicatorEnabled) { override fun onPageOpened(page: Page, dependencies: List>) { super.onPageOpened(page, dependencies) @@ -39,7 +40,7 @@ class ContactsViewSource(private val data: LiveData>, p } override fun getElementType(data: T): Int { - return elementType + return data.elementType } override fun dependsOn(source: Source<*>) = false @@ -49,6 +50,14 @@ class ContactsViewSource(private val data: LiveData>, 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 } } \ No newline at end of file