mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-25 01:07:47 +00:00
Refresh contacts on open
This commit is contained in:
parent
cad96001d1
commit
853c165949
@ -206,6 +206,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
|
|||||||
// Set up toolbar buttons
|
// Set up toolbar buttons
|
||||||
binding.profileButton.setOnClickListener { openSettings() }
|
binding.profileButton.setOnClickListener { openSettings() }
|
||||||
binding.searchViewContainer.setOnClickListener {
|
binding.searchViewContainer.setOnClickListener {
|
||||||
|
globalSearchViewModel.refresh()
|
||||||
binding.globalSearchInputLayout.requestFocus()
|
binding.globalSearchInputLayout.requestFocus()
|
||||||
}
|
}
|
||||||
binding.sessionToolbar.disableClipping()
|
binding.sessionToolbar.disableClipping()
|
||||||
@ -287,29 +288,25 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
|
|||||||
val hasNames = result.contacts.filter { it.nickname != null || it.name != null }
|
val hasNames = result.contacts.filter { it.nickname != null || it.name != null }
|
||||||
.groupByNotNull { (it.nickname?.firstOrNull() ?: it.name?.firstOrNull())?.uppercase() }
|
.groupByNotNull { (it.nickname?.firstOrNull() ?: it.name?.firstOrNull())?.uppercase() }
|
||||||
.toSortedMap(compareBy { it })
|
.toSortedMap(compareBy { it })
|
||||||
.flatMap { (key, contacts) -> listOf(GlobalSearchAdapter.Model.SubHeader(key)) + contacts.map(GlobalSearchAdapter.Model::Contact) }
|
.flatMap { (key, contacts) -> listOf(GlobalSearchAdapter.Model.SubHeader(key)) + contacts.sortedBy { it.nickname ?: it.name }.map(GlobalSearchAdapter.Model::Contact) }
|
||||||
|
|
||||||
val noNames = result.contacts.filter { it.nickname == null && it.name == null }
|
val noNames = result.contacts.filter { it.nickname == null && it.name == null }
|
||||||
.sortedBy { it.sessionID }
|
.sortedBy { it.sessionID }
|
||||||
.map { GlobalSearchAdapter.Model.Contact(it) }
|
.map { GlobalSearchAdapter.Model.Contact(it) }
|
||||||
.takeIf { it.isNotEmpty() }
|
|
||||||
?.let {
|
|
||||||
buildList {
|
|
||||||
add(GlobalSearchAdapter.Model.Header("Unknown"))
|
|
||||||
addAll(it)
|
|
||||||
}
|
|
||||||
} ?: emptyList()
|
|
||||||
|
|
||||||
buildList {
|
buildList {
|
||||||
add(GlobalSearchAdapter.Model.Header("Contacts"))
|
add(GlobalSearchAdapter.Model.Header(R.string.contacts))
|
||||||
add(GlobalSearchAdapter.Model.SavedMessages(publicKey))
|
add(GlobalSearchAdapter.Model.SavedMessages(publicKey))
|
||||||
addAll(hasNames)
|
addAll(hasNames)
|
||||||
addAll(noNames)
|
noNames.takeIf { it.isNotEmpty() }?.let {
|
||||||
|
add(GlobalSearchAdapter.Model.Header(R.string.unknown))
|
||||||
|
addAll(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val currentUserPublicKey = publicKey
|
val currentUserPublicKey = publicKey
|
||||||
val contactAndGroupList = result.contacts.map { GlobalSearchAdapter.Model.Contact(it) } +
|
val contactAndGroupList = result.contacts.map(GlobalSearchAdapter.Model::Contact) +
|
||||||
result.threads.map { GlobalSearchAdapter.Model.GroupConversation(it) }
|
result.threads.map(GlobalSearchAdapter.Model::GroupConversation)
|
||||||
|
|
||||||
val contactResults = contactAndGroupList.toMutableList()
|
val contactResults = contactAndGroupList.toMutableList()
|
||||||
|
|
||||||
@ -323,7 +320,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (contactResults.isNotEmpty()) {
|
if (contactResults.isNotEmpty()) {
|
||||||
contactResults.add(0, GlobalSearchAdapter.Model.Header(R.string.global_search_contacts_groups))
|
contactResults.add(0, GlobalSearchAdapter.Model.Header(R.string.conversations))
|
||||||
}
|
}
|
||||||
|
|
||||||
val unreadThreadMap = result.messages
|
val unreadThreadMap = result.messages
|
||||||
@ -393,7 +390,8 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
|
|||||||
Spacer(Modifier.width(12.dp))
|
Spacer(Modifier.width(12.dp))
|
||||||
OutlineButton(
|
OutlineButton(
|
||||||
textId = R.string.continue_2,
|
textId = R.string.continue_2,
|
||||||
Modifier.align(Alignment.CenterVertically)
|
Modifier
|
||||||
|
.align(Alignment.CenterVertically)
|
||||||
.contentDescription(R.string.AccessibilityId_reveal_recovery_phrase_button),
|
.contentDescription(R.string.AccessibilityId_reveal_recovery_phrase_button),
|
||||||
onClick = { start<RecoveryPasswordActivity>() }
|
onClick = { start<RecoveryPasswordActivity>() }
|
||||||
)
|
)
|
||||||
|
@ -3,19 +3,27 @@ package org.thoughtcrime.securesms.home.search
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.channels.BufferOverflow
|
import kotlinx.coroutines.channels.BufferOverflow
|
||||||
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.buffer
|
import kotlinx.coroutines.flow.buffer
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.consumeAsFlow
|
||||||
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
|
import kotlinx.coroutines.flow.flowOf
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.mapLatest
|
import kotlinx.coroutines.flow.mapLatest
|
||||||
|
import kotlinx.coroutines.flow.merge
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.plus
|
import kotlinx.coroutines.plus
|
||||||
import org.session.libsession.utilities.truncateIdForDisplay
|
|
||||||
import org.session.libsignal.utilities.SettableFuture
|
import org.session.libsignal.utilities.SettableFuture
|
||||||
import org.thoughtcrime.securesms.database.SessionContactDatabase
|
|
||||||
import org.thoughtcrime.securesms.search.SearchRepository
|
import org.thoughtcrime.securesms.search.SearchRepository
|
||||||
import org.thoughtcrime.securesms.search.model.SearchResult
|
import org.thoughtcrime.securesms.search.model.SearchResult
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
@ -32,6 +40,8 @@ class GlobalSearchViewModel @Inject constructor(
|
|||||||
|
|
||||||
val result: StateFlow<GlobalSearchResult> = _result
|
val result: StateFlow<GlobalSearchResult> = _result
|
||||||
|
|
||||||
|
val refreshes = Channel<Unit>()
|
||||||
|
|
||||||
private val _queryText: MutableStateFlow<CharSequence> = MutableStateFlow("")
|
private val _queryText: MutableStateFlow<CharSequence> = MutableStateFlow("")
|
||||||
|
|
||||||
fun postQuery(charSequence: CharSequence?) {
|
fun postQuery(charSequence: CharSequence?) {
|
||||||
@ -39,14 +49,22 @@ class GlobalSearchViewModel @Inject constructor(
|
|||||||
_queryText.value = charSequence
|
_queryText.value = charSequence
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun refresh() {
|
||||||
|
executor.launch { refreshes.send(Unit) }
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
_queryText.buffer(onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
_queryText
|
||||||
|
.reEmit(refreshes)
|
||||||
|
.buffer(onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
||||||
.mapLatest { query ->
|
.mapLatest { query ->
|
||||||
// User input delay in case we get a new query within a few hundred ms this
|
// User input delay in case we get a new query within a few hundred ms this
|
||||||
// coroutine will be cancelled and the expensive query will not be run.
|
// coroutine will be cancelled and the expensive query will not be run.
|
||||||
delay(300)
|
delay(300)
|
||||||
|
|
||||||
if (query.trim().isEmpty()) {
|
if (query.trim().isEmpty()) {
|
||||||
|
// searching for 05 as contactDb#getAllContacts was not returning contacts
|
||||||
|
// without a nickname/name who haven't approved us.
|
||||||
GlobalSearchResult(query.toString(), searchRepository.queryContacts("05").first.toList())
|
GlobalSearchResult(query.toString(), searchRepository.queryContacts("05").first.toList())
|
||||||
} else {
|
} else {
|
||||||
val settableFuture = SettableFuture<SearchResult>()
|
val settableFuture = SettableFuture<SearchResult>()
|
||||||
@ -64,3 +82,9 @@ class GlobalSearchViewModel @Inject constructor(
|
|||||||
}.launchIn(executor)
|
}.launchIn(executor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-emit whenevr refreshes emits.
|
||||||
|
* */
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
private fun <T> Flow<T>.reEmit(refreshes: Channel<Unit>) = flatMapLatest { query -> merge(flowOf(query), refreshes.consumeAsFlow().map { query }) }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user