Pagination loading indicator

This commit is contained in:
Koitharu
2020-05-31 12:10:43 +03:00
parent 3539e6a892
commit e01b74ee3d
12 changed files with 123 additions and 44 deletions

View File

@@ -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)
} }
} }

View File

@@ -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
} }
} }

View File

@@ -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
} }

View File

@@ -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()
} }
} }

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)
} }

View File

@@ -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)
} }

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -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)
}
} }
} }
} }

View File

@@ -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" />