浏览代码

Added library sort by mean Tracker score (#10005)

Caleb Morris 1 年之前
父节点
当前提交
5d91b77c93

+ 8 - 1
app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt

@@ -144,6 +144,13 @@ private fun ColumnScope.SortPage(
     val sortingMode = category.sort.type
     val sortDescending = !category.sort.isAscending
 
+    val trackerSortOption =
+        if (screenModel.trackers.isEmpty()) {
+            emptyList()
+        } else {
+            listOf(R.string.action_sort_tracker_score to LibrarySort.Type.TrackerMean)
+        }
+
     listOf(
         R.string.action_sort_alpha to LibrarySort.Type.Alphabetical,
         R.string.action_sort_total to LibrarySort.Type.TotalChapters,
@@ -153,7 +160,7 @@ private fun ColumnScope.SortPage(
         R.string.action_sort_latest_chapter to LibrarySort.Type.LatestChapter,
         R.string.action_sort_chapter_fetch_date to LibrarySort.Type.ChapterFetchDate,
         R.string.action_sort_date_added to LibrarySort.Type.DateAdded,
-    ).map { (titleRes, mode) ->
+    ).plus(trackerSortOption).map { (titleRes, mode) ->
         SortItem(
             label = stringResource(titleRes),
             sortDescending = sortDescending.takeIf { sortingMode == mode },

+ 2 - 0
app/src/main/java/eu/kanade/tachiyomi/data/track/TrackerManager.kt

@@ -30,6 +30,8 @@ class TrackerManager {
 
     val trackers = listOf(myAnimeList, aniList, kitsu, shikimori, bangumi, komga, mangaUpdates, kavita, suwayomi)
 
+    fun loggedInTrackers() = trackers.filter { it.isLoggedIn }
+
     fun get(id: Long) = trackers.find { it.id == id }
 
     fun hasLoggedIn() = trackers.any { it.isLoggedIn }

+ 30 - 5
app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt

@@ -61,6 +61,7 @@ import tachiyomi.domain.manga.model.MangaUpdate
 import tachiyomi.domain.manga.model.applyFilter
 import tachiyomi.domain.source.service.SourceManager
 import tachiyomi.domain.track.interactor.GetTracksPerManga
+import tachiyomi.domain.track.model.Track
 import tachiyomi.source.local.isLocal
 import uy.kohesive.injekt.Injekt
 import uy.kohesive.injekt.api.get
@@ -104,7 +105,7 @@ class LibraryScreenModel(
             ) { searchQuery, library, tracks, loggedInTrackers, _ ->
                 library
                     .applyFilters(tracks, loggedInTrackers)
-                    .applySort()
+                    .applySort(tracks)
                     .mapValues { (_, value) ->
                         if (searchQuery != null) {
                             // Filter query
@@ -168,7 +169,7 @@ class LibraryScreenModel(
      * Applies library filters to the given map of manga.
      */
     private suspend fun LibraryMap.applyFilters(
-        trackMap: Map<Long, List<Long>>,
+        trackMap: Map<Long, List<Track>>,
         loggedInTrackers: Map<Long, TriState>,
     ): LibraryMap {
         val prefs = getLibraryItemPreferencesFlow().first()
@@ -213,7 +214,9 @@ class LibraryScreenModel(
         val filterFnTracking: (LibraryItem) -> Boolean = tracking@{ item ->
             if (isNotLoggedInAnyTrack || trackFiltersIsIgnored) return@tracking true
 
-            val mangaTracks = trackMap[item.libraryManga.id].orEmpty()
+            val mangaTracks = trackMap
+                .mapValues { entry -> entry.value.map { it.syncId } }[item.libraryManga.id]
+                .orEmpty()
 
             val isExcluded = excludedTracks.isNotEmpty() && mangaTracks.fastAny { it in excludedTracks }
             val isIncluded = includedTracks.isEmpty() || mangaTracks.fastAny { it in includedTracks }
@@ -236,7 +239,10 @@ class LibraryScreenModel(
     /**
      * Applies library sorting to the given map of manga.
      */
-    private fun LibraryMap.applySort(): LibraryMap {
+    private fun LibraryMap.applySort(
+        // Map<MangaId, List<Track>>
+        trackMap: Map<Long, List<Track>>,
+    ): LibraryMap {
         val locale = Locale.getDefault()
         val collator = Collator.getInstance(locale).apply {
             strength = Collator.PRIMARY
@@ -245,6 +251,20 @@ class LibraryScreenModel(
             collator.compare(i1.libraryManga.manga.title.lowercase(locale), i2.libraryManga.manga.title.lowercase(locale))
         }
 
+        val defaultTrackerScoreSortValue = -1.0
+        val trackerScores by lazy {
+            val trackerMap = trackerManager.loggedInTrackers().associateBy { e -> e.id }
+            trackMap.mapValues { entry ->
+                when {
+                    entry.value.isEmpty() -> null
+                    else ->
+                        entry.value
+                            .mapNotNull { trackerMap[it.syncId]?.get10PointScore(it) }
+                            .average()
+                }
+            }
+        }
+
         val sortFn: (LibraryItem, LibraryItem) -> Int = { i1, i2 ->
             val sort = keys.find { it.id == i1.libraryManga.category }!!.sort
             when (sort.type) {
@@ -276,6 +296,11 @@ class LibraryScreenModel(
                 LibrarySort.Type.DateAdded -> {
                     i1.libraryManga.manga.dateAdded.compareTo(i2.libraryManga.manga.dateAdded)
                 }
+                LibrarySort.Type.TrackerMean -> {
+                    val item1Score = trackerScores[i1.libraryManga.id] ?: defaultTrackerScoreSortValue
+                    val item2Score = trackerScores[i2.libraryManga.id] ?: defaultTrackerScoreSortValue
+                    item1Score.compareTo(item2Score)
+                }
             }
         }
 
@@ -366,7 +391,7 @@ class LibraryScreenModel(
      * @return map of track id with the filter value
      */
     private fun getTrackingFilterFlow(): Flow<Map<Long, TriState>> {
-        val loggedInTrackers = trackerManager.trackers.filter { it.isLoggedIn }
+        val loggedInTrackers = trackerManager.loggedInTrackers()
         return if (loggedInTrackers.isNotEmpty()) {
             val prefFlows = loggedInTrackers
                 .map { libraryPreferences.filterTracking(it.id.toInt()).changes() }

+ 4 - 0
domain/src/main/java/tachiyomi/domain/library/model/LibrarySortMode.kt

@@ -30,6 +30,7 @@ data class LibrarySort(
         data object LatestChapter : Type(0b00010100)
         data object ChapterFetchDate : Type(0b00011000)
         data object DateAdded : Type(0b00011100)
+        data object TrackerMean : Type(0b000100000)
 
         companion object {
             fun valueOf(flag: Long): Type {
@@ -75,6 +76,7 @@ data class LibrarySort(
                 Type.LatestChapter,
                 Type.ChapterFetchDate,
                 Type.DateAdded,
+                Type.TrackerMean,
             )
         }
         val directions by lazy { setOf(Direction.Ascending, Direction.Descending) }
@@ -101,6 +103,7 @@ data class LibrarySort(
                     "LATEST_CHAPTER" -> Type.LatestChapter
                     "CHAPTER_FETCH_DATE" -> Type.ChapterFetchDate
                     "DATE_ADDED" -> Type.DateAdded
+                    "TRACKER_MEAN" -> Type.TrackerMean
                     else -> Type.Alphabetical
                 }
                 val ascending = if (values[1] == "ASCENDING") Direction.Ascending else Direction.Descending
@@ -121,6 +124,7 @@ data class LibrarySort(
             Type.LatestChapter -> "LATEST_CHAPTER"
             Type.ChapterFetchDate -> "CHAPTER_FETCH_DATE"
             Type.DateAdded -> "DATE_ADDED"
+            Type.TrackerMean -> "TRACKER_MEAN"
         }
         val direction = if (direction == Direction.Ascending) "ASCENDING" else "DESCENDING"
         return "$type,$direction"

+ 3 - 8
domain/src/main/java/tachiyomi/domain/track/interactor/GetTracksPerManga.kt

@@ -2,19 +2,14 @@ package tachiyomi.domain.track.interactor
 
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.map
+import tachiyomi.domain.track.model.Track
 import tachiyomi.domain.track.repository.TrackRepository
 
 class GetTracksPerManga(
     private val trackRepository: TrackRepository,
 ) {
 
-    fun subscribe(): Flow<Map<Long, List<Long>>> {
-        return trackRepository.getTracksAsFlow().map { tracks ->
-            tracks
-                .groupBy { it.mangaId }
-                .mapValues { entry ->
-                    entry.value.map { it.syncId }
-                }
-        }
+    fun subscribe(): Flow<Map<Long, List<Track>>> {
+        return trackRepository.getTracksAsFlow().map { tracks -> tracks.groupBy { it.mangaId } }
     }
 }

+ 1 - 1
domain/src/test/java/tachiyomi/domain/library/model/LibraryFlagsTest.kt

@@ -12,7 +12,7 @@ class LibraryFlagsTest {
     @Test
     fun `Check the amount of flags`() {
         LibraryDisplayMode.values.size shouldBe 4
-        LibrarySort.types.size shouldBe 8
+        LibrarySort.types.size shouldBe 9
         LibrarySort.directions.size shouldBe 2
     }
 

+ 1 - 0
i18n/src/main/res/values/strings.xml

@@ -66,6 +66,7 @@
     <string name="action_sort_latest_chapter">Latest chapter</string>
     <string name="action_sort_chapter_fetch_date">Chapter fetch date</string>
     <string name="action_sort_date_added">Date added</string>
+    <string name="action_sort_tracker_score">Tracker score</string>
     <string name="action_search">Search</string>
     <string name="action_search_hint">Search…</string>
     <string name="action_search_settings">Search settings</string>