mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-30 22:07:57 +01:00 
			
		
		
		
	Add "Started" library filter and library update restriction (#6382)
* Add chapter read count to library manga Co-Authored-By: Jays2Kings <jays@outlook.com> * Add "Started" library filter and library update restriction * Update Filter when its changed * Add back accidentally removed stuff. * Update.. * Change variable names * Change Variable name where I missed Co-authored-by: Jays2Kings <jays@outlook.com>
This commit is contained in:
		| @@ -2,7 +2,14 @@ package eu.kanade.tachiyomi.data.database.models | ||||
|  | ||||
| class LibraryManga : MangaImpl() { | ||||
|  | ||||
|     var unread: Int = 0 | ||||
|     var unreadCount: Int = 0 | ||||
|     var readCount: Int = 0 | ||||
|  | ||||
|     val totalChapters | ||||
|         get() = readCount + unreadCount | ||||
|  | ||||
|     val hasStarted | ||||
|         get() = readCount > 0 | ||||
|  | ||||
|     var category: Int = 0 | ||||
| } | ||||
|   | ||||
| @@ -8,21 +8,28 @@ import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable as MangaCateg | ||||
| import eu.kanade.tachiyomi.data.database.tables.MangaTable as Manga | ||||
|  | ||||
| /** | ||||
|  * Query to get the manga from the library, with their categories and unread count. | ||||
|  * Query to get the manga from the library, with their categories, read and unread count. | ||||
|  */ | ||||
| val libraryQuery = | ||||
|     """ | ||||
|     SELECT M.*, COALESCE(MC.${MangaCategory.COL_CATEGORY_ID}, 0) AS ${Manga.COL_CATEGORY} | ||||
|     FROM ( | ||||
|         SELECT ${Manga.TABLE}.*, COALESCE(C.unread, 0) AS ${Manga.COL_UNREAD} | ||||
|         SELECT ${Manga.TABLE}.*, COALESCE(C.unreadCount, 0) AS ${Manga.COMPUTED_COL_UNREAD_COUNT}, COALESCE(R.readCount, 0) AS ${Manga.COMPUTED_COL_READ_COUNT} | ||||
|         FROM ${Manga.TABLE} | ||||
|         LEFT JOIN ( | ||||
|             SELECT ${Chapter.COL_MANGA_ID}, COUNT(*) AS unread | ||||
|             SELECT ${Chapter.COL_MANGA_ID}, COUNT(*) AS unreadCount | ||||
|             FROM ${Chapter.TABLE} | ||||
|             WHERE ${Chapter.COL_READ} = 0 | ||||
|             GROUP BY ${Chapter.COL_MANGA_ID} | ||||
|         ) AS C | ||||
|         ON ${Manga.COL_ID} = C.${Chapter.COL_MANGA_ID} | ||||
|         LEFT JOIN ( | ||||
|             SELECT ${Chapter.COL_MANGA_ID}, COUNT(*) AS readCount | ||||
|             FROM ${Chapter.TABLE} | ||||
|             WHERE ${Chapter.COL_READ} = 1 | ||||
|             GROUP BY ${Chapter.COL_MANGA_ID} | ||||
|         ) AS R | ||||
|         ON ${Manga.COL_ID} = R.${Chapter.COL_MANGA_ID} | ||||
|         WHERE ${Manga.COL_FAVORITE} = 1 | ||||
|         GROUP BY ${Manga.COL_ID} | ||||
|         ORDER BY ${Manga.COL_TITLE} | ||||
|   | ||||
| @@ -16,8 +16,9 @@ class LibraryMangaGetResolver : DefaultGetResolver<LibraryManga>(), BaseMangaGet | ||||
|         val manga = LibraryManga() | ||||
|  | ||||
|         mapBaseFromCursor(manga, cursor) | ||||
|         manga.unread = cursor.getInt(cursor.getColumnIndexOrThrow(MangaTable.COL_UNREAD)) | ||||
|         manga.unreadCount = cursor.getInt(cursor.getColumnIndexOrThrow(MangaTable.COMPUTED_COL_UNREAD_COUNT)) | ||||
|         manga.category = cursor.getInt(cursor.getColumnIndexOrThrow(MangaTable.COL_CATEGORY)) | ||||
|         manga.readCount = cursor.getInt(cursor.getColumnIndexOrThrow(MangaTable.COMPUTED_COL_READ_COUNT)) | ||||
|  | ||||
|         return manga | ||||
|     } | ||||
|   | ||||
| @@ -39,12 +39,15 @@ object MangaTable { | ||||
|  | ||||
|     const val COL_CHAPTER_FLAGS = "chapter_flags" | ||||
|  | ||||
|     const val COL_UNREAD = "unread" | ||||
|  | ||||
|     const val COL_CATEGORY = "category" | ||||
|  | ||||
|     const val COL_COVER_LAST_MODIFIED = "cover_last_modified" | ||||
|  | ||||
|     // Not an actual value but computed when created | ||||
|     const val COMPUTED_COL_UNREAD_COUNT = "unread_count" | ||||
|  | ||||
|     const val COMPUTED_COL_READ_COUNT = "read_count" | ||||
|  | ||||
|     val createTableQuery: String | ||||
|         get() = | ||||
|             """CREATE TABLE $TABLE( | ||||
|   | ||||
| @@ -21,6 +21,7 @@ import eu.kanade.tachiyomi.data.library.LibraryUpdateService.Companion.start | ||||
| import eu.kanade.tachiyomi.data.notification.Notifications | ||||
| import eu.kanade.tachiyomi.data.preference.MANGA_FULLY_READ | ||||
| import eu.kanade.tachiyomi.data.preference.MANGA_ONGOING | ||||
| import eu.kanade.tachiyomi.data.preference.MANGA_STARTED | ||||
| import eu.kanade.tachiyomi.data.preference.PreferencesHelper | ||||
| import eu.kanade.tachiyomi.data.track.EnhancedTrackService | ||||
| import eu.kanade.tachiyomi.data.track.TrackManager | ||||
| @@ -266,7 +267,14 @@ class LibraryUpdateService( | ||||
|                 listToUpdate = listToUpdate.filterNot { it.status == SManga.COMPLETED } | ||||
|             } | ||||
|             if (MANGA_FULLY_READ in restrictions) { | ||||
|                 listToUpdate = listToUpdate.filter { it.unread == 0 } | ||||
|                 listToUpdate = listToUpdate.filter { it.unreadCount == 0 } | ||||
|             } | ||||
|             if (MANGA_STARTED in restrictions) { | ||||
|                 listToUpdate = listToUpdate.filter { manga -> | ||||
|                     // If the manga has 0 chapters you can't actually start reading it | ||||
|                     if (manga.totalChapters == 0) true | ||||
|                     else manga.hasStarted | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -31,6 +31,8 @@ object PreferenceKeys { | ||||
|  | ||||
|     const val filterUnread = "pref_filter_library_unread" | ||||
|  | ||||
|     const val filterStarted = "pref_filter_library_started" | ||||
|  | ||||
|     const val filterCompleted = "pref_filter_library_completed" | ||||
|  | ||||
|     const val filterTracked = "pref_filter_library_tracked" | ||||
|   | ||||
| @@ -7,6 +7,7 @@ const val DEVICE_CHARGING = "ac" | ||||
|  | ||||
| const val MANGA_ONGOING = "manga_ongoing" | ||||
| const val MANGA_FULLY_READ = "manga_fully_read" | ||||
| const val MANGA_STARTED = "manga_started" | ||||
|  | ||||
| /** | ||||
|  * This class stores the values for the preferences in the application. | ||||
|   | ||||
| @@ -224,7 +224,7 @@ class PreferencesHelper(val context: Context) { | ||||
|     fun libraryUpdateInterval() = flowPrefs.getInt("pref_library_update_interval_key", 24) | ||||
|  | ||||
|     fun libraryUpdateDeviceRestriction() = flowPrefs.getStringSet("library_update_restriction", setOf(DEVICE_ONLY_ON_WIFI)) | ||||
|     fun libraryUpdateMangaRestriction() = flowPrefs.getStringSet("library_update_manga_restriction", setOf(MANGA_FULLY_READ, MANGA_ONGOING)) | ||||
|     fun libraryUpdateMangaRestriction() = flowPrefs.getStringSet("library_update_manga_restriction", setOf(MANGA_FULLY_READ, MANGA_ONGOING, MANGA_STARTED)) | ||||
|  | ||||
|     fun showUpdatesNavBadge() = flowPrefs.getBoolean("library_update_show_tab_badge", false) | ||||
|     fun unreadUpdatesCount() = flowPrefs.getInt("library_unread_updates_count", 0) | ||||
| @@ -252,6 +252,8 @@ class PreferencesHelper(val context: Context) { | ||||
|  | ||||
|     fun filterUnread() = flowPrefs.getInt(Keys.filterUnread, ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value) | ||||
|  | ||||
|     fun filterStarted() = flowPrefs.getInt(Keys.filterStarted, ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value) | ||||
|  | ||||
|     fun filterCompleted() = flowPrefs.getInt(Keys.filterCompleted, ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value) | ||||
|  | ||||
|     fun filterTracking(name: Int) = flowPrefs.getInt("${Keys.filterTracked}_$name", ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value) | ||||
|   | ||||
| @@ -120,6 +120,7 @@ class LibraryPresenter( | ||||
|         val downloadedOnly = preferences.downloadedOnly().get() | ||||
|         val filterDownloaded = preferences.filterDownloaded().get() | ||||
|         val filterUnread = preferences.filterUnread().get() | ||||
|         val filterStarted = preferences.filterStarted().get() | ||||
|         val filterCompleted = preferences.filterCompleted().get() | ||||
|         val loggedInServices = trackManager.services.filter { trackService -> trackService.isLogged } | ||||
|             .associate { trackService -> | ||||
| @@ -127,22 +128,6 @@ class LibraryPresenter( | ||||
|             } | ||||
|         val isNotAnyLoggedIn = !loggedInServices.values.any() | ||||
|  | ||||
|         val filterFnUnread: (LibraryItem) -> Boolean = unread@{ item -> | ||||
|             if (filterUnread == State.IGNORE.value) return@unread true | ||||
|             val isUnread = item.manga.unread != 0 | ||||
|  | ||||
|             return@unread if (filterUnread == State.INCLUDE.value) isUnread | ||||
|             else !isUnread | ||||
|         } | ||||
|  | ||||
|         val filterFnCompleted: (LibraryItem) -> Boolean = completed@{ item -> | ||||
|             if (filterCompleted == State.IGNORE.value) return@completed true | ||||
|             val isCompleted = item.manga.status == SManga.COMPLETED | ||||
|  | ||||
|             return@completed if (filterCompleted == State.INCLUDE.value) isCompleted | ||||
|             else !isCompleted | ||||
|         } | ||||
|  | ||||
|         val filterFnDownloaded: (LibraryItem) -> Boolean = downloaded@{ item -> | ||||
|             if (!downloadedOnly && filterDownloaded == State.IGNORE.value) return@downloaded true | ||||
|             val isDownloaded = when { | ||||
| @@ -155,6 +140,30 @@ class LibraryPresenter( | ||||
|             else !isDownloaded | ||||
|         } | ||||
|  | ||||
|         val filterFnUnread: (LibraryItem) -> Boolean = unread@{ item -> | ||||
|             if (filterUnread == State.IGNORE.value) return@unread true | ||||
|             val isUnread = item.manga.unreadCount != 0 | ||||
|  | ||||
|             return@unread if (filterUnread == State.INCLUDE.value) isUnread | ||||
|             else !isUnread | ||||
|         } | ||||
|  | ||||
|         val filterFnStarted: (LibraryItem) -> Boolean = started@{ item -> | ||||
|             if (filterStarted == State.IGNORE.value) return@started true | ||||
|             val hasStarted = item.manga.hasStarted | ||||
|  | ||||
|             return@started if (filterStarted == State.INCLUDE.value) hasStarted | ||||
|             else !hasStarted | ||||
|         } | ||||
|  | ||||
|         val filterFnCompleted: (LibraryItem) -> Boolean = completed@{ item -> | ||||
|             if (filterCompleted == State.IGNORE.value) return@completed true | ||||
|             val isCompleted = item.manga.status == SManga.COMPLETED | ||||
|  | ||||
|             return@completed if (filterCompleted == State.INCLUDE.value) isCompleted | ||||
|             else !isCompleted | ||||
|         } | ||||
|  | ||||
|         val filterFnTracking: (LibraryItem) -> Boolean = tracking@{ item -> | ||||
|             if (isNotAnyLoggedIn) return@tracking true | ||||
|  | ||||
| @@ -181,9 +190,10 @@ class LibraryPresenter( | ||||
|  | ||||
|         val filterFn: (LibraryItem) -> Boolean = filter@{ item -> | ||||
|             return@filter !( | ||||
|                 !filterFnUnread(item) || | ||||
|                 !filterFnDownloaded(item) || | ||||
|                     !filterFnUnread(item) || | ||||
|                     !filterFnStarted(item) || | ||||
|                     !filterFnCompleted(item) || | ||||
|                     !filterFnDownloaded(item) || | ||||
|                     !filterFnTracking(item) | ||||
|                 ) | ||||
|         } | ||||
| @@ -212,7 +222,7 @@ class LibraryPresenter( | ||||
|                 } | ||||
|  | ||||
|                 item.unreadCount = if (showUnreadBadges) { | ||||
|                     item.manga.unread | ||||
|                     item.manga.unreadCount | ||||
|                 } else { | ||||
|                     // Unset unread count if not enabled | ||||
|                     -1 | ||||
| @@ -245,10 +255,6 @@ class LibraryPresenter( | ||||
|             var counter = 0 | ||||
|             db.getLastReadManga().executeAsBlocking().associate { it.id!! to counter++ } | ||||
|         } | ||||
|         val totalChapterManga by lazy { | ||||
|             var counter = 0 | ||||
|             db.getTotalChapterManga().executeAsBlocking().associate { it.id!! to counter++ } | ||||
|         } | ||||
|         val latestChapterManga by lazy { | ||||
|             var counter = 0 | ||||
|             db.getLatestChapterManga().executeAsBlocking().associate { it.id!! to counter++ } | ||||
| @@ -262,7 +268,7 @@ class LibraryPresenter( | ||||
|             (category.id ?: 0) to SortModeSetting.get(preferences, category) | ||||
|         } | ||||
|  | ||||
|         val sortAscending = categories.associate { category -> | ||||
|         val sortDirections = categories.associate { category -> | ||||
|             (category.id ?: 0) to SortDirectionSetting.get(preferences, category) | ||||
|         } | ||||
|  | ||||
| @@ -272,7 +278,7 @@ class LibraryPresenter( | ||||
|         } | ||||
|         val sortFn: (LibraryItem, LibraryItem) -> Int = { i1, i2 -> | ||||
|             val sortingMode = sortingModes[i1.manga.category]!! | ||||
|             val sortAscending = sortAscending[i1.manga.category]!! == SortDirectionSetting.ASCENDING | ||||
|             val sortAscending = sortDirections[i1.manga.category]!! == SortDirectionSetting.ASCENDING | ||||
|             when (sortingMode) { | ||||
|                 SortModeSetting.ALPHABETICAL -> { | ||||
|                     collator.compare(i1.manga.title.lowercase(locale), i2.manga.title.lowercase(locale)) | ||||
| @@ -286,15 +292,13 @@ class LibraryPresenter( | ||||
|                 SortModeSetting.LAST_CHECKED -> i2.manga.last_update.compareTo(i1.manga.last_update) | ||||
|                 SortModeSetting.UNREAD -> when { | ||||
|                     // Ensure unread content comes first | ||||
|                     i1.manga.unread == i2.manga.unread -> 0 | ||||
|                     i1.manga.unread == 0 -> if (sortAscending) 1 else -1 | ||||
|                     i2.manga.unread == 0 -> if (sortAscending) -1 else 1 | ||||
|                     else -> i1.manga.unread.compareTo(i2.manga.unread) | ||||
|                     i1.manga.unreadCount == i2.manga.unreadCount -> 0 | ||||
|                     i1.manga.unreadCount == 0 -> if (sortAscending) 1 else -1 | ||||
|                     i2.manga.unreadCount == 0 -> if (sortAscending) -1 else 1 | ||||
|                     else -> i1.manga.unreadCount.compareTo(i2.manga.unreadCount) | ||||
|                 } | ||||
|                 SortModeSetting.TOTAL_CHAPTERS -> { | ||||
|                     val manga1TotalChapter = totalChapterManga[i1.manga.id!!] ?: 0 | ||||
|                     val mange2TotalChapter = totalChapterManga[i2.manga.id!!] ?: 0 | ||||
|                     manga1TotalChapter.compareTo(mange2TotalChapter) | ||||
|                     i1.manga.totalChapters.compareTo(i2.manga.totalChapters) | ||||
|                 } | ||||
|                 SortModeSetting.LATEST_CHAPTER -> { | ||||
|                     val manga1latestChapter = latestChapterManga[i1.manga.id!!] | ||||
| @@ -315,7 +319,7 @@ class LibraryPresenter( | ||||
|         } | ||||
|  | ||||
|         return map.mapValues { entry -> | ||||
|             val sortAscending = sortAscending[entry.key]!! == SortDirectionSetting.ASCENDING | ||||
|             val sortAscending = sortDirections[entry.key]!! == SortDirectionSetting.ASCENDING | ||||
|  | ||||
|             val comparator = if (sortAscending) { | ||||
|                 Comparator(sortFn) | ||||
|   | ||||
| @@ -89,6 +89,7 @@ class LibrarySettingsSheet( | ||||
|  | ||||
|             private val downloaded = Item.TriStateGroup(R.string.action_filter_downloaded, this) | ||||
|             private val unread = Item.TriStateGroup(R.string.action_filter_unread, this) | ||||
|             private val started = Item.TriStateGroup(R.string.action_filter_started, this) | ||||
|             private val completed = Item.TriStateGroup(R.string.completed, this) | ||||
|             private val trackFilters: Map<Int, Item.TriStateGroup> | ||||
|  | ||||
| @@ -103,7 +104,7 @@ class LibrarySettingsSheet( | ||||
|                         trackFilters = services.associate { service -> | ||||
|                             Pair(service.id, Item.TriStateGroup(getServiceResId(service, size), this)) | ||||
|                         } | ||||
|                         val list: MutableList<Item> = mutableListOf(downloaded, unread, completed) | ||||
|                         val list: MutableList<Item> = mutableListOf(downloaded, unread, started, completed) | ||||
|                         if (size > 1) list.add(Item.Header(R.string.action_filter_tracked)) | ||||
|                         list.addAll(trackFilters.values) | ||||
|                         items = list | ||||
| @@ -122,6 +123,7 @@ class LibrarySettingsSheet( | ||||
|                     downloaded.state = preferences.filterDownloaded().get() | ||||
|                 } | ||||
|                 unread.state = preferences.filterUnread().get() | ||||
|                 started.state = preferences.filterStarted().get() | ||||
|                 completed.state = preferences.filterCompleted().get() | ||||
|  | ||||
|                 trackFilters.forEach { trackFilter -> | ||||
| @@ -141,6 +143,7 @@ class LibrarySettingsSheet( | ||||
|                 when (item) { | ||||
|                     downloaded -> preferences.filterDownloaded().set(newState) | ||||
|                     unread -> preferences.filterUnread().set(newState) | ||||
|                     started -> preferences.filterStarted().set(newState) | ||||
|                     completed -> preferences.filterCompleted().set(newState) | ||||
|                     else -> { | ||||
|                         trackFilters.forEach { trackFilter -> | ||||
|   | ||||
| @@ -15,6 +15,7 @@ import eu.kanade.tachiyomi.data.preference.DEVICE_CHARGING | ||||
| import eu.kanade.tachiyomi.data.preference.DEVICE_ONLY_ON_WIFI | ||||
| import eu.kanade.tachiyomi.data.preference.MANGA_FULLY_READ | ||||
| import eu.kanade.tachiyomi.data.preference.MANGA_ONGOING | ||||
| import eu.kanade.tachiyomi.data.preference.MANGA_STARTED | ||||
| import eu.kanade.tachiyomi.data.preference.PreferencesHelper | ||||
| import eu.kanade.tachiyomi.data.track.TrackManager | ||||
| import eu.kanade.tachiyomi.databinding.PrefLibraryColumnsBinding | ||||
| @@ -195,16 +196,16 @@ class SettingsLibraryController : SettingsController() { | ||||
|             multiSelectListPreference { | ||||
|                 bindTo(preferences.libraryUpdateMangaRestriction()) | ||||
|                 titleRes = R.string.pref_library_update_manga_restriction | ||||
|                 entriesRes = arrayOf(R.string.pref_update_only_completely_read, R.string.pref_update_only_non_completed) | ||||
|                 entryValues = arrayOf(MANGA_FULLY_READ, MANGA_ONGOING) | ||||
|                 entriesRes = arrayOf(R.string.pref_update_only_completely_read, R.string.pref_update_only_non_completed, R.string.pref_update_only_started) | ||||
|                 entryValues = arrayOf(MANGA_FULLY_READ, MANGA_ONGOING, MANGA_STARTED) | ||||
|  | ||||
|                 fun updateSummary() { | ||||
|                     val restrictions = preferences.libraryUpdateMangaRestriction().get() | ||||
|                         .sorted() | ||||
|                     val restrictions = preferences.libraryUpdateMangaRestriction().get().sorted() | ||||
|                         .map { | ||||
|                             when (it) { | ||||
|                                 MANGA_ONGOING -> context.getString(R.string.pref_update_only_non_completed) | ||||
|                                 MANGA_FULLY_READ -> context.getString(R.string.pref_update_only_completely_read) | ||||
|                                 MANGA_STARTED -> context.getString(R.string.pref_update_only_started) | ||||
|                                 else -> it | ||||
|                             } | ||||
|                         } | ||||
|   | ||||
| @@ -38,6 +38,7 @@ | ||||
|     <string name="action_filter_bookmarked">Bookmarked</string> | ||||
|     <string name="action_filter_tracked">Tracked</string> | ||||
|     <string name="action_filter_unread">Unread</string> | ||||
|     <string name="action_filter_started">Started</string> | ||||
|     <string name="action_filter_empty">Remove filter</string> | ||||
|     <string name="action_sort_alpha">Alphabetically</string> | ||||
|     <string name="action_sort_count">Total manga</string> | ||||
| @@ -222,10 +223,12 @@ | ||||
|     <string name="connected_to_wifi">Only on Wi-Fi</string> | ||||
|     <string name="charging">Charging</string> | ||||
|     <string name="restrictions">Restrictions: %s</string> | ||||
|  | ||||
|     <string name="pref_library_update_manga_restriction">Library update restrictions</string> | ||||
|     <string name="only_update_restrictions">Only update: %s</string> | ||||
|     <string name="pref_update_only_completely_read">Completely read series</string> | ||||
|     <string name="pref_update_only_non_completed">Ongoing series</string> | ||||
|     <string name="pref_update_only_started">Started series</string> | ||||
|     <string name="pref_library_update_show_tab_badge">Show unread count on Updates icon</string> | ||||
|     <string name="pref_library_update_refresh_metadata">Automatically refresh metadata</string> | ||||
|     <string name="pref_library_update_refresh_metadata_summary">Check for new cover and details when updating library</string> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user