diff --git a/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt b/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt index 6d7882f43f..c977409406 100644 --- a/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt +++ b/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 }, diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackerManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackerManager.kt index 3943537e24..a62ad44011 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackerManager.kt +++ b/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 } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt index af0f5c81af..0120abcfc5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt +++ b/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>, + trackMap: Map>, loggedInTrackers: Map, ): 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> + trackMap: Map>, + ): 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> { - 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() } diff --git a/domain/src/main/java/tachiyomi/domain/library/model/LibrarySortMode.kt b/domain/src/main/java/tachiyomi/domain/library/model/LibrarySortMode.kt index 7f525eb578..6a89d4e526 100644 --- a/domain/src/main/java/tachiyomi/domain/library/model/LibrarySortMode.kt +++ b/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" diff --git a/domain/src/main/java/tachiyomi/domain/track/interactor/GetTracksPerManga.kt b/domain/src/main/java/tachiyomi/domain/track/interactor/GetTracksPerManga.kt index 9b8290d708..36478fdfea 100644 --- a/domain/src/main/java/tachiyomi/domain/track/interactor/GetTracksPerManga.kt +++ b/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>> { - return trackRepository.getTracksAsFlow().map { tracks -> - tracks - .groupBy { it.mangaId } - .mapValues { entry -> - entry.value.map { it.syncId } - } - } + fun subscribe(): Flow>> { + return trackRepository.getTracksAsFlow().map { tracks -> tracks.groupBy { it.mangaId } } } } diff --git a/domain/src/test/java/tachiyomi/domain/library/model/LibraryFlagsTest.kt b/domain/src/test/java/tachiyomi/domain/library/model/LibraryFlagsTest.kt index 408dee18a9..a3a2237822 100644 --- a/domain/src/test/java/tachiyomi/domain/library/model/LibraryFlagsTest.kt +++ b/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 } diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml index 11df5d7014..ef8354abc0 100644 --- a/i18n/src/main/res/values/strings.xml +++ b/i18n/src/main/res/values/strings.xml @@ -66,6 +66,7 @@ Latest chapter Chapter fetch date Date added + Tracker score Search Search… Search settings