Add Sort filter [Catalogs] (#633)

* Add Sort filter

* remove old views

* onClick default descending

* update remaining catalogs
This commit is contained in:
paronos 2017-01-12 15:37:38 +01:00 committed by inorichi
parent f717c57648
commit a03dceff7d
6 changed files with 108 additions and 37 deletions

View File

@ -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)
}
}

View File

@ -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),

View File

@ -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"),

View File

@ -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"),

View File

@ -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"),

View File

@ -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
}
}
}
}