Pagination loading indicator
This commit is contained in:
@@ -12,13 +12,15 @@ abstract class BoundsScrollListener(private val offsetTop: Int, private val offs
|
|||||||
super.onScrolled(recyclerView, dx, dy)
|
super.onScrolled(recyclerView, dx, dy)
|
||||||
val layoutManager = (recyclerView.layoutManager as? LinearLayoutManager) ?: return
|
val layoutManager = (recyclerView.layoutManager as? LinearLayoutManager) ?: return
|
||||||
val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
|
val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
|
||||||
|
if (firstVisibleItemPosition == RecyclerView.NO_POSITION) {
|
||||||
|
return
|
||||||
|
}
|
||||||
if (firstVisibleItemPosition <= offsetTop) {
|
if (firstVisibleItemPosition <= offsetTop) {
|
||||||
onScrolledToStart(recyclerView)
|
onScrolledToStart(recyclerView)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
val visibleItemCount = layoutManager.childCount
|
val visibleItemCount = layoutManager.childCount
|
||||||
val totalItemCount = layoutManager.itemCount
|
val totalItemCount = layoutManager.itemCount
|
||||||
if (visibleItemCount + firstVisibleItemPosition >= totalItemCount - offsetBottom && firstVisibleItemPosition >= 0) {
|
if (visibleItemCount + firstVisibleItemPosition >= totalItemCount - offsetBottom) {
|
||||||
onScrolledToEnd(recyclerView)
|
onScrolledToEnd(recyclerView)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,24 +2,31 @@ package org.koitharu.kotatsu.ui.common.list
|
|||||||
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
|
||||||
class PaginationScrollListener(offset: Int, private val callback: Callback) : BoundsScrollListener(0, offset) {
|
class PaginationScrollListener(offset: Int, private val callback: Callback) :
|
||||||
|
BoundsScrollListener(0, offset) {
|
||||||
|
|
||||||
private var lastTotalCount = 0
|
private var lastTotalCount = 0
|
||||||
|
|
||||||
override fun onScrolledToStart(recyclerView: RecyclerView) = Unit
|
override fun onScrolledToStart(recyclerView: RecyclerView) = Unit
|
||||||
|
|
||||||
override fun onScrolledToEnd(recyclerView: RecyclerView) {
|
override fun onScrolledToEnd(recyclerView: RecyclerView) {
|
||||||
val total = recyclerView.adapter?.itemCount ?: 0
|
val total = callback.getItemsCount()
|
||||||
if (total > lastTotalCount) {
|
if (total > lastTotalCount) {
|
||||||
callback.onRequestMoreItems(total)
|
|
||||||
lastTotalCount = total
|
lastTotalCount = total
|
||||||
|
callback.onRequestMoreItems(total)
|
||||||
} else if (total < lastTotalCount) {
|
} else if (total < lastTotalCount) {
|
||||||
lastTotalCount = total
|
lastTotalCount = total
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun reset() {
|
||||||
|
lastTotalCount = 0
|
||||||
|
}
|
||||||
|
|
||||||
interface Callback {
|
interface Callback {
|
||||||
|
|
||||||
fun onRequestMoreItems(offset: Int)
|
fun onRequestMoreItems(offset: Int)
|
||||||
|
|
||||||
|
fun getItemsCount(): Int
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,7 @@ import android.view.ViewGroup
|
|||||||
|
|
||||||
class ProgressBarAdapter : BaseRecyclerAdapter<Boolean, Unit>() {
|
class ProgressBarAdapter : BaseRecyclerAdapter<Boolean, Unit>() {
|
||||||
|
|
||||||
var isVisible: Boolean
|
var isProgressVisible: Boolean
|
||||||
get() = dataSet.isNotEmpty()
|
get() = dataSet.isNotEmpty()
|
||||||
set(value) {
|
set(value) {
|
||||||
if (value == dataSet.isEmpty()) {
|
if (value == dataSet.isEmpty()) {
|
||||||
@@ -20,5 +20,5 @@ class ProgressBarAdapter : BaseRecyclerAdapter<Boolean, Unit>() {
|
|||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup) = ProgressBarHolder(parent)
|
override fun onCreateViewHolder(parent: ViewGroup) = ProgressBarHolder(parent)
|
||||||
|
|
||||||
override fun onGetItemId(item: Boolean) = 1L
|
override fun onGetItemId(item: Boolean) = -1L
|
||||||
}
|
}
|
||||||
@@ -8,11 +8,27 @@ import org.koitharu.kotatsu.R
|
|||||||
class ProgressBarHolder(parent: ViewGroup) :
|
class ProgressBarHolder(parent: ViewGroup) :
|
||||||
BaseViewHolder<Boolean, Unit>(parent, R.layout.item_progress) {
|
BaseViewHolder<Boolean, Unit>(parent, R.layout.item_progress) {
|
||||||
|
|
||||||
|
private var pendingVisibility: Int = View.GONE
|
||||||
|
private val action = Runnable {
|
||||||
|
progressBar?.visibility = pendingVisibility
|
||||||
|
pendingVisibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
override fun onBind(data: Boolean, extra: Unit) {
|
override fun onBind(data: Boolean, extra: Unit) {
|
||||||
progressBar.visibility = if (data) {
|
val visibility = if (data) {
|
||||||
View.VISIBLE
|
View.VISIBLE
|
||||||
} else {
|
} else {
|
||||||
View.INVISIBLE
|
View.INVISIBLE
|
||||||
}
|
}
|
||||||
|
if (visibility != progressBar.visibility && visibility != pendingVisibility) {
|
||||||
|
progressBar.removeCallbacks(action)
|
||||||
|
pendingVisibility = visibility
|
||||||
|
progressBar.postDelayed(action, 400)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRecycled() {
|
||||||
|
progressBar.removeCallbacks(action)
|
||||||
|
super.onRecycled()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,10 +9,7 @@ import androidx.core.view.GravityCompat
|
|||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.drawerlayout.widget.DrawerLayout
|
import androidx.drawerlayout.widget.DrawerLayout
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration
|
import androidx.recyclerview.widget.*
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import kotlinx.android.synthetic.main.fragment_list.*
|
import kotlinx.android.synthetic.main.fragment_list.*
|
||||||
@@ -28,6 +25,7 @@ import org.koitharu.kotatsu.core.prefs.ListMode
|
|||||||
import org.koitharu.kotatsu.ui.common.BaseFragment
|
import org.koitharu.kotatsu.ui.common.BaseFragment
|
||||||
import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener
|
import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener
|
||||||
import org.koitharu.kotatsu.ui.common.list.PaginationScrollListener
|
import org.koitharu.kotatsu.ui.common.list.PaginationScrollListener
|
||||||
|
import org.koitharu.kotatsu.ui.common.list.ProgressBarAdapter
|
||||||
import org.koitharu.kotatsu.ui.common.list.decor.ItemTypeDividerDecoration
|
import org.koitharu.kotatsu.ui.common.list.decor.ItemTypeDividerDecoration
|
||||||
import org.koitharu.kotatsu.ui.common.list.decor.SectionItemDecoration
|
import org.koitharu.kotatsu.ui.common.list.decor.SectionItemDecoration
|
||||||
import org.koitharu.kotatsu.ui.common.list.decor.SpacingItemDecoration
|
import org.koitharu.kotatsu.ui.common.list.decor.SpacingItemDecoration
|
||||||
@@ -38,14 +36,19 @@ import org.koitharu.kotatsu.utils.UiUtils
|
|||||||
import org.koitharu.kotatsu.utils.ext.*
|
import org.koitharu.kotatsu.utils.ext.*
|
||||||
|
|
||||||
abstract class MangaListFragment<E> : BaseFragment(R.layout.fragment_list),
|
abstract class MangaListFragment<E> : BaseFragment(R.layout.fragment_list),
|
||||||
MangaListView<E>,
|
MangaListView<E>, PaginationScrollListener.Callback, OnRecyclerItemClickListener<Manga>,
|
||||||
PaginationScrollListener.Callback, OnRecyclerItemClickListener<Manga>,
|
|
||||||
SharedPreferences.OnSharedPreferenceChangeListener, OnFilterChangedListener,
|
SharedPreferences.OnSharedPreferenceChangeListener, OnFilterChangedListener,
|
||||||
SectionItemDecoration.Callback, SwipeRefreshLayout.OnRefreshListener {
|
SectionItemDecoration.Callback, SwipeRefreshLayout.OnRefreshListener {
|
||||||
|
|
||||||
private val settings by inject<AppSettings>()
|
private val settings by inject<AppSettings>()
|
||||||
|
private val adapterConfig = MergeAdapter.Config.Builder()
|
||||||
|
.setIsolateViewTypes(true)
|
||||||
|
.setStableIdMode(MergeAdapter.Config.StableIdMode.SHARED_STABLE_IDS)
|
||||||
|
.build()
|
||||||
|
|
||||||
private var adapter: MangaListAdapter? = null
|
private var adapter: MangaListAdapter? = null
|
||||||
|
private var progressAdapter: ProgressBarAdapter? = null
|
||||||
|
private var paginationListener : PaginationScrollListener? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@@ -56,10 +59,11 @@ abstract class MangaListFragment<E> : BaseFragment(R.layout.fragment_list),
|
|||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
drawer?.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
|
drawer?.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
|
||||||
adapter = MangaListAdapter(this)
|
adapter = MangaListAdapter(this)
|
||||||
|
progressAdapter = ProgressBarAdapter()
|
||||||
|
paginationListener = PaginationScrollListener(4, this)
|
||||||
recyclerView.setHasFixedSize(true)
|
recyclerView.setHasFixedSize(true)
|
||||||
initListMode(settings.listMode)
|
initListMode(settings.listMode)
|
||||||
// recyclerView.adapter = adapter
|
recyclerView.addOnScrollListener(paginationListener!!)
|
||||||
recyclerView.addOnScrollListener(PaginationScrollListener(4, this))
|
|
||||||
swipeRefreshLayout.setOnRefreshListener(this)
|
swipeRefreshLayout.setOnRefreshListener(this)
|
||||||
recyclerView_filter.setHasFixedSize(true)
|
recyclerView_filter.setHasFixedSize(true)
|
||||||
recyclerView_filter.addItemDecoration(ItemTypeDividerDecoration(view.context))
|
recyclerView_filter.addItemDecoration(ItemTypeDividerDecoration(view.context))
|
||||||
@@ -72,6 +76,8 @@ abstract class MangaListFragment<E> : BaseFragment(R.layout.fragment_list),
|
|||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
adapter = null
|
adapter = null
|
||||||
|
progressAdapter = null
|
||||||
|
paginationListener = null
|
||||||
settings.unsubscribe(this)
|
settings.unsubscribe(this)
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
@@ -123,6 +129,7 @@ abstract class MangaListFragment<E> : BaseFragment(R.layout.fragment_list),
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onListChanged(list: List<Manga>) {
|
override fun onListChanged(list: List<Manga>) {
|
||||||
|
paginationListener?.reset()
|
||||||
adapter?.replaceData(list)
|
adapter?.replaceData(list)
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
setUpEmptyListHolder()
|
setUpEmptyListHolder()
|
||||||
@@ -130,11 +137,16 @@ abstract class MangaListFragment<E> : BaseFragment(R.layout.fragment_list),
|
|||||||
} else {
|
} else {
|
||||||
layout_holder.isVisible = false
|
layout_holder.isVisible = false
|
||||||
}
|
}
|
||||||
|
progressAdapter?.isProgressVisible = list.isNotEmpty()
|
||||||
recyclerView.callOnScrollListeners()
|
recyclerView.callOnScrollListeners()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onListAppended(list: List<Manga>) {
|
override fun onListAppended(list: List<Manga>) {
|
||||||
adapter?.appendData(list)
|
adapter?.appendData(list)
|
||||||
|
progressAdapter?.isProgressVisible = list.isNotEmpty()
|
||||||
|
if (list.isNotEmpty()) {
|
||||||
|
layout_holder.isVisible = false
|
||||||
|
}
|
||||||
recyclerView.callOnScrollListeners()
|
recyclerView.callOnScrollListeners()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,11 +243,19 @@ abstract class MangaListFragment<E> : BaseFragment(R.layout.fragment_list),
|
|||||||
recyclerView.removeOnLayoutChangeListener(UiUtils.SpanCountResolver)
|
recyclerView.removeOnLayoutChangeListener(UiUtils.SpanCountResolver)
|
||||||
adapter?.listMode = mode
|
adapter?.listMode = mode
|
||||||
recyclerView.layoutManager = when (mode) {
|
recyclerView.layoutManager = when (mode) {
|
||||||
ListMode.GRID -> GridLayoutManager(ctx, UiUtils.resolveGridSpanCount(ctx))
|
ListMode.GRID -> {
|
||||||
|
val spanCount = UiUtils.resolveGridSpanCount(ctx)
|
||||||
|
GridLayoutManager(ctx, spanCount).apply {
|
||||||
|
spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
||||||
|
override fun getSpanSize(position: Int) = if (position < getItemsCount())
|
||||||
|
1 else spanCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> LinearLayoutManager(ctx)
|
else -> LinearLayoutManager(ctx)
|
||||||
}
|
}
|
||||||
recyclerView.recycledViewPool.clear()
|
recyclerView.recycledViewPool.clear()
|
||||||
recyclerView.adapter = adapter
|
recyclerView.adapter = MergeAdapter(adapterConfig, adapter, progressAdapter)
|
||||||
recyclerView.addItemDecoration(
|
recyclerView.addItemDecoration(
|
||||||
when (mode) {
|
when (mode) {
|
||||||
ListMode.LIST -> DividerItemDecoration(ctx, RecyclerView.VERTICAL)
|
ListMode.LIST -> DividerItemDecoration(ctx, RecyclerView.VERTICAL)
|
||||||
@@ -252,6 +272,8 @@ abstract class MangaListFragment<E> : BaseFragment(R.layout.fragment_list),
|
|||||||
recyclerView.firstItem = position
|
recyclerView.firstItem = position
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getItemsCount() = adapter?.itemCount ?: 0
|
||||||
|
|
||||||
final override fun isSection(position: Int): Boolean {
|
final override fun isSection(position: Int): Boolean {
|
||||||
return position == 0 || recyclerView_filter.adapter?.run {
|
return position == 0 || recyclerView_filter.adapter?.run {
|
||||||
getItemViewType(position) != getItemViewType(position - 1)
|
getItemViewType(position) != getItemViewType(position - 1)
|
||||||
|
|||||||
@@ -7,10 +7,7 @@ import android.view.View
|
|||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration
|
import androidx.recyclerview.widget.*
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
@@ -27,6 +24,7 @@ import org.koitharu.kotatsu.core.prefs.ListMode
|
|||||||
import org.koitharu.kotatsu.ui.common.BaseBottomSheet
|
import org.koitharu.kotatsu.ui.common.BaseBottomSheet
|
||||||
import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener
|
import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener
|
||||||
import org.koitharu.kotatsu.ui.common.list.PaginationScrollListener
|
import org.koitharu.kotatsu.ui.common.list.PaginationScrollListener
|
||||||
|
import org.koitharu.kotatsu.ui.common.list.ProgressBarAdapter
|
||||||
import org.koitharu.kotatsu.ui.common.list.decor.SpacingItemDecoration
|
import org.koitharu.kotatsu.ui.common.list.decor.SpacingItemDecoration
|
||||||
import org.koitharu.kotatsu.ui.details.MangaDetailsActivity
|
import org.koitharu.kotatsu.ui.details.MangaDetailsActivity
|
||||||
import org.koitharu.kotatsu.utils.UiUtils
|
import org.koitharu.kotatsu.utils.UiUtils
|
||||||
@@ -38,12 +36,18 @@ abstract class MangaListSheet<E> : BaseBottomSheet(R.layout.sheet_list),
|
|||||||
SharedPreferences.OnSharedPreferenceChangeListener, Toolbar.OnMenuItemClickListener {
|
SharedPreferences.OnSharedPreferenceChangeListener, Toolbar.OnMenuItemClickListener {
|
||||||
|
|
||||||
private val settings by inject<AppSettings>()
|
private val settings by inject<AppSettings>()
|
||||||
|
private val adapterConfig = MergeAdapter.Config.Builder()
|
||||||
|
.setIsolateViewTypes(true)
|
||||||
|
.setStableIdMode(MergeAdapter.Config.StableIdMode.SHARED_STABLE_IDS)
|
||||||
|
.build()
|
||||||
|
|
||||||
private var adapter: MangaListAdapter? = null
|
private var adapter: MangaListAdapter? = null
|
||||||
|
private var progressAdapter: ProgressBarAdapter? = null
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
adapter = MangaListAdapter(this)
|
adapter = MangaListAdapter(this)
|
||||||
|
progressAdapter = ProgressBarAdapter()
|
||||||
initListMode(settings.listMode)
|
initListMode(settings.listMode)
|
||||||
recyclerView.adapter = adapter
|
recyclerView.adapter = adapter
|
||||||
recyclerView.addOnScrollListener(PaginationScrollListener(4, this))
|
recyclerView.addOnScrollListener(PaginationScrollListener(4, this))
|
||||||
@@ -66,6 +70,7 @@ abstract class MangaListSheet<E> : BaseBottomSheet(R.layout.sheet_list),
|
|||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
settings.unsubscribe(this)
|
settings.unsubscribe(this)
|
||||||
adapter = null
|
adapter = null
|
||||||
|
progressAdapter = null
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,6 +128,7 @@ abstract class MangaListSheet<E> : BaseBottomSheet(R.layout.sheet_list),
|
|||||||
override fun onListChanged(list: List<Manga>) {
|
override fun onListChanged(list: List<Manga>) {
|
||||||
adapter?.replaceData(list)
|
adapter?.replaceData(list)
|
||||||
textView_holder.isVisible = list.isEmpty()
|
textView_holder.isVisible = list.isEmpty()
|
||||||
|
progressAdapter?.isProgressVisible = list.isNotEmpty()
|
||||||
recyclerView.callOnScrollListeners()
|
recyclerView.callOnScrollListeners()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,6 +137,7 @@ abstract class MangaListSheet<E> : BaseBottomSheet(R.layout.sheet_list),
|
|||||||
if (list.isNotEmpty()) {
|
if (list.isNotEmpty()) {
|
||||||
textView_holder.isVisible = false
|
textView_holder.isVisible = false
|
||||||
}
|
}
|
||||||
|
progressAdapter?.isProgressVisible = list.isNotEmpty()
|
||||||
recyclerView.callOnScrollListeners()
|
recyclerView.callOnScrollListeners()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,6 +145,8 @@ abstract class MangaListSheet<E> : BaseBottomSheet(R.layout.sheet_list),
|
|||||||
Snackbar.make(recyclerView, e.getDisplayMessage(resources), Snackbar.LENGTH_SHORT).show()
|
Snackbar.make(recyclerView, e.getDisplayMessage(resources), Snackbar.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getItemsCount() = adapter?.itemCount ?: 0
|
||||||
|
|
||||||
override fun onInitFilter(
|
override fun onInitFilter(
|
||||||
sortOrders: List<SortOrder>,
|
sortOrders: List<SortOrder>,
|
||||||
tags: List<MangaTag>,
|
tags: List<MangaTag>,
|
||||||
@@ -171,10 +180,18 @@ abstract class MangaListSheet<E> : BaseBottomSheet(R.layout.sheet_list),
|
|||||||
recyclerView.removeOnLayoutChangeListener(UiUtils.SpanCountResolver)
|
recyclerView.removeOnLayoutChangeListener(UiUtils.SpanCountResolver)
|
||||||
adapter?.listMode = mode
|
adapter?.listMode = mode
|
||||||
recyclerView.layoutManager = when (mode) {
|
recyclerView.layoutManager = when (mode) {
|
||||||
ListMode.GRID -> GridLayoutManager(ctx, UiUtils.resolveGridSpanCount(ctx))
|
ListMode.GRID -> {
|
||||||
|
val spanCount = UiUtils.resolveGridSpanCount(ctx)
|
||||||
|
GridLayoutManager(ctx, spanCount).apply {
|
||||||
|
spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
||||||
|
override fun getSpanSize(position: Int) = if (position < getItemsCount())
|
||||||
|
1 else spanCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> LinearLayoutManager(ctx)
|
else -> LinearLayoutManager(ctx)
|
||||||
}
|
}
|
||||||
recyclerView.adapter = adapter
|
recyclerView.adapter = MergeAdapter(adapterConfig, adapter, progressAdapter)
|
||||||
recyclerView.addItemDecoration(
|
recyclerView.addItemDecoration(
|
||||||
when (mode) {
|
when (mode) {
|
||||||
ListMode.LIST -> DividerItemDecoration(ctx, RecyclerView.VERTICAL)
|
ListMode.LIST -> DividerItemDecoration(ctx, RecyclerView.VERTICAL)
|
||||||
|
|||||||
@@ -102,6 +102,10 @@ class FeedFragment : BaseFragment(R.layout.fragment_tracklogs), FeedView,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getItemsCount(): Int {
|
||||||
|
return adapter?.itemCount ?: 0
|
||||||
|
}
|
||||||
|
|
||||||
override fun onRequestMoreItems(offset: Int) {
|
override fun onRequestMoreItems(offset: Int) {
|
||||||
presenter.loadList(offset)
|
presenter.loadList(offset)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,13 +25,21 @@ class FeedPresenter : BasePresenter<FeedView>() {
|
|||||||
val list = withContext(Dispatchers.IO) {
|
val list = withContext(Dispatchers.IO) {
|
||||||
repository.getTrackingLog(offset, 20)
|
repository.getTrackingLog(offset, 20)
|
||||||
}
|
}
|
||||||
viewState.onListChanged(list)
|
if (offset == 0) {
|
||||||
|
viewState.onListChanged(list)
|
||||||
|
} else {
|
||||||
|
viewState.onListAppended(list)
|
||||||
|
}
|
||||||
} catch (e: CancellationException) {
|
} catch (e: CancellationException) {
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
viewState.onListError(e)
|
if (offset == 0) {
|
||||||
|
viewState.onListError(e)
|
||||||
|
} else {
|
||||||
|
viewState.onError(e)
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
viewState.onLoadingStateChanged(false)
|
viewState.onLoadingStateChanged(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,7 @@ class LocalListFragment : MangaListFragment<File>(), ActivityResultCallback<Uri>
|
|||||||
private val presenter by moxyPresenter(factory = ::LocalListPresenter)
|
private val presenter by moxyPresenter(factory = ::LocalListPresenter)
|
||||||
|
|
||||||
override fun onRequestMoreItems(offset: Int) {
|
override fun onRequestMoreItems(offset: Int) {
|
||||||
if (offset == 0) {
|
presenter.loadList(offset)
|
||||||
presenter.loadList()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
|
|||||||
@@ -37,7 +37,11 @@ class LocalListPresenter : BasePresenter<MangaListView<File>>() {
|
|||||||
super.onFirstViewAttach()
|
super.onFirstViewAttach()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadList() {
|
fun loadList(offset: Int) {
|
||||||
|
if (offset != 0) {
|
||||||
|
viewState.onListAppended(emptyList())
|
||||||
|
return
|
||||||
|
}
|
||||||
presenterScope.launch {
|
presenterScope.launch {
|
||||||
viewState.onLoadingStateChanged(true)
|
viewState.onLoadingStateChanged(true)
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
package org.koitharu.kotatsu.ui.search.global
|
package org.koitharu.kotatsu.ui.search.global
|
||||||
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.flow.collect
|
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
import kotlinx.coroutines.flow.onEmpty
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import moxy.presenterScope
|
import moxy.presenterScope
|
||||||
import org.koitharu.kotatsu.domain.MangaSearchRepository
|
import org.koitharu.kotatsu.domain.MangaSearchRepository
|
||||||
import org.koitharu.kotatsu.ui.common.BasePresenter
|
import org.koitharu.kotatsu.ui.common.BasePresenter
|
||||||
import org.koitharu.kotatsu.ui.list.MangaListView
|
import org.koitharu.kotatsu.ui.list.MangaListView
|
||||||
import org.koitharu.kotatsu.utils.ext.onFirst
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class GlobalSearchPresenter : BasePresenter<MangaListView<Unit>>() {
|
class GlobalSearchPresenter : BasePresenter<MangaListView<Unit>>() {
|
||||||
@@ -26,23 +22,27 @@ class GlobalSearchPresenter : BasePresenter<MangaListView<Unit>>() {
|
|||||||
fun startSearch(query: String) {
|
fun startSearch(query: String) {
|
||||||
presenterScope.launch {
|
presenterScope.launch {
|
||||||
viewState.onLoadingStateChanged(isLoading = true)
|
viewState.onLoadingStateChanged(isLoading = true)
|
||||||
|
var isFirstCall = true
|
||||||
repository.globalSearch(query)
|
repository.globalSearch(query)
|
||||||
.flowOn(Dispatchers.IO)
|
.flowOn(Dispatchers.IO)
|
||||||
.catch { e ->
|
.catch { e ->
|
||||||
if (e is IOException) {
|
if (e is IOException) {
|
||||||
viewState.onError(e)
|
viewState.onError(e)
|
||||||
}
|
}
|
||||||
}
|
}.filterNot { x -> x.isEmpty() }
|
||||||
.onFirst {
|
|
||||||
viewState.onListChanged(emptyList())
|
|
||||||
viewState.onLoadingStateChanged(isLoading = false)
|
|
||||||
}
|
|
||||||
.onEmpty {
|
.onEmpty {
|
||||||
viewState.onListChanged(emptyList())
|
viewState.onListChanged(emptyList())
|
||||||
viewState.onLoadingStateChanged(isLoading = false)
|
viewState.onLoadingStateChanged(isLoading = false)
|
||||||
}
|
}.onCompletion {
|
||||||
.collect {
|
viewState.onListAppended(emptyList())
|
||||||
viewState.onListAppended(it)
|
}.collect {
|
||||||
|
if (isFirstCall) {
|
||||||
|
isFirstCall = false
|
||||||
|
viewState.onListChanged(it)
|
||||||
|
viewState.onLoadingStateChanged(isLoading = false)
|
||||||
|
} else {
|
||||||
|
viewState.onListAppended(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
android:id="@+id/progressBar"
|
android:id="@+id/progressBar"
|
||||||
style="@style/Widget.AppCompat.ProgressBar"
|
style="@style/Widget.AppCompat.ProgressBar"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
android:visibility="invisible"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center" />
|
android:layout_gravity="center" />
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user