Add action for empty list state

This commit is contained in:
Koitharu
2022-03-08 16:06:51 +02:00
parent d7f60fa95a
commit 179b08b96a
28 changed files with 208 additions and 108 deletions

View File

@@ -36,13 +36,14 @@ class FavouritesListViewModel(
when {
list.isEmpty() -> listOf(
EmptyState(
R.drawable.ic_heart_outline,
R.string.text_empty_holder_primary,
if (categoryId == 0L) {
icon = R.drawable.ic_heart_outline,
textPrimary = R.string.text_empty_holder_primary,
textSecondary = if (categoryId == 0L) {
R.string.you_have_not_favourites_yet
} else {
R.string.favourites_category_empty
}
},
actionStringRes = 0,
)
)
else -> list.toUi(mode, this)

View File

@@ -45,7 +45,14 @@ class HistoryListViewModel(
createListModeFlow()
) { list, grouped, mode ->
when {
list.isEmpty() -> listOf(EmptyState(R.drawable.ic_history, R.string.text_history_holder_primary, R.string.text_history_holder_secondary))
list.isEmpty() -> listOf(
EmptyState(
icon = R.drawable.ic_history,
textPrimary = R.string.text_history_holder_primary,
textSecondary = R.string.text_history_holder_secondary,
actionStringRes = 0,
)
)
else -> mapList(list, grouped, mode)
}
}.onFirst {

View File

@@ -15,17 +15,18 @@ import kotlinx.coroutines.launch
import org.koin.android.ext.android.get
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseFragment
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.base.ui.list.PaginationScrollListener
import org.koitharu.kotatsu.base.ui.list.decor.SpacingItemDecoration
import org.koitharu.kotatsu.browser.cloudflare.CloudFlareDialog
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
import org.koitharu.kotatsu.core.exceptions.resolve.ResolvableException
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaTag
import org.koitharu.kotatsu.core.prefs.ListMode
import org.koitharu.kotatsu.databinding.FragmentListBinding
import org.koitharu.kotatsu.details.ui.DetailsActivity
import org.koitharu.kotatsu.list.ui.adapter.MangaListAdapter
import org.koitharu.kotatsu.list.ui.adapter.MangaListListener
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.main.ui.AppBarOwner
import org.koitharu.kotatsu.main.ui.MainActivity
@@ -33,7 +34,7 @@ import org.koitharu.kotatsu.utils.RecycledViewPoolHolder
import org.koitharu.kotatsu.utils.ext.*
abstract class MangaListFragment : BaseFragment<FragmentListBinding>(),
PaginationScrollListener.Callback, OnListItemClickListener<Manga>,
PaginationScrollListener.Callback, MangaListListener,
SwipeRefreshLayout.OnRefreshListener {
private var listAdapter: MangaListAdapter? = null
@@ -62,10 +63,7 @@ abstract class MangaListFragment : BaseFragment<FragmentListBinding>(),
listAdapter = MangaListAdapter(
coil = get(),
lifecycleOwner = viewLifecycleOwner,
clickListener = this,
onRetryClick = ::resolveException,
onTagRemoveClick = viewModel::onRemoveFilterTag,
onFilterClickListener = this::onFilterClick,
listener = this,
)
paginationListener = PaginationScrollListener(4, this)
with(binding.recyclerView) {
@@ -192,7 +190,17 @@ abstract class MangaListFragment : BaseFragment<FragmentListBinding>(),
}
}
protected open fun onFilterClick() = Unit
override fun onFilterClick() = Unit
override fun onEmptyActionClick() = Unit
override fun onRetryClick(error: Throwable) {
resolveException(error)
}
override fun onTagRemoveClick(tag: MangaTag) {
viewModel.onRemoveFilterTag(tag)
}
private fun onGridScaleChanged(scale: Float) {
spanSizeLookup.invalidateCache()

View File

@@ -1,23 +1,23 @@
package org.koitharu.kotatsu.list.ui.adapter
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegate
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.widgets.ChipsView
import org.koitharu.kotatsu.core.model.MangaTag
import org.koitharu.kotatsu.databinding.ItemCurrentFilterBinding
import org.koitharu.kotatsu.list.ui.model.CurrentFilterModel
import org.koitharu.kotatsu.list.ui.model.ListModel
fun currentFilterAD(
onTagRemoveClick: (MangaTag) -> Unit,
) = adapterDelegateViewBinding<CurrentFilterModel, ListModel, ItemCurrentFilterBinding>(
{ inflater, parent -> ItemCurrentFilterBinding.inflate(inflater, parent, false) }
) {
listener: MangaListListener,
) = adapterDelegate<CurrentFilterModel, ListModel>(R.layout.item_current_filter) {
binding.chipsTags.onChipCloseClickListener = ChipsView.OnChipCloseClickListener { chip, data ->
onTagRemoveClick(data as? MangaTag ?: return@OnChipCloseClickListener)
val chipGroup = itemView as ChipsView
chipGroup.onChipCloseClickListener = ChipsView.OnChipCloseClickListener { chip, data ->
listener.onTagRemoveClick(data as? MangaTag ?: return@OnChipCloseClickListener)
}
bind {
binding.chipsTags.setChips(item.chips)
chipGroup.setChips(item.chips)
}
}

View File

@@ -4,20 +4,20 @@ import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import org.koitharu.kotatsu.databinding.ItemEmptyStateBinding
import org.koitharu.kotatsu.list.ui.model.EmptyState
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.utils.ext.setTextAndVisible
fun emptyStateListAD() = adapterDelegateViewBinding<EmptyState, ListModel, ItemEmptyStateBinding>(
fun emptyStateListAD(
listener: MangaListListener,
) = adapterDelegateViewBinding<EmptyState, ListModel, ItemEmptyStateBinding>(
{ inflater, parent -> ItemEmptyStateBinding.inflate(inflater, parent, false) }
) {
binding.buttonRetry.setOnClickListener { listener.onEmptyActionClick() }
bind {
with(binding.icon) {
setImageResource(item.icon)
}
with(binding.textPrimary) {
setText(item.textPrimary)
}
with(binding.textSecondary) {
setText(item.textSecondary)
}
binding.icon.setImageResource(item.icon)
binding.textPrimary.setText(item.textPrimary)
binding.textSecondary.setTextAndVisible(item.textSecondary)
binding.buttonRetry.setTextAndVisible(item.actionStringRes)
}
}

View File

@@ -7,13 +7,13 @@ import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
fun errorFooterAD(
onRetryClick: (Throwable) -> Unit
listener: MangaListListener,
) = adapterDelegateViewBinding<ErrorFooter, ListModel, ItemErrorFooterBinding>(
{ inflater, parent -> ItemErrorFooterBinding.inflate(inflater, parent, false) }
) {
binding.root.setOnClickListener {
onRetryClick(item.exception)
listener.onRetryClick(item.exception)
}
bind {

View File

@@ -8,13 +8,13 @@ import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
fun errorStateListAD(
onRetryClick: (Throwable) -> Unit
listener: MangaListListener,
) = adapterDelegateViewBinding<ErrorState, ListModel, ItemErrorStateBinding>(
{ inflater, parent -> ItemErrorStateBinding.inflate(inflater, parent, false) }
) {
binding.buttonRetry.setOnClickListener {
onRetryClick(item.exception)
listener.onRetryClick(item.exception)
}
bind {

View File

@@ -24,14 +24,14 @@ fun listHeaderAD() = adapterDelegate<ListHeader, ListModel>(
}
fun listHeaderWithFilterAD(
onFilterClickListener: () -> Unit,
listener: MangaListListener,
) = adapterDelegateViewBinding<ListHeader, ListModel, ItemHeaderWithFilterBinding>(
viewBinding = { inflater, parent -> ItemHeaderWithFilterBinding.inflate(inflater, parent, false) },
on = { item, _, _ -> item is ListHeader && item.sortOrder != null },
) {
binding.textViewFilter.setOnClickListener {
onFilterClickListener()
listener.onFilterClick()
}
bind {

View File

@@ -4,9 +4,6 @@ import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.DiffUtil
import coil.ImageLoader
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaTag
import org.koitharu.kotatsu.core.ui.DateTimeAgo
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.list.ui.model.MangaGridModel
@@ -17,32 +14,29 @@ import kotlin.jvm.internal.Intrinsics
class MangaListAdapter(
coil: ImageLoader,
lifecycleOwner: LifecycleOwner,
clickListener: OnListItemClickListener<Manga>,
onRetryClick: (Throwable) -> Unit,
onTagRemoveClick: (MangaTag) -> Unit,
onFilterClickListener: () -> Unit,
listener: MangaListListener,
) : AsyncListDifferDelegationAdapter<ListModel>(DiffCallback()) {
init {
delegatesManager
.addDelegate(
ITEM_TYPE_MANGA_LIST,
mangaListItemAD(coil, lifecycleOwner, clickListener)
mangaListItemAD(coil, lifecycleOwner, listener)
)
.addDelegate(
ITEM_TYPE_MANGA_LIST_DETAILED,
mangaListDetailedItemAD(coil, lifecycleOwner, clickListener)
mangaListDetailedItemAD(coil, lifecycleOwner, listener)
)
.addDelegate(ITEM_TYPE_MANGA_GRID, mangaGridItemAD(coil, lifecycleOwner, clickListener))
.addDelegate(ITEM_TYPE_MANGA_GRID, mangaGridItemAD(coil, lifecycleOwner, listener))
.addDelegate(ITEM_TYPE_LOADING_FOOTER, loadingFooterAD())
.addDelegate(ITEM_TYPE_LOADING_STATE, loadingStateAD())
.addDelegate(ITEM_TYPE_DATE, relatedDateItemAD())
.addDelegate(ITEM_TYPE_ERROR_STATE, errorStateListAD(onRetryClick))
.addDelegate(ITEM_TYPE_ERROR_FOOTER, errorFooterAD(onRetryClick))
.addDelegate(ITEM_TYPE_EMPTY, emptyStateListAD())
.addDelegate(ITEM_TYPE_ERROR_STATE, errorStateListAD(listener))
.addDelegate(ITEM_TYPE_ERROR_FOOTER, errorFooterAD(listener))
.addDelegate(ITEM_TYPE_EMPTY, emptyStateListAD(listener))
.addDelegate(ITEM_TYPE_HEADER, listHeaderAD())
.addDelegate(ITEM_TYPE_FILTER, currentFilterAD(onTagRemoveClick))
.addDelegate(ITEM_TYPE_HEADER_FILTER, listHeaderWithFilterAD(onFilterClickListener))
.addDelegate(ITEM_TYPE_FILTER, currentFilterAD(listener))
.addDelegate(ITEM_TYPE_HEADER_FILTER, listHeaderWithFilterAD(listener))
}
private class DiffCallback : DiffUtil.ItemCallback<ListModel>() {

View File

@@ -0,0 +1,13 @@
package org.koitharu.kotatsu.list.ui.adapter
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaTag
interface MangaListListener : OnListItemClickListener<Manga> {
fun onRetryClick(error: Throwable)
fun onTagRemoveClick(tag: MangaTag)
fun onFilterClick()
fun onEmptyActionClick()
}

View File

@@ -24,10 +24,15 @@ class FilterBottomSheet : BaseBottomSheet<SheetFilterBinding>() {
) {
parametersOf(
requireArguments().getParcelable<MangaSource>(ARG_SOURCE),
requireArguments().getParcelable<FilterState>(ARG_STATE),
)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val state = requireArguments().getParcelable<FilterState>(ARG_STATE)
viewModel.updateState(state)
}
override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): SheetFilterBinding {
return SheetFilterBinding.inflate(inflater, container, false)
}

View File

@@ -11,28 +11,24 @@ import org.koitharu.kotatsu.base.ui.BaseViewModel
import org.koitharu.kotatsu.core.model.MangaTag
import org.koitharu.kotatsu.core.model.SortOrder
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
import org.koitharu.kotatsu.utils.ext.replaceWith
import java.util.*
class FilterViewModel(
private val repository: RemoteMangaRepository,
dataRepository: MangaDataRepository,
state: FilterState,
) : BaseViewModel(), OnFilterChangedListener {
val filter = MutableLiveData<List<FilterItem>>()
val result = MutableLiveData<FilterState>()
private var job: Job? = null
private var selectedSortOrder: SortOrder? = state.sortOrder
private val selectedTags = HashSet(state.tags)
private var selectedSortOrder: SortOrder? = repository.sortOrders.firstOrNull()
private val selectedTags = HashSet<MangaTag>()
private val localTagsDeferred = viewModelScope.async(Dispatchers.Default) {
dataRepository.findTags(repository.source)
}
private var availableTagsDeferred = loadTagsAsync()
init {
showFilter()
}
override fun onSortItemClick(item: FilterItem.Sort) {
selectedSortOrder = item.order
updateFilters()
@@ -49,6 +45,18 @@ class FilterViewModel(
}
}
fun updateState(state: FilterState?) {
if (state != null) {
selectedSortOrder = state.sortOrder
selectedTags.replaceWith(state.tags)
}
if (job == null) {
showFilter()
} else {
updateFilters()
}
}
@AnyThread
private fun updateFilters() {
val previousJob = job

View File

@@ -6,5 +6,6 @@ import androidx.annotation.StringRes
data class EmptyState(
@DrawableRes val icon: Int,
@StringRes val textPrimary: Int,
@StringRes val textSecondary: Int
@StringRes val textSecondary: Int,
@StringRes val actionStringRes: Int,
) : ListModel

View File

@@ -62,6 +62,21 @@ class LocalListFragment : MangaListFragment(), ActivityResultCallback<List<@JvmS
override fun onScrolledToEnd() = Unit
override fun onEmptyActionClick() {
try {
importCall.launch(arrayOf("*/*"))
} catch (e: ActivityNotFoundException) {
if (BuildConfig.DEBUG) {
e.printStackTrace()
}
Snackbar.make(
binding.recyclerView,
R.string.operation_not_supported,
Snackbar.LENGTH_SHORT
).show()
}
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.opt_local, menu)
super.onCreateOptionsMenu(menu, inflater)
@@ -70,18 +85,7 @@ class LocalListFragment : MangaListFragment(), ActivityResultCallback<List<@JvmS
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_import -> {
try {
importCall.launch(arrayOf("*/*"))
} catch (e: ActivityNotFoundException) {
if (BuildConfig.DEBUG) {
e.printStackTrace()
}
Snackbar.make(
binding.recyclerView,
R.string.operation_not_supported,
Snackbar.LENGTH_SHORT
).show()
}
onEmptyActionClick()
true
}
else -> super.onOptionsItemSelected(item)

View File

@@ -45,9 +45,10 @@ class LocalListViewModel(
list == null -> listOf(LoadingState)
list.isEmpty() -> listOf(
EmptyState(
R.drawable.ic_storage,
R.string.text_local_holder_primary,
R.string.text_local_holder_secondary
icon = R.drawable.ic_storage,
textPrimary = R.string.text_local_holder_primary,
textSecondary = R.string.text_local_holder_secondary,
actionStringRes = R.string._import,
)
)
else -> ArrayList<ListModel>(list.size + 1).apply {

View File

@@ -23,7 +23,6 @@ val remoteListModule
FilterViewModel(
repository = get<MangaRepository>(named(params.get<MangaSource>())) as RemoteMangaRepository,
dataRepository = get(),
state = params.get(),
)
}
}

View File

@@ -12,6 +12,7 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.list.ui.MangaListFragment
import org.koitharu.kotatsu.list.ui.filter.FilterBottomSheet
import org.koitharu.kotatsu.list.ui.filter.FilterState
import org.koitharu.kotatsu.reader.ui.SimpleSettingsActivity
import org.koitharu.kotatsu.utils.ext.parcelableArgument
import org.koitharu.kotatsu.utils.ext.withArgs
@@ -65,6 +66,10 @@ class RemoteListFragment : MangaListFragment(), FragmentResultListener {
FilterBottomSheet.show(childFragmentManager, source, viewModel.filter)
}
override fun onEmptyActionClick() {
viewModel.applyFilter(FilterState(viewModel.filter.sortOrder, emptySet()))
}
override fun onFragmentResult(requestKey: String, result: Bundle) {
when (requestKey) {
FilterBottomSheet.REQUEST_KEY -> viewModel.applyFilter(

View File

@@ -42,7 +42,7 @@ class RemoteListViewModel(
when {
list.isNullOrEmpty() && error != null -> listOf(error.toErrorState(canRetry = true))
list == null -> listOf(LoadingState)
list.isEmpty() -> listOf(EmptyState(R.drawable.ic_book_cross, R.string.nothing_found, R.string.empty))
list.isEmpty() -> createEmptyState()
else -> {
val result = ArrayList<ListModel>(list.size + 3)
result += header
@@ -126,4 +126,13 @@ class RemoteListViewModel(
CurrentFilterModel(tags.map { ChipsView.ChipModel(0, it.title, it) })
}
}
private fun createEmptyState() = listOf(
EmptyState(
icon = R.drawable.ic_book_cross,
textPrimary = R.string.nothing_found,
textSecondary = 0,
actionStringRes = if (filter.tags.isEmpty()) 0 else R.string.reset_filter,
)
)
}

View File

@@ -33,7 +33,14 @@ class SearchViewModel(
when {
list.isNullOrEmpty() && error != null -> listOf(error.toErrorState(canRetry = true))
list == null -> listOf(LoadingState)
list.isEmpty() -> listOf(EmptyState(R.drawable.ic_book_search, R.string.nothing_found, R.string.text_search_holder_secondary))
list.isEmpty() -> listOf(
EmptyState(
icon = R.drawable.ic_book_search,
textPrimary = R.string.nothing_found,
textSecondary = R.string.text_search_holder_secondary,
actionStringRes = 0,
)
)
else -> {
val result = ArrayList<ListModel>(list.size + 1)
list.toUi(result, mode)

View File

@@ -13,7 +13,6 @@ import org.koitharu.kotatsu.list.ui.model.*
import org.koitharu.kotatsu.search.domain.MangaSearchRepository
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
import org.koitharu.kotatsu.utils.ext.onFirst
import java.util.*
class GlobalSearchViewModel(
private val query: String,
@@ -35,7 +34,14 @@ class GlobalSearchViewModel(
when {
list.isNullOrEmpty() && error != null -> listOf(error.toErrorState(canRetry = true))
list == null -> listOf(LoadingState)
list.isEmpty() -> listOf(EmptyState(R.drawable.ic_book_search, R.string.nothing_found, R.string.text_search_holder_secondary))
list.isEmpty() -> listOf(
EmptyState(
icon = R.drawable.ic_book_search,
textPrimary = R.string.nothing_found,
textSecondary = R.string.text_search_holder_secondary,
actionStringRes = 0,
)
)
else -> {
val result = ArrayList<ListModel>(list.size + 1)
list.toUi(result, mode)

View File

@@ -24,11 +24,14 @@ class SuggestionsViewModel(
createListModeFlow()
) { list, mode ->
when {
list.isEmpty() -> listOf(EmptyState(
icon = R.drawable.ic_book_cross,
textPrimary = R.string.nothing_found,
textSecondary = R.string.text_suggestion_holder,
))
list.isEmpty() -> listOf(
EmptyState(
icon = R.drawable.ic_book_cross,
textPrimary = R.string.nothing_found,
textSecondary = R.string.text_suggestion_holder,
actionStringRes = 0,
)
)
else -> buildList<ListModel>(list.size + 1) {
add(headerModel)
list.toUi(this, mode)

View File

@@ -10,12 +10,13 @@ import org.koin.android.ext.android.get
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseFragment
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.base.ui.list.PaginationScrollListener
import org.koitharu.kotatsu.base.ui.list.decor.SpacingItemDecoration
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaTag
import org.koitharu.kotatsu.databinding.FragmentFeedBinding
import org.koitharu.kotatsu.details.ui.DetailsActivity
import org.koitharu.kotatsu.list.ui.adapter.MangaListListener
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.main.ui.AppBarOwner
import org.koitharu.kotatsu.tracker.ui.adapter.FeedAdapter
@@ -25,7 +26,7 @@ import org.koitharu.kotatsu.utils.ext.measureHeight
import org.koitharu.kotatsu.utils.progress.Progress
class FeedFragment : BaseFragment<FragmentFeedBinding>(), PaginationScrollListener.Callback,
OnListItemClickListener<Manga> {
MangaListListener {
private val viewModel by viewModel<FeedViewModel>()
@@ -114,6 +115,14 @@ class FeedFragment : BaseFragment<FragmentFeedBinding>(), PaginationScrollListen
)
}
override fun onRetryClick(error: Throwable) = Unit
override fun onTagRemoveClick(tag: MangaTag) = Unit
override fun onFilterClick() = Unit
override fun onEmptyActionClick() = Unit
private fun onListChanged(list: List<ListModel>) {
feedAdapter?.items = list
}

View File

@@ -37,7 +37,14 @@ class FeedViewModel(
hasNextPage
) { list, isHasNextPage ->
when {
list.isEmpty() -> listOf(EmptyState(R.drawable.ic_feed, R.string.text_empty_holder_primary, R.string.text_feed_holder))
list.isEmpty() -> listOf(
EmptyState(
icon = R.drawable.ic_feed,
textPrimary = R.string.text_empty_holder_primary,
textSecondary = R.string.text_feed_holder,
actionStringRes = 0,
)
)
isHasNextPage -> list + LoadingFooter
else -> list
}

View File

@@ -4,8 +4,6 @@ import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.DiffUtil
import coil.ImageLoader
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.list.ui.adapter.*
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.list.ui.model.LoadingFooter
@@ -15,17 +13,17 @@ import kotlin.jvm.internal.Intrinsics
class FeedAdapter(
coil: ImageLoader,
lifecycleOwner: LifecycleOwner,
clickListener: OnListItemClickListener<Manga>
listener: MangaListListener,
) : AsyncListDifferDelegationAdapter<ListModel>(DiffCallback()) {
init {
delegatesManager
.addDelegate(ITEM_TYPE_FEED, feedItemAD(coil, lifecycleOwner, clickListener))
.addDelegate(ITEM_TYPE_FEED, feedItemAD(coil, lifecycleOwner, listener))
.addDelegate(ITEM_TYPE_LOADING_FOOTER, loadingFooterAD())
.addDelegate(ITEM_TYPE_LOADING_STATE, loadingStateAD())
.addDelegate(ITEM_TYPE_ERROR_FOOTER, errorFooterAD {})
.addDelegate(ITEM_TYPE_ERROR_STATE, errorStateListAD {})
.addDelegate(ITEM_TYPE_EMPTY, emptyStateListAD())
.addDelegate(ITEM_TYPE_ERROR_FOOTER, errorFooterAD(listener))
.addDelegate(ITEM_TYPE_ERROR_STATE, errorStateListAD(listener))
.addDelegate(ITEM_TYPE_EMPTY, emptyStateListAD(listener))
}
private class DiffCallback : DiffUtil.ItemCallback<ListModel>() {

View File

@@ -3,6 +3,7 @@ package org.koitharu.kotatsu.utils.ext
import android.graphics.drawable.Drawable
import android.view.View
import android.widget.TextView
import androidx.annotation.StringRes
import androidx.core.view.isGone
var TextView.textAndVisible: CharSequence?
@@ -17,4 +18,14 @@ var TextView.drawableStart: Drawable?
set(value) {
val dr = compoundDrawablesRelative
setCompoundDrawablesRelativeWithIntrinsicBounds(value, dr[1], dr[2], dr[3])
}
}
fun TextView.setTextAndVisible(@StringRes textResId: Int) {
if (textResId == 0) {
text = null
isGone = true
} else {
setText(textResId)
isGone = text.isNullOrEmpty()
}
}

View File

@@ -1,15 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<HorizontalScrollView
<org.koitharu.kotatsu.base.ui.widgets.ChipsView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/chips_tags"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<org.koitharu.kotatsu.base.ui.widgets.ChipsView
android:id="@+id/chips_tags"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:closeIconEnabled="true"
app:singleLine="true" />
</HorizontalScrollView>
android:layout_height="wrap_content"
app:closeIconEnabled="true" />

View File

@@ -34,4 +34,14 @@
android:textAppearance="?attr/textAppearanceBodyMedium"
tools:text="@tools:sample/lorem[15]" />
<Button
android:id="@+id/button_retry"
style="@style/Widget.Material3.Button.TonalButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:visibility="gone"
tools:text="@string/try_again"
tools:visibility="visible" />
</LinearLayout>

View File

@@ -264,4 +264,5 @@
<string name="enabled">Enabled</string>
<string name="disabled">Disabled</string>
<string name="filter_load_error">Unable to load genres list</string>
<string name="reset_filter">Reset filter</string>
</resources>