mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-27 12:05:22 +00:00
More changes
This commit is contained in:
parent
c1d82cc574
commit
31f4de22cd
@ -202,10 +202,6 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
|
||||
binding.createNewPrivateChatButton.setOnClickListener { showNewConversation() }
|
||||
IP2Country.configureIfNeeded(this@HomeActivity)
|
||||
|
||||
ApplicationContext.getInstance(this@HomeActivity).typingStatusRepository.typingThreads.observe(this) { threadIds ->
|
||||
homeAdapter.typingThreadIDs = (threadIds ?: setOf())
|
||||
}
|
||||
|
||||
// Set up new conversation button
|
||||
binding.newConversationButton.setOnClickListener { showNewConversation() }
|
||||
// Observe blocked contacts changed events
|
||||
|
@ -9,7 +9,6 @@ import androidx.recyclerview.widget.ListUpdateCallback
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.NO_ID
|
||||
import network.loki.messenger.R
|
||||
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
||||
import org.thoughtcrime.securesms.dependencies.ConfigFactory
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||
|
||||
@ -26,7 +25,7 @@ class HomeAdapter(
|
||||
|
||||
var header: View? = null
|
||||
|
||||
var data: List<ThreadRecord> = emptyList()
|
||||
var data: HomeViewModel.HomeData = HomeViewModel.HomeData(emptyList(), emptySet())
|
||||
set(newData) {
|
||||
if (field !== newData) {
|
||||
val diff = HomeDiffUtil(field, newData, context, configFactory)
|
||||
@ -60,18 +59,10 @@ class HomeAdapter(
|
||||
override fun getItemId(position: Int): Long {
|
||||
if (hasHeaderView() && position == 0) return NO_ID
|
||||
val offsetPosition = if (hasHeaderView()) position-1 else position
|
||||
return data[offsetPosition].threadId
|
||||
return data.threads[offsetPosition].threadId
|
||||
}
|
||||
|
||||
lateinit var glide: GlideRequests
|
||||
var typingThreadIDs = setOf<Long>()
|
||||
set(value) {
|
||||
if (field == value) { return }
|
||||
|
||||
field = value
|
||||
// TODO: replace this with a diffed update or a partial change set with payloads
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =
|
||||
when (viewType) {
|
||||
@ -94,8 +85,8 @@ class HomeAdapter(
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
if (holder is ConversationViewHolder) {
|
||||
val offset = if (hasHeaderView()) position - 1 else position
|
||||
val thread = data[offset]
|
||||
val isTyping = typingThreadIDs.contains(thread.threadId)
|
||||
val thread = data.threads[offset]
|
||||
val isTyping = data.typingThreadIDs.contains(thread.threadId)
|
||||
holder.view.bind(thread, isTyping, glide)
|
||||
}
|
||||
}
|
||||
@ -112,7 +103,7 @@ class HomeAdapter(
|
||||
if (hasHeaderView() && position == 0) HEADER
|
||||
else ITEM
|
||||
|
||||
override fun getItemCount(): Int = data.size + if (hasHeaderView()) 1 else 0
|
||||
override fun getItemCount(): Int = data.threads.size + if (hasHeaderView()) 1 else 0
|
||||
|
||||
class ConversationViewHolder(val view: ConversationView) : RecyclerView.ViewHolder(view)
|
||||
|
||||
|
@ -7,22 +7,22 @@ import org.thoughtcrime.securesms.dependencies.ConfigFactory
|
||||
import org.thoughtcrime.securesms.util.getConversationUnread
|
||||
|
||||
class HomeDiffUtil(
|
||||
private val old: List<ThreadRecord>,
|
||||
private val new: List<ThreadRecord>,
|
||||
private val old: HomeViewModel.HomeData,
|
||||
private val new: HomeViewModel.HomeData,
|
||||
private val context: Context,
|
||||
private val configFactory: ConfigFactory
|
||||
): DiffUtil.Callback() {
|
||||
|
||||
override fun getOldListSize(): Int = old.size
|
||||
override fun getOldListSize(): Int = old.threads.size
|
||||
|
||||
override fun getNewListSize(): Int = new.size
|
||||
override fun getNewListSize(): Int = new.threads.size
|
||||
|
||||
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean =
|
||||
old[oldItemPosition].threadId == new[newItemPosition].threadId
|
||||
old.threads[oldItemPosition].threadId == new.threads[newItemPosition].threadId
|
||||
|
||||
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||
val oldItem = old[oldItemPosition]
|
||||
val newItem = new[newItemPosition]
|
||||
val oldItem = old.threads[oldItemPosition]
|
||||
val newItem = new.threads[newItemPosition]
|
||||
|
||||
// return early to save getDisplayBody or expensive calls
|
||||
var isSameItem = true
|
||||
@ -47,7 +47,8 @@ class HomeDiffUtil(
|
||||
oldItem.isSent == newItem.isSent &&
|
||||
oldItem.isPending == newItem.isPending &&
|
||||
oldItem.lastSeen == newItem.lastSeen &&
|
||||
configFactory.convoVolatile?.getConversationUnread(newItem) != true
|
||||
configFactory.convoVolatile?.getConversationUnread(newItem) != true &&
|
||||
old.typingThreadIDs.contains(oldItem.threadId) == new.typingThreadIDs.contains(newItem.threadId)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1,31 +1,37 @@
|
||||
package org.thoughtcrime.securesms.home
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.asFlow
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.database.DatabaseContentProviders
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase
|
||||
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
||||
import org.thoughtcrime.securesms.util.observeChanges
|
||||
import javax.inject.Inject
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext as ApplicationContextQualifier
|
||||
|
||||
@HiltViewModel
|
||||
class HomeViewModel @Inject constructor(
|
||||
private val threadDb: ThreadDatabase,
|
||||
@ApplicationContext appContext: Context,
|
||||
contentResolver: ContentResolver,
|
||||
@ApplicationContextQualifier context: Context,
|
||||
) : ViewModel() {
|
||||
// SharedFlow that emits whenever the user asks us to reload the conversation
|
||||
private val manualReloadTrigger = MutableSharedFlow<Unit>(
|
||||
@ -34,33 +40,52 @@ class HomeViewModel @Inject constructor(
|
||||
)
|
||||
|
||||
/**
|
||||
* A [StateFlow] that emits the list of threads in the conversation list.
|
||||
* A [StateFlow] that emits the list of threads and the typing status of each thread.
|
||||
*
|
||||
* This flow will emit whenever the user asks us to reload the conversation list or
|
||||
* whenever the conversation list changes.
|
||||
*/
|
||||
@Suppress("OPT_IN_USAGE")
|
||||
val threads: StateFlow<List<ThreadRecord>?> = merge(
|
||||
manualReloadTrigger,
|
||||
appContext.contentResolver.observeChanges(DatabaseContentProviders.ConversationList.CONTENT_URI))
|
||||
.debounce(CHANGE_NOTIFICATION_DEBOUNCE_MILLS)
|
||||
.onStart { emit(Unit) }
|
||||
.mapLatest { _ ->
|
||||
withContext(Dispatchers.IO) {
|
||||
threadDb.approvedConversationList.use { openCursor ->
|
||||
val reader = threadDb.readerFor(openCursor)
|
||||
buildList(reader.length) {
|
||||
while (true) {
|
||||
add(reader.next ?: break)
|
||||
val threads: StateFlow<HomeData?> =
|
||||
combine(
|
||||
// The conversation list data
|
||||
merge(
|
||||
manualReloadTrigger,
|
||||
contentResolver.observeChanges(DatabaseContentProviders.ConversationList.CONTENT_URI))
|
||||
.debounce(CHANGE_NOTIFICATION_DEBOUNCE_MILLS)
|
||||
.onStart { emit(Unit) }
|
||||
.mapLatest { _ ->
|
||||
withContext(Dispatchers.IO) {
|
||||
threadDb.approvedConversationList.use { openCursor ->
|
||||
val reader = threadDb.readerFor(openCursor)
|
||||
buildList(reader.length) {
|
||||
while (true) {
|
||||
add(reader.next ?: break)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// The typing status of each thread
|
||||
ApplicationContext.getInstance(context).typingStatusRepository
|
||||
.typingThreads
|
||||
.asFlow()
|
||||
.onStart { emit(emptySet()) }
|
||||
.distinctUntilChanged(),
|
||||
|
||||
// The final result that we emit to the UI
|
||||
::HomeData
|
||||
)
|
||||
.stateIn(viewModelScope, SharingStarted.Eagerly, null)
|
||||
|
||||
fun tryReload() = manualReloadTrigger.tryEmit(Unit)
|
||||
|
||||
data class HomeData(
|
||||
val threads: List<ThreadRecord>,
|
||||
val typingThreadIDs: Set<Long>
|
||||
)
|
||||
|
||||
companion object {
|
||||
private const val CHANGE_NOTIFICATION_DEBOUNCE_MILLS = 100L
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user