Local manga source filter

master
Koitharu 2 years ago
parent 8365603bf1
commit bc4622d610
Signed by: Koitharu
GPG Key ID: 676DEE768C17A9D7

@ -97,7 +97,7 @@ class FilterFieldLayout @JvmOverloads constructor(
label, label,
context.getThemeColorStateList(materialR.attr.colorControlNormal), context.getThemeColorStateList(materialR.attr.colorControlNormal),
) )
addView(errorView) addView(label)
errorView = label errorView = label
return label return label
} }

@ -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.ListModelDiffCallback
import org.koitharu.kotatsu.list.ui.adapter.ListItemType import org.koitharu.kotatsu.list.ui.adapter.ListItemType
import org.koitharu.kotatsu.list.ui.adapter.errorFooterAD 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.loadingFooterAD
import org.koitharu.kotatsu.list.ui.adapter.loadingStateAD import org.koitharu.kotatsu.list.ui.adapter.loadingStateAD
import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.list.ui.model.ListModel
@ -24,6 +25,7 @@ class TagsCatalogAdapter(
addDelegate(ListItemType.STATE_LOADING, loadingStateAD()) addDelegate(ListItemType.STATE_LOADING, loadingStateAD())
addDelegate(ListItemType.FOOTER_LOADING, loadingFooterAD()) addDelegate(ListItemType.FOOTER_LOADING, loadingFooterAD())
addDelegate(ListItemType.FOOTER_ERROR, errorFooterAD(null)) addDelegate(ListItemType.FOOTER_ERROR, errorFooterAD(null))
addDelegate(ListItemType.STATE_ERROR, errorStateListAD(null))
} }
override fun getSectionText(context: Context, position: Int): CharSequence? { override fun getSectionText(context: Context, position: Int): CharSequence? {

@ -11,11 +11,12 @@ import org.koitharu.kotatsu.list.ui.model.ErrorState
import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.list.ui.model.ListModel
fun errorStateListAD( fun errorStateListAD(
listener: ListStateHolderListener, listener: ListStateHolderListener?,
) = adapterDelegateViewBinding<ErrorState, ListModel, ItemErrorStateBinding>( ) = adapterDelegateViewBinding<ErrorState, ListModel, ItemErrorStateBinding>(
{ inflater, parent -> ItemErrorStateBinding.inflate(inflater, parent, false) }, { inflater, parent -> ItemErrorStateBinding.inflate(inflater, parent, false) },
) { ) {
if (listener != null) {
val onClickListener = View.OnClickListener { v -> val onClickListener = View.OnClickListener { v ->
when (v.id) { when (v.id) {
R.id.button_retry -> listener.onRetryClick(item.exception) R.id.button_retry -> listener.onRetryClick(item.exception)
@ -25,6 +26,7 @@ fun errorStateListAD(
binding.buttonRetry.setOnClickListener(onClickListener) binding.buttonRetry.setOnClickListener(onClickListener)
binding.buttonSecondary.setOnClickListener(onClickListener) binding.buttonSecondary.setOnClickListener(onClickListener)
}
bind { bind {
with(binding.textViewError) { with(binding.textViewError) {
@ -32,7 +34,7 @@ fun errorStateListAD(
setCompoundDrawablesWithIntrinsicBounds(0, item.icon, 0, 0) setCompoundDrawablesWithIntrinsicBounds(0, item.icon, 0, 0)
} }
with(binding.buttonRetry) { with(binding.buttonRetry) {
isVisible = item.canRetry isVisible = item.canRetry && listener != null
setText(item.buttonText) setText(item.buttonText)
} }
binding.buttonSecondary.setTextAndVisible(item.secondaryButtonText) binding.buttonSecondary.setTextAndVisible(item.secondaryButtonText)

@ -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.data.output.LocalMangaUtil
import org.koitharu.kotatsu.local.domain.MangaLock import org.koitharu.kotatsu.local.domain.MangaLock
import org.koitharu.kotatsu.local.domain.model.LocalManga 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.Manga
import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaListFilter import org.koitharu.kotatsu.parsers.model.MangaListFilter
import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities
import org.koitharu.kotatsu.parsers.model.MangaListFilterOptions import org.koitharu.kotatsu.parsers.model.MangaListFilterOptions
import org.koitharu.kotatsu.parsers.model.MangaPage 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.model.SortOrder
import org.koitharu.kotatsu.parsers.util.mapToSet
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import java.io.File import java.io.File
import java.util.EnumSet import java.util.EnumSet
@ -67,7 +70,14 @@ class LocalMangaRepository @Inject constructor(
settings.localListOrder = value 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<Manga> { override suspend fun getList(offset: Int, order: SortOrder?, filter: MangaListFilter?): List<Manga> {
if (offset > 0) { if (offset > 0) {
@ -75,7 +85,7 @@ class LocalMangaRepository @Inject constructor(
} }
val list = getRawList() val list = getRawList()
if (settings.isNsfwContentDisabled) { if (settings.isNsfwContentDisabled) {
list.removeIf { it.manga.isNsfw } list.removeAll { it.manga.isNsfw }
} }
if (filter != null) { if (filter != null) {
val query = filter.query val query = filter.query
@ -83,10 +93,14 @@ class LocalMangaRepository @Inject constructor(
list.retainAll { x -> x.isMatchesQuery(query) } list.retainAll { x -> x.isMatchesQuery(query) }
} }
if (filter.tags.isNotEmpty()) { 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()) { 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) { when (order) {

@ -16,6 +16,7 @@ import org.koitharu.kotatsu.local.data.LocalMangaRepository
import org.koitharu.kotatsu.local.data.LocalStorageManager import org.koitharu.kotatsu.local.data.LocalStorageManager
import org.koitharu.kotatsu.local.data.input.LocalMangaInput import org.koitharu.kotatsu.local.data.input.LocalMangaInput
import org.koitharu.kotatsu.local.domain.model.LocalManga import org.koitharu.kotatsu.local.domain.model.LocalManga
import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import java.io.File import java.io.File
import javax.inject.Inject import javax.inject.Inject
@ -85,6 +86,10 @@ class LocalMangaIndex @Inject constructor(
db.getLocalMangaIndexDao().delete(mangaId) db.getLocalMangaIndexDao().delete(mangaId)
} }
suspend fun getAvailableTags(): List<String> {
return db.getLocalMangaIndexDao().findTags()
}
private fun LocalManga.toEntity() = LocalMangaIndexEntity( private fun LocalManga.toEntity() = LocalMangaIndexEntity(
mangaId = manga.id, mangaId = manga.id,
path = file.path, path = file.path,

@ -3,7 +3,6 @@ package org.koitharu.kotatsu.local.data.index
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import androidx.room.Upsert import androidx.room.Upsert
import org.koitharu.kotatsu.core.db.entity.TagEntity
@Dao @Dao
interface LocalMangaIndexDao { interface LocalMangaIndexDao {
@ -11,6 +10,9 @@ interface LocalMangaIndexDao {
@Query("SELECT path FROM local_index WHERE manga_id = :mangaId") @Query("SELECT path FROM local_index WHERE manga_id = :mangaId")
suspend fun findPath(mangaId: Long): String? 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<String>
@Upsert @Upsert
suspend fun upsert(entity: LocalMangaIndexEntity) suspend fun upsert(entity: LocalMangaIndexEntity)

@ -23,17 +23,20 @@ data class LocalManga(
fun isMatchesQuery(query: String): Boolean { fun isMatchesQuery(query: String): Boolean {
return manga.title.contains(query, ignoreCase = true) || 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<MangaTag>): Boolean { fun containsTags(tags: Collection<String>): Boolean {
return manga.tags.containsAll(tags) return tags.all { tag -> tag in manga.tags }
} }
fun containsAnyTag(tags: Set<MangaTag>): Boolean { fun containsAnyTag(tags: Collection<String>): Boolean {
return tags.any { tag -> return tags.any { tag -> tag in manga.tags }
manga.tags.contains(tag)
} }
private operator fun Collection<MangaTag>.contains(title: String): Boolean {
return any { it.title.equals(title, ignoreCase = true) }
} }
override fun toString(): String { override fun toString(): String {

Loading…
Cancel
Save