Skip to content

Commit adfa417

Browse files
authored
Merge pull request libre-tube#7267 from Bnyro/master
fix: subscriptions feed scroll state reset after switching tabs
2 parents aaa6f96 + 9318a2b commit adfa417

2 files changed

Lines changed: 27 additions & 33 deletions

File tree

app/src/main/java/com/github/libretube/ui/fragments/SubscriptionsFragment.kt

Lines changed: 22 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package com.github.libretube.ui.fragments
22

33
import android.annotation.SuppressLint
4-
import android.content.res.Configuration
54
import android.os.Bundle
6-
import android.os.Parcelable
75
import android.view.View
86
import android.view.ViewGroup.MarginLayoutParams
97
import androidx.core.os.bundleOf
@@ -61,7 +59,6 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
6159
set(value) = PreferenceHelper.putInt(PreferenceKeys.SELECTED_CHANNEL_GROUP, value)
6260
get() = PreferenceHelper.getInt(PreferenceKeys.SELECTED_CHANNEL_GROUP, 0)
6361

64-
private var isCurrentTabSubChannels = false
6562
private var isAppBarFullyExpanded = true
6663

6764
private var feedAdapter = VideosAdapter()
@@ -85,9 +82,6 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
8582
field = value
8683
}
8784

88-
private var subChannelsRecyclerViewState: Parcelable? = null
89-
private var subFeedRecyclerViewState: Parcelable? = null
90-
9185
private val legacySubscriptionsAdapter = LegacySubscriptionAdapter()
9286
private val channelsAdapter = SubscriptionChannelAdapter()
9387

@@ -144,11 +138,11 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
144138
}
145139

146140
viewModel.videoFeed.observe(viewLifecycleOwner) {
147-
if (!isCurrentTabSubChannels && it != null) showFeed()
141+
if (!viewModel.isCurrentTabSubChannels && it != null) showFeed()
148142
}
149143

150144
viewModel.subscriptions.observe(viewLifecycleOwner) {
151-
if (isCurrentTabSubChannels && it != null) showSubscriptions()
145+
if (viewModel.isCurrentTabSubChannels && it != null) showSubscriptions()
152146
}
153147

154148
viewModel.feedProgress.observe(viewLifecycleOwner) { progress ->
@@ -172,18 +166,18 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
172166
binding.toggleSubs.setOnClickListener {
173167
binding.subProgress.isVisible = true
174168
binding.subRefresh.isRefreshing = true
175-
isCurrentTabSubChannels = !isCurrentTabSubChannels
169+
viewModel.isCurrentTabSubChannels = !viewModel.isCurrentTabSubChannels
176170

177-
if (isCurrentTabSubChannels) showSubscriptions() else showFeed()
171+
if (viewModel.isCurrentTabSubChannels) showSubscriptions() else showFeed()
178172

179-
binding.subChannels.isVisible = isCurrentTabSubChannels
180-
binding.subFeed.isGone = isCurrentTabSubChannels
173+
binding.subChannels.isVisible = viewModel.isCurrentTabSubChannels
174+
binding.subFeed.isGone = viewModel.isCurrentTabSubChannels
181175
}
182176

183177
binding.subChannels.addOnBottomReachedListener {
184178
val binding = _binding ?: return@addOnBottomReachedListener
185179

186-
if (viewModel.subscriptions.value != null && isCurrentTabSubChannels) {
180+
if (viewModel.subscriptions.value != null && viewModel.isCurrentTabSubChannels) {
187181
binding.subRefresh.isRefreshing = true
188182
binding.subRefresh.isRefreshing = false
189183
}
@@ -199,7 +193,7 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
199193

200194
binding.channelGroups.setOnCheckedStateChangeListener { group, _ ->
201195
selectedFilterGroup = group.children.indexOfFirst { it.id == group.checkedChipId }
202-
if (isCurrentTabSubChannels) showSubscriptions() else showFeed()
196+
if (viewModel.isCurrentTabSubChannels) showSubscriptions() else showFeed()
203197
}
204198

205199
channelGroupsModel.groups.observe(viewLifecycleOwner) {
@@ -214,14 +208,18 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
214208
binding.subChannels.addOnScrollListener(object : RecyclerView.OnScrollListener() {
215209
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
216210
super.onScrollStateChanged(recyclerView, newState)
217-
subChannelsRecyclerViewState = binding.subChannels.layoutManager?.onSaveInstanceState()
211+
viewModel.subChannelsRecyclerViewState = binding.subChannels.layoutManager?.onSaveInstanceState()?.takeIf {
212+
binding.subChannels.computeVerticalScrollOffset() != 0
213+
}
218214
}
219215
})
220216

221217
binding.subFeed.addOnScrollListener(object : RecyclerView.OnScrollListener() {
222218
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
223219
super.onScrollStateChanged(recyclerView, newState)
224-
subFeedRecyclerViewState = binding.subFeed.layoutManager?.onSaveInstanceState()
220+
viewModel.subFeedRecyclerViewState = binding.subFeed.layoutManager?.onSaveInstanceState()?.takeIf {
221+
binding.subFeed.computeVerticalScrollOffset() != 0
222+
}
225223
}
226224
})
227225

@@ -239,7 +237,7 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
239237
private fun loadFeedItems(sortedFeed: List<StreamItem>) {
240238
val binding = _binding ?: return
241239

242-
if (viewModel.videoFeed.value != null && !isCurrentTabSubChannels && !binding.subRefresh.isRefreshing) {
240+
if (viewModel.videoFeed.value != null && !viewModel.isCurrentTabSubChannels && !binding.subRefresh.isRefreshing) {
243241
binding.subRefresh.isRefreshing = true
244242

245243
lifecycleScope.launch {
@@ -250,7 +248,9 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
250248
}
251249

252250
feedAdapter.submitList(streamItemsToInsert) {
253-
binding.subFeed.scrollToPosition(0)
251+
// manually restore the previous feed state
252+
binding.subFeed.layoutManager?.onRestoreInstanceState(viewModel.subFeedRecyclerViewState)
253+
binding.subscriptionsAppBar.setExpanded(viewModel.subFeedRecyclerViewState == null)
254254
}
255255
binding.subRefresh.isRefreshing = false
256256
}
@@ -422,14 +422,10 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
422422
false
423423
)
424424

425-
if (legacySubscriptions) {
426-
legacySubscriptionsAdapter.submitList(subscriptions) {
427-
binding.subFeed.scrollToPosition(0)
428-
}
429-
} else {
430-
channelsAdapter.submitList(subscriptions) {
431-
binding.subFeed.scrollToPosition(0)
432-
}
425+
val adapter = if (legacySubscriptions) legacySubscriptionsAdapter else channelsAdapter
426+
adapter.submitList(subscriptions) {
427+
binding.subFeed.layoutManager?.onRestoreInstanceState(viewModel.subChannelsRecyclerViewState)
428+
binding.subscriptionsAppBar.setExpanded(viewModel.subChannelsRecyclerViewState == null)
433429
}
434430

435431
binding.subRefresh.isRefreshing = false
@@ -447,11 +443,4 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
447443
fun removeItem(videoId: String) {
448444
feedAdapter.removeItemById(videoId)
449445
}
450-
451-
override fun onConfigurationChanged(newConfig: Configuration) {
452-
super.onConfigurationChanged(newConfig)
453-
// manually restore the recyclerview state due to https://github.com/material-components/material-components-android/issues/3473
454-
binding.subChannels.layoutManager?.onRestoreInstanceState(subChannelsRecyclerViewState)
455-
binding.subFeed.layoutManager?.onRestoreInstanceState(subFeedRecyclerViewState)
456-
}
457446
}

app/src/main/java/com/github/libretube/ui/models/SubscriptionsViewModel.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.github.libretube.ui.models
22

33
import android.content.Context
4+
import android.os.Parcelable
45
import android.util.Log
56
import androidx.lifecycle.MutableLiveData
67
import androidx.lifecycle.ViewModel
@@ -23,6 +24,10 @@ class SubscriptionsViewModel : ViewModel() {
2324
var subscriptions = MutableLiveData<List<Subscription>?>()
2425
val feedProgress = MutableLiveData<FeedProgress?>()
2526

27+
var isCurrentTabSubChannels = false
28+
var subChannelsRecyclerViewState: Parcelable? = null
29+
var subFeedRecyclerViewState: Parcelable? = null
30+
2631
fun fetchFeed(context: Context, forceRefresh: Boolean) {
2732
viewModelScope.launch(Dispatchers.IO) {
2833
val videoFeed = try {

0 commit comments

Comments
 (0)