Local manga source filter
This commit is contained in:
@@ -97,7 +97,7 @@ class FilterFieldLayout @JvmOverloads constructor(
|
||||
label,
|
||||
context.getThemeColorStateList(materialR.attr.colorControlNormal),
|
||||
)
|
||||
addView(errorView)
|
||||
addView(label)
|
||||
errorView = 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.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? {
|
||||
|
||||
@@ -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<ErrorState, ListModel, ItemErrorStateBinding>(
|
||||
{ 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)
|
||||
|
||||
@@ -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<Manga> {
|
||||
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) {
|
||||
|
||||
@@ -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<String> {
|
||||
return db.getLocalMangaIndexDao().findTags()
|
||||
}
|
||||
|
||||
private fun LocalManga.toEntity() = LocalMangaIndexEntity(
|
||||
mangaId = manga.id,
|
||||
path = file.path,
|
||||
|
||||
@@ -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<String>
|
||||
|
||||
@Upsert
|
||||
suspend fun upsert(entity: LocalMangaIndexEntity)
|
||||
|
||||
|
||||
@@ -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<MangaTag>): Boolean {
|
||||
return manga.tags.containsAll(tags)
|
||||
fun containsTags(tags: Collection<String>): Boolean {
|
||||
return tags.all { tag -> tag in manga.tags }
|
||||
}
|
||||
|
||||
fun containsAnyTag(tags: Set<MangaTag>): Boolean {
|
||||
return tags.any { tag ->
|
||||
manga.tags.contains(tag)
|
||||
}
|
||||
fun containsAnyTag(tags: Collection<String>): Boolean {
|
||||
return tags.any { tag -> tag in manga.tags }
|
||||
}
|
||||
|
||||
private operator fun Collection<MangaTag>.contains(title: String): Boolean {
|
||||
return any { it.title.equals(title, ignoreCase = true) }
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
|
||||
Reference in New Issue
Block a user