Эх сурвалжийг харах

Add Sort filter [Catalogs] (#633)

* Add Sort filter

* remove old views

* onClick default descending

* update remaining catalogs
paronos 8 жил өмнө
parent
commit
a03dceff7d

+ 7 - 0
app/src/main/java/eu/kanade/tachiyomi/data/source/model/Filter.kt

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

+ 10 - 2
app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Batoto.kt

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

+ 14 - 12
app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangafox.kt

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

+ 13 - 7
app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangahere.kt

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

+ 13 - 14
app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangasee.kt

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

+ 51 - 2
app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueNavigationView.kt

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