diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/FilterFieldLayout.kt b/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/FilterFieldLayout.kt index 107f66072..90ca05cd8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/FilterFieldLayout.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/FilterFieldLayout.kt @@ -97,7 +97,7 @@ class FilterFieldLayout @JvmOverloads constructor( label, context.getThemeColorStateList(materialR.attr.colorControlNormal), ) - addView(errorView) + addView(label) errorView = label return label } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/tags/TagsCatalogAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/tags/TagsCatalogAdapter.kt index 1fad15e5b..0dd728c61 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/tags/TagsCatalogAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/tags/TagsCatalogAdapter.kt @@ -11,6 +11,7 @@ import org.koitharu.kotatsu.filter.ui.model.TagCatalogItem import org.koitharu.kotatsu.list.ui.ListModelDiffCallback import org.koitharu.kotatsu.list.ui.adapter.ListItemType import org.koitharu.kotatsu.list.ui.adapter.errorFooterAD +import org.koitharu.kotatsu.list.ui.adapter.errorStateListAD import org.koitharu.kotatsu.list.ui.adapter.loadingFooterAD import org.koitharu.kotatsu.list.ui.adapter.loadingStateAD import org.koitharu.kotatsu.list.ui.model.ListModel @@ -24,6 +25,7 @@ class TagsCatalogAdapter( addDelegate(ListItemType.STATE_LOADING, loadingStateAD()) addDelegate(ListItemType.FOOTER_LOADING, loadingFooterAD()) addDelegate(ListItemType.FOOTER_ERROR, errorFooterAD(null)) + addDelegate(ListItemType.STATE_ERROR, errorStateListAD(null)) } override fun getSectionText(context: Context, position: Int): CharSequence? { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/ErrorStateListAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/ErrorStateListAD.kt index 89f9741ab..0bc648675 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/ErrorStateListAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/ErrorStateListAD.kt @@ -11,20 +11,22 @@ import org.koitharu.kotatsu.list.ui.model.ErrorState import org.koitharu.kotatsu.list.ui.model.ListModel fun errorStateListAD( - listener: ListStateHolderListener, + listener: ListStateHolderListener?, ) = adapterDelegateViewBinding( { inflater, parent -> ItemErrorStateBinding.inflate(inflater, parent, false) }, ) { - val onClickListener = View.OnClickListener { v -> - when (v.id) { - R.id.button_retry -> listener.onRetryClick(item.exception) - R.id.button_secondary -> listener.onSecondaryErrorActionClick(item.exception) + if (listener != null) { + val onClickListener = View.OnClickListener { v -> + when (v.id) { + R.id.button_retry -> listener.onRetryClick(item.exception) + R.id.button_secondary -> listener.onSecondaryErrorActionClick(item.exception) + } } - } - binding.buttonRetry.setOnClickListener(onClickListener) - binding.buttonSecondary.setOnClickListener(onClickListener) + binding.buttonRetry.setOnClickListener(onClickListener) + binding.buttonSecondary.setOnClickListener(onClickListener) + } bind { with(binding.textViewError) { @@ -32,7 +34,7 @@ fun errorStateListAD( setCompoundDrawablesWithIntrinsicBounds(0, item.icon, 0, 0) } with(binding.buttonRetry) { - isVisible = item.canRetry + isVisible = item.canRetry && listener != null setText(item.buttonText) } binding.buttonSecondary.setTextAndVisible(item.secondaryButtonText) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/LocalMangaRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/LocalMangaRepository.kt index d43bf2805..c27deb7ef 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/LocalMangaRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/LocalMangaRepository.kt @@ -25,13 +25,16 @@ import org.koitharu.kotatsu.local.data.output.LocalMangaOutput import org.koitharu.kotatsu.local.data.output.LocalMangaUtil import org.koitharu.kotatsu.local.domain.MangaLock import org.koitharu.kotatsu.local.domain.model.LocalManga +import org.koitharu.kotatsu.parsers.model.ContentRating import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.parsers.model.MangaListFilter import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaListFilterOptions import org.koitharu.kotatsu.parsers.model.MangaPage +import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.model.SortOrder +import org.koitharu.kotatsu.parsers.util.mapToSet import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import java.io.File import java.util.EnumSet @@ -67,7 +70,14 @@ class LocalMangaRepository @Inject constructor( settings.localListOrder = value } - override suspend fun getFilterOptions() = MangaListFilterOptions() + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = localMangaIndex.getAvailableTags().mapToSet { MangaTag(title = it, key = it, source = source) }, + availableContentRating = if (!settings.isNsfwContentDisabled) { + EnumSet.of(ContentRating.SAFE, ContentRating.ADULT) + } else { + emptySet() + }, + ) override suspend fun getList(offset: Int, order: SortOrder?, filter: MangaListFilter?): List { if (offset > 0) { @@ -75,7 +85,7 @@ class LocalMangaRepository @Inject constructor( } val list = getRawList() if (settings.isNsfwContentDisabled) { - list.removeIf { it.manga.isNsfw } + list.removeAll { it.manga.isNsfw } } if (filter != null) { val query = filter.query @@ -83,10 +93,14 @@ class LocalMangaRepository @Inject constructor( list.retainAll { x -> x.isMatchesQuery(query) } } if (filter.tags.isNotEmpty()) { - list.retainAll { x -> x.containsTags(filter.tags) } + list.retainAll { x -> x.containsTags(filter.tags.mapToSet { it.title }) } } if (filter.tagsExclude.isNotEmpty()) { - list.removeAll { x -> x.containsAnyTag(filter.tags) } + list.removeAll { x -> x.containsAnyTag(filter.tagsExclude.mapToSet { it.title }) } + } + filter.contentRating.singleOrNull()?.let { contentRating -> + val isNsfw = contentRating == ContentRating.ADULT + list.retainAll { it.manga.isNsfw == isNsfw } } } when (order) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/index/LocalMangaIndex.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/index/LocalMangaIndex.kt index f954f78b5..e68067acf 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/index/LocalMangaIndex.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/index/LocalMangaIndex.kt @@ -16,6 +16,7 @@ import org.koitharu.kotatsu.local.data.LocalMangaRepository import org.koitharu.kotatsu.local.data.LocalStorageManager import org.koitharu.kotatsu.local.data.input.LocalMangaInput import org.koitharu.kotatsu.local.domain.model.LocalManga +import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import java.io.File import javax.inject.Inject @@ -85,6 +86,10 @@ class LocalMangaIndex @Inject constructor( db.getLocalMangaIndexDao().delete(mangaId) } + suspend fun getAvailableTags(): List { + return db.getLocalMangaIndexDao().findTags() + } + private fun LocalManga.toEntity() = LocalMangaIndexEntity( mangaId = manga.id, path = file.path, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/index/LocalMangaIndexDao.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/index/LocalMangaIndexDao.kt index 909a0458c..8b1b6f1b0 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/index/LocalMangaIndexDao.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/index/LocalMangaIndexDao.kt @@ -3,7 +3,6 @@ package org.koitharu.kotatsu.local.data.index import androidx.room.Dao import androidx.room.Query import androidx.room.Upsert -import org.koitharu.kotatsu.core.db.entity.TagEntity @Dao interface LocalMangaIndexDao { @@ -11,6 +10,9 @@ interface LocalMangaIndexDao { @Query("SELECT path FROM local_index WHERE manga_id = :mangaId") suspend fun findPath(mangaId: Long): String? + @Query("SELECT title FROM local_index LEFT JOIN manga_tags ON manga_tags.manga_id = local_index.manga_id LEFT JOIN tags ON tags.tag_id = manga_tags.tag_id WHERE title IS NOT NULL GROUP BY title") + suspend fun findTags(): List + @Upsert suspend fun upsert(entity: LocalMangaIndexEntity) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/domain/model/LocalManga.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/domain/model/LocalManga.kt index 1f23d304f..2d8e8941e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/domain/model/LocalManga.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/domain/model/LocalManga.kt @@ -23,17 +23,20 @@ data class LocalManga( fun isMatchesQuery(query: String): Boolean { return manga.title.contains(query, ignoreCase = true) || - manga.altTitle?.contains(query, ignoreCase = true) == true + manga.altTitle?.contains(query, ignoreCase = true) == true || + manga.author?.contains(query, ignoreCase = true) == true } - fun containsTags(tags: Set): Boolean { - return manga.tags.containsAll(tags) + fun containsTags(tags: Collection): Boolean { + return tags.all { tag -> tag in manga.tags } } - fun containsAnyTag(tags: Set): Boolean { - return tags.any { tag -> - manga.tags.contains(tag) - } + fun containsAnyTag(tags: Collection): Boolean { + return tags.any { tag -> tag in manga.tags } + } + + private operator fun Collection.contains(title: String): Boolean { + return any { it.title.equals(title, ignoreCase = true) } } override fun toString(): String {