mirror of
https://github.com/mihonapp/mihon.git
synced 2024-11-21 20:47:03 -05:00
Add Sort filter [Catalogs] (#633)
* Add Sort filter * remove old views * onClick default descending * update remaining catalogs
This commit is contained in:
parent
f717c57648
commit
a03dceff7d
6 changed files with 108 additions and 37 deletions
|
@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.data.source.model
|
|||
|
||||
sealed class Filter<T>(val name: String, var state: T) {
|
||||
open class Header(name: String) : Filter<Any>(name, 0)
|
||||
open class Separator(name: String = "") : Filter<Any>(name, 0)
|
||||
abstract class List<V>(name: String, val values: Array<V>, state: Int = 0) : Filter<Int>(name, state)
|
||||
abstract class Text(name: String, state: String = "") : Filter<String>(name, state)
|
||||
abstract class CheckBox(name: String, state: Boolean = false) : Filter<Boolean>(name, state)
|
||||
|
@ -9,10 +10,16 @@ sealed class Filter<T>(val name: String, var state: T) {
|
|||
fun isIgnored() = state == STATE_IGNORE
|
||||
fun isIncluded() = state == STATE_INCLUDE
|
||||
fun isExcluded() = state == STATE_EXCLUDE
|
||||
|
||||
companion object {
|
||||
const val STATE_IGNORE = 0
|
||||
const val STATE_INCLUDE = 1
|
||||
const val STATE_EXCLUDE = 2
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Sort<V>(name: String, val values: Array<V>, state: Selection? = null)
|
||||
: Filter<Sort.Selection?>(name, state) {
|
||||
data class Selection(val index: Int, val ascending: Boolean)
|
||||
}
|
||||
}
|
|
@ -107,6 +107,10 @@ class Batoto : ParsedOnlineSource(), LoginSource {
|
|||
val sel = if (filter.state) filter.valTrue else filter.valFalse
|
||||
if (!sel.isEmpty()) url.addQueryParameter(filter.key, sel)
|
||||
}
|
||||
is OrderBy -> {
|
||||
url.addQueryParameter("order_cond", arrayOf("title", "author", "artist", "rating", "views", "update")[filter.state!!.index])
|
||||
url.addQueryParameter("order", if (filter.state?.ascending == true) "asc" else "desc")
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!genres.isEmpty()) url.addQueryParameter("genres", genres)
|
||||
|
@ -288,6 +292,9 @@ class Batoto : ParsedOnlineSource(), LoginSource {
|
|||
private class TextField(name: String, val key: String) : Filter.Text(name)
|
||||
private class ListField(name: String, val key: String, values: Array<ListValue>, state: Int = 0) : Filter.List<ListValue>(name, values, state)
|
||||
private class Flag(name: String, val key: String, val valTrue: String, val valFalse: String) : Filter.CheckBox(name)
|
||||
private class OrderBy() : Filter.Sort<String>("Order by",
|
||||
arrayOf("Title", "Author", "Artist", "Rating", "Views", "Last Update"),
|
||||
Filter.Sort.Selection(4, false))
|
||||
|
||||
// [...document.querySelectorAll("#advanced_options div.genre_buttons")].map((el,i) => {
|
||||
// const onClick=el.getAttribute('onclick');const id=onClick.substr(14,onClick.length-16);return `Genre("${el.textContent.trim()}", ${id})`
|
||||
|
@ -298,8 +305,9 @@ class Batoto : ParsedOnlineSource(), LoginSource {
|
|||
ListField("Type", "type", arrayOf(ListValue("Any", ""), ListValue("Manga (Jp)", "jp"), ListValue("Manhwa (Kr)", "kr"), ListValue("Manhua (Cn)", "cn"), ListValue("Artbook", "ar"), ListValue("Other", "ot"))),
|
||||
Status(),
|
||||
Flag("Exclude mature", "mature", "m", ""),
|
||||
ListField("Order by", "order_cond", arrayOf(ListValue("Title", "title"), ListValue("Author", "author"), ListValue("Artist", "artist"), ListValue("Rating", "rating"), ListValue("Views", "views"), ListValue("Last Update", "update")), 4),
|
||||
Flag("Ascending order", "order", "asc", "desc"),
|
||||
Filter.Separator(),
|
||||
OrderBy(),
|
||||
Filter.Separator(),
|
||||
Filter.Header("Genres"),
|
||||
ListField("Inclusion mode", "genre_cond", arrayOf(ListValue("And (all selected genres)", "and"), ListValue("Or (any selected genres) ", "or"))),
|
||||
Genre("4-Koma", 40),
|
||||
|
|
|
@ -24,7 +24,7 @@ class Mangafox : ParsedOnlineSource() {
|
|||
override val supportsLatest = true
|
||||
|
||||
override fun popularMangaSelector() = "div#mangalist > ul.list > li"
|
||||
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request {
|
||||
val pageStr = if (page != 1) "$page.htm" else ""
|
||||
return GET("$baseUrl/directory/$pageStr", headers)
|
||||
|
@ -60,8 +60,11 @@ class Mangafox : ParsedOnlineSource() {
|
|||
when (filter) {
|
||||
is Genre -> url.addQueryParameter(filter.id, filter.state.toString())
|
||||
is TextField -> url.addQueryParameter(filter.key, filter.state)
|
||||
is ListField -> url.addQueryParameter(filter.key, filter.values[filter.state].value)
|
||||
is Order -> url.addQueryParameter("order", if (filter.state) "az" else "za")
|
||||
is Type -> url.addQueryParameter("type", if(filter.state == 0) "" else filter.state.toString())
|
||||
is OrderBy -> {
|
||||
url.addQueryParameter("sort", arrayOf("name", "rating", "views", "total_chapters", "last_chapter_time")[filter.state!!.index])
|
||||
url.addQueryParameter("order", if (filter.state?.ascending == true) "az" else "za")
|
||||
}
|
||||
}
|
||||
}
|
||||
url.addQueryParameter("page", page.toString())
|
||||
|
@ -158,24 +161,23 @@ class Mangafox : ParsedOnlineSource() {
|
|||
}
|
||||
}
|
||||
|
||||
private data class ListValue(val name: String, val value: String) {
|
||||
override fun toString(): String = name
|
||||
}
|
||||
|
||||
private class Genre(name: String, val id: String = "genres[$name]") : Filter.TriState(name)
|
||||
private class TextField(name: String, val key: String) : Filter.Text(name)
|
||||
private class ListField(name: String, val key: String, values: Array<ListValue>, state: Int = 0) : Filter.List<ListValue>(name, values, state)
|
||||
private class Order() : Filter.CheckBox("Ascending order")
|
||||
private class Type() : Filter.List<String>("Type", arrayOf("Any", "Japanese Manga", "Korean Manhwa", "Chinese Manhua"))
|
||||
private class OrderBy() : Filter.Sort<String>("Order by",
|
||||
arrayOf("Series name", "Rating", "Views", "Total chapters", "Last chapter"),
|
||||
Filter.Sort.Selection(2, false))
|
||||
|
||||
// $('select.genres').map((i,el)=>`Genre("${$(el).next().text().trim()}", "${$(el).attr('name')}")`).get().join(',\n')
|
||||
// on http://mangafox.me/search.php
|
||||
override fun getFilterList() = FilterList(
|
||||
TextField("Author", "author"),
|
||||
TextField("Artist", "artist"),
|
||||
ListField("Type", "type", arrayOf(ListValue("Any", ""), ListValue("Japanese Manga", "1"), ListValue("Korean Manhwa", "2"), ListValue("Chinese Manhua", "3"))),
|
||||
Type(),
|
||||
Genre("Completed", "is_completed"),
|
||||
ListField("Order by", "sort", arrayOf(ListValue("Series name", "name"), ListValue("Rating", "rating"), ListValue("Views", "views"), ListValue("Total chapters", "total_chapters"), ListValue("Last chapter", "last_chapter_time")), 2),
|
||||
Order(),
|
||||
Filter.Separator(),
|
||||
OrderBy(),
|
||||
Filter.Separator(),
|
||||
Filter.Header("Genres"),
|
||||
Genre("Action"),
|
||||
Genre("Adult"),
|
||||
|
|
|
@ -63,8 +63,11 @@ class Mangahere : ParsedOnlineSource() {
|
|||
is Status -> url.addQueryParameter("is_completed", arrayOf("", "1", "0")[filter.state])
|
||||
is Genre -> url.addQueryParameter(filter.id, filter.state.toString())
|
||||
is TextField -> url.addQueryParameter(filter.key, filter.state)
|
||||
is ListField -> url.addQueryParameter(filter.key, filter.values[filter.state].value)
|
||||
is Order -> url.addQueryParameter("order", if (filter.state) "az" else "za")
|
||||
is Type -> url.addQueryParameter("direction", arrayOf("", "rl", "lr")[filter.state])
|
||||
is OrderBy -> {
|
||||
url.addQueryParameter("sort", arrayOf("name", "rating", "views", "total_chapters", "last_chapter_time")[filter.state!!.index])
|
||||
url.addQueryParameter("order", if (filter.state?.ascending == true) "az" else "za")
|
||||
}
|
||||
}
|
||||
}
|
||||
url.addQueryParameter("page", page.toString())
|
||||
|
@ -166,18 +169,21 @@ class Mangahere : ParsedOnlineSource() {
|
|||
private class Status() : Filter.TriState("Completed")
|
||||
private class Genre(name: String, val id: String = "genres[$name]") : Filter.TriState(name)
|
||||
private class TextField(name: String, val key: String) : Filter.Text(name)
|
||||
private class ListField(name: String, val key: String, values: Array<ListValue>, state: Int = 0) : Filter.List<ListValue>(name, values, state)
|
||||
private class Order() : Filter.CheckBox("Ascending order")
|
||||
private class Type() : Filter.List<String>("Type", arrayOf("Any", "Japanese Manga (read from right to left)", "Korean Manhwa (read from left to right)"))
|
||||
private class OrderBy() : Filter.Sort<String>("Order by",
|
||||
arrayOf("Series name", "Rating", "Views", "Total chapters", "Last chapter"),
|
||||
Filter.Sort.Selection(2, false))
|
||||
|
||||
// [...document.querySelectorAll("select[id^='genres'")].map((el,i) => `Genre("${el.nextSibling.nextSibling.textContent.trim()}", "${el.getAttribute('name')}")`).join(',\n')
|
||||
// http://www.mangahere.co/advsearch.htm
|
||||
override fun getFilterList() = FilterList(
|
||||
TextField("Author", "author"),
|
||||
TextField("Artist", "artist"),
|
||||
ListField("Type", "direction", arrayOf(ListValue("Any", ""), ListValue("Japanese Manga (read from right to left)", "rl"), ListValue("Korean Manhwa (read from left to right)", "lr"))),
|
||||
Type(),
|
||||
Status(),
|
||||
ListField("Order by", "sort", arrayOf(ListValue("Series name", "name"), ListValue("Rating", "rating"), ListValue("Views", "views"), ListValue("Total chapters", "total_chapters"), ListValue("Last chapter", "last_chapter_time")), 2),
|
||||
Order(),
|
||||
Filter.Separator(),
|
||||
OrderBy(),
|
||||
Filter.Separator(),
|
||||
Filter.Header("Genres"),
|
||||
Genre("Action"),
|
||||
Genre("Adventure"),
|
||||
|
|
|
@ -30,7 +30,7 @@ class Mangasee : ParsedOnlineSource() {
|
|||
override fun popularMangaSelector() = "div.requested > div.row"
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request {
|
||||
val (body, requestUrl) = convertQueryToPost(page, "$baseUrl/search/request.php?sortBy=popularity&sortOrder=descending&todo=1")
|
||||
val (body, requestUrl) = convertQueryToPost(page, "$baseUrl/search/request.php?sortBy=popularity&sortOrder=descending")
|
||||
return POST(requestUrl, headers, body.build())
|
||||
}
|
||||
|
||||
|
@ -54,14 +54,17 @@ class Mangasee : ParsedOnlineSource() {
|
|||
var genresNo: String? = null
|
||||
for (filter in if (filters.isEmpty()) getFilterList() else filters) {
|
||||
when (filter) {
|
||||
is Sort -> filter.values[filter.state].keys.forEachIndexed { i, s ->
|
||||
url.addQueryParameter(s, filter.values[filter.state].values[i])
|
||||
is Sort -> {
|
||||
if (filter.state?.index != 0)
|
||||
url.addQueryParameter("sortBy", if (filter.state?.index == 1) "dateUpdated" else "popularity")
|
||||
if (filter.state?.ascending != true)
|
||||
url.addQueryParameter("sortOrder", "descending")
|
||||
}
|
||||
is ListField -> if (filter.state != 0) url.addQueryParameter(filter.key, filter.values[filter.state])
|
||||
is TextField -> if (!filter.state.isEmpty()) url.addQueryParameter(filter.key, filter.state)
|
||||
is Genre -> when (filter.state) {
|
||||
Filter.TriState.STATE_INCLUDE -> genres = if (genres == null) filter.id else genres + "," + filter.id
|
||||
Filter.TriState.STATE_EXCLUDE -> genresNo = if (genresNo == null) filter.id else genresNo + "," + filter.id
|
||||
Filter.TriState.STATE_INCLUDE -> genres = if (genres == null) filter.name else genres + "," + filter.name
|
||||
Filter.TriState.STATE_EXCLUDE -> genresNo = if (genresNo == null) filter.name else genresNo + "," + filter.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -156,8 +159,8 @@ class Mangasee : ParsedOnlineSource() {
|
|||
override fun toString(): String = name
|
||||
}
|
||||
|
||||
private class Sort(name: String, values: Array<SortOption>, state: Int = 0) : Filter.List<SortOption>(name, values, state)
|
||||
private class Genre(name: String, val id: String = name.replace(' ', '_')) : Filter.TriState(name)
|
||||
private class Sort() : Filter.Sort<String>("Sort", arrayOf("Alphabetically", "Date updated", "Popularity"), Filter.Sort.Selection(2, false))
|
||||
private class Genre(name: String) : Filter.TriState(name)
|
||||
private class TextField(name: String, val key: String) : Filter.Text(name)
|
||||
private class ListField(name: String, val key: String, values: Array<String>, state: Int = 0) : Filter.List<String>(name, values, state)
|
||||
|
||||
|
@ -166,16 +169,12 @@ class Mangasee : ParsedOnlineSource() {
|
|||
override fun getFilterList() = FilterList(
|
||||
TextField("Years", "year"),
|
||||
TextField("Author", "author"),
|
||||
Sort("Sort By", arrayOf(SortOption("Alphabetical A-Z", emptyArray(), emptyArray()),
|
||||
SortOption("Alphabetical Z-A", arrayOf("sortOrder"), arrayOf("descending")),
|
||||
SortOption("Newest", arrayOf("sortBy", "sortOrder"), arrayOf("dateUpdated", "descending")),
|
||||
SortOption("Oldest", arrayOf("sortBy"), arrayOf("dateUpdated")),
|
||||
SortOption("Most Popular", arrayOf("sortBy", "sortOrder"), arrayOf("popularity", "descending")),
|
||||
SortOption("Least Popular", arrayOf("sortBy"), arrayOf("popularity"))
|
||||
), 4),
|
||||
ListField("Scan Status", "status", arrayOf("Any", "Complete", "Discontinued", "Hiatus", "Incomplete", "Ongoing")),
|
||||
ListField("Publish Status", "pstatus", arrayOf("Any", "Cancelled", "Complete", "Discontinued", "Hiatus", "Incomplete", "Ongoing", "Unfinished")),
|
||||
ListField("Type", "type", arrayOf("Any", "Doujinshi", "Manga", "Manhua", "Manhwa", "OEL", "One-shot")),
|
||||
Filter.Separator(),
|
||||
Sort(),
|
||||
Filter.Separator(),
|
||||
Filter.Header("Genres"),
|
||||
Genre("Action"),
|
||||
Genre("Adult"),
|
||||
|
|
|
@ -2,12 +2,12 @@ package eu.kanade.tachiyomi.ui.catalogue
|
|||
|
||||
import android.content.Context
|
||||
import android.support.graphics.drawable.VectorDrawableCompat
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.TextView
|
||||
import android.widget.*
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.source.model.Filter
|
||||
import eu.kanade.tachiyomi.data.source.model.FilterList
|
||||
|
@ -55,16 +55,19 @@ class CatalogueNavigationView @JvmOverloads constructor(context: Context, attrs:
|
|||
override fun getItemViewType(position: Int): Int {
|
||||
return when (items[position]) {
|
||||
is Filter.Header -> VIEW_TYPE_HEADER
|
||||
is Filter.Separator -> VIEW_TYPE_SEPARATOR
|
||||
is Filter.CheckBox -> VIEW_TYPE_CHECKBOX
|
||||
is Filter.TriState -> VIEW_TYPE_MULTISTATE
|
||||
is Filter.List<*> -> VIEW_TYPE_LIST
|
||||
is Filter.Text -> VIEW_TYPE_TEXT
|
||||
is Filter.Sort<*> -> VIEW_TYPE_SORT
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
|
||||
return when (viewType) {
|
||||
VIEW_TYPE_HEADER -> HeaderHolder(parent)
|
||||
VIEW_TYPE_SEPARATOR -> SeparatorHolder(parent)
|
||||
VIEW_TYPE_CHECKBOX -> CheckboxHolder(parent, null)
|
||||
VIEW_TYPE_MULTISTATE -> MultiStateHolder(parent, null).apply {
|
||||
// Adjust view with checkbox
|
||||
|
@ -73,6 +76,7 @@ class CatalogueNavigationView @JvmOverloads constructor(context: Context, attrs:
|
|||
}
|
||||
VIEW_TYPE_LIST -> SpinnerHolder(parent)
|
||||
VIEW_TYPE_TEXT -> EditTextHolder(parent)
|
||||
VIEW_TYPE_SORT -> SortHolder(parent)
|
||||
else -> throw Exception("Unknown view type")
|
||||
}
|
||||
}
|
||||
|
@ -144,9 +148,54 @@ class CatalogueNavigationView @JvmOverloads constructor(context: Context, attrs:
|
|||
}
|
||||
})
|
||||
}
|
||||
is Filter.Sort<*> -> {
|
||||
val view = (holder as SortHolder).sortView
|
||||
view.removeAllViews()
|
||||
if (!filter.name.isEmpty()) {
|
||||
val header = HeaderHolder(view)
|
||||
(header.itemView as TextView).text = filter.name
|
||||
view.addView(header.itemView)
|
||||
}
|
||||
val holders = Array<MultiStateHolder>(filter.values.size, { MultiStateHolder(view, null) })
|
||||
for ((i, rb) in holders.withIndex()) {
|
||||
rb.text.text = filter.values[i].toString()
|
||||
|
||||
fun getIcon() = when (filter.state) {
|
||||
Filter.Sort.Selection(i, false) -> VectorDrawableCompat.create(view.resources, R.drawable.ic_keyboard_arrow_down_black_32dp, null)
|
||||
?.apply { setTint(view.context.getResourceColor(R.attr.colorAccent)) }
|
||||
Filter.Sort.Selection(i, true) -> VectorDrawableCompat.create(view.resources, R.drawable.ic_keyboard_arrow_up_black_32dp, null)
|
||||
?.apply { setTint(view.context.getResourceColor(R.attr.colorAccent)) }
|
||||
else -> ContextCompat.getDrawable(context, R.drawable.empty_drawable_32dp)
|
||||
}
|
||||
|
||||
rb.text.setCompoundDrawablesWithIntrinsicBounds(getIcon(), null, null, null)
|
||||
rb.itemView.setOnClickListener {
|
||||
val pre = filter.state?.index ?: i
|
||||
if (pre != i) {
|
||||
holders[pre].text.setCompoundDrawablesWithIntrinsicBounds(getIcon(), null, null, null)
|
||||
filter.state = Filter.Sort.Selection(i, false)
|
||||
} else {
|
||||
filter.state = Filter.Sort.Selection(i, filter.state?.ascending == false)
|
||||
}
|
||||
rb.text.setCompoundDrawablesWithIntrinsicBounds(getIcon(), null, null, null)
|
||||
}
|
||||
|
||||
view.addView(rb.itemView)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val VIEW_TYPE_SORT = 0
|
||||
|
||||
private class SortHolder(parent: ViewGroup, val sortView: SortView = SortView(parent)) : Holder(sortView) {
|
||||
class SortView(parent: ViewGroup) : LinearLayout(parent.context) {
|
||||
init {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue