From 0483440381383672998ec495c39b7564b7761383 Mon Sep 17 00:00:00 2001 From: Konrad Pozniak Date: Fri, 31 May 2024 13:41:22 +0200 Subject: [PATCH] fix timeline jumping (#4471) Two things changed here: The check for `positionStart`only in `onItemRangeInserted` is not always correct - we only want to jump up when something is inserted at the top, if we already are at the top. `enablePlaceholders = false` has unintended side effects - the recyclerview adapter sometimes receives an "onItemRangeRemoved" followed by an "onItemRangeInserted", instead of just "onItemRangeChanged". Together they should make sure the timelines stay were they are. --- .../components/account/media/AccountMediaViewModel.kt | 3 +-- .../components/conversation/ConversationsFragment.kt | 3 ++- .../components/conversation/ConversationsViewModel.kt | 3 +-- .../components/domainblocks/DomainBlocksRepository.kt | 3 +-- .../tusky/components/drafts/DraftsViewModel.kt | 3 +-- .../components/followedtags/FollowedTagsViewModel.kt | 3 +-- .../components/notifications/NotificationsFragment.kt | 3 ++- .../components/notifications/NotificationsViewModel.kt | 3 +-- .../tusky/components/report/ReportViewModel.kt | 3 +-- .../components/scheduled/ScheduledStatusViewModel.kt | 3 +-- .../tusky/components/search/SearchViewModel.kt | 9 +++------ .../tusky/components/timeline/TimelineFragment.kt | 3 ++- .../timeline/viewmodel/CachedTimelineViewModel.kt | 3 +-- .../timeline/viewmodel/NetworkTimelineViewModel.kt | 3 +-- .../tusky/components/trending/TrendingTagsFragment.kt | 4 +++- 15 files changed, 22 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/account/media/AccountMediaViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/account/media/AccountMediaViewModel.kt index 38ddcc26d..0f9077286 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/account/media/AccountMediaViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/account/media/AccountMediaViewModel.kt @@ -45,8 +45,7 @@ class AccountMediaViewModel @Inject constructor( val media = Pager( config = PagingConfig( pageSize = LOAD_AT_ONCE, - prefetchDistance = LOAD_AT_ONCE * 2, - enablePlaceholders = false + prefetchDistance = LOAD_AT_ONCE * 2 ), pagingSourceFactory = { AccountMediaPagingSource( diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt index 36db437c3..90aae62fe 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt @@ -162,7 +162,8 @@ class ConversationsFragment : adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { - if (positionStart == 0 && adapter.itemCount != itemCount) { + val firstPos = (binding.recyclerView.layoutManager as LinearLayoutManager).findFirstCompletelyVisibleItemPosition() + if (firstPos == 0 && positionStart == 0 && adapter.itemCount != itemCount) { binding.recyclerView.post { if (getView() != null) { binding.recyclerView.scrollBy(0, Utils.dpToPx(requireContext(), -30)) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsViewModel.kt index 584eb39fe..27f63ae13 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsViewModel.kt @@ -45,8 +45,7 @@ class ConversationsViewModel @Inject constructor( @OptIn(ExperimentalPagingApi::class) val conversationFlow = Pager( config = PagingConfig( - pageSize = 30, - enablePlaceholders = false + pageSize = 30 ), remoteMediator = ConversationsRemoteMediator(api, database, accountManager), pagingSourceFactory = { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/domainblocks/DomainBlocksRepository.kt b/app/src/main/java/com/keylesspalace/tusky/components/domainblocks/DomainBlocksRepository.kt index 7d0917b06..c1c993d3d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/domainblocks/DomainBlocksRepository.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/domainblocks/DomainBlocksRepository.kt @@ -41,8 +41,7 @@ class DomainBlocksRepository @Inject constructor( val domainPager = Pager( config = PagingConfig( pageSize = PAGE_SIZE, - initialLoadSize = PAGE_SIZE, - enablePlaceholders = false + initialLoadSize = PAGE_SIZE ), remoteMediator = DomainBlocksRemoteMediator(api, this), pagingSourceFactory = factory diff --git a/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftsViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftsViewModel.kt index e5f3cd992..b56985cb6 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftsViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftsViewModel.kt @@ -40,8 +40,7 @@ class DraftsViewModel @Inject constructor( val drafts = Pager( config = PagingConfig( - pageSize = 20, - enablePlaceholders = false + pageSize = 20 ), pagingSourceFactory = { database.draftDao().draftsPagingSource( diff --git a/app/src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsViewModel.kt index 1c130fa1e..bebc4ff9f 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsViewModel.kt @@ -27,8 +27,7 @@ class FollowedTagsViewModel @Inject constructor( @OptIn(ExperimentalPagingApi::class) val pager = Pager( config = PagingConfig( - pageSize = 100, - enablePlaceholders = false + pageSize = 100 ), remoteMediator = FollowedTagsRemoteMediator(api, this), pagingSourceFactory = { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsFragment.kt index 33e51b6ef..a8021bd2b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsFragment.kt @@ -229,7 +229,8 @@ class NotificationsFragment : adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { - if (positionStart == 0 && adapter.itemCount != itemCount) { + val firstPos = (binding.recyclerView.layoutManager as LinearLayoutManager).findFirstCompletelyVisibleItemPosition() + if (firstPos == 0 && positionStart == 0 && adapter.itemCount != itemCount) { binding.recyclerView.post { if (getView() != null) { binding.recyclerView.scrollBy( diff --git a/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsViewModel.kt index 35e3094e2..e0626bf76 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsViewModel.kt @@ -95,8 +95,7 @@ class NotificationsViewModel @Inject constructor( val notifications = refreshTrigger.flatMapLatest { Pager( config = PagingConfig( - pageSize = LOAD_AT_ONCE, - enablePlaceholders = false + pageSize = LOAD_AT_ONCE ), remoteMediator = remoteMediator, pagingSourceFactory = { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/report/ReportViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/report/ReportViewModel.kt index 5bb629f11..644ed122c 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/report/ReportViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/report/ReportViewModel.kt @@ -79,8 +79,7 @@ class ReportViewModel @Inject constructor( initialKey = statusId, config = PagingConfig( pageSize = 20, - initialLoadSize = 20, - enablePlaceholders = false + initialLoadSize = 20 ), pagingSourceFactory = { StatusesPagingSource(accountId, mastodonApi) } ).flow diff --git a/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledStatusViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledStatusViewModel.kt index 92a31d157..774058354 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledStatusViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledStatusViewModel.kt @@ -40,8 +40,7 @@ class ScheduledStatusViewModel @Inject constructor( val data = Pager( config = PagingConfig( pageSize = 20, - initialLoadSize = 20, - enablePlaceholders = false + initialLoadSize = 20 ), pagingSourceFactory = pagingSourceFactory ).flow diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt index 6ef6db46b..8c94d6842 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt @@ -90,8 +90,7 @@ class SearchViewModel @Inject constructor( val statusesFlow = Pager( config = PagingConfig( pageSize = DEFAULT_LOAD_SIZE, - initialLoadSize = DEFAULT_LOAD_SIZE, - enablePlaceholders = false + initialLoadSize = DEFAULT_LOAD_SIZE ), pagingSourceFactory = statusesPagingSourceFactory ).flow @@ -100,8 +99,7 @@ class SearchViewModel @Inject constructor( val accountsFlow = Pager( config = PagingConfig( pageSize = DEFAULT_LOAD_SIZE, - initialLoadSize = DEFAULT_LOAD_SIZE, - enablePlaceholders = false + initialLoadSize = DEFAULT_LOAD_SIZE ), pagingSourceFactory = accountsPagingSourceFactory ).flow @@ -110,8 +108,7 @@ class SearchViewModel @Inject constructor( val hashtagsFlow = Pager( config = PagingConfig( pageSize = DEFAULT_LOAD_SIZE, - initialLoadSize = DEFAULT_LOAD_SIZE, - enablePlaceholders = false + initialLoadSize = DEFAULT_LOAD_SIZE ), pagingSourceFactory = hashtagsPagingSourceFactory ).flow diff --git a/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt index 6588cfce2..db27aa377 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt @@ -252,7 +252,8 @@ class TimelineFragment : adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { - if (positionStart == 0 && adapter.itemCount != itemCount) { + val firstPos = (binding.recyclerView.layoutManager as LinearLayoutManager).findFirstCompletelyVisibleItemPosition() + if (firstPos == 0 && positionStart == 0 && adapter.itemCount != itemCount) { binding.recyclerView.post { if (getView() != null) { if (isSwipeToRefreshEnabled) { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/CachedTimelineViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/CachedTimelineViewModel.kt index deb9c6b1c..a1e2bbfa0 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/CachedTimelineViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/CachedTimelineViewModel.kt @@ -86,8 +86,7 @@ class CachedTimelineViewModel @Inject constructor( @OptIn(ExperimentalPagingApi::class) override val statuses = Pager( config = PagingConfig( - pageSize = LOAD_AT_ONCE, - enablePlaceholders = false + pageSize = LOAD_AT_ONCE ), remoteMediator = CachedTimelineRemoteMediator(accountManager, api, db), pagingSourceFactory = { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/NetworkTimelineViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/NetworkTimelineViewModel.kt index 5baed2093..a40bb418e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/NetworkTimelineViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/NetworkTimelineViewModel.kt @@ -88,8 +88,7 @@ class NetworkTimelineViewModel @Inject constructor( @OptIn(ExperimentalPagingApi::class) override val statuses = Pager( config = PagingConfig( - pageSize = LOAD_AT_ONCE, - enablePlaceholders = false + pageSize = LOAD_AT_ONCE ), pagingSourceFactory = { NetworkTimelinePagingSource( diff --git a/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingTagsFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingTagsFragment.kt index ba068469a..a6c98aa05 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingTagsFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingTagsFragment.kt @@ -26,6 +26,7 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup +import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.SimpleItemAnimator import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener @@ -76,7 +77,8 @@ class TrendingTagsFragment : adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { - if (positionStart == 0 && adapter.itemCount != itemCount) { + val firstPos = (binding.recyclerView.layoutManager as LinearLayoutManager).findFirstCompletelyVisibleItemPosition() + if (firstPos == 0 && positionStart == 0 && adapter.itemCount != itemCount) { binding.recyclerView.post { if (getView() != null) { binding.recyclerView.scrollBy(