diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index dd488ba03e..6078572406 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -44,6 +44,7 @@ import androidx.fragment.app.DialogFragment import androidx.lifecycle.Lifecycle import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.loader.app.LoaderManager @@ -58,6 +59,7 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.consumeAsFlow import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import kotlinx.coroutines.withContext 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.toPx import java.lang.ref.WeakReference +import java.time.Instant +import java.util.Date import java.util.Locale import java.util.concurrent.ExecutionException import java.util.concurrent.atomic.AtomicBoolean @@ -187,6 +191,10 @@ import kotlin.math.abs import kotlin.math.min import kotlin.math.roundToInt 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 // 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.setOnReactionSelectedListener(this) lifecycleScope.launch { - lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) { // only update the conversation every 3 seconds maximum // channel is rendezvous and shouldn't block on try send calls as often as we want - val bufferedFlow = bufferedLastSeenChannel.consumeAsFlow() - bufferedFlow.filter { - it > storage.getLastSeen(viewModel.threadId) - }.collectLatest { latestMessageRead -> - withContext(Dispatchers.IO) { - storage.markConversationAsRead(viewModel.threadId, latestMessageRead) + bufferedLastSeenChannel.receiveAsFlow() + .flowWithLifecycle(lifecycle, Lifecycle.State.RESUMED) + .collectLatest { + withContext(Dispatchers.IO) { + try { + 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 targetVisiblePosition = maybeTargetVisiblePosition ?: RecyclerView.NO_POSITION if (!firstLoad.get() && targetVisiblePosition != RecyclerView.NO_POSITION) { - val visibleItemTimestamp = adapter.getTimestampForItemAt(targetVisiblePosition) - if (visibleItemTimestamp != null) { - bufferedLastSeenChannel.trySend(visibleItemTimestamp) + adapter.getTimestampForItemAt(targetVisiblePosition)?.let { visibleItemTimestamp -> + bufferedLastSeenChannel.trySend(visibleItemTimestamp).apply { + if (isFailure) Log.e(TAG, "trySend failed", exceptionOrNull()) + } } } if (reverseMessageList) { unreadCount = min(unreadCount, targetVisiblePosition).coerceAtLeast(0) - } - else { + } else { val layoutUnreadCount = layoutManager?.let { (it.itemCount - 1) - it.findLastVisibleItemPosition() } ?: RecyclerView.NO_POSITION unreadCount = min(unreadCount, layoutUnreadCount).coerceAtLeast(0)