mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-26 11:26:01 +03:00
Avoid chat position jumps during message loading
Sometimes, the chat list would jump without the user scrolling: - During intial loading of a room content, i.e. when it is expected that the list stays scrolled to bottom - During loading of messages after jumping to a linked message With this commit, the target event is repeatedly scrolled to upon list changes until the users scroll themselves, to avoid above scenarios. Change-Id: Iabbe76832e7e68686431b0baed9356c88eb50901
This commit is contained in:
parent
4e9ab0c6f9
commit
b808d8b464
4 changed files with 43 additions and 0 deletions
|
@ -56,6 +56,11 @@ interface Timeline {
|
|||
*/
|
||||
fun restartWithEventId(eventId: String?)
|
||||
|
||||
/**
|
||||
* Event that should be displayed first, before the user scrolls.
|
||||
*/
|
||||
fun getInitialEventId(): String?
|
||||
|
||||
/**
|
||||
* Check if the timeline can be enriched by paginating.
|
||||
* @param direction the direction to check in
|
||||
|
|
|
@ -221,6 +221,10 @@ internal class DefaultTimeline(
|
|||
postSnapshot()
|
||||
}
|
||||
|
||||
override fun getInitialEventId(): String? {
|
||||
return initialEventId
|
||||
}
|
||||
|
||||
override fun getTimelineEventAtIndex(index: Int): TimelineEvent? {
|
||||
return builtEvents.getOrNull(index)
|
||||
}
|
||||
|
|
|
@ -740,6 +740,8 @@ class RoomDetailFragment @Inject constructor(
|
|||
}
|
||||
|
||||
private fun navigateToEvent(action: RoomDetailViewEvents.NavigateToEvent) {
|
||||
scrollOnNewMessageCallback.initialForceScroll = true
|
||||
scrollOnNewMessageCallback.initialForceScrollEventId = action.eventId
|
||||
val scrollPosition = timelineEventController.searchPositionOfEvent(action.eventId)
|
||||
if (scrollPosition == null) {
|
||||
scrollOnHighlightedEventCallback.scheduleScrollTo(action.eventId)
|
||||
|
@ -1067,6 +1069,8 @@ class RoomDetailFragment @Inject constructor(
|
|||
layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, true)
|
||||
val stateRestorer = LayoutManagerStateRestorer(layoutManager).register()
|
||||
scrollOnNewMessageCallback = ScrollOnNewMessageCallback(layoutManager, timelineEventController)
|
||||
// Force scroll until the user has scrolled to address the bug where the list would jump during initial loading
|
||||
scrollOnNewMessageCallback.initialForceScroll = true
|
||||
scrollOnHighlightedEventCallback = ScrollOnHighlightedEventCallback(views.timelineRecyclerView, layoutManager, timelineEventController)
|
||||
views.timelineRecyclerView.layoutManager = layoutManager
|
||||
views.timelineRecyclerView.itemAnimator = null
|
||||
|
@ -1080,6 +1084,15 @@ class RoomDetailFragment @Inject constructor(
|
|||
}
|
||||
timelineEventController.addModelBuildListener(modelBuildListener)
|
||||
views.timelineRecyclerView.adapter = timelineEventController.adapter
|
||||
views.timelineRecyclerView.addOnScrollListener(object: RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
if (dy != 0) {
|
||||
// User has scrolled, stop force scrolling
|
||||
scrollOnNewMessageCallback.initialForceScroll = false
|
||||
}
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
}
|
||||
})
|
||||
|
||||
if (vectorPreferences.swipeToReplyIsEnabled()) {
|
||||
val quickReplyHandler = object : RoomMessageTouchHelperCallback.QuickReplayHandler {
|
||||
|
|
|
@ -28,8 +28,23 @@ class ScrollOnNewMessageCallback(private val layoutManager: LinearLayoutManager,
|
|||
|
||||
private val newTimelineEventIds = CopyOnWriteArrayList<String>()
|
||||
private var forceScroll = false
|
||||
var initialForceScroll = false
|
||||
var initialForceScrollEventId: String? = null
|
||||
get() = field ?: timelineEventController.timeline?.getInitialEventId()
|
||||
|
||||
fun addNewTimelineEventIds(eventIds: List<String>) {
|
||||
// Disable initial force scroll
|
||||
initialForceScroll = false
|
||||
// Update force scroll id when sticking to the bottom - TODO try this if staying at bottom is not reliable as well
|
||||
/*
|
||||
if (eventIds.isNotEmpty()) {
|
||||
initialForceScrollEventId.let {
|
||||
if (it != null && it == timelineEventController.timeline?.getInitialEventId()) {
|
||||
initialForceScrollEventId = eventIds[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
newTimelineEventIds.addAll(0, eventIds)
|
||||
}
|
||||
|
||||
|
@ -38,6 +53,12 @@ class ScrollOnNewMessageCallback(private val layoutManager: LinearLayoutManager,
|
|||
}
|
||||
|
||||
override fun onInserted(position: Int, count: Int) {
|
||||
if (initialForceScroll) {
|
||||
timelineEventController.searchPositionOfEvent(initialForceScrollEventId)?.let {
|
||||
layoutManager.scrollToPosition(it)
|
||||
}
|
||||
return
|
||||
}
|
||||
if (position != 0) {
|
||||
return
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue