Fix read receipt behavior when the timeline is opened. (#4679)
* Use Timber instead of println * Fix ReadReceipt not sent when new event is received: - filter Virtual item regarding typing to compute NewEventState - always scroll, since the latest event is always the typing notification (but with 0 height). This triggers the sending of RR. * Improve the fix to fix regression detected by unit tests. * Remove unnecessary parenthesis * Document parameter.
This commit is contained in:
parent
9ea4853e88
commit
32916444e5
2 changed files with 17 additions and 7 deletions
|
|
@ -28,6 +28,7 @@ import io.element.android.features.messages.impl.timeline.factories.TimelineItem
|
||||||
import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactoryConfig
|
import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactoryConfig
|
||||||
import io.element.android.features.messages.impl.timeline.model.NewEventState
|
import io.element.android.features.messages.impl.timeline.model.NewEventState
|
||||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||||
|
import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemTypingNotificationModel
|
||||||
import io.element.android.features.messages.impl.typing.TypingNotificationState
|
import io.element.android.features.messages.impl.typing.TypingNotificationState
|
||||||
import io.element.android.features.messages.impl.voicemessages.timeline.RedactedVoiceMessageManager
|
import io.element.android.features.messages.impl.voicemessages.timeline.RedactedVoiceMessageManager
|
||||||
import io.element.android.features.poll.api.actions.EndPollAction
|
import io.element.android.features.poll.api.actions.EndPollAction
|
||||||
|
|
@ -131,7 +132,7 @@ class TimelinePresenter @AssistedInject constructor(
|
||||||
if (event.firstIndex == 0) {
|
if (event.firstIndex == 0) {
|
||||||
newEventState.value = NewEventState.None
|
newEventState.value = NewEventState.None
|
||||||
}
|
}
|
||||||
println("## sendReadReceiptIfNeeded firstVisibleIndex: ${event.firstIndex}")
|
Timber.d("## sendReadReceiptIfNeeded firstVisibleIndex: ${event.firstIndex}")
|
||||||
appScope.sendReadReceiptIfNeeded(
|
appScope.sendReadReceiptIfNeeded(
|
||||||
firstVisibleIndex = event.firstIndex,
|
firstVisibleIndex = event.firstIndex,
|
||||||
timelineItems = timelineItems,
|
timelineItems = timelineItems,
|
||||||
|
|
@ -278,7 +279,10 @@ class TimelinePresenter @AssistedInject constructor(
|
||||||
if (newEventState.value == NewEventState.FromMe) {
|
if (newEventState.value == NewEventState.FromMe) {
|
||||||
return@withContext
|
return@withContext
|
||||||
}
|
}
|
||||||
val newMostRecentItem = timelineItems.firstOrNull()
|
val newMostRecentItem = timelineItems.firstOrNull {
|
||||||
|
// Ignore typing item
|
||||||
|
(it as? TimelineItem.Virtual)?.model !is TimelineItemTypingNotificationModel
|
||||||
|
}
|
||||||
val prevMostRecentItemIdValue = prevMostRecentItemId.value
|
val prevMostRecentItemIdValue = prevMostRecentItemId.value
|
||||||
val newMostRecentItemId = newMostRecentItem?.identifier()
|
val newMostRecentItemId = newMostRecentItem?.identifier()
|
||||||
val hasNewEvent = prevMostRecentItemIdValue != null &&
|
val hasNewEvent = prevMostRecentItemIdValue != null &&
|
||||||
|
|
|
||||||
|
|
@ -286,11 +286,16 @@ private fun BoxScope.TimelineScrollHelper(
|
||||||
}
|
}
|
||||||
var jumpToLiveHandled by remember { mutableStateOf(true) }
|
var jumpToLiveHandled by remember { mutableStateOf(true) }
|
||||||
|
|
||||||
fun scrollToBottom() {
|
/**
|
||||||
|
* @param force If true, scroll to the bottom even if the user is already seeing the most recent item.
|
||||||
|
* This fixes the issue where the user is seeing typing notification and so the read receipt is not sent
|
||||||
|
* when a new message comes in.
|
||||||
|
*/
|
||||||
|
fun scrollToBottom(force: Boolean) {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
if (lazyListState.firstVisibleItemIndex > 10) {
|
if (lazyListState.firstVisibleItemIndex > 10) {
|
||||||
lazyListState.scrollToItem(0)
|
lazyListState.scrollToItem(0)
|
||||||
} else if (lazyListState.firstVisibleItemIndex != 0) {
|
} else if (force || lazyListState.firstVisibleItemIndex != 0) {
|
||||||
lazyListState.animateScrollToItem(0)
|
lazyListState.animateScrollToItem(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -298,7 +303,7 @@ private fun BoxScope.TimelineScrollHelper(
|
||||||
|
|
||||||
fun jumpToBottom() {
|
fun jumpToBottom() {
|
||||||
if (isLive) {
|
if (isLive) {
|
||||||
scrollToBottom()
|
scrollToBottom(force = false)
|
||||||
} else {
|
} else {
|
||||||
jumpToLiveHandled = false
|
jumpToLiveHandled = false
|
||||||
onJumpToLive()
|
onJumpToLive()
|
||||||
|
|
@ -321,9 +326,10 @@ private fun BoxScope.TimelineScrollHelper(
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(canAutoScroll, newEventState) {
|
LaunchedEffect(canAutoScroll, newEventState) {
|
||||||
val shouldScrollToBottom = isScrollFinished && (canAutoScroll || newEventState == NewEventState.FromMe)
|
val shouldScrollToBottom = isScrollFinished &&
|
||||||
|
(canAutoScroll && newEventState == NewEventState.FromOther || newEventState == NewEventState.FromMe)
|
||||||
if (shouldScrollToBottom) {
|
if (shouldScrollToBottom) {
|
||||||
scrollToBottom()
|
scrollToBottom(force = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue