Move LibraryManga to domain layer (#8126)

This commit is contained in:
AntsyLich 2022-10-01 21:30:51 +06:00 committed by GitHub
parent b04d1e5f50
commit ea8383978b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 217 additions and 232 deletions

View file

@ -1,7 +1,7 @@
package eu.kanade.data.manga package eu.kanade.data.manga
import eu.kanade.domain.library.model.LibraryManga
import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.Manga
import eu.kanade.tachiyomi.data.database.models.LibraryManga
import eu.kanade.tachiyomi.source.model.UpdateStrategy import eu.kanade.tachiyomi.source.model.UpdateStrategy
val mangaMapper: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy) -> Manga = val mangaMapper: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy) -> Manga =
@ -29,28 +29,31 @@ val mangaMapper: (Long, Long, String, String?, String?, String?, List<String>?,
} }
val libraryManga: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long, Long) -> LibraryManga = val libraryManga: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long, Long) -> LibraryManga =
{ _id, source, url, artist, author, description, genre, title, status, thumbnail_url, favorite, last_update, next_update, initialized, viewer, chapter_flags, cover_last_modified, date_added, update_strategy, unread_count, read_count, category -> { _id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, updateStrategy, unreadCount, readCount, category ->
LibraryManga().apply { LibraryManga(
this.id = _id manga = mangaMapper(
this.source = source _id,
this.url = url source,
this.artist = artist url,
this.author = author artist,
this.description = description author,
this.genre = genre?.joinToString() description,
this.title = title genre,
this.status = status.toInt() title,
this.thumbnail_url = thumbnail_url status,
this.favorite = favorite thumbnailUrl,
this.last_update = last_update ?: 0 favorite,
this.update_strategy = update_strategy lastUpdate,
this.initialized = initialized nextUpdate,
this.viewer_flags = viewer.toInt() initialized,
this.chapter_flags = chapter_flags.toInt() viewerFlags,
this.cover_last_modified = cover_last_modified chapterFlags,
this.date_added = date_added coverLastModified,
this.unreadCount = unread_count.toInt() dateAdded,
this.readCount = read_count.toInt() updateStrategy,
this.category = category.toInt() ),
} category = category,
unreadCount = unreadCount,
readCount = readCount,
)
} }

View file

@ -3,10 +3,10 @@ package eu.kanade.data.manga
import eu.kanade.data.DatabaseHandler import eu.kanade.data.DatabaseHandler
import eu.kanade.data.listOfStringsAdapter import eu.kanade.data.listOfStringsAdapter
import eu.kanade.data.updateStrategyAdapter import eu.kanade.data.updateStrategyAdapter
import eu.kanade.domain.library.model.LibraryManga
import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.model.MangaUpdate import eu.kanade.domain.manga.model.MangaUpdate
import eu.kanade.domain.manga.repository.MangaRepository import eu.kanade.domain.manga.repository.MangaRepository
import eu.kanade.tachiyomi.data.database.models.LibraryManga
import eu.kanade.tachiyomi.util.system.logcat import eu.kanade.tachiyomi.util.system.logcat
import eu.kanade.tachiyomi.util.system.toLong import eu.kanade.tachiyomi.util.system.toLong
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow

View file

@ -0,0 +1,16 @@
package eu.kanade.domain.library.model
import eu.kanade.domain.manga.model.Manga
data class LibraryManga(
val manga: Manga,
val category: Long,
val unreadCount: Long,
val readCount: Long,
) {
val totalChapters
get() = readCount + unreadCount
val hasStarted
get() = readCount > 0
}

View file

@ -1,7 +1,7 @@
package eu.kanade.domain.manga.interactor package eu.kanade.domain.manga.interactor
import eu.kanade.domain.library.model.LibraryManga
import eu.kanade.domain.manga.repository.MangaRepository import eu.kanade.domain.manga.repository.MangaRepository
import eu.kanade.tachiyomi.data.database.models.LibraryManga
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
class GetLibraryManga( class GetLibraryManga(

View file

@ -1,8 +1,8 @@
package eu.kanade.domain.manga.repository package eu.kanade.domain.manga.repository
import eu.kanade.domain.library.model.LibraryManga
import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.model.MangaUpdate import eu.kanade.domain.manga.model.MangaUpdate
import eu.kanade.tachiyomi.data.database.models.LibraryManga
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
interface MangaRepository { interface MangaRepository {

View file

@ -55,7 +55,7 @@ fun LibraryScreen(
onChangeCategoryClicked = onChangeCategoryClicked, onChangeCategoryClicked = onChangeCategoryClicked,
onMarkAsReadClicked = onMarkAsReadClicked, onMarkAsReadClicked = onMarkAsReadClicked,
onMarkAsUnreadClicked = onMarkAsUnreadClicked, onMarkAsUnreadClicked = onMarkAsUnreadClicked,
onDownloadClicked = onDownloadClicked.takeIf { presenter.selection.none { it.source == LocalSource.ID } }, onDownloadClicked = onDownloadClicked.takeIf { presenter.selection.none { it.manga.source == LocalSource.ID } },
onDeleteClicked = onDeleteClicked, onDeleteClicked = onDeleteClicked,
) )
}, },

View file

@ -6,7 +6,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import eu.kanade.domain.category.model.Category import eu.kanade.domain.category.model.Category
import eu.kanade.tachiyomi.data.database.models.LibraryManga import eu.kanade.domain.library.model.LibraryManga
import eu.kanade.tachiyomi.ui.library.LibraryPresenter import eu.kanade.tachiyomi.ui.library.LibraryPresenter
@Stable @Stable

View file

@ -12,8 +12,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import eu.kanade.domain.library.model.LibraryManga
import eu.kanade.domain.manga.model.MangaCover import eu.kanade.domain.manga.model.MangaCover
import eu.kanade.tachiyomi.data.database.models.LibraryManga
import eu.kanade.tachiyomi.ui.library.LibraryItem import eu.kanade.tachiyomi.ui.library.LibraryItem
@Composable @Composable
@ -38,7 +38,7 @@ fun LibraryComfortableGrid(
) { libraryItem -> ) { libraryItem ->
LibraryComfortableGridItem( LibraryComfortableGridItem(
libraryItem, libraryItem,
libraryItem.manga in selection, libraryItem.libraryManga in selection,
onClick, onClick,
onLongClick, onLongClick,
) )
@ -53,26 +53,27 @@ fun LibraryComfortableGridItem(
onClick: (LibraryManga) -> Unit, onClick: (LibraryManga) -> Unit,
onLongClick: (LibraryManga) -> Unit, onLongClick: (LibraryManga) -> Unit,
) { ) {
val manga = item.manga val libraryManga = item.libraryManga
val manga = libraryManga.manga
LibraryGridItemSelectable(isSelected = isSelected) { LibraryGridItemSelectable(isSelected = isSelected) {
Column( Column(
modifier = Modifier modifier = Modifier
.combinedClickable( .combinedClickable(
onClick = { onClick = {
onClick(manga) onClick(libraryManga)
}, },
onLongClick = { onLongClick = {
onLongClick(manga) onLongClick(libraryManga)
}, },
), ),
) { ) {
LibraryGridCover( LibraryGridCover(
mangaCover = MangaCover( mangaCover = MangaCover(
manga.id!!, manga.id,
manga.source, manga.source,
manga.favorite, manga.favorite,
manga.thumbnail_url, manga.thumbnailUrl,
manga.cover_last_modified, manga.coverLastModified,
), ),
downloadCount = item.downloadCount, downloadCount = item.downloadCount,
unreadCount = item.unreadCount, unreadCount = item.unreadCount,

View file

@ -22,7 +22,7 @@ import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import eu.kanade.tachiyomi.data.database.models.LibraryManga import eu.kanade.domain.library.model.LibraryManga
import eu.kanade.tachiyomi.ui.library.LibraryItem import eu.kanade.tachiyomi.ui.library.LibraryItem
@Composable @Composable
@ -47,7 +47,7 @@ fun LibraryCompactGrid(
) { libraryItem -> ) { libraryItem ->
LibraryCompactGridItem( LibraryCompactGridItem(
item = libraryItem, item = libraryItem,
isSelected = libraryItem.manga in selection, isSelected = libraryItem.libraryManga in selection,
onClick = onClick, onClick = onClick,
onLongClick = onLongClick, onLongClick = onLongClick,
) )
@ -62,24 +62,25 @@ fun LibraryCompactGridItem(
onClick: (LibraryManga) -> Unit, onClick: (LibraryManga) -> Unit,
onLongClick: (LibraryManga) -> Unit, onLongClick: (LibraryManga) -> Unit,
) { ) {
val manga = item.manga val libraryManga = item.libraryManga
val manga = libraryManga.manga
LibraryGridCover( LibraryGridCover(
modifier = Modifier modifier = Modifier
.selectedOutline(isSelected) .selectedOutline(isSelected)
.combinedClickable( .combinedClickable(
onClick = { onClick = {
onClick(manga) onClick(libraryManga)
}, },
onLongClick = { onLongClick = {
onLongClick(manga) onLongClick(libraryManga)
}, },
), ),
mangaCover = eu.kanade.domain.manga.model.MangaCover( mangaCover = eu.kanade.domain.manga.model.MangaCover(
manga.id!!, manga.id,
manga.source, manga.source,
manga.favorite, manga.favorite,
manga.thumbnail_url, manga.thumbnailUrl,
manga.cover_last_modified, manga.coverLastModified,
), ),
downloadCount = item.downloadCount, downloadCount = item.downloadCount,
unreadCount = item.unreadCount, unreadCount = item.unreadCount,

View file

@ -19,11 +19,11 @@ import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
import eu.kanade.core.prefs.PreferenceMutableState import eu.kanade.core.prefs.PreferenceMutableState
import eu.kanade.domain.category.model.Category import eu.kanade.domain.category.model.Category
import eu.kanade.domain.library.model.LibraryDisplayMode import eu.kanade.domain.library.model.LibraryDisplayMode
import eu.kanade.domain.library.model.LibraryManga
import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.SwipeRefreshIndicator import eu.kanade.presentation.components.SwipeRefreshIndicator
import eu.kanade.presentation.library.LibraryState import eu.kanade.presentation.library.LibraryState
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.LibraryManga
import eu.kanade.tachiyomi.ui.library.LibraryItem import eu.kanade.tachiyomi.ui.library.LibraryItem
import eu.kanade.tachiyomi.widget.EmptyView import eu.kanade.tachiyomi.widget.EmptyView
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -72,7 +72,7 @@ fun LibraryContent(
val onClickManga = { manga: LibraryManga -> val onClickManga = { manga: LibraryManga ->
if (state.selectionMode.not()) { if (state.selectionMode.not()) {
onMangaClicked(manga.id!!) onMangaClicked(manga.manga.id)
} else { } else {
onToggleSelection(manga) onToggleSelection(manga)
} }

View file

@ -5,7 +5,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.grid.items
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import eu.kanade.tachiyomi.data.database.models.LibraryManga import eu.kanade.domain.library.model.LibraryManga
import eu.kanade.tachiyomi.ui.library.LibraryItem import eu.kanade.tachiyomi.ui.library.LibraryItem
@Composable @Composable
@ -30,7 +30,7 @@ fun LibraryCoverOnlyGrid(
) { libraryItem -> ) { libraryItem ->
LibraryCoverOnlyGridItem( LibraryCoverOnlyGridItem(
item = libraryItem, item = libraryItem,
isSelected = libraryItem.manga in selection, isSelected = libraryItem.libraryManga in selection,
onClick = onClick, onClick = onClick,
onLongClick = onLongClick, onLongClick = onLongClick,
) )
@ -45,24 +45,25 @@ fun LibraryCoverOnlyGridItem(
onClick: (LibraryManga) -> Unit, onClick: (LibraryManga) -> Unit,
onLongClick: (LibraryManga) -> Unit, onLongClick: (LibraryManga) -> Unit,
) { ) {
val manga = item.manga val libraryManga = item.libraryManga
val manga = libraryManga.manga
LibraryGridCover( LibraryGridCover(
modifier = Modifier modifier = Modifier
.selectedOutline(isSelected) .selectedOutline(isSelected)
.combinedClickable( .combinedClickable(
onClick = { onClick = {
onClick(manga) onClick(libraryManga)
}, },
onLongClick = { onLongClick = {
onLongClick(manga) onLongClick(libraryManga)
}, },
), ),
mangaCover = eu.kanade.domain.manga.model.MangaCover( mangaCover = eu.kanade.domain.manga.model.MangaCover(
manga.id!!, manga.id,
manga.source, manga.source,
manga.favorite, manga.favorite,
manga.thumbnail_url, manga.thumbnailUrl,
manga.cover_last_modified, manga.coverLastModified,
), ),
downloadCount = item.downloadCount, downloadCount = item.downloadCount,
unreadCount = item.unreadCount, unreadCount = item.unreadCount,

View file

@ -56,8 +56,8 @@ fun MangaGridCover(
fun LibraryGridCover( fun LibraryGridCover(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
mangaCover: eu.kanade.domain.manga.model.MangaCover, mangaCover: eu.kanade.domain.manga.model.MangaCover,
downloadCount: Int, downloadCount: Long,
unreadCount: Int, unreadCount: Long,
isLocal: Boolean, isLocal: Boolean,
language: String, language: String,
content: @Composable BoxScope.() -> Unit = {}, content: @Composable BoxScope.() -> Unit = {},

View file

@ -17,6 +17,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex import androidx.compose.ui.zIndex
import eu.kanade.domain.library.model.LibraryManga
import eu.kanade.domain.manga.model.MangaCover import eu.kanade.domain.manga.model.MangaCover
import eu.kanade.presentation.components.Badge import eu.kanade.presentation.components.Badge
import eu.kanade.presentation.components.BadgeGroup import eu.kanade.presentation.components.BadgeGroup
@ -28,7 +29,6 @@ import eu.kanade.presentation.util.horizontalPadding
import eu.kanade.presentation.util.selectedBackground import eu.kanade.presentation.util.selectedBackground
import eu.kanade.presentation.util.verticalPadding import eu.kanade.presentation.util.verticalPadding
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.LibraryManga
import eu.kanade.tachiyomi.ui.library.LibraryItem import eu.kanade.tachiyomi.ui.library.LibraryItem
@Composable @Composable
@ -61,7 +61,7 @@ fun LibraryList(
) { libraryItem -> ) { libraryItem ->
LibraryListItem( LibraryListItem(
item = libraryItem, item = libraryItem,
isSelected = libraryItem.manga in selection, isSelected = libraryItem.libraryManga in selection,
onClick = onClick, onClick = onClick,
onLongClick = onLongClick, onLongClick = onLongClick,
) )
@ -76,19 +76,20 @@ fun LibraryListItem(
onClick: (LibraryManga) -> Unit, onClick: (LibraryManga) -> Unit,
onLongClick: (LibraryManga) -> Unit, onLongClick: (LibraryManga) -> Unit,
) { ) {
val manga = item.manga val libraryManga = item.libraryManga
val manga = libraryManga.manga
MangaListItem( MangaListItem(
modifier = Modifier.selectedBackground(isSelected), modifier = Modifier.selectedBackground(isSelected),
title = manga.title, title = manga.title,
cover = MangaCover( cover = MangaCover(
manga.id!!, manga.id,
manga.source, manga.source,
manga.favorite, manga.favorite,
manga.thumbnail_url, manga.thumbnailUrl,
manga.cover_last_modified, manga.coverLastModified,
), ),
onClick = { onClick(manga) }, onClick = { onClick(libraryManga) },
onLongClick = { onLongClick(manga) }, onLongClick = { onLongClick(libraryManga) },
) { ) {
if (item.downloadCount > 0) { if (item.downloadCount > 0) {
Badge( Badge(

View file

@ -13,7 +13,7 @@ import com.google.accompanist.pager.HorizontalPager
import com.google.accompanist.pager.PagerState import com.google.accompanist.pager.PagerState
import eu.kanade.core.prefs.PreferenceMutableState import eu.kanade.core.prefs.PreferenceMutableState
import eu.kanade.domain.library.model.LibraryDisplayMode import eu.kanade.domain.library.model.LibraryDisplayMode
import eu.kanade.tachiyomi.data.database.models.LibraryManga import eu.kanade.domain.library.model.LibraryManga
import eu.kanade.tachiyomi.ui.library.LibraryItem import eu.kanade.tachiyomi.ui.library.LibraryItem
@Composable @Composable

View file

@ -1,35 +0,0 @@
package eu.kanade.tachiyomi.data.database.models
class LibraryManga : MangaImpl() {
var unreadCount: Int = 0
var readCount: Int = 0
val totalChapters
get() = readCount + unreadCount
val hasStarted
get() = readCount > 0
var category: Int = 0
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is LibraryManga) return false
if (!super.equals(other)) return false
if (unreadCount != other.unreadCount) return false
if (readCount != other.readCount) return false
if (category != other.category) return false
return true
}
override fun hashCode(): Int {
var result = super.hashCode()
result = 31 * result + unreadCount
result = 31 * result + readCount
result = 31 * result + category
return result
}
}

View file

@ -14,10 +14,12 @@ import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay
import eu.kanade.domain.chapter.model.toDbChapter import eu.kanade.domain.chapter.model.toDbChapter
import eu.kanade.domain.download.service.DownloadPreferences import eu.kanade.domain.download.service.DownloadPreferences
import eu.kanade.domain.library.model.LibraryManga
import eu.kanade.domain.library.service.LibraryPreferences import eu.kanade.domain.library.service.LibraryPreferences
import eu.kanade.domain.manga.interactor.GetLibraryManga import eu.kanade.domain.manga.interactor.GetLibraryManga
import eu.kanade.domain.manga.interactor.GetManga import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.model.toMangaUpdate import eu.kanade.domain.manga.model.toMangaUpdate
import eu.kanade.domain.track.interactor.GetTracks import eu.kanade.domain.track.interactor.GetTracks
import eu.kanade.domain.track.interactor.InsertTrack import eu.kanade.domain.track.interactor.InsertTrack
@ -26,10 +28,7 @@ import eu.kanade.domain.track.model.toDomainTrack
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.LibraryManga
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.toDomainChapter import eu.kanade.tachiyomi.data.database.models.toDomainChapter
import eu.kanade.tachiyomi.data.database.models.toDomainManga
import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.download.DownloadService import eu.kanade.tachiyomi.data.download.DownloadService
import eu.kanade.tachiyomi.data.library.LibraryUpdateService.Companion.start import eu.kanade.tachiyomi.data.library.LibraryUpdateService.Companion.start
@ -75,7 +74,6 @@ import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import eu.kanade.domain.chapter.model.Chapter as DomainChapter import eu.kanade.domain.chapter.model.Chapter as DomainChapter
import eu.kanade.domain.manga.model.Manga as DomainManga
/** /**
* This class will take care of updating the chapters of the manga from the library. It can be * This class will take care of updating the chapters of the manga from the library. It can be
@ -261,20 +259,20 @@ class LibraryUpdateService(
* *
* @param categoryId the ID of the category to update, or -1 if no category specified. * @param categoryId the ID of the category to update, or -1 if no category specified.
*/ */
fun addMangaToQueue(categoryId: Long) { private fun addMangaToQueue(categoryId: Long) {
val libraryManga = runBlocking { getLibraryManga.await() } val libraryManga = runBlocking { getLibraryManga.await() }
val listToUpdate = if (categoryId != -1L) { val listToUpdate = if (categoryId != -1L) {
libraryManga.filter { it.category.toLong() == categoryId } libraryManga.filter { it.category == categoryId }
} else { } else {
val categoriesToUpdate = libraryPreferences.libraryUpdateCategories().get().map(String::toInt) val categoriesToUpdate = libraryPreferences.libraryUpdateCategories().get().map { it.toLong() }
val listToInclude = if (categoriesToUpdate.isNotEmpty()) { val listToInclude = if (categoriesToUpdate.isNotEmpty()) {
libraryManga.filter { it.category in categoriesToUpdate } libraryManga.filter { it.category in categoriesToUpdate }
} else { } else {
libraryManga libraryManga
} }
val categoriesToExclude = libraryPreferences.libraryUpdateCategoriesExclude().get().map(String::toInt) val categoriesToExclude = libraryPreferences.libraryUpdateCategoriesExclude().get().map { it.toLong() }
val listToExclude = if (categoriesToExclude.isNotEmpty()) { val listToExclude = if (categoriesToExclude.isNotEmpty()) {
libraryManga.filter { it.category in categoriesToExclude } libraryManga.filter { it.category in categoriesToExclude }
} else { } else {
@ -285,12 +283,12 @@ class LibraryUpdateService(
} }
mangaToUpdate = listToUpdate mangaToUpdate = listToUpdate
.distinctBy { it.id } .distinctBy { it.manga.id }
.sortedBy { it.title } .sortedBy { it.manga.title }
// Warn when excessively checking a single source // Warn when excessively checking a single source
val maxUpdatesFromSource = mangaToUpdate val maxUpdatesFromSource = mangaToUpdate
.groupBy { it.source } .groupBy { it.manga.source }
.filterKeys { sourceManager.get(it) !is UnmeteredSource } .filterKeys { sourceManager.get(it) !is UnmeteredSource }
.maxOfOrNull { it.value.size } ?: 0 .maxOfOrNull { it.value.size } ?: 0
if (maxUpdatesFromSource > MANGA_PER_SOURCE_QUEUE_WARNING_THRESHOLD) { if (maxUpdatesFromSource > MANGA_PER_SOURCE_QUEUE_WARNING_THRESHOLD) {
@ -309,8 +307,8 @@ class LibraryUpdateService(
private suspend fun updateChapterList() { private suspend fun updateChapterList() {
val semaphore = Semaphore(5) val semaphore = Semaphore(5)
val progressCount = AtomicInteger(0) val progressCount = AtomicInteger(0)
val currentlyUpdatingManga = CopyOnWriteArrayList<LibraryManga>() val currentlyUpdatingManga = CopyOnWriteArrayList<Manga>()
val newUpdates = CopyOnWriteArrayList<Pair<DomainManga, Array<DomainChapter>>>() val newUpdates = CopyOnWriteArrayList<Pair<Manga, Array<DomainChapter>>>()
val skippedUpdates = CopyOnWriteArrayList<Pair<Manga, String?>>() val skippedUpdates = CopyOnWriteArrayList<Pair<Manga, String?>>()
val failedUpdates = CopyOnWriteArrayList<Pair<Manga, String?>>() val failedUpdates = CopyOnWriteArrayList<Pair<Manga, String?>>()
val hasDownloads = AtomicBoolean(false) val hasDownloads = AtomicBoolean(false)
@ -319,60 +317,58 @@ class LibraryUpdateService(
val restrictions = libraryPreferences.libraryUpdateMangaRestriction().get() val restrictions = libraryPreferences.libraryUpdateMangaRestriction().get()
withIOContext { withIOContext {
mangaToUpdate.groupBy { it.source } mangaToUpdate.groupBy { it.manga.source }
.values .values
.map { mangaInSource -> .map { mangaInSource ->
async { async {
semaphore.withPermit { semaphore.withPermit {
mangaInSource.forEach { manga -> mangaInSource.forEach { libraryManga ->
val manga = libraryManga.manga
if (updateJob?.isActive != true) { if (updateJob?.isActive != true) {
return@async return@async
} }
// Don't continue to update if manga not in library // Don't continue to update if manga is not in library
manga.id?.let { getManga.await(it) } ?: return@forEach manga.id.let { getManga.await(it) } ?: return@forEach
withUpdateNotification( withUpdateNotification(
currentlyUpdatingManga, currentlyUpdatingManga,
progressCount, progressCount,
manga, manga,
) { mangaWithNotif -> ) {
try { try {
when { when {
MANGA_NON_COMPLETED in restrictions && mangaWithNotif.status == SManga.COMPLETED -> MANGA_NON_COMPLETED in restrictions && manga.status.toInt() == SManga.COMPLETED ->
skippedUpdates.add(mangaWithNotif to getString(R.string.skipped_reason_completed)) skippedUpdates.add(manga to getString(R.string.skipped_reason_completed))
MANGA_HAS_UNREAD in restrictions && mangaWithNotif.unreadCount != 0 -> MANGA_HAS_UNREAD in restrictions && libraryManga.unreadCount != 0L ->
skippedUpdates.add(mangaWithNotif to getString(R.string.skipped_reason_not_caught_up)) skippedUpdates.add(manga to getString(R.string.skipped_reason_not_caught_up))
MANGA_NON_READ in restrictions && mangaWithNotif.totalChapters > 0 && !mangaWithNotif.hasStarted -> MANGA_NON_READ in restrictions && libraryManga.totalChapters > 0L && !libraryManga.hasStarted ->
skippedUpdates.add(mangaWithNotif to getString(R.string.skipped_reason_not_started)) skippedUpdates.add(manga to getString(R.string.skipped_reason_not_started))
mangaWithNotif.update_strategy != UpdateStrategy.ALWAYS_UPDATE -> manga.updateStrategy != UpdateStrategy.ALWAYS_UPDATE ->
skippedUpdates.add(mangaWithNotif to getString(R.string.skipped_reason_not_always_update)) skippedUpdates.add(manga to getString(R.string.skipped_reason_not_always_update))
else -> { else -> {
// Convert to the manga that contains new chapters val newChapters = updateManga(manga)
mangaWithNotif.toDomainManga()?.let { domainManga -> val newDbChapters = newChapters.map { it.toDbChapter() }
val newChapters = updateManga(domainManga)
val newDbChapters = newChapters.map { it.toDbChapter() }
if (newChapters.isNotEmpty()) { if (newChapters.isNotEmpty()) {
val categoryIds = getCategories.await(domainManga.id).map { it.id } val categoryIds = getCategories.await(manga.id).map { it.id }
if (domainManga.shouldDownloadNewChapters(categoryIds, downloadPreferences)) { if (manga.shouldDownloadNewChapters(categoryIds, downloadPreferences)) {
downloadChapters(mangaWithNotif, newDbChapters) downloadChapters(manga, newDbChapters)
hasDownloads.set(true) hasDownloads.set(true)
}
// Convert to the manga that contains new chapters
newUpdates.add(
mangaWithNotif.toDomainManga()!! to
newDbChapters
.map { it.toDomainChapter()!! }
.sortedByDescending { it.sourceOrder }
.toTypedArray(),
)
} }
// Convert to the manga that contains new chapters
newUpdates.add(
manga to
newDbChapters
.map { it.toDomainChapter()!! }
.sortedByDescending { it.sourceOrder }
.toTypedArray(),
)
} }
} }
} }
@ -383,11 +379,11 @@ class LibraryUpdateService(
is SourceManager.SourceNotInstalledException -> getString(R.string.loader_not_implemented_error) is SourceManager.SourceNotInstalledException -> getString(R.string.loader_not_implemented_error)
else -> e.message else -> e.message
} }
failedUpdates.add(mangaWithNotif to errorMessage) failedUpdates.add(manga to errorMessage)
} }
if (libraryPreferences.autoUpdateTrackers().get()) { if (libraryPreferences.autoUpdateTrackers().get()) {
updateTrackings(mangaWithNotif, loggedServices) updateTrackings(manga, loggedServices)
} }
} }
} }
@ -423,7 +419,7 @@ class LibraryUpdateService(
private fun downloadChapters(manga: Manga, chapters: List<Chapter>) { private fun downloadChapters(manga: Manga, chapters: List<Chapter>) {
// We don't want to start downloading while the library is updating, because websites // We don't want to start downloading while the library is updating, because websites
// may don't like it and they could ban the user. // may don't like it and they could ban the user.
downloadManager.downloadChapters(manga.toDomainManga()!!, chapters, false) downloadManager.downloadChapters(manga, chapters, false)
} }
/** /**
@ -432,7 +428,7 @@ class LibraryUpdateService(
* @param manga the manga to update. * @param manga the manga to update.
* @return a pair of the inserted and removed chapters. * @return a pair of the inserted and removed chapters.
*/ */
private suspend fun updateManga(manga: DomainManga): List<DomainChapter> { private suspend fun updateManga(manga: Manga): List<DomainChapter> {
val source = sourceManager.getOrStub(manga.source) val source = sourceManager.getOrStub(manga.source)
// Update manga metadata if needed // Update manga metadata if needed
@ -455,15 +451,16 @@ class LibraryUpdateService(
private suspend fun updateCovers() { private suspend fun updateCovers() {
val semaphore = Semaphore(5) val semaphore = Semaphore(5)
val progressCount = AtomicInteger(0) val progressCount = AtomicInteger(0)
val currentlyUpdatingManga = CopyOnWriteArrayList<LibraryManga>() val currentlyUpdatingManga = CopyOnWriteArrayList<Manga>()
withIOContext { withIOContext {
mangaToUpdate.groupBy { it.source } mangaToUpdate.groupBy { it.manga.source }
.values .values
.map { mangaInSource -> .map { mangaInSource ->
async { async {
semaphore.withPermit { semaphore.withPermit {
mangaInSource.forEach { manga -> mangaInSource.forEach { libraryManga ->
val manga = libraryManga.manga
if (updateJob?.isActive != true) { if (updateJob?.isActive != true) {
return@async return@async
} }
@ -472,14 +469,14 @@ class LibraryUpdateService(
currentlyUpdatingManga, currentlyUpdatingManga,
progressCount, progressCount,
manga, manga,
) { mangaWithNotif -> ) {
val source = sourceManager.get(mangaWithNotif.source) ?: return@withUpdateNotification val source = sourceManager.get(manga.source) ?: return@withUpdateNotification
try { try {
val networkManga = source.getMangaDetails(mangaWithNotif.copy()) val networkManga = source.getMangaDetails(manga.toSManga())
mangaWithNotif.prepUpdateCover(coverCache, networkManga, true) val updatedManga = manga.prepUpdateCover(coverCache, networkManga, true)
mangaWithNotif.copyFrom(networkManga) .copyFrom(networkManga)
try { try {
updateManga.await(mangaWithNotif.toDomainManga()!!.toMangaUpdate()) updateManga.await(updatedManga.toMangaUpdate())
} catch (e: Exception) { } catch (e: Exception) {
logcat(LogPriority.ERROR) { "Manga doesn't exist anymore" } logcat(LogPriority.ERROR) { "Manga doesn't exist anymore" }
} }
@ -506,12 +503,13 @@ class LibraryUpdateService(
var progressCount = 0 var progressCount = 0
val loggedServices = trackManager.services.filter { it.isLogged } val loggedServices = trackManager.services.filter { it.isLogged }
mangaToUpdate.forEach { manga -> mangaToUpdate.forEach { libraryManga ->
val manga = libraryManga.manga
if (updateJob?.isActive != true) { if (updateJob?.isActive != true) {
return return
} }
notifier.showProgressNotification(listOf(manga.toDomainManga()!!), progressCount++, mangaToUpdate.size) notifier.showProgressNotification(listOf(manga), progressCount++, mangaToUpdate.size)
// Update the tracking details. // Update the tracking details.
updateTrackings(manga, loggedServices) updateTrackings(manga, loggedServices)
@ -520,8 +518,8 @@ class LibraryUpdateService(
notifier.cancelProgressNotification() notifier.cancelProgressNotification()
} }
private suspend fun updateTrackings(manga: LibraryManga, loggedServices: List<TrackService>) { private suspend fun updateTrackings(manga: Manga, loggedServices: List<TrackService>) {
getTracks.await(manga.id!!) getTracks.await(manga.id)
.map { track -> .map { track ->
supervisorScope { supervisorScope {
async { async {
@ -532,7 +530,7 @@ class LibraryUpdateService(
insertTrack.await(updatedTrack.toDomainTrack()!!) insertTrack.await(updatedTrack.toDomainTrack()!!)
if (service is EnhancedTrackService) { if (service is EnhancedTrackService) {
val chapters = getChapterByMangaId.await(manga.id!!) val chapters = getChapterByMangaId.await(manga.id)
syncChaptersWithTrackServiceTwoWay.await(chapters, track, service) syncChaptersWithTrackServiceTwoWay.await(chapters, track, service)
} }
} catch (e: Throwable) { } catch (e: Throwable) {
@ -547,10 +545,10 @@ class LibraryUpdateService(
} }
private suspend fun withUpdateNotification( private suspend fun withUpdateNotification(
updatingManga: CopyOnWriteArrayList<LibraryManga>, updatingManga: CopyOnWriteArrayList<Manga>,
completed: AtomicInteger, completed: AtomicInteger,
manga: LibraryManga, manga: Manga,
block: suspend (LibraryManga) -> Unit, block: suspend () -> Unit,
) { ) {
if (updateJob?.isActive != true) { if (updateJob?.isActive != true) {
return return
@ -558,12 +556,12 @@ class LibraryUpdateService(
updatingManga.add(manga) updatingManga.add(manga)
notifier.showProgressNotification( notifier.showProgressNotification(
updatingManga.map { it.toDomainManga()!! }, updatingManga,
completed.get(), completed.get(),
mangaToUpdate.size, mangaToUpdate.size,
) )
block(manga) block()
if (updateJob?.isActive != true) { if (updateJob?.isActive != true) {
return return
@ -572,7 +570,7 @@ class LibraryUpdateService(
updatingManga.remove(manga) updatingManga.remove(manga)
completed.getAndIncrement() completed.getAndIncrement()
notifier.showProgressNotification( notifier.showProgressNotification(
updatingManga.map { it.toDomainManga()!! }, updatingManga,
completed.get(), completed.get(),
mangaToUpdate.size, mangaToUpdate.size,
) )

View file

@ -16,7 +16,6 @@ import eu.kanade.presentation.components.ChangeCategoryDialog
import eu.kanade.presentation.components.DeleteLibraryMangaDialog import eu.kanade.presentation.components.DeleteLibraryMangaDialog
import eu.kanade.presentation.library.LibraryScreen import eu.kanade.presentation.library.LibraryScreen
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.toDomainManga
import eu.kanade.tachiyomi.data.library.LibraryUpdateService import eu.kanade.tachiyomi.data.library.LibraryUpdateService
import eu.kanade.tachiyomi.ui.base.controller.FullComposeController import eu.kanade.tachiyomi.ui.base.controller.FullComposeController
import eu.kanade.tachiyomi.ui.base.controller.RootController import eu.kanade.tachiyomi.ui.base.controller.RootController
@ -197,7 +196,7 @@ class LibraryController(
private fun showMangaCategoriesDialog() { private fun showMangaCategoriesDialog() {
viewScope.launchIO { viewScope.launchIO {
// Create a copy of selected manga // Create a copy of selected manga
val mangaList = presenter.selection.mapNotNull { it.toDomainManga() }.toList() val mangaList = presenter.selection.map { it.manga }
// Hide the default category because it has a different behavior than the ones from db. // Hide the default category because it has a different behavior than the ones from db.
val categories = presenter.categories.filter { it.id != 0L } val categories = presenter.categories.filter { it.id != 0L }
@ -219,18 +218,18 @@ class LibraryController(
private fun downloadUnreadChapters() { private fun downloadUnreadChapters() {
val mangaList = presenter.selection.toList() val mangaList = presenter.selection.toList()
presenter.downloadUnreadChapters(mangaList.mapNotNull { it.toDomainManga() }) presenter.downloadUnreadChapters(mangaList.map { it.manga })
presenter.clearSelection() presenter.clearSelection()
} }
private fun markReadStatus(read: Boolean) { private fun markReadStatus(read: Boolean) {
val mangaList = presenter.selection.toList() val mangaList = presenter.selection.toList()
presenter.markReadStatus(mangaList.mapNotNull { it.toDomainManga() }, read) presenter.markReadStatus(mangaList.map { it.manga }, read)
presenter.clearSelection() presenter.clearSelection()
} }
private fun showDeleteMangaDialog() { private fun showDeleteMangaDialog() {
val mangaList = presenter.selection.mapNotNull { it.toDomainManga() }.toList() val mangaList = presenter.selection.map { it.manga }
presenter.dialog = LibraryPresenter.Dialog.DeleteManga(mangaList) presenter.dialog = LibraryPresenter.Dialog.DeleteManga(mangaList)
} }
} }

View file

@ -1,19 +1,19 @@
package eu.kanade.tachiyomi.ui.library package eu.kanade.tachiyomi.ui.library
import eu.kanade.tachiyomi.data.database.models.LibraryManga import eu.kanade.domain.library.model.LibraryManga
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.getNameForMangaInfo import eu.kanade.tachiyomi.source.getNameForMangaInfo
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
class LibraryItem( class LibraryItem(
val manga: LibraryManga, val libraryManga: LibraryManga,
private val sourceManager: SourceManager = Injekt.get(), private val sourceManager: SourceManager = Injekt.get(),
) { ) {
var displayMode: Long = -1 var displayMode: Long = -1
var downloadCount = -1 var downloadCount: Long = -1
var unreadCount = -1 var unreadCount: Long = -1
var isLocal = false var isLocal = false
var sourceLanguage = "" var sourceLanguage = ""
@ -24,12 +24,12 @@ class LibraryItem(
* @return true if the manga should be included, false otherwise. * @return true if the manga should be included, false otherwise.
*/ */
fun filter(constraint: String): Boolean { fun filter(constraint: String): Boolean {
val sourceName by lazy { sourceManager.getOrStub(manga.source).getNameForMangaInfo() } val sourceName by lazy { sourceManager.getOrStub(libraryManga.manga.source).getNameForMangaInfo() }
val genres by lazy { manga.getGenres() } val genres by lazy { libraryManga.manga.genre }
return manga.title.contains(constraint, true) || return libraryManga.manga.title.contains(constraint, true) ||
(manga.author?.contains(constraint, true) ?: false) || (libraryManga.manga.author?.contains(constraint, true) ?: false) ||
(manga.artist?.contains(constraint, true) ?: false) || (libraryManga.manga.artist?.contains(constraint, true) ?: false) ||
(manga.description?.contains(constraint, true) ?: false) || (libraryManga.manga.description?.contains(constraint, true) ?: false) ||
if (constraint.contains(",")) { if (constraint.contains(",")) {
constraint.split(",").all { containsSourceOrGenre(it.trim(), sourceName, genres) } constraint.split(",").all { containsSourceOrGenre(it.trim(), sourceName, genres) }
} else { } else {
@ -73,7 +73,7 @@ class LibraryItem(
other as LibraryItem other as LibraryItem
if (manga != other.manga) return false if (libraryManga != other.libraryManga) return false
if (sourceManager != other.sourceManager) return false if (sourceManager != other.sourceManager) return false
if (displayMode != other.displayMode) return false if (displayMode != other.displayMode) return false
if (downloadCount != other.downloadCount) return false if (downloadCount != other.downloadCount) return false
@ -85,11 +85,11 @@ class LibraryItem(
} }
override fun hashCode(): Int { override fun hashCode(): Int {
var result = manga.hashCode() var result = libraryManga.hashCode()
result = 31 * result + sourceManager.hashCode() result = 31 * result + sourceManager.hashCode()
result = 31 * result + displayMode.hashCode() result = 31 * result + displayMode.hashCode()
result = 31 * result + downloadCount result = 31 * result + downloadCount.toInt()
result = 31 * result + unreadCount result = 31 * result + unreadCount.toInt()
result = 31 * result + isLocal.hashCode() result = 31 * result + isLocal.hashCode()
result = 31 * result + sourceLanguage.hashCode() result = 31 * result + sourceLanguage.hashCode()
return result return result

View file

@ -23,6 +23,7 @@ import eu.kanade.domain.category.model.Category
import eu.kanade.domain.chapter.interactor.GetChapterByMangaId import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
import eu.kanade.domain.chapter.interactor.SetReadStatus import eu.kanade.domain.chapter.interactor.SetReadStatus
import eu.kanade.domain.chapter.model.toDbChapter import eu.kanade.domain.chapter.model.toDbChapter
import eu.kanade.domain.library.model.LibraryManga
import eu.kanade.domain.library.model.LibrarySort import eu.kanade.domain.library.model.LibrarySort
import eu.kanade.domain.library.model.sort import eu.kanade.domain.library.model.sort
import eu.kanade.domain.library.service.LibraryPreferences import eu.kanade.domain.library.service.LibraryPreferences
@ -38,7 +39,6 @@ import eu.kanade.presentation.library.LibraryStateImpl
import eu.kanade.presentation.library.components.LibraryToolbarTitle import eu.kanade.presentation.library.components.LibraryToolbarTitle
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.database.models.LibraryManga
import eu.kanade.tachiyomi.data.database.models.toDomainManga import eu.kanade.tachiyomi.data.database.models.toDomainManga
import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackManager
@ -185,9 +185,9 @@ class LibraryPresenter(
val filterFnDownloaded: (LibraryItem) -> Boolean = downloaded@{ item -> val filterFnDownloaded: (LibraryItem) -> Boolean = downloaded@{ item ->
if (!downloadedOnly && filterDownloaded == State.IGNORE.value) return@downloaded true if (!downloadedOnly && filterDownloaded == State.IGNORE.value) return@downloaded true
val isDownloaded = when { val isDownloaded = when {
item.manga.toDomainManga()!!.isLocal() -> true item.libraryManga.manga.isLocal() -> true
item.downloadCount != -1 -> item.downloadCount > 0 item.downloadCount != -1L -> item.downloadCount > 0
else -> downloadManager.getDownloadCount(item.manga.toDomainManga()!!) > 0 else -> downloadManager.getDownloadCount(item.libraryManga.manga) > 0
} }
return@downloaded if (downloadedOnly || filterDownloaded == State.INCLUDE.value) { return@downloaded if (downloadedOnly || filterDownloaded == State.INCLUDE.value) {
@ -199,7 +199,7 @@ class LibraryPresenter(
val filterFnUnread: (LibraryItem) -> Boolean = unread@{ item -> val filterFnUnread: (LibraryItem) -> Boolean = unread@{ item ->
if (filterUnread == State.IGNORE.value) return@unread true if (filterUnread == State.IGNORE.value) return@unread true
val isUnread = item.manga.unreadCount != 0 val isUnread = item.libraryManga.unreadCount != 0L
return@unread if (filterUnread == State.INCLUDE.value) { return@unread if (filterUnread == State.INCLUDE.value) {
isUnread isUnread
@ -210,7 +210,7 @@ class LibraryPresenter(
val filterFnStarted: (LibraryItem) -> Boolean = started@{ item -> val filterFnStarted: (LibraryItem) -> Boolean = started@{ item ->
if (filterStarted == State.IGNORE.value) return@started true if (filterStarted == State.IGNORE.value) return@started true
val hasStarted = item.manga.hasStarted val hasStarted = item.libraryManga.hasStarted
return@started if (filterStarted == State.INCLUDE.value) { return@started if (filterStarted == State.INCLUDE.value) {
hasStarted hasStarted
@ -221,7 +221,7 @@ class LibraryPresenter(
val filterFnCompleted: (LibraryItem) -> Boolean = completed@{ item -> val filterFnCompleted: (LibraryItem) -> Boolean = completed@{ item ->
if (filterCompleted == State.IGNORE.value) return@completed true if (filterCompleted == State.IGNORE.value) return@completed true
val isCompleted = item.manga.status == SManga.COMPLETED val isCompleted = item.libraryManga.manga.status.toInt() == SManga.COMPLETED
return@completed if (filterCompleted == State.INCLUDE.value) { return@completed if (filterCompleted == State.INCLUDE.value) {
isCompleted isCompleted
@ -233,7 +233,7 @@ class LibraryPresenter(
val filterFnTracking: (LibraryItem) -> Boolean = tracking@{ item -> val filterFnTracking: (LibraryItem) -> Boolean = tracking@{ item ->
if (isNotAnyLoggedIn) return@tracking true if (isNotAnyLoggedIn) return@tracking true
val trackedManga = trackMap[item.manga.id ?: -1] val trackedManga = trackMap[item.libraryManga.manga.id]
val containsExclude = loggedInServices.filterValues { it == State.EXCLUDE.value } val containsExclude = loggedInServices.filterValues { it == State.EXCLUDE.value }
val containsInclude = loggedInServices.filterValues { it == State.INCLUDE.value } val containsInclude = loggedInServices.filterValues { it == State.INCLUDE.value }
@ -281,28 +281,28 @@ class LibraryPresenter(
for ((_, itemList) in map) { for ((_, itemList) in map) {
for (item in itemList) { for (item in itemList) {
item.downloadCount = if (showDownloadBadges) { item.downloadCount = if (showDownloadBadges) {
downloadManager.getDownloadCount(item.manga.toDomainManga()!!) downloadManager.getDownloadCount(item.libraryManga.manga).toLong()
} else { } else {
// Unset download count if not enabled // Unset download count if not enabled
-1 -1
} }
item.unreadCount = if (showUnreadBadges) { item.unreadCount = if (showUnreadBadges) {
item.manga.unreadCount item.libraryManga.unreadCount
} else { } else {
// Unset unread count if not enabled // Unset unread count if not enabled
-1 -1
} }
item.isLocal = if (showLocalBadges) { item.isLocal = if (showLocalBadges) {
item.manga.toDomainManga()!!.isLocal() item.libraryManga.manga.isLocal()
} else { } else {
// Hide / Unset local badge if not enabled // Hide / Unset local badge if not enabled
false false
} }
item.sourceLanguage = if (showLanguageBadges) { item.sourceLanguage = if (showLanguageBadges) {
sourceManager.getOrStub(item.manga.source).lang.uppercase() sourceManager.getOrStub(item.libraryManga.manga.source).lang.uppercase()
} else { } else {
// Unset source language if not enabled // Unset source language if not enabled
"" ""
@ -354,43 +354,43 @@ class LibraryPresenter(
strength = Collator.PRIMARY strength = Collator.PRIMARY
} }
val sortFn: (LibraryItem, LibraryItem) -> Int = { i1, i2 -> val sortFn: (LibraryItem, LibraryItem) -> Int = { i1, i2 ->
val sort = sortModes[i1.manga.category.toLong()]!! val sort = sortModes[i1.libraryManga.category]!!
when (sort.type) { when (sort.type) {
LibrarySort.Type.Alphabetical -> { LibrarySort.Type.Alphabetical -> {
collator.compare(i1.manga.title.lowercase(locale), i2.manga.title.lowercase(locale)) collator.compare(i1.libraryManga.manga.title.lowercase(locale), i2.libraryManga.manga.title.lowercase(locale))
} }
LibrarySort.Type.LastRead -> { LibrarySort.Type.LastRead -> {
val manga1LastRead = lastReadManga[i1.manga.id!!] ?: 0 val manga1LastRead = lastReadManga[i1.libraryManga.manga.id] ?: 0
val manga2LastRead = lastReadManga[i2.manga.id!!] ?: 0 val manga2LastRead = lastReadManga[i2.libraryManga.manga.id] ?: 0
manga1LastRead.compareTo(manga2LastRead) manga1LastRead.compareTo(manga2LastRead)
} }
LibrarySort.Type.LastUpdate -> { LibrarySort.Type.LastUpdate -> {
i1.manga.last_update.compareTo(i2.manga.last_update) i1.libraryManga.manga.lastUpdate.compareTo(i2.libraryManga.manga.lastUpdate)
} }
LibrarySort.Type.UnreadCount -> when { LibrarySort.Type.UnreadCount -> when {
// Ensure unread content comes first // Ensure unread content comes first
i1.manga.unreadCount == i2.manga.unreadCount -> 0 i1.libraryManga.unreadCount == i2.libraryManga.unreadCount -> 0
i1.manga.unreadCount == 0 -> if (sort.isAscending) 1 else -1 i1.libraryManga.unreadCount == 0L -> if (sort.isAscending) 1 else -1
i2.manga.unreadCount == 0 -> if (sort.isAscending) -1 else 1 i2.libraryManga.unreadCount == 0L -> if (sort.isAscending) -1 else 1
else -> i1.manga.unreadCount.compareTo(i2.manga.unreadCount) else -> i1.libraryManga.unreadCount.compareTo(i2.libraryManga.unreadCount)
} }
LibrarySort.Type.TotalChapters -> { LibrarySort.Type.TotalChapters -> {
i1.manga.totalChapters.compareTo(i2.manga.totalChapters) i1.libraryManga.totalChapters.compareTo(i2.libraryManga.totalChapters)
} }
LibrarySort.Type.LatestChapter -> { LibrarySort.Type.LatestChapter -> {
val manga1latestChapter = latestChapterManga[i1.manga.id!!] val manga1latestChapter = latestChapterManga[i1.libraryManga.manga.id]
?: latestChapterManga.size ?: latestChapterManga.size
val manga2latestChapter = latestChapterManga[i2.manga.id!!] val manga2latestChapter = latestChapterManga[i2.libraryManga.manga.id]
?: latestChapterManga.size ?: latestChapterManga.size
manga1latestChapter.compareTo(manga2latestChapter) manga1latestChapter.compareTo(manga2latestChapter)
} }
LibrarySort.Type.ChapterFetchDate -> { LibrarySort.Type.ChapterFetchDate -> {
val manga1chapterFetchDate = chapterFetchDateManga[i1.manga.id!!] ?: 0 val manga1chapterFetchDate = chapterFetchDateManga[i1.libraryManga.manga.id] ?: 0
val manga2chapterFetchDate = chapterFetchDateManga[i2.manga.id!!] ?: 0 val manga2chapterFetchDate = chapterFetchDateManga[i2.libraryManga.manga.id] ?: 0
manga1chapterFetchDate.compareTo(manga2chapterFetchDate) manga1chapterFetchDate.compareTo(manga2chapterFetchDate)
} }
LibrarySort.Type.DateAdded -> { LibrarySort.Type.DateAdded -> {
i1.manga.date_added.compareTo(i2.manga.date_added) i1.libraryManga.manga.dateAdded.compareTo(i1.libraryManga.manga.dateAdded)
} }
else -> throw IllegalStateException("Invalid SortModeSetting: ${sort.type}") else -> throw IllegalStateException("Invalid SortModeSetting: ${sort.type}")
} }
@ -419,7 +419,7 @@ class LibraryPresenter(
list.map { libraryManga -> list.map { libraryManga ->
// Display mode based on user preference: take it from global library setting or category // Display mode based on user preference: take it from global library setting or category
LibraryItem(libraryManga) LibraryItem(libraryManga)
}.groupBy { it.manga.category.toLong() } }.groupBy { it.libraryManga.category.toLong() }
} }
return combine(categoriesFlow, libraryMangasFlow) { dbCategories, libraryManga -> return combine(categoriesFlow, libraryMangasFlow) { dbCategories, libraryManga ->
val categories = if (libraryManga.isNotEmpty() && libraryManga.containsKey(0).not()) { val categories = if (libraryManga.isNotEmpty() && libraryManga.containsKey(0).not()) {
@ -631,7 +631,7 @@ class LibraryPresenter(
val count = when { val count = when {
category == null || mangaCountVisibility.not() -> null category == null || mangaCountVisibility.not() -> null
tabVisibility.not() -> loadedManga[category.id]?.size tabVisibility.not() -> loadedManga[category.id]?.size
else -> loadedManga.values.flatten().distinctBy { it.manga.id }.size else -> loadedManga.values.flatten().distinctBy { it.libraryManga.manga.id }.size
} }
value = when (category) { value = when (category) {
@ -665,7 +665,7 @@ class LibraryPresenter(
fun toggleSelection(manga: LibraryManga) { fun toggleSelection(manga: LibraryManga) {
val mutableList = state.selection.toMutableList() val mutableList = state.selection.toMutableList()
if (selection.fastAny { it.id == manga.id }) { if (selection.fastAny { it.manga.id == manga.manga.id }) {
mutableList.remove(manga) mutableList.remove(manga)
} else { } else {
mutableList.add(manga) mutableList.add(manga)
@ -677,13 +677,13 @@ class LibraryPresenter(
val category = categories[index] val category = categories[index]
val items = loadedManga[category.id] ?: emptyList() val items = loadedManga[category.id] ?: emptyList()
state.selection = state.selection.toMutableList().apply { state.selection = state.selection.toMutableList().apply {
addAll(items.filterNot { it.manga in selection }.map { it.manga }) addAll(items.filterNot { it.libraryManga in selection }.map { it.libraryManga })
} }
} }
fun invertSelection(index: Int) { fun invertSelection(index: Int) {
val category = categories[index] val category = categories[index]
val items = (loadedManga[category.id] ?: emptyList()).map { it.manga } val items = (loadedManga[category.id] ?: emptyList()).map { it.libraryManga }
state.selection = items.filterNot { it in selection } state.selection = items.filterNot { it in selection }
} }

View file

@ -20,26 +20,26 @@ import eu.kanade.domain.manga.model.Manga as DomainManga
/** /**
* Call before updating [Manga.thumbnail_url] to ensure old cover can be cleared from cache * Call before updating [Manga.thumbnail_url] to ensure old cover can be cleared from cache
*/ */
fun Manga.prepUpdateCover(coverCache: CoverCache, remoteManga: SManga, refreshSameUrl: Boolean) { fun DomainManga.prepUpdateCover(coverCache: CoverCache, remoteManga: SManga, refreshSameUrl: Boolean): DomainManga {
// Never refresh covers if the new url is null, as the current url has possibly become invalid // Never refresh covers if the new url is null, as the current url has possibly become invalid
val newUrl = remoteManga.thumbnail_url ?: return val newUrl = remoteManga.thumbnail_url ?: return this
// Never refresh covers if the url is empty to avoid "losing" existing covers // Never refresh covers if the url is empty to avoid "losing" existing covers
if (newUrl.isEmpty()) return if (newUrl.isEmpty()) return this
if (!refreshSameUrl && thumbnail_url == newUrl) return if (!refreshSameUrl && thumbnailUrl == newUrl) return this
val domainManga = toDomainManga()!! return when {
when { isLocal() -> {
domainManga.isLocal() -> { this.copy(coverLastModified = Date().time)
cover_last_modified = Date().time
} }
domainManga.hasCustomCover(coverCache) -> { hasCustomCover(coverCache) -> {
coverCache.deleteFromCache(this, false) coverCache.deleteFromCache(this, false)
this
} }
else -> { else -> {
cover_last_modified = Date().time
coverCache.deleteFromCache(this, false) coverCache.deleteFromCache(this, false)
this.copy(coverLastModified = Date().time)
} }
} }
} }