mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-21 17:29:32 +01:00
Add support for message and thread results.
This commit is contained in:
committed by
Greyson Parrelli
parent
8dd1d3bdeb
commit
b4a34599d7
@@ -11,7 +11,13 @@ interface ArbitraryRepository {
|
||||
/**
|
||||
* Get the data for the given arbitrary rows within the start and end index.
|
||||
*/
|
||||
fun getData(section: ContactSearchConfiguration.Section.Arbitrary, query: String?, startIndex: Int, endIndex: Int): List<ContactSearchData.Arbitrary>
|
||||
fun getData(
|
||||
section: ContactSearchConfiguration.Section.Arbitrary,
|
||||
query: String?,
|
||||
startIndex: Int,
|
||||
endIndex: Int,
|
||||
totalSearchSize: Int
|
||||
): List<ContactSearchData.Arbitrary>
|
||||
|
||||
/**
|
||||
* Map an arbitrary object to a mapping model
|
||||
|
||||
@@ -94,6 +94,9 @@ open class ContactSearchAdapter(
|
||||
is ContactSearchData.Header -> HeaderModel(it)
|
||||
is ContactSearchData.TestRow -> error("This row exists for testing only.")
|
||||
is ContactSearchData.Arbitrary -> arbitraryRepository?.getMappingModel(it) ?: error("This row must be handled manually")
|
||||
is ContactSearchData.Message -> MessageModel(it)
|
||||
is ContactSearchData.Thread -> ThreadModel(it)
|
||||
is ContactSearchData.Empty -> EmptyModel(it)
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -294,7 +297,8 @@ open class ContactSearchAdapter(
|
||||
/**
|
||||
* Base Recipient View Holder
|
||||
*/
|
||||
private abstract class BaseRecipientViewHolder<T : MappingModel<T>, D : ContactSearchData>(
|
||||
|
||||
abstract class BaseRecipientViewHolder<T : MappingModel<T>, D : ContactSearchData>(
|
||||
itemView: View,
|
||||
private val displayCheckBox: Boolean,
|
||||
private val displaySmsTag: DisplaySmsTag,
|
||||
@@ -391,6 +395,32 @@ open class ContactSearchAdapter(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapping Model for messages
|
||||
*/
|
||||
class MessageModel(val message: ContactSearchData.Message) : MappingModel<MessageModel> {
|
||||
override fun areItemsTheSame(newItem: MessageModel): Boolean = message.contactSearchKey == newItem.message.contactSearchKey
|
||||
|
||||
override fun areContentsTheSame(newItem: MessageModel): Boolean {
|
||||
return message == newItem.message
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapping Model for threads
|
||||
*/
|
||||
class ThreadModel(val thread: ContactSearchData.Thread) : MappingModel<ThreadModel> {
|
||||
override fun areItemsTheSame(newItem: ThreadModel): Boolean = thread.contactSearchKey == newItem.thread.contactSearchKey
|
||||
override fun areContentsTheSame(newItem: ThreadModel): Boolean {
|
||||
return thread == newItem.thread
|
||||
}
|
||||
}
|
||||
|
||||
class EmptyModel(val empty: ContactSearchData.Empty) : MappingModel<EmptyModel> {
|
||||
override fun areItemsTheSame(newItem: EmptyModel): Boolean = true
|
||||
override fun areContentsTheSame(newItem: EmptyModel): Boolean = newItem.empty == empty
|
||||
}
|
||||
|
||||
/**
|
||||
* View Holder for section headers
|
||||
*/
|
||||
@@ -408,6 +438,8 @@ open class ContactSearchAdapter(
|
||||
ContactSearchConfiguration.SectionKey.GROUPS -> R.string.ContactsCursorLoader_groups
|
||||
ContactSearchConfiguration.SectionKey.ARBITRARY -> error("This section does not support HEADER")
|
||||
ContactSearchConfiguration.SectionKey.GROUP_MEMBERS -> R.string.ContactsCursorLoader_group_members
|
||||
ContactSearchConfiguration.SectionKey.CHATS -> R.string.ContactsCursorLoader__chats
|
||||
ContactSearchConfiguration.SectionKey.MESSAGES -> R.string.ContactsCursorLoader__messages
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import org.thoughtcrime.securesms.contacts.HeaderAction
|
||||
*/
|
||||
class ContactSearchConfiguration private constructor(
|
||||
val query: String?,
|
||||
val hasEmptyState: Boolean,
|
||||
val sections: List<Section>
|
||||
) {
|
||||
sealed class Section(val sectionKey: SectionKey) {
|
||||
@@ -81,6 +82,17 @@ class ContactSearchConfiguration private constructor(
|
||||
override val includeHeader: Boolean = true,
|
||||
override val expandConfig: ExpandConfig? = null
|
||||
) : Section(SectionKey.GROUP_MEMBERS)
|
||||
|
||||
data class Chats(
|
||||
val isUnreadOnly: Boolean = false,
|
||||
override val includeHeader: Boolean = true,
|
||||
override val expandConfig: ExpandConfig? = null
|
||||
) : Section(SectionKey.CHATS)
|
||||
|
||||
data class Messages(
|
||||
override val includeHeader: Boolean = true,
|
||||
override val expandConfig: ExpandConfig? = null
|
||||
) : Section(SectionKey.MESSAGES)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,7 +128,17 @@ class ContactSearchConfiguration private constructor(
|
||||
* Contacts that are members of groups user is in that they've not explicitly
|
||||
* started a conversation with.
|
||||
*/
|
||||
GROUP_MEMBERS
|
||||
GROUP_MEMBERS,
|
||||
|
||||
/**
|
||||
* 1:1 and Group chats
|
||||
*/
|
||||
CHATS,
|
||||
|
||||
/**
|
||||
* Messages from 1:1 and Group chats
|
||||
*/
|
||||
MESSAGES
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,6 +169,7 @@ class ContactSearchConfiguration private constructor(
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
@JvmStatic
|
||||
fun build(builderFunction: Builder.() -> Unit): ContactSearchConfiguration {
|
||||
return ConfigurationBuilder().let {
|
||||
it.builderFunction()
|
||||
@@ -162,13 +185,14 @@ class ContactSearchConfiguration private constructor(
|
||||
private val sections: MutableList<Section> = mutableListOf()
|
||||
|
||||
override var query: String? = null
|
||||
override var hasEmptyState: Boolean = false
|
||||
|
||||
override fun addSection(section: Section) {
|
||||
sections.add(section)
|
||||
}
|
||||
|
||||
fun build(): ContactSearchConfiguration {
|
||||
return ContactSearchConfiguration(query, sections)
|
||||
return ContactSearchConfiguration(query, hasEmptyState, sections)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,6 +201,8 @@ class ContactSearchConfiguration private constructor(
|
||||
*/
|
||||
interface Builder {
|
||||
var query: String?
|
||||
var hasEmptyState: Boolean
|
||||
|
||||
fun arbitrary(first: String, vararg rest: String) {
|
||||
addSection(Section.Arbitrary(setOf(first) + rest.toSet()))
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@ import android.os.Bundle
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import org.thoughtcrime.securesms.contacts.HeaderAction
|
||||
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
|
||||
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.search.MessageResult
|
||||
|
||||
/**
|
||||
* Represents the data backed by a ContactSearchKey
|
||||
@@ -32,6 +34,22 @@ sealed class ContactSearchData(val contactSearchKey: ContactSearchKey) {
|
||||
val groupsInCommon: GroupsInCommon = GroupsInCommon(0, listOf())
|
||||
) : ContactSearchData(ContactSearchKey.RecipientSearchKey(recipient.id, false))
|
||||
|
||||
/**
|
||||
* A row displaying a message
|
||||
*/
|
||||
data class Message(
|
||||
val query: String,
|
||||
val messageResult: MessageResult
|
||||
) : ContactSearchData(ContactSearchKey.Message(messageResult.messageId))
|
||||
|
||||
/**
|
||||
* A row displaying a thread
|
||||
*/
|
||||
data class Thread(
|
||||
val query: String,
|
||||
val threadRecord: ThreadRecord
|
||||
) : ContactSearchData(ContactSearchKey.Thread(threadRecord.threadId))
|
||||
|
||||
/**
|
||||
* A row containing a title for a given section
|
||||
*/
|
||||
@@ -50,6 +68,11 @@ sealed class ContactSearchData(val contactSearchKey: ContactSearchKey) {
|
||||
*/
|
||||
class Arbitrary(val type: String, val data: Bundle? = null) : ContactSearchData(ContactSearchKey.Arbitrary(type))
|
||||
|
||||
/**
|
||||
* Empty state, only included if no other rows exist.
|
||||
*/
|
||||
data class Empty(val query: String?) : ContactSearchData(ContactSearchKey.Empty)
|
||||
|
||||
/**
|
||||
* A row which contains an integer, for testing.
|
||||
*/
|
||||
|
||||
@@ -42,4 +42,16 @@ sealed class ContactSearchKey {
|
||||
* This is used to allow arbitrary extra data to be added to the contact search system.
|
||||
*/
|
||||
data class Arbitrary(val type: String) : ContactSearchKey()
|
||||
|
||||
/**
|
||||
* Search key for a ThreadRecord
|
||||
*/
|
||||
data class Thread(val threadId: Long) : ContactSearchKey()
|
||||
|
||||
/**
|
||||
* Search key for a MessageRecord
|
||||
*/
|
||||
data class Message(val messageId: Long) : ContactSearchKey()
|
||||
|
||||
object Empty : ContactSearchKey()
|
||||
}
|
||||
|
||||
@@ -5,23 +5,25 @@ import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.conversationlist.chatfilter.ConversationFilterRequest
|
||||
import org.thoughtcrime.securesms.groups.SelectionLimits
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.search.SearchRepository
|
||||
import org.thoughtcrime.securesms.stories.settings.custom.PrivateStorySettingsFragment
|
||||
import org.thoughtcrime.securesms.stories.settings.my.MyStorySettingsFragment
|
||||
import org.thoughtcrime.securesms.stories.settings.privacy.ChooseInitialMyStoryMembershipBottomSheetDialogFragment
|
||||
import org.thoughtcrime.securesms.util.Debouncer
|
||||
import org.thoughtcrime.securesms.util.SpanUtil
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.PagingMappingAdapter
|
||||
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class ContactSearchMediator(
|
||||
private val fragment: Fragment,
|
||||
recyclerView: RecyclerView,
|
||||
selectionLimits: SelectionLimits,
|
||||
displayCheckBox: Boolean,
|
||||
displaySmsTag: ContactSearchAdapter.DisplaySmsTag,
|
||||
@@ -29,25 +31,31 @@ class ContactSearchMediator(
|
||||
private val contactSelectionPreFilter: (View?, Set<ContactSearchKey>) -> Set<ContactSearchKey> = { _, s -> s },
|
||||
performSafetyNumberChecks: Boolean = true,
|
||||
adapterFactory: AdapterFactory = DefaultAdapterFactory,
|
||||
arbitraryRepository: ArbitraryRepository? = null
|
||||
arbitraryRepository: ArbitraryRepository? = null,
|
||||
) {
|
||||
|
||||
private val queryDebouncer = Debouncer(300, TimeUnit.MILLISECONDS)
|
||||
|
||||
private val viewModel: ContactSearchViewModel = ViewModelProvider(
|
||||
fragment,
|
||||
ContactSearchViewModel.Factory(selectionLimits, ContactSearchRepository(), performSafetyNumberChecks, arbitraryRepository)
|
||||
ContactSearchViewModel.Factory(
|
||||
selectionLimits = selectionLimits,
|
||||
repository = ContactSearchRepository(),
|
||||
performSafetyNumberChecks = performSafetyNumberChecks,
|
||||
arbitraryRepository = arbitraryRepository,
|
||||
searchRepository = SearchRepository(fragment.requireContext().getString(R.string.note_to_self))
|
||||
)
|
||||
)[ContactSearchViewModel::class.java]
|
||||
|
||||
val adapter = adapterFactory.create(
|
||||
displayCheckBox = displayCheckBox,
|
||||
displaySmsTag = displaySmsTag,
|
||||
recipientListener = this::toggleSelection,
|
||||
storyListener = this::toggleStorySelection,
|
||||
storyContextMenuCallbacks = StoryContextMenuCallbacks()
|
||||
) { viewModel.expandSection(it.sectionKey) }
|
||||
|
||||
init {
|
||||
val adapter = adapterFactory.create(
|
||||
displayCheckBox,
|
||||
displaySmsTag,
|
||||
this::toggleSelection,
|
||||
this::toggleStorySelection,
|
||||
StoryContextMenuCallbacks()
|
||||
) { viewModel.expandSection(it.sectionKey) }
|
||||
|
||||
recyclerView.adapter = adapter
|
||||
|
||||
val dataAndSelection: LiveData<Pair<List<ContactSearchData>, Set<ContactSearchKey>>> = LiveDataUtil.combineLatest(
|
||||
viewModel.data,
|
||||
viewModel.selectionState,
|
||||
@@ -68,7 +76,13 @@ class ContactSearchMediator(
|
||||
}
|
||||
|
||||
fun onFilterChanged(filter: String?) {
|
||||
viewModel.setQuery(filter)
|
||||
queryDebouncer.publish {
|
||||
viewModel.setQuery(filter)
|
||||
}
|
||||
}
|
||||
|
||||
fun onConversationFilterRequestChanged(conversationFilterRequest: ConversationFilterRequest) {
|
||||
viewModel.setConversationFilterRequest(conversationFilterRequest)
|
||||
}
|
||||
|
||||
fun setKeysSelected(keys: Set<ContactSearchKey>) {
|
||||
|
||||
@@ -8,10 +8,15 @@ import org.thoughtcrime.securesms.contacts.paged.collections.CursorSearchIterato
|
||||
import org.thoughtcrime.securesms.contacts.paged.collections.StoriesSearchCollection
|
||||
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
|
||||
import org.thoughtcrime.securesms.database.model.GroupRecord
|
||||
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.keyvalue.StorySend
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.search.MessageResult
|
||||
import org.thoughtcrime.securesms.search.MessageSearchResult
|
||||
import org.thoughtcrime.securesms.search.SearchRepository
|
||||
import org.thoughtcrime.securesms.search.ThreadSearchResult
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
@@ -20,7 +25,8 @@ import java.util.concurrent.TimeUnit
|
||||
class ContactSearchPagedDataSource(
|
||||
private val contactConfiguration: ContactSearchConfiguration,
|
||||
private val contactSearchPagedDataSourceRepository: ContactSearchPagedDataSourceRepository = ContactSearchPagedDataSourceRepository(ApplicationDependencies.getApplication()),
|
||||
private val arbitraryRepository: ArbitraryRepository? = null
|
||||
private val arbitraryRepository: ArbitraryRepository? = null,
|
||||
private val searchRepository: SearchRepository? = null
|
||||
) : PagedDataSource<ContactSearchKey, ContactSearchData> {
|
||||
|
||||
companion object {
|
||||
@@ -31,13 +37,26 @@ class ContactSearchPagedDataSource(
|
||||
|
||||
private val activeStoryCount = latestStorySends.size
|
||||
|
||||
private var searchCache = SearchCache()
|
||||
private var searchSize = -1
|
||||
|
||||
override fun size(): Int {
|
||||
return contactConfiguration.sections.sumOf {
|
||||
searchSize = contactConfiguration.sections.sumOf {
|
||||
getSectionSize(it, contactConfiguration.query)
|
||||
}
|
||||
|
||||
return if (searchSize == 0 && contactConfiguration.hasEmptyState) {
|
||||
1
|
||||
} else {
|
||||
searchSize
|
||||
}
|
||||
}
|
||||
|
||||
override fun load(start: Int, length: Int, cancellationSignal: PagedDataSource.CancellationSignal): MutableList<ContactSearchData> {
|
||||
if (searchSize == 0 && contactConfiguration.hasEmptyState) {
|
||||
return mutableListOf(ContactSearchData.Empty(contactConfiguration.query))
|
||||
}
|
||||
|
||||
val sizeMap: Map<ContactSearchConfiguration.Section, Int> = contactConfiguration.sections.associateWith { getSectionSize(it, contactConfiguration.query) }
|
||||
val startIndex: Index = findIndex(sizeMap, start)
|
||||
val endIndex: Index = findIndex(sizeMap, start + length)
|
||||
@@ -92,6 +111,8 @@ class ContactSearchPagedDataSource(
|
||||
is ContactSearchConfiguration.Section.Stories -> getStoriesSearchIterator(query).getCollectionSize(section, query, null)
|
||||
is ContactSearchConfiguration.Section.Arbitrary -> arbitraryRepository?.getSize(section, query) ?: error("Invalid arbitrary section.")
|
||||
is ContactSearchConfiguration.Section.GroupMembers -> getGroupMembersSearchIterator(query).getCollectionSize(section, query, null)
|
||||
is ContactSearchConfiguration.Section.Chats -> getThreadData(query, section.isUnreadOnly).getCollectionSize(section, query, null)
|
||||
is ContactSearchConfiguration.Section.Messages -> getMessageData(query).getCollectionSize(section, query, null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,8 +143,10 @@ class ContactSearchPagedDataSource(
|
||||
is ContactSearchConfiguration.Section.Individuals -> getNonGroupContactsData(section, query, startIndex, endIndex)
|
||||
is ContactSearchConfiguration.Section.Recents -> getRecentsContactData(section, query, startIndex, endIndex)
|
||||
is ContactSearchConfiguration.Section.Stories -> getStoriesContactData(section, query, startIndex, endIndex)
|
||||
is ContactSearchConfiguration.Section.Arbitrary -> arbitraryRepository?.getData(section, query, startIndex, endIndex) ?: error("Invalid arbitrary section.")
|
||||
is ContactSearchConfiguration.Section.Arbitrary -> arbitraryRepository?.getData(section, query, startIndex, endIndex, searchSize) ?: error("Invalid arbitrary section.")
|
||||
is ContactSearchConfiguration.Section.GroupMembers -> getGroupMembersContactData(section, query, startIndex, endIndex)
|
||||
is ContactSearchConfiguration.Section.Chats -> getThreadContactData(section, query, startIndex, endIndex)
|
||||
is ContactSearchConfiguration.Section.Messages -> getMessageContactData(section, query, startIndex, endIndex)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,6 +300,63 @@ class ContactSearchPagedDataSource(
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMessageData(query: String?): ContactSearchIterator<MessageResult> {
|
||||
check(searchRepository != null)
|
||||
|
||||
if (searchCache.messageSearchResult == null && query != null) {
|
||||
searchCache = searchCache.copy(messageSearchResult = searchRepository.queryMessagesSync(query))
|
||||
}
|
||||
|
||||
return if (query != null) {
|
||||
ListSearchIterator(searchCache.messageSearchResult!!.results)
|
||||
} else {
|
||||
ListSearchIterator(emptyList())
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMessageContactData(section: ContactSearchConfiguration.Section.Messages, query: String?, startIndex: Int, endIndex: Int): List<ContactSearchData> {
|
||||
return getMessageData(query).use { records ->
|
||||
readContactData(
|
||||
records = records,
|
||||
recordsPredicate = null,
|
||||
section = section,
|
||||
startIndex = startIndex,
|
||||
endIndex = endIndex,
|
||||
recordMapper = {
|
||||
ContactSearchData.Message(query ?: "", it)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getThreadData(query: String?, unreadOnly: Boolean): ContactSearchIterator<ThreadRecord> {
|
||||
check(searchRepository != null)
|
||||
if (searchCache.threadSearchResult == null && query != null) {
|
||||
searchCache = searchCache.copy(threadSearchResult = searchRepository.queryThreadsSync(query, unreadOnly))
|
||||
}
|
||||
|
||||
return if (query != null) {
|
||||
ListSearchIterator(searchCache.threadSearchResult!!.results)
|
||||
} else {
|
||||
ListSearchIterator(emptyList())
|
||||
}
|
||||
}
|
||||
|
||||
private fun getThreadContactData(section: ContactSearchConfiguration.Section.Chats, query: String?, startIndex: Int, endIndex: Int): List<ContactSearchData> {
|
||||
return getThreadData(query, section.isUnreadOnly).use { records ->
|
||||
readContactData(
|
||||
records = records,
|
||||
recordsPredicate = null,
|
||||
section = section,
|
||||
startIndex = startIndex,
|
||||
endIndex = endIndex,
|
||||
recordMapper = {
|
||||
ContactSearchData.Thread(query ?: "", it)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun <R> createResultsCollection(
|
||||
section: ContactSearchConfiguration.Section,
|
||||
records: ContactSearchIterator<R>,
|
||||
@@ -290,6 +370,14 @@ class ContactSearchPagedDataSource(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches search results of particularly intensive queries.
|
||||
*/
|
||||
private data class SearchCache(
|
||||
val messageSearchResult: MessageSearchResult? = null,
|
||||
val threadSearchResult: ThreadSearchResult? = null
|
||||
)
|
||||
|
||||
/**
|
||||
* StoryComparator
|
||||
*/
|
||||
@@ -308,4 +396,21 @@ class ContactSearchPagedDataSource(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ListSearchIterator<T>(val list: List<T>) : ContactSearchIterator<T> {
|
||||
|
||||
private var position = -1
|
||||
|
||||
override fun moveToPosition(n: Int) {
|
||||
position = n
|
||||
}
|
||||
|
||||
override fun getCount(): Int = list.size
|
||||
|
||||
override fun hasNext(): Boolean = position < list.lastIndex
|
||||
|
||||
override fun next(): T = list[++position]
|
||||
|
||||
override fun close() = Unit
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,8 @@ class ContactSearchRepository {
|
||||
return Single.fromCallable {
|
||||
contactSearchKeys.map {
|
||||
val isSelectable = when (it) {
|
||||
is ContactSearchKey.Expand -> false
|
||||
is ContactSearchKey.Header -> false
|
||||
is ContactSearchKey.RecipientSearchKey -> canSelectRecipient(it.recipientId)
|
||||
is ContactSearchKey.Arbitrary -> false
|
||||
else -> false
|
||||
}
|
||||
ContactSearchSelectionResult(it, isSelectable)
|
||||
}.toSet()
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package org.thoughtcrime.securesms.contacts.paged
|
||||
|
||||
import org.thoughtcrime.securesms.conversationlist.chatfilter.ConversationFilterRequest
|
||||
|
||||
/**
|
||||
* Simple search state for contacts.
|
||||
*/
|
||||
data class ContactSearchState(
|
||||
val query: String? = null,
|
||||
val conversationFilterRequest: ConversationFilterRequest? = null,
|
||||
val expandedSections: Set<ContactSearchConfiguration.SectionKey> = emptySet(),
|
||||
val groupStories: Set<ContactSearchData.Story> = emptySet()
|
||||
)
|
||||
|
||||
@@ -13,9 +13,11 @@ import org.signal.paging.LivePagedData
|
||||
import org.signal.paging.PagedData
|
||||
import org.signal.paging.PagingConfig
|
||||
import org.signal.paging.PagingController
|
||||
import org.thoughtcrime.securesms.conversationlist.chatfilter.ConversationFilterRequest
|
||||
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
|
||||
import org.thoughtcrime.securesms.groups.SelectionLimits
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.search.SearchRepository
|
||||
import org.thoughtcrime.securesms.util.livedata.Store
|
||||
import org.whispersystems.signalservice.api.util.Preconditions
|
||||
|
||||
@@ -27,7 +29,8 @@ class ContactSearchViewModel(
|
||||
private val contactSearchRepository: ContactSearchRepository,
|
||||
private val performSafetyNumberChecks: Boolean,
|
||||
private val safetyNumberRepository: SafetyNumberRepository = SafetyNumberRepository(),
|
||||
private val arbitraryRepository: ArbitraryRepository?
|
||||
private val arbitraryRepository: ArbitraryRepository?,
|
||||
private val searchRepository: SearchRepository
|
||||
) : ViewModel() {
|
||||
|
||||
private val disposables = CompositeDisposable()
|
||||
@@ -54,7 +57,7 @@ class ContactSearchViewModel(
|
||||
}
|
||||
|
||||
fun setConfiguration(contactSearchConfiguration: ContactSearchConfiguration) {
|
||||
val pagedDataSource = ContactSearchPagedDataSource(contactSearchConfiguration, arbitraryRepository = arbitraryRepository)
|
||||
val pagedDataSource = ContactSearchPagedDataSource(contactSearchConfiguration, arbitraryRepository = arbitraryRepository, searchRepository = searchRepository)
|
||||
pagedData.value = PagedData.createForLiveData(pagedDataSource, pagingConfig)
|
||||
}
|
||||
|
||||
@@ -62,6 +65,10 @@ class ContactSearchViewModel(
|
||||
configurationStore.update { it.copy(query = query) }
|
||||
}
|
||||
|
||||
fun setConversationFilterRequest(conversationFilterRequest: ConversationFilterRequest) {
|
||||
configurationStore.update { it.copy(conversationFilterRequest = conversationFilterRequest) }
|
||||
}
|
||||
|
||||
fun expandSection(sectionKey: ContactSearchConfiguration.SectionKey) {
|
||||
configurationStore.update { it.copy(expandedSections = it.expandedSections + sectionKey) }
|
||||
}
|
||||
@@ -141,10 +148,19 @@ class ContactSearchViewModel(
|
||||
private val selectionLimits: SelectionLimits,
|
||||
private val repository: ContactSearchRepository,
|
||||
private val performSafetyNumberChecks: Boolean,
|
||||
private val arbitraryRepository: ArbitraryRepository?
|
||||
private val arbitraryRepository: ArbitraryRepository?,
|
||||
private val searchRepository: SearchRepository
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return modelClass.cast(ContactSearchViewModel(selectionLimits, repository, performSafetyNumberChecks, arbitraryRepository = arbitraryRepository)) as T
|
||||
return modelClass.cast(
|
||||
ContactSearchViewModel(
|
||||
selectionLimits = selectionLimits,
|
||||
contactSearchRepository = repository,
|
||||
performSafetyNumberChecks = performSafetyNumberChecks,
|
||||
arbitraryRepository = arbitraryRepository,
|
||||
searchRepository = searchRepository
|
||||
)
|
||||
) as T
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user