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.
This commit is contained in:
parent
83403ebb58
commit
0483440381
|
@ -45,8 +45,7 @@ class AccountMediaViewModel @Inject constructor(
|
||||||
val media = Pager(
|
val media = Pager(
|
||||||
config = PagingConfig(
|
config = PagingConfig(
|
||||||
pageSize = LOAD_AT_ONCE,
|
pageSize = LOAD_AT_ONCE,
|
||||||
prefetchDistance = LOAD_AT_ONCE * 2,
|
prefetchDistance = LOAD_AT_ONCE * 2
|
||||||
enablePlaceholders = false
|
|
||||||
),
|
),
|
||||||
pagingSourceFactory = {
|
pagingSourceFactory = {
|
||||||
AccountMediaPagingSource(
|
AccountMediaPagingSource(
|
||||||
|
|
|
@ -162,7 +162,8 @@ class ConversationsFragment :
|
||||||
|
|
||||||
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||||
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
|
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 {
|
binding.recyclerView.post {
|
||||||
if (getView() != null) {
|
if (getView() != null) {
|
||||||
binding.recyclerView.scrollBy(0, Utils.dpToPx(requireContext(), -30))
|
binding.recyclerView.scrollBy(0, Utils.dpToPx(requireContext(), -30))
|
||||||
|
|
|
@ -45,8 +45,7 @@ class ConversationsViewModel @Inject constructor(
|
||||||
@OptIn(ExperimentalPagingApi::class)
|
@OptIn(ExperimentalPagingApi::class)
|
||||||
val conversationFlow = Pager(
|
val conversationFlow = Pager(
|
||||||
config = PagingConfig(
|
config = PagingConfig(
|
||||||
pageSize = 30,
|
pageSize = 30
|
||||||
enablePlaceholders = false
|
|
||||||
),
|
),
|
||||||
remoteMediator = ConversationsRemoteMediator(api, database, accountManager),
|
remoteMediator = ConversationsRemoteMediator(api, database, accountManager),
|
||||||
pagingSourceFactory = {
|
pagingSourceFactory = {
|
||||||
|
|
|
@ -41,8 +41,7 @@ class DomainBlocksRepository @Inject constructor(
|
||||||
val domainPager = Pager(
|
val domainPager = Pager(
|
||||||
config = PagingConfig(
|
config = PagingConfig(
|
||||||
pageSize = PAGE_SIZE,
|
pageSize = PAGE_SIZE,
|
||||||
initialLoadSize = PAGE_SIZE,
|
initialLoadSize = PAGE_SIZE
|
||||||
enablePlaceholders = false
|
|
||||||
),
|
),
|
||||||
remoteMediator = DomainBlocksRemoteMediator(api, this),
|
remoteMediator = DomainBlocksRemoteMediator(api, this),
|
||||||
pagingSourceFactory = factory
|
pagingSourceFactory = factory
|
||||||
|
|
|
@ -40,8 +40,7 @@ class DraftsViewModel @Inject constructor(
|
||||||
|
|
||||||
val drafts = Pager(
|
val drafts = Pager(
|
||||||
config = PagingConfig(
|
config = PagingConfig(
|
||||||
pageSize = 20,
|
pageSize = 20
|
||||||
enablePlaceholders = false
|
|
||||||
),
|
),
|
||||||
pagingSourceFactory = {
|
pagingSourceFactory = {
|
||||||
database.draftDao().draftsPagingSource(
|
database.draftDao().draftsPagingSource(
|
||||||
|
|
|
@ -27,8 +27,7 @@ class FollowedTagsViewModel @Inject constructor(
|
||||||
@OptIn(ExperimentalPagingApi::class)
|
@OptIn(ExperimentalPagingApi::class)
|
||||||
val pager = Pager(
|
val pager = Pager(
|
||||||
config = PagingConfig(
|
config = PagingConfig(
|
||||||
pageSize = 100,
|
pageSize = 100
|
||||||
enablePlaceholders = false
|
|
||||||
),
|
),
|
||||||
remoteMediator = FollowedTagsRemoteMediator(api, this),
|
remoteMediator = FollowedTagsRemoteMediator(api, this),
|
||||||
pagingSourceFactory = {
|
pagingSourceFactory = {
|
||||||
|
|
|
@ -229,7 +229,8 @@ class NotificationsFragment :
|
||||||
|
|
||||||
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||||
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
|
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 {
|
binding.recyclerView.post {
|
||||||
if (getView() != null) {
|
if (getView() != null) {
|
||||||
binding.recyclerView.scrollBy(
|
binding.recyclerView.scrollBy(
|
||||||
|
|
|
@ -95,8 +95,7 @@ class NotificationsViewModel @Inject constructor(
|
||||||
val notifications = refreshTrigger.flatMapLatest {
|
val notifications = refreshTrigger.flatMapLatest {
|
||||||
Pager(
|
Pager(
|
||||||
config = PagingConfig(
|
config = PagingConfig(
|
||||||
pageSize = LOAD_AT_ONCE,
|
pageSize = LOAD_AT_ONCE
|
||||||
enablePlaceholders = false
|
|
||||||
),
|
),
|
||||||
remoteMediator = remoteMediator,
|
remoteMediator = remoteMediator,
|
||||||
pagingSourceFactory = {
|
pagingSourceFactory = {
|
||||||
|
|
|
@ -79,8 +79,7 @@ class ReportViewModel @Inject constructor(
|
||||||
initialKey = statusId,
|
initialKey = statusId,
|
||||||
config = PagingConfig(
|
config = PagingConfig(
|
||||||
pageSize = 20,
|
pageSize = 20,
|
||||||
initialLoadSize = 20,
|
initialLoadSize = 20
|
||||||
enablePlaceholders = false
|
|
||||||
),
|
),
|
||||||
pagingSourceFactory = { StatusesPagingSource(accountId, mastodonApi) }
|
pagingSourceFactory = { StatusesPagingSource(accountId, mastodonApi) }
|
||||||
).flow
|
).flow
|
||||||
|
|
|
@ -40,8 +40,7 @@ class ScheduledStatusViewModel @Inject constructor(
|
||||||
val data = Pager(
|
val data = Pager(
|
||||||
config = PagingConfig(
|
config = PagingConfig(
|
||||||
pageSize = 20,
|
pageSize = 20,
|
||||||
initialLoadSize = 20,
|
initialLoadSize = 20
|
||||||
enablePlaceholders = false
|
|
||||||
),
|
),
|
||||||
pagingSourceFactory = pagingSourceFactory
|
pagingSourceFactory = pagingSourceFactory
|
||||||
).flow
|
).flow
|
||||||
|
|
|
@ -90,8 +90,7 @@ class SearchViewModel @Inject constructor(
|
||||||
val statusesFlow = Pager(
|
val statusesFlow = Pager(
|
||||||
config = PagingConfig(
|
config = PagingConfig(
|
||||||
pageSize = DEFAULT_LOAD_SIZE,
|
pageSize = DEFAULT_LOAD_SIZE,
|
||||||
initialLoadSize = DEFAULT_LOAD_SIZE,
|
initialLoadSize = DEFAULT_LOAD_SIZE
|
||||||
enablePlaceholders = false
|
|
||||||
),
|
),
|
||||||
pagingSourceFactory = statusesPagingSourceFactory
|
pagingSourceFactory = statusesPagingSourceFactory
|
||||||
).flow
|
).flow
|
||||||
|
@ -100,8 +99,7 @@ class SearchViewModel @Inject constructor(
|
||||||
val accountsFlow = Pager(
|
val accountsFlow = Pager(
|
||||||
config = PagingConfig(
|
config = PagingConfig(
|
||||||
pageSize = DEFAULT_LOAD_SIZE,
|
pageSize = DEFAULT_LOAD_SIZE,
|
||||||
initialLoadSize = DEFAULT_LOAD_SIZE,
|
initialLoadSize = DEFAULT_LOAD_SIZE
|
||||||
enablePlaceholders = false
|
|
||||||
),
|
),
|
||||||
pagingSourceFactory = accountsPagingSourceFactory
|
pagingSourceFactory = accountsPagingSourceFactory
|
||||||
).flow
|
).flow
|
||||||
|
@ -110,8 +108,7 @@ class SearchViewModel @Inject constructor(
|
||||||
val hashtagsFlow = Pager(
|
val hashtagsFlow = Pager(
|
||||||
config = PagingConfig(
|
config = PagingConfig(
|
||||||
pageSize = DEFAULT_LOAD_SIZE,
|
pageSize = DEFAULT_LOAD_SIZE,
|
||||||
initialLoadSize = DEFAULT_LOAD_SIZE,
|
initialLoadSize = DEFAULT_LOAD_SIZE
|
||||||
enablePlaceholders = false
|
|
||||||
),
|
),
|
||||||
pagingSourceFactory = hashtagsPagingSourceFactory
|
pagingSourceFactory = hashtagsPagingSourceFactory
|
||||||
).flow
|
).flow
|
||||||
|
|
|
@ -252,7 +252,8 @@ class TimelineFragment :
|
||||||
|
|
||||||
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||||
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
|
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 {
|
binding.recyclerView.post {
|
||||||
if (getView() != null) {
|
if (getView() != null) {
|
||||||
if (isSwipeToRefreshEnabled) {
|
if (isSwipeToRefreshEnabled) {
|
||||||
|
|
|
@ -86,8 +86,7 @@ class CachedTimelineViewModel @Inject constructor(
|
||||||
@OptIn(ExperimentalPagingApi::class)
|
@OptIn(ExperimentalPagingApi::class)
|
||||||
override val statuses = Pager(
|
override val statuses = Pager(
|
||||||
config = PagingConfig(
|
config = PagingConfig(
|
||||||
pageSize = LOAD_AT_ONCE,
|
pageSize = LOAD_AT_ONCE
|
||||||
enablePlaceholders = false
|
|
||||||
),
|
),
|
||||||
remoteMediator = CachedTimelineRemoteMediator(accountManager, api, db),
|
remoteMediator = CachedTimelineRemoteMediator(accountManager, api, db),
|
||||||
pagingSourceFactory = {
|
pagingSourceFactory = {
|
||||||
|
|
|
@ -88,8 +88,7 @@ class NetworkTimelineViewModel @Inject constructor(
|
||||||
@OptIn(ExperimentalPagingApi::class)
|
@OptIn(ExperimentalPagingApi::class)
|
||||||
override val statuses = Pager(
|
override val statuses = Pager(
|
||||||
config = PagingConfig(
|
config = PagingConfig(
|
||||||
pageSize = LOAD_AT_ONCE,
|
pageSize = LOAD_AT_ONCE
|
||||||
enablePlaceholders = false
|
|
||||||
),
|
),
|
||||||
pagingSourceFactory = {
|
pagingSourceFactory = {
|
||||||
NetworkTimelinePagingSource(
|
NetworkTimelinePagingSource(
|
||||||
|
|
|
@ -26,6 +26,7 @@ import androidx.fragment.app.viewModels
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup
|
import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
|
||||||
|
@ -76,7 +77,8 @@ class TrendingTagsFragment :
|
||||||
|
|
||||||
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||||
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
|
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 {
|
binding.recyclerView.post {
|
||||||
if (getView() != null) {
|
if (getView() != null) {
|
||||||
binding.recyclerView.scrollBy(
|
binding.recyclerView.scrollBy(
|
||||||
|
|
Loading…
Reference in New Issue