Commit 16cba682 authored by Christophe Henry's avatar Christophe Henry

Solves #62: When comming back from subscription, sticky header is broken

parent 833a67f2
......@@ -41,7 +41,6 @@ class SubscriptionsFragment : Fragment(), Observer<Subscriptions> {
private val args: SubscriptionsFragmentArgs by navArgs()
private val section by lazy { args.section }
private val category by lazy { args.category }
private val adapter by lazy { SubscriptionsFlexibleAdapter(this) }
val model by lazy {
if (category != VoidCategory)
......@@ -58,19 +57,10 @@ class SubscriptionsFragment : Fragment(), Observer<Subscriptions> {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_subscriptions, container, false)
Store.refreshingPromise.observe(this, Observer { toggleProgressCircle(model.liveData.value ?: listOf()) })
model.liveData.observe(this, this)
initRecyclerView(view)
view.findViewById<RecyclerView>(R.id.fragment_subscriptions_list).let {
it.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
it.adapter = adapter
it.addOnLayoutChangeListener { _, _, top, _, bottom, _, oldTop, _, oldBottom ->
if (top == oldTop && bottom == oldBottom) return@addOnLayoutChangeListener
fast_scroller.visibility =
if (fragment_subscriptions_list.height >= fragment_subscriptions_list_container.height) View.VISIBLE
else View.GONE
}
}
Store.refreshingPromise.observe(this, Observer { toggleProgressCircle(model.subscriptions.value ?: listOf()) })
model.subscriptions.observe(this, this)
val contentDescription = if (category != VoidCategory)
FreshRSSApplication.getStringR(R.string.empty_subscriptions_category, category.label) else
......@@ -83,11 +73,6 @@ class SubscriptionsFragment : Fragment(), Observer<Subscriptions> {
view.findViewById<TextView>(R.id.fragment_subscriptions_empty_list).text =
FreshRSSApplication.getStringR(R.string.empty_subscription_section_template, contentDescription)
view.findViewById<FastScroller>(R.id.fast_scroller).let {
it.isAutoHideEnabled = false
adapter.fastScroller = it
}
view.findViewById<SwipeRefreshLayout>(R.id.subcription_pull_to_refresh)?.let {
Store.refreshingPromise.observe(this, Observer<Promise<Unit, Exception>?> { v ->
it.isRefreshing = v != null
......@@ -112,6 +97,26 @@ class SubscriptionsFragment : Fragment(), Observer<Subscriptions> {
MainNavDirections.actionGlobalSubscriptionsFragment(subscriptionCategory, section)
.let { findNavController().navigate(it) }
private fun initRecyclerView(view: View) {
val adapter = SubscriptionsFlexibleAdapter(this)
view.findViewById<RecyclerView>(R.id.fragment_subscriptions_list).let {
it.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
it.adapter = adapter
it.addOnLayoutChangeListener { _, _, top, _, bottom, _, oldTop, _, oldBottom ->
if (top == oldTop && bottom == oldBottom) return@addOnLayoutChangeListener
fast_scroller.visibility =
if (fragment_subscriptions_list.height >= fragment_subscriptions_list_container.height) View.VISIBLE
else View.GONE
}
}
view.findViewById<FastScroller>(R.id.fast_scroller).let {
it.isAutoHideEnabled = false
adapter.fastScroller = it
}
}
private fun toggleProgressCircle(subscriptions: Subscriptions) {
if (Store.refreshingPromise.value == null) {
fragment_subscriptions_list_container.visibility =
......
......@@ -50,7 +50,7 @@ sealed class AbstractSubscriptionViewItem(header: SubscriptionViewHeaderItem) :
}
class SubscriptionViewItem(val subscription: Subscription, header: String) :
AbstractSubscriptionViewItem(SubscriptionViewHeaderItem(header)) {
AbstractSubscriptionViewItem(SubscriptionViewHeaderItem.of(header)) {
override val virtualSubscription: Subscription get() = subscription
override fun equals(other: Any?): Boolean =
......
......@@ -9,8 +9,9 @@ import eu.davidea.flexibleadapter.items.IFlexible
import eu.davidea.viewholders.FlexibleViewHolder
import fr.chenry.android.freshrss.FreshRSSApplication
import fr.chenry.android.freshrss.R
import java.util.concurrent.ConcurrentHashMap
open class SubscriptionViewHeaderItem(val title: String) :
open class SubscriptionViewHeaderItem protected constructor(val title: String) :
AbstractHeaderItem<SubscriptionViewHeaderItem.SubscriptionViewHeaderHolder>() {
init {
isDraggable = false
......@@ -44,6 +45,12 @@ open class SubscriptionViewHeaderItem(val title: String) :
view.findViewById<TextView>(R.id.subscription_section_header_title).text = header
}
}
companion object {
private val headers = ConcurrentHashMap<String, SubscriptionViewHeaderItem>()
fun of(title: String): SubscriptionViewHeaderItem =
headers.getOrPut(title) { SubscriptionViewHeaderItem(title) }
}
}
object SubscriptionCategoryViewHeaderItem :
......
......@@ -7,40 +7,38 @@ import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.FlexibleAdapter.OnItemClickListener
import eu.davidea.flexibleadapter.items.IFlexible
import fr.chenry.android.freshrss.components.subscriptions.SubscriptionsFragment
import fr.chenry.android.freshrss.store.database.models.Subscription
import fr.chenry.android.freshrss.store.database.models.SubscriptionCategories
import fr.chenry.android.freshrss.store.database.models.Subscriptions
import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.task
class SubscriptionsFlexibleAdapter(private val fragment: SubscriptionsFragment):
FlexibleAdapter<AbstractSubscriptionViewItem>(initFlexAdapter(fragment), null, true),
class SubscriptionsFlexibleAdapter(private val fragment: SubscriptionsFragment) :
FlexibleAdapter<AbstractSubscriptionViewItem>(listOf(), null, true),
OnItemClickListener {
private val mediator = MediatorLiveData<List<AbstractSubscriptionViewItem>>()
private val subscriptionCategories
get() = fragment.model.subscriptionCategoriesLiveData.value ?: listOf()
get() = fragment.model.subscriptionCategories.value ?: listOf()
private val subscriptions
get() = fragment.model.liveData.value ?: listOf()
get() = fragment.model.subscriptions.value ?: listOf()
private val grouper get() = fragment.model.grouper
init {
setStickyHeaders(true)
setDisplayHeadersAtStartUp(true)
mediator.observe(fragment, Observer { updateDataSet(it) })
mediator.addSource(fragment.model.liveData) {
mediator.observe(fragment, Observer { updateDataSet(it, true) })
mediator.addSource(fragment.model.subscriptions) {
computeNewDataSet(it, subscriptionCategories) success { res -> mediator.postValue(res) }
}
mediator.addSource(fragment.model.subscriptionCategoriesLiveData) {
mediator.addSource(fragment.model.subscriptionCategories) {
computeNewDataSet(subscriptions, it) success { res -> mediator.postValue(res) }
}
addListener(this)
}
override fun isEmpty(): Boolean = fragment.model.liveData.value.isNullOrEmpty()
override fun isEmpty(): Boolean = fragment.model.subscriptions.value.isNullOrEmpty()
override fun onCreateBubbleText(position: Int): String? {
val item = getGenericItem(position)
return when (item) {
return when (val item = getGenericItem(position)) {
is AbstractSubscriptionViewItem -> item.header.title
is SubscriptionViewHeaderItem -> item.title
else -> null
......@@ -55,9 +53,6 @@ class SubscriptionsFlexibleAdapter(private val fragment: SubscriptionsFragment):
true
} ?: false
// This is probably one of the stupidest API design I've ever met
// This is actually known and the author refuses to fix
// WTF: https://github.com/davideas/FlexibleAdapter/issues/604
private fun getGenericItem(position: Int): IFlexible<*>? = (this as FlexibleAdapter<*>).getItem(position)
private fun computeNewDataSet(
......@@ -81,18 +76,10 @@ class SubscriptionsFlexibleAdapter(private val fragment: SubscriptionsFragment):
}
override fun getItemId(position: Int): Long = getGenericItem(position)?.let {
when(it) {
when (it) {
is SubscriptionViewItem -> it.subscription.id.hashCode().toLong()
is SubscriptionCategoryViewItem -> it.subscriptionCategory.id.hashCode().toLong()
else -> super.getItemId(position)
}
} ?: super.getItemId(position)
companion object {
private fun initFlexAdapter(fragment: SubscriptionsFragment) =
getSubscriptionViewItems(fragment.model.liveData.value ?: listOf(), fragment.model.grouper)
private fun getSubscriptionViewItems(subscriptions: Subscriptions, grouper: (Subscription) -> String) =
subscriptions.map { SubscriptionViewItem(it, grouper(it)) }
}
}
......@@ -19,16 +19,16 @@ import fr.chenry.android.freshrss.store.database.models.Subscriptions
import org.joda.time.LocalDateTime
sealed class SubscriptionsVM : ViewModel() {
abstract val subscriptionCategoriesLiveData: LiveData<SubscriptionCategories>
abstract val subscriptionCategories: LiveData<SubscriptionCategories>
abstract val grouper: (Subscription) -> String
protected abstract val comparator: Comparator<Subscription>
protected abstract val subscriptionsLiveData: LiveData<Subscriptions>
protected abstract val subscriptionsOrigin: LiveData<Subscriptions>
val liveData: LiveData<Subscriptions> by lazy {
val subscriptions: LiveData<Subscriptions> by lazy {
MutableLiveData<Subscriptions>().apply {
value = (subscriptionsLiveData.value ?: listOf()).sortedWith(comparator)
subscriptionsLiveData.observeForever { value = it.sortedWith(comparator) }
value = (subscriptionsOrigin.value ?: listOf()).sortedWith(comparator)
subscriptionsOrigin.observeForever { value = it.sortedWith(comparator) }
}
}
......@@ -67,19 +67,19 @@ sealed class SubscriptionsVM : ViewModel() {
class AllSubscriptionsVM : SubscriptionsVM() {
override val comparator: Comparator<Subscription> get() = titleComparator
override val grouper: (Subscription) -> String get() = titleGrouper
override val subscriptionsLiveData = FreshRSSApplication.database.getAllSubcriptions().toLiveData()
override val subscriptionCategoriesLiveData: LiveData<SubscriptionCategories>
override val subscriptionsOrigin = FreshRSSApplication.database.getAllSubcriptions().toLiveData()
override val subscriptionCategories: LiveData<SubscriptionCategories>
private val _subscriptionCategoriesLiveData: LiveData<SubscriptionCategories>
init {
val flowable = FreshRSSApplication.database.getAllCategories()
_subscriptionCategoriesLiveData = flowable.toLiveData()
subscriptionCategoriesLiveData = MutableLiveData<SubscriptionCategories>().apply {
subscriptionCategories = MutableLiveData<SubscriptionCategories>().apply {
value = flowable.blockingFirst().orEmpty().sortedWith(labelComparator)
}
_subscriptionCategoriesLiveData.observeForever {
subscriptionCategoriesLiveData.value = it.sortedWith(labelComparator)
subscriptionCategories.value = it.sortedWith(labelComparator)
}
}
}
......@@ -87,9 +87,9 @@ class AllSubscriptionsVM : SubscriptionsVM() {
class UnreadSubscriptionsVM : SubscriptionsVM() {
override val comparator: Comparator<Subscription> get() = dateComparator
override val grouper: (Subscription) -> String get() = dateGrouper
override val subscriptionsLiveData = FreshRSSApplication.database.getAllUnreadSubcriptions().toLiveData()
override val subscriptionsOrigin = FreshRSSApplication.database.getAllUnreadSubcriptions().toLiveData()
override val subscriptionCategoriesLiveData by lazy {
override val subscriptionCategories by lazy {
MutableLiveData<SubscriptionCategories>().apply {
val observer = Observer<Subscriptions> {
val categories = (it ?: listOf()).flatMap { self -> self.subscriptionCategories }
......@@ -100,8 +100,8 @@ class UnreadSubscriptionsVM : SubscriptionsVM() {
.sortedWith(labelComparator)
)
}
observer.onChanged(subscriptionsLiveData.value ?: listOf())
subscriptionsLiveData.observeForever(observer)
observer.onChanged(subscriptionsOrigin.value ?: listOf())
subscriptionsOrigin.observeForever(observer)
}
}
}
......@@ -109,9 +109,9 @@ class UnreadSubscriptionsVM : SubscriptionsVM() {
class FavoritesSubscriptionsVM : SubscriptionsVM() {
override val comparator: Comparator<Subscription> get() = titleComparator
override val grouper: (Subscription) -> String get() = titleGrouper
override val subscriptionCategoriesLiveData =
override val subscriptionCategories =
MutableLiveData<SubscriptionCategories>().apply { value = listOf() }
override val subscriptionsLiveData: LiveData<Subscriptions> =
override val subscriptionsOrigin: LiveData<Subscriptions> =
MutableLiveData<Subscriptions>().apply { value = listOf() }
}
......@@ -123,7 +123,7 @@ class SubscriptionsForCategoryVM(
if (subscriptionSection == UNREAD) dateComparator else titleComparator
override val grouper: (Subscription) -> String =
if (subscriptionSection == UNREAD) dateGrouper else titleGrouper
override val subscriptionsLiveData: LiveData<Subscriptions> by lazy {
override val subscriptionsOrigin: LiveData<Subscriptions> by lazy {
when (subscriptionSection) {
FAVORITES ->
FreshRSSApplication.database
......@@ -140,7 +140,7 @@ class SubscriptionsForCategoryVM(
}
}
override val subscriptionCategoriesLiveData = MutableLiveData<SubscriptionCategories>().apply { value = listOf() }
override val subscriptionCategories = MutableLiveData<SubscriptionCategories>().apply { value = listOf() }
}
class SubscriptionsFragmentCategoryVMF(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment