Fix last seen channel being closed by consuming flow

This commit is contained in:
Andrew 2024-02-05 09:29:44 +10:30
parent a326c47e56
commit a47bf230e2

View File

@ -44,6 +44,7 @@ import androidx.fragment.app.DialogFragment
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import androidx.loader.app.LoaderManager import androidx.loader.app.LoaderManager
@ -58,6 +59,7 @@ import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.consumeAsFlow import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import network.loki.messenger.R import network.loki.messenger.R
@ -177,6 +179,8 @@ import org.thoughtcrime.securesms.util.push
import org.thoughtcrime.securesms.util.show import org.thoughtcrime.securesms.util.show
import org.thoughtcrime.securesms.util.toPx import org.thoughtcrime.securesms.util.toPx
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import java.time.Instant
import java.util.Date
import java.util.Locale import java.util.Locale
import java.util.concurrent.ExecutionException import java.util.concurrent.ExecutionException
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
@ -187,6 +191,10 @@ import kotlin.math.abs
import kotlin.math.min import kotlin.math.min
import kotlin.math.roundToInt import kotlin.math.roundToInt
import kotlin.math.sqrt import kotlin.math.sqrt
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
private const val TAG = "ConversationActivityV2"
// Some things that seemingly belong to the input bar (e.g. the voice message recording UI) are actually // Some things that seemingly belong to the input bar (e.g. the voice message recording UI) are actually
// part of the conversation activity layout. This is just because it makes the layout a lot simpler. The // part of the conversation activity layout. This is just because it makes the layout a lot simpler. The
@ -451,18 +459,21 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
reactionDelegate = ConversationReactionDelegate(reactionOverlayStub) reactionDelegate = ConversationReactionDelegate(reactionOverlayStub)
reactionDelegate.setOnReactionSelectedListener(this) reactionDelegate.setOnReactionSelectedListener(this)
lifecycleScope.launch { lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
// only update the conversation every 3 seconds maximum // only update the conversation every 3 seconds maximum
// channel is rendezvous and shouldn't block on try send calls as often as we want // channel is rendezvous and shouldn't block on try send calls as often as we want
val bufferedFlow = bufferedLastSeenChannel.consumeAsFlow() bufferedLastSeenChannel.receiveAsFlow()
bufferedFlow.filter { .flowWithLifecycle(lifecycle, Lifecycle.State.RESUMED)
it > storage.getLastSeen(viewModel.threadId) .collectLatest {
}.collectLatest { latestMessageRead -> withContext(Dispatchers.IO) {
withContext(Dispatchers.IO) { try {
storage.markConversationAsRead(viewModel.threadId, latestMessageRead) if (it > storage.getLastSeen(viewModel.threadId)) {
storage.markConversationAsRead(viewModel.threadId, it)
}
} catch (e: Exception) {
Log.d(TAG, "bufferedLastSeenChannel collectLatest", e)
}
}
} }
}
}
} }
} }
@ -1055,16 +1066,16 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
val maybeTargetVisiblePosition = if (reverseMessageList) layoutManager?.findFirstVisibleItemPosition() else layoutManager?.findLastVisibleItemPosition() val maybeTargetVisiblePosition = if (reverseMessageList) layoutManager?.findFirstVisibleItemPosition() else layoutManager?.findLastVisibleItemPosition()
val targetVisiblePosition = maybeTargetVisiblePosition ?: RecyclerView.NO_POSITION val targetVisiblePosition = maybeTargetVisiblePosition ?: RecyclerView.NO_POSITION
if (!firstLoad.get() && targetVisiblePosition != RecyclerView.NO_POSITION) { if (!firstLoad.get() && targetVisiblePosition != RecyclerView.NO_POSITION) {
val visibleItemTimestamp = adapter.getTimestampForItemAt(targetVisiblePosition) adapter.getTimestampForItemAt(targetVisiblePosition)?.let { visibleItemTimestamp ->
if (visibleItemTimestamp != null) { bufferedLastSeenChannel.trySend(visibleItemTimestamp).apply {
bufferedLastSeenChannel.trySend(visibleItemTimestamp) if (isFailure) Log.e(TAG, "trySend failed", exceptionOrNull())
}
} }
} }
if (reverseMessageList) { if (reverseMessageList) {
unreadCount = min(unreadCount, targetVisiblePosition).coerceAtLeast(0) unreadCount = min(unreadCount, targetVisiblePosition).coerceAtLeast(0)
} } else {
else {
val layoutUnreadCount = layoutManager?.let { (it.itemCount - 1) - it.findLastVisibleItemPosition() } val layoutUnreadCount = layoutManager?.let { (it.itemCount - 1) - it.findLastVisibleItemPosition() }
?: RecyclerView.NO_POSITION ?: RecyclerView.NO_POSITION
unreadCount = min(unreadCount, layoutUnreadCount).coerceAtLeast(0) unreadCount = min(unreadCount, layoutUnreadCount).coerceAtLeast(0)