Scroll to first unread message in conversation upon entering it

This commit is contained in:
Sylvain Berfini 2023-12-02 17:36:32 +01:00
parent 006fa3fa4a
commit ddc8ba7105
3 changed files with 58 additions and 4 deletions

View file

@ -28,6 +28,7 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import org.linphone.R
import org.linphone.core.tools.Log
import org.linphone.databinding.ChatBubbleIncomingBinding
import org.linphone.databinding.ChatBubbleOutgoingBinding
import org.linphone.databinding.ChatConversationEventBinding
@ -41,6 +42,8 @@ class ConversationEventAdapter : ListAdapter<EventLogModel, RecyclerView.ViewHol
EventLogDiffCallback()
) {
companion object {
private const val TAG = "[Conversation Event Adapter]"
const val INCOMING_CHAT_MESSAGE = 1
const val OUTGOING_CHAT_MESSAGE = 2
const val EVENT = 3
@ -76,6 +79,21 @@ class ConversationEventAdapter : ListAdapter<EventLogModel, RecyclerView.ViewHol
return INCOMING_CHAT_MESSAGE
}
fun getFirstUnreadMessagePosition(): Int {
var index = 0
for (eventLog in currentList) {
if (eventLog.model is MessageModel) {
if (!eventLog.model.isRead) {
Log.i("$TAG First unread message is [${eventLog.model.id}] at index [$index]")
return index
}
}
index += 1
}
Log.i("$TAG No unread message found in list of [${currentList.size}] events")
return -1
}
private fun createIncomingChatBubble(parent: ViewGroup): IncomingBubbleViewHolder {
val binding: ChatBubbleIncomingBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),

View file

@ -127,11 +127,15 @@ class ConversationFragment : GenericFragment() {
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
if (positionStart == 0 && adapter.itemCount == itemCount) {
// First time we fill the list with messages
Log.i("$TAG [$itemCount] events have been loaded")
binding.eventsList.scrollToPosition(adapter.itemCount - 1)
Log.i(
"$TAG [$itemCount] events have been loaded, scrolling to first unread message"
)
scrollToFirstUnreadMessageOrBottom(false)
} else {
Log.i("$TAG [$itemCount] new events have been loaded, scrolling to bottom")
binding.eventsList.smoothScrollToPosition(adapter.itemCount - 1)
Log.i(
"$TAG [$itemCount] new events have been loaded, scrolling to first unread message"
)
scrollToFirstUnreadMessageOrBottom(true)
}
}
}
@ -565,6 +569,36 @@ class ConversationFragment : GenericFragment() {
super.onPause()
}
private fun scrollToFirstUnreadMessageOrBottom(smooth: Boolean): Boolean {
if (adapter.itemCount > 0) {
val recyclerView = binding.eventsList
// Scroll to first unread message if any, unless we are already on it
val firstUnreadMessagePosition = adapter.getFirstUnreadMessagePosition()
val currentPosition = (recyclerView.layoutManager as LinearLayoutManager).findFirstCompletelyVisibleItemPosition()
val indexToScrollTo = if (firstUnreadMessagePosition != -1 && firstUnreadMessagePosition != currentPosition) {
firstUnreadMessagePosition
} else {
adapter.itemCount - 1
}
Log.i(
"$TAG Scrolling to position $indexToScrollTo, first unread message is at $firstUnreadMessagePosition"
)
if (smooth) {
recyclerView.smoothScrollToPosition(indexToScrollTo)
} else {
recyclerView.scrollToPosition(indexToScrollTo)
}
if (firstUnreadMessagePosition == 0) {
// Return true only if all unread messages don't fit in the recyclerview height
return recyclerView.computeVerticalScrollRange() > recyclerView.height
}
}
return false
}
private fun showChatMessageLongPressMenu(chatMessageModel: MessageModel) {
Compatibility.setBlurRenderEffect(binding.root)

View file

@ -82,6 +82,8 @@ class MessageModel @WorkerThread constructor(
val id = chatMessage.messageId
val isRead = chatMessage.isRead
val isOutgoing = chatMessage.isOutgoing
val isInError = chatMessage.state == ChatMessage.State.NotDelivered