Scroll to first unread message instead of bottom + load enough events to have all unread messages
This commit is contained in:
parent
c2b06e5cfd
commit
db45a2bb78
3 changed files with 72 additions and 22 deletions
|
@ -173,6 +173,10 @@ class ChatMessagesListAdapter(
|
||||||
firstUnreadMessagePosition = -1
|
firstUnreadMessagePosition = -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getFirstUnreadMessagePosition(): Int {
|
||||||
|
return firstUnreadMessagePosition
|
||||||
|
}
|
||||||
|
|
||||||
private fun computeFirstUnreadMessagePosition() {
|
private fun computeFirstUnreadMessagePosition() {
|
||||||
if (unreadMessagesCount > 0) {
|
if (unreadMessagesCount > 0) {
|
||||||
var messageCount = 0
|
var messageCount = 0
|
||||||
|
|
|
@ -27,6 +27,7 @@ import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.view.*
|
import android.view.*
|
||||||
|
import android.view.ViewTreeObserver.OnGlobalLayoutListener
|
||||||
import android.widget.PopupWindow
|
import android.widget.PopupWindow
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import androidx.core.view.doOnPreDraw
|
import androidx.core.view.doOnPreDraw
|
||||||
|
@ -75,13 +76,18 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
|
||||||
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
|
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
|
||||||
adapter.notifyItemChanged(positionStart - 1) // For grouping purposes
|
adapter.notifyItemChanged(positionStart - 1) // For grouping purposes
|
||||||
|
|
||||||
// Scroll to newly added messages automatically
|
if (positionStart == 0 && adapter.itemCount == itemCount) {
|
||||||
if (positionStart == adapter.itemCount - itemCount) {
|
// First time we fill the list with messages
|
||||||
// But only if user hasn't initiated a scroll up in the messages history
|
Log.i("[Chat Room] History first $itemCount messages loaded")
|
||||||
if (viewModel.isUserScrollingUp.value == false) {
|
} else {
|
||||||
scrollToBottom()
|
// Scroll to newly added messages automatically
|
||||||
} else {
|
if (positionStart == adapter.itemCount - itemCount) {
|
||||||
Log.w("[Chat Room] User has scrolled up manually in the messages history, don't scroll to the newly added message at the bottom & don't mark the chat room as read")
|
// But only if user hasn't initiated a scroll up in the messages history
|
||||||
|
if (viewModel.isUserScrollingUp.value == false) {
|
||||||
|
scrollToFirstUnreadMessageOrBottom(false)
|
||||||
|
} else {
|
||||||
|
Log.w("[Chat Room] User has scrolled up manually in the messages history, don't scroll to the newly added message at the bottom & don't mark the chat room as read")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,8 +176,8 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
|
||||||
_adapter = ChatMessagesListAdapter(listSelectionViewModel, viewLifecycleOwner)
|
_adapter = ChatMessagesListAdapter(listSelectionViewModel, viewLifecycleOwner)
|
||||||
// SubmitList is done on a background thread
|
// SubmitList is done on a background thread
|
||||||
// We need this adapter data observer to know when to scroll
|
// We need this adapter data observer to know when to scroll
|
||||||
binding.chatMessagesList.adapter = adapter
|
|
||||||
adapter.registerAdapterDataObserver(observer)
|
adapter.registerAdapterDataObserver(observer)
|
||||||
|
binding.chatMessagesList.adapter = adapter
|
||||||
|
|
||||||
val layoutManager = LinearLayoutManager(activity)
|
val layoutManager = LinearLayoutManager(activity)
|
||||||
layoutManager.stackFromEnd = true
|
layoutManager.stackFromEnd = true
|
||||||
|
@ -181,6 +187,21 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
|
||||||
val headerItemDecoration = RecyclerViewHeaderDecoration(requireContext(), adapter)
|
val headerItemDecoration = RecyclerViewHeaderDecoration(requireContext(), adapter)
|
||||||
binding.chatMessagesList.addItemDecoration(headerItemDecoration)
|
binding.chatMessagesList.addItemDecoration(headerItemDecoration)
|
||||||
|
|
||||||
|
// Wait for items to be displayed before scrolling for the first time
|
||||||
|
binding.chatMessagesList
|
||||||
|
.viewTreeObserver
|
||||||
|
.addOnGlobalLayoutListener(
|
||||||
|
object : OnGlobalLayoutListener {
|
||||||
|
override fun onGlobalLayout() {
|
||||||
|
binding.chatMessagesList
|
||||||
|
.viewTreeObserver
|
||||||
|
.removeOnGlobalLayoutListener(this)
|
||||||
|
Log.i("[Chat Room] Messages have been displayed, scrolling to first unread message if any")
|
||||||
|
scrollToFirstUnreadMessageOrBottom(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// Swipe action
|
// Swipe action
|
||||||
/*val swipeConfiguration = RecyclerViewSwipeConfiguration()
|
/*val swipeConfiguration = RecyclerViewSwipeConfiguration()
|
||||||
swipeConfiguration.leftToRightAction = RecyclerViewSwipeConfiguration.Action(icon = R.drawable.menu_reply_default)
|
swipeConfiguration.leftToRightAction = RecyclerViewSwipeConfiguration.Action(icon = R.drawable.menu_reply_default)
|
||||||
|
@ -205,6 +226,7 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
|
||||||
|
|
||||||
val chatScrollListener = object : ChatScrollListener(layoutManager) {
|
val chatScrollListener = object : ChatScrollListener(layoutManager) {
|
||||||
override fun onLoadMore(totalItemsCount: Int) {
|
override fun onLoadMore(totalItemsCount: Int) {
|
||||||
|
Log.i("[Chat Room] User has scrolled up far enough, load more items from history (currently there are $totalItemsCount messages displayed)")
|
||||||
listViewModel.loadMoreData(totalItemsCount)
|
listViewModel.loadMoreData(totalItemsCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,8 +236,10 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
|
||||||
|
|
||||||
override fun onScrolledToEnd() {
|
override fun onScrolledToEnd() {
|
||||||
viewModel.isUserScrollingUp.value = false
|
viewModel.isUserScrollingUp.value = false
|
||||||
Log.i("[Chat Room] User has scrolled to the latest message, mark chat room as read")
|
if (viewModel.unreadMessagesCount.value != 0) {
|
||||||
viewModel.chatRoom.markAsRead()
|
Log.i("[Chat Room] User has scrolled to the latest message, mark chat room as read")
|
||||||
|
viewModel.chatRoom.markAsRead()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
binding.chatMessagesList.addOnScrollListener(chatScrollListener)
|
binding.chatMessagesList.addOnScrollListener(chatScrollListener)
|
||||||
|
@ -468,7 +492,7 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.setScrollToBottomClickListener {
|
binding.setScrollToBottomClickListener {
|
||||||
smoothScrollToPosition()
|
scrollToFirstUnreadMessageOrBottom(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (textToShare?.isNotEmpty() == true) {
|
if (textToShare?.isNotEmpty() == true) {
|
||||||
|
@ -746,18 +770,21 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
|
||||||
popupWindow.showAsDropDown(binding.menu, 0, 0, Gravity.BOTTOM)
|
popupWindow.showAsDropDown(binding.menu, 0, 0, Gravity.BOTTOM)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun scrollToBottom() {
|
private fun scrollToFirstUnreadMessageOrBottom(smooth: Boolean) {
|
||||||
if (_adapter != null && adapter.itemCount > 0) {
|
if (_adapter != null && adapter.itemCount > 0) {
|
||||||
binding.chatMessagesList.scrollToPosition(adapter.itemCount - 1)
|
// Scroll to first unread message if any
|
||||||
}
|
val firstUnreadMessagePosition = adapter.getFirstUnreadMessagePosition()
|
||||||
}
|
val indexToScrollTo = if (firstUnreadMessagePosition != -1) {
|
||||||
|
firstUnreadMessagePosition
|
||||||
private fun smoothScrollToPosition() {
|
|
||||||
if (_adapter != null && adapter.itemCount > 0) {
|
|
||||||
if (corePreferences.enableAnimations) {
|
|
||||||
binding.chatMessagesList.smoothScrollToPosition(adapter.itemCount - 1)
|
|
||||||
} else {
|
} else {
|
||||||
binding.chatMessagesList.scrollToPosition(adapter.itemCount - 1)
|
adapter.itemCount - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.i("[Chat Room] Scrolling to position $indexToScrollTo, first unread message is at $firstUnreadMessagePosition")
|
||||||
|
if (smooth && corePreferences.enableAnimations) {
|
||||||
|
binding.chatMessagesList.smoothScrollToPosition(indexToScrollTo)
|
||||||
|
} else {
|
||||||
|
binding.chatMessagesList.scrollToPosition(indexToScrollTo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -214,12 +214,31 @@ class ChatMessagesListViewModel(private val chatRoom: ChatRoom) : ViewModel() {
|
||||||
private fun getEvents(): ArrayList<EventLogData> {
|
private fun getEvents(): ArrayList<EventLogData> {
|
||||||
val list = arrayListOf<EventLogData>()
|
val list = arrayListOf<EventLogData>()
|
||||||
val unreadCount = chatRoom.unreadMessagesCount
|
val unreadCount = chatRoom.unreadMessagesCount
|
||||||
val loadCount = max(MESSAGES_PER_PAGE, unreadCount)
|
var loadCount = max(MESSAGES_PER_PAGE, unreadCount)
|
||||||
Log.i("[Chat Messages] $unreadCount unread messages in this chat room, loading $loadCount from history")
|
Log.i("[Chat Messages] $unreadCount unread messages in this chat room, loading $loadCount from history")
|
||||||
|
|
||||||
val history = chatRoom.getHistoryEvents(loadCount)
|
val history = chatRoom.getHistoryEvents(loadCount)
|
||||||
|
var messageCount = 0
|
||||||
for (eventLog in history) {
|
for (eventLog in history) {
|
||||||
list.add(EventLogData(eventLog))
|
list.add(EventLogData(eventLog))
|
||||||
|
if (eventLog.type == EventLog.Type.ConferenceChatMessage) {
|
||||||
|
messageCount += 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load enough events to have at least all unread messages
|
||||||
|
while (unreadCount > 0 && messageCount < unreadCount) {
|
||||||
|
Log.w("[Chat Messages] There is only $messageCount messages in the last $loadCount events, loading $MESSAGES_PER_PAGE more")
|
||||||
|
val moreHistory = chatRoom.getHistoryRangeEvents(loadCount, loadCount + MESSAGES_PER_PAGE)
|
||||||
|
loadCount += MESSAGES_PER_PAGE
|
||||||
|
for (eventLog in moreHistory) {
|
||||||
|
list.add(EventLogData(eventLog))
|
||||||
|
if (eventLog.type == EventLog.Type.ConferenceChatMessage) {
|
||||||
|
messageCount += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue