Move unapprovedMessageCount to IO

This commit is contained in:
Andrew 2024-05-28 12:57:50 +09:30
parent b757691334
commit cd302f9f27
3 changed files with 51 additions and 34 deletions

View File

@ -185,7 +185,6 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
binding.seedReminderView.isVisible = false binding.seedReminderView.isVisible = false
} }
} }
setupMessageRequestsBanner()
// Set up recycler view // Set up recycler view
binding.globalSearchInputLayout.listener = this binding.globalSearchInputLayout.listener = this
homeAdapter.setHasStableIds(true) homeAdapter.setHasStableIds(true)
@ -218,9 +217,9 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
// Subscribe to threads and update the UI // Subscribe to threads and update the UI
lifecycleScope.launch { lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) { repeatOnLifecycle(Lifecycle.State.STARTED) {
homeViewModel.threads homeViewModel.data
.filterNotNull() // We don't actually want the null value here as it indicates a loading state (maybe we need a loading state?) .filterNotNull() // We don't actually want the null value here as it indicates a loading state (maybe we need a loading state?)
.collectLatest { threads -> .collectLatest { data ->
val manager = binding.recyclerView.layoutManager as LinearLayoutManager val manager = binding.recyclerView.layoutManager as LinearLayoutManager
val firstPos = manager.findFirstCompletelyVisibleItemPosition() val firstPos = manager.findFirstCompletelyVisibleItemPosition()
val offsetTop = if(firstPos >= 0) { val offsetTop = if(firstPos >= 0) {
@ -228,9 +227,9 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
manager.getDecoratedTop(view) - manager.getTopDecorationHeight(view) manager.getDecoratedTop(view) - manager.getTopDecorationHeight(view)
} ?: 0 } ?: 0
} else 0 } else 0
homeAdapter.data = threads homeAdapter.data = data
if(firstPos >= 0) { manager.scrollToPositionWithOffset(firstPos, offsetTop) } if(firstPos >= 0) { manager.scrollToPositionWithOffset(firstPos, offsetTop) }
setupMessageRequestsBanner() setupMessageRequestsBanner(data.unapprovedConversationCount, data.hasHiddenMessageRequests)
updateEmptyState() updateEmptyState()
} }
} }
@ -341,10 +340,9 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
binding.newConversationButton.isVisible = !isShown binding.newConversationButton.isVisible = !isShown
} }
private fun setupMessageRequestsBanner() { private fun setupMessageRequestsBanner(messageRequestCount: Int, hasHiddenMessageRequests: Boolean) {
val messageRequestCount = threadDb.unapprovedConversationCount
// Set up message requests // Set up message requests
if (messageRequestCount > 0 && !textSecurePreferences.hasHiddenMessageRequests()) { if (messageRequestCount > 0 && !hasHiddenMessageRequests) {
with(ViewMessageRequestBannerBinding.inflate(layoutInflater)) { with(ViewMessageRequestBannerBinding.inflate(layoutInflater)) {
unreadCountTextView.text = messageRequestCount.toString() unreadCountTextView.text = messageRequestCount.toString()
timestampTextView.text = DateUtils.getDisplayFormattedTimeSpanString( timestampTextView.text = DateUtils.getDisplayFormattedTimeSpanString(
@ -664,7 +662,6 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
text("Hide message requests?") text("Hide message requests?")
button(R.string.yes) { button(R.string.yes) {
textSecurePreferences.setHasHiddenMessageRequests() textSecurePreferences.setHasHiddenMessageRequests()
setupMessageRequestsBanner()
homeViewModel.tryReload() homeViewModel.tryReload()
} }
button(R.string.no) button(R.string.no)

View File

@ -25,11 +25,9 @@ class HomeAdapter(
var header: View? = null var header: View? = null
var data: HomeViewModel.Data = HomeViewModel.Data(emptyList(), emptySet()) var data: HomeViewModel.Data = HomeViewModel.Data(emptyList(), 0, false, emptySet())
set(newData) { set(newData) {
if (field === newData) { if (field === newData) return
return
}
val diff = HomeDiffUtil(field, newData, context, configFactory) val diff = HomeDiffUtil(field, newData, context, configFactory)
val diffResult = DiffUtil.calculateDiff(diff) val diffResult = DiffUtil.calculateDiff(diff)

View File

@ -15,11 +15,14 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext import org.session.libsession.utilities.TextSecurePreferences
import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.database.DatabaseContentProviders import org.thoughtcrime.securesms.database.DatabaseContentProviders
import org.thoughtcrime.securesms.database.ThreadDatabase import org.thoughtcrime.securesms.database.ThreadDatabase
@ -30,9 +33,10 @@ import dagger.hilt.android.qualifiers.ApplicationContext as ApplicationContextQu
@HiltViewModel @HiltViewModel
class HomeViewModel @Inject constructor( class HomeViewModel @Inject constructor(
private val threadDb: ThreadDatabase, private val threadDb: ThreadDatabase,
private val contentResolver: ContentResolver, private val contentResolver: ContentResolver,
@ApplicationContextQualifier private val context: Context, private val prefs: TextSecurePreferences,
@ApplicationContextQualifier private val context: Context,
) : ViewModel() { ) : ViewModel() {
// SharedFlow that emits whenever the user asks us to reload the conversation // SharedFlow that emits whenever the user asks us to reload the conversation
private val manualReloadTrigger = MutableSharedFlow<Unit>( private val manualReloadTrigger = MutableSharedFlow<Unit>(
@ -46,8 +50,18 @@ class HomeViewModel @Inject constructor(
* This flow will emit whenever the user asks us to reload the conversation list or * This flow will emit whenever the user asks us to reload the conversation list or
* whenever the conversation list changes. * whenever the conversation list changes.
*/ */
val threads: StateFlow<Data?> = combine(observeConversationList(), observeTypingStatus(), ::Data) val data: StateFlow<Data?> = combine(
.stateIn(viewModelScope, SharingStarted.Eagerly, null) observeConversationList(),
unapprovedConversationCount(),
hasHiddenMessageRequestsFlow(),
observeTypingStatus(),
::Data
).stateIn(viewModelScope, SharingStarted.Eagerly, null)
private fun hasHiddenMessageRequestsFlow() = TextSecurePreferences.events
.filter { it == TextSecurePreferences.HAS_HIDDEN_MESSAGE_REQUESTS }
.map { prefs.hasHiddenMessageRequests() }
.onStart { emit(prefs.hasHiddenMessageRequests()) }
private fun observeTypingStatus(): Flow<Set<Long>> = private fun observeTypingStatus(): Flow<Set<Long>> =
ApplicationContext.getInstance(context).typingStatusRepository ApplicationContext.getInstance(context).typingStatusRepository
@ -56,30 +70,38 @@ class HomeViewModel @Inject constructor(
.onStart { emit(emptySet()) } .onStart { emit(emptySet()) }
.distinctUntilChanged() .distinctUntilChanged()
private fun unapprovedConversationCount() =
contentResolver.observeChanges(DatabaseContentProviders.ConversationList.CONTENT_URI)
.flowOn(Dispatchers.IO)
.map { threadDb.unapprovedConversationCount }
.onStart { emit(threadDb.unapprovedConversationCount) }
@Suppress("OPT_IN_USAGE") @Suppress("OPT_IN_USAGE")
private fun observeConversationList(): Flow<List<ThreadRecord>> = merge( private fun observeConversationList(): Flow<List<ThreadRecord>> = merge(
manualReloadTrigger, manualReloadTrigger,
contentResolver.observeChanges(DatabaseContentProviders.ConversationList.CONTENT_URI)) contentResolver.observeChanges(DatabaseContentProviders.ConversationList.CONTENT_URI)
.debounce(CHANGE_NOTIFICATION_DEBOUNCE_MILLS) )
.onStart { emit(Unit) } .flowOn(Dispatchers.IO)
.mapLatest { _ -> .debounce(CHANGE_NOTIFICATION_DEBOUNCE_MILLS)
withContext(Dispatchers.IO) { .onStart { emit(Unit) }
threadDb.approvedConversationList.use { openCursor -> .mapLatest { _ ->
val reader = threadDb.readerFor(openCursor) threadDb.approvedConversationList.use { openCursor ->
buildList(reader.count) { val reader = threadDb.readerFor(openCursor)
while (true) { buildList(reader.count) {
add(reader.next ?: break) while (true) {
} add(reader.next ?: break)
}
} }
} }
} }
}
fun tryReload() = manualReloadTrigger.tryEmit(Unit) fun tryReload() = manualReloadTrigger.tryEmit(Unit)
data class Data( data class Data(
val threads: List<ThreadRecord>, val threads: List<ThreadRecord>,
val typingThreadIDs: Set<Long> val unapprovedConversationCount: Int,
val hasHiddenMessageRequests: Boolean,
val typingThreadIDs: Set<Long>
) )
companion object { companion object {