Refactor list extra provider

This commit is contained in:
Koitharu
2023-06-04 17:37:54 +03:00
parent 02d5dfb375
commit 1847759ec3
21 changed files with 143 additions and 257 deletions

View File

@@ -40,8 +40,6 @@ import org.koitharu.kotatsu.core.util.IncognitoModeIndicator
import org.koitharu.kotatsu.core.util.ext.activityManager
import org.koitharu.kotatsu.core.util.ext.connectivityManager
import org.koitharu.kotatsu.core.util.ext.isLowRamDevice
import org.koitharu.kotatsu.list.domain.ListExtraProvider
import org.koitharu.kotatsu.list.domain.ListExtraProviderImpl
import org.koitharu.kotatsu.local.data.CacheDir
import org.koitharu.kotatsu.local.data.CbzFetcher
import org.koitharu.kotatsu.local.data.LocalStorageChanges
@@ -65,9 +63,6 @@ interface AppModule {
@Binds
fun bindImageGetter(coilImageGetter: CoilImageGetter): Html.ImageGetter
@Binds
fun bindListExtraProvider(impl: ListExtraProviderImpl): ListExtraProvider
companion object {
@Provides

View File

@@ -1,37 +0,0 @@
package org.koitharu.kotatsu.core.parser
import android.content.Context
import androidx.annotation.ColorRes
import dagger.Reusable
import dagger.hilt.android.qualifiers.ApplicationContext
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.parsers.model.MangaTag
import javax.inject.Inject
@Reusable
class MangaTagHighlighter @Inject constructor(
@ApplicationContext context: Context,
) {
private val dict by lazy {
context.resources.openRawResource(R.raw.tags_redlist).use {
val set = HashSet<String>()
it.bufferedReader().forEachLine { x ->
val line = x.trim()
if (line.isNotEmpty()) {
set.add(line)
}
}
set
}
}
@ColorRes
fun getTint(tag: MangaTag): Int {
return if (tag.title.lowercase() in dict) {
R.color.warning
} else {
0
}
}
}

View File

@@ -23,7 +23,6 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.bookmarks.domain.Bookmark
import org.koitharu.kotatsu.bookmarks.ui.adapter.BookmarksAdapter
import org.koitharu.kotatsu.core.model.countChaptersByBranch
import org.koitharu.kotatsu.core.parser.MangaTagHighlighter
import org.koitharu.kotatsu.core.ui.BaseFragment
import org.koitharu.kotatsu.core.ui.image.CoverSizeResolver
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
@@ -45,6 +44,7 @@ import org.koitharu.kotatsu.details.ui.scrobbling.ScrobblingItemDecoration
import org.koitharu.kotatsu.details.ui.scrobbling.ScrollingInfoAdapter
import org.koitharu.kotatsu.history.data.PROGRESS_NONE
import org.koitharu.kotatsu.image.ui.ImageActivity
import org.koitharu.kotatsu.list.domain.ListExtraProvider
import org.koitharu.kotatsu.main.ui.owners.NoModalBottomSheetOwner
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaSource
@@ -67,7 +67,7 @@ class DetailsFragment :
lateinit var coil: ImageLoader
@Inject
lateinit var tagHighlighter: MangaTagHighlighter
lateinit var tagHighlighter: ListExtraProvider
private val viewModel by activityViewModels<DetailsViewModel>()
@@ -283,7 +283,7 @@ class DetailsFragment :
manga.tags.map { tag ->
ChipsView.ChipModel(
title = tag.title,
tint = tagHighlighter.getTint(tag),
tint = tagHighlighter.getTagTint(tag),
icon = 0,
data = tag,
isCheckable = false,

View File

@@ -13,7 +13,6 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.plus
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.parser.MangaTagHighlighter
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.ui.util.ReversibleAction
import org.koitharu.kotatsu.core.util.ext.call
@@ -21,8 +20,6 @@ import org.koitharu.kotatsu.download.ui.worker.DownloadWorker
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment.Companion.ARG_CATEGORY_ID
import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment.Companion.NO_ID
import org.koitharu.kotatsu.history.data.HistoryRepository
import org.koitharu.kotatsu.history.data.PROGRESS_NONE
import org.koitharu.kotatsu.list.domain.ListExtraProvider
import org.koitharu.kotatsu.list.ui.MangaListViewModel
import org.koitharu.kotatsu.list.ui.model.EmptyState
@@ -30,19 +27,16 @@ import org.koitharu.kotatsu.list.ui.model.LoadingState
import org.koitharu.kotatsu.list.ui.model.toErrorState
import org.koitharu.kotatsu.list.ui.model.toUi
import org.koitharu.kotatsu.parsers.model.SortOrder
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
import javax.inject.Inject
@HiltViewModel
class FavouritesListViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
private val repository: FavouritesRepository,
private val trackingRepository: TrackingRepository,
private val historyRepository: HistoryRepository,
private val settings: AppSettings,
private val tagHighlighter: MangaTagHighlighter,
private val listExtraProvider: ListExtraProvider,
settings: AppSettings,
downloadScheduler: DownloadWorker.Scheduler,
) : MangaListViewModel(settings, downloadScheduler), ListExtraProvider {
) : MangaListViewModel(settings, downloadScheduler) {
val categoryId: Long = savedStateHandle[ARG_CATEGORY_ID] ?: NO_ID
@@ -76,7 +70,7 @@ class FavouritesListViewModel @Inject constructor(
),
)
else -> list.toUi(mode, this, tagHighlighter)
else -> list.toUi(mode, listExtraProvider)
}
}.catch {
emit(listOf(it.toErrorState(canRetry = false)))
@@ -108,20 +102,4 @@ class FavouritesListViewModel @Inject constructor(
repository.setCategoryOrder(categoryId, order)
}
}
override suspend fun getCounter(mangaId: Long): Int {
return if (settings.isTrackerEnabled) {
trackingRepository.getNewChaptersCount(mangaId)
} else {
0
}
}
override suspend fun getProgress(mangaId: Long): Float {
return if (settings.isReadingIndicatorsEnabled) {
historyRepository.getProgress(mangaId)
} else {
PROGRESS_NONE
}
}
}

View File

@@ -1,6 +1,10 @@
package org.koitharu.kotatsu.history.data
import androidx.room.*
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Transaction
import kotlinx.coroutines.flow.Flow
import org.koitharu.kotatsu.core.db.entity.MangaEntity
import org.koitharu.kotatsu.core.db.entity.TagEntity
@@ -20,6 +24,10 @@ abstract class HistoryDao {
@Query("SELECT * FROM history WHERE deleted_at = 0 ORDER BY updated_at DESC")
abstract fun observeAll(): Flow<List<HistoryWithManga>>
@Transaction
@Query("SELECT * FROM history WHERE deleted_at = 0 ORDER BY updated_at DESC LIMIT :limit")
abstract fun observeAll(limit: Int): Flow<List<HistoryWithManga>>
@Query("SELECT * FROM manga WHERE manga_id IN (SELECT manga_id FROM history WHERE deleted_at = 0)")
abstract suspend fun findAllManga(): List<MangaEntity>

View File

@@ -51,6 +51,12 @@ class HistoryRepository @Inject constructor(
}
}
fun observeAll(limit: Int): Flow<List<Manga>> {
return db.historyDao.observeAll(limit).mapItems {
it.manga.toManga(it.tags.toMangaTags())
}
}
fun observeAllWithHistory(): Flow<List<MangaWithHistory>> {
return db.historyDao.observeAll().mapItems {
MangaWithHistory(

View File

@@ -10,7 +10,6 @@ import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.plus
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.parser.MangaTagHighlighter
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.prefs.ListMode
import org.koitharu.kotatsu.core.prefs.observeAsStateFlow
@@ -21,8 +20,8 @@ import org.koitharu.kotatsu.core.util.ext.daysDiff
import org.koitharu.kotatsu.core.util.ext.onFirst
import org.koitharu.kotatsu.download.ui.worker.DownloadWorker
import org.koitharu.kotatsu.history.data.HistoryRepository
import org.koitharu.kotatsu.history.data.PROGRESS_NONE
import org.koitharu.kotatsu.history.domain.model.MangaWithHistory
import org.koitharu.kotatsu.list.domain.ListExtraProvider
import org.koitharu.kotatsu.list.ui.MangaListViewModel
import org.koitharu.kotatsu.list.ui.model.EmptyState
import org.koitharu.kotatsu.list.ui.model.ListModel
@@ -31,7 +30,6 @@ import org.koitharu.kotatsu.list.ui.model.toErrorState
import org.koitharu.kotatsu.list.ui.model.toGridModel
import org.koitharu.kotatsu.list.ui.model.toListDetailedModel
import org.koitharu.kotatsu.list.ui.model.toListModel
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
import java.util.Date
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@@ -40,8 +38,7 @@ import javax.inject.Inject
class HistoryListViewModel @Inject constructor(
private val repository: HistoryRepository,
private val settings: AppSettings,
private val trackingRepository: TrackingRepository,
private val tagHighlighter: MangaTagHighlighter,
private val extraProvider: ListExtraProvider,
downloadScheduler: DownloadWorker.Scheduler,
) : MangaListViewModel(settings, downloadScheduler) {
@@ -106,7 +103,6 @@ class HistoryListViewModel @Inject constructor(
mode: ListMode,
): List<ListModel> {
val result = ArrayList<ListModel>(if (grouped) (list.size * 1.4).toInt() else list.size + 1)
val showPercent = settings.isReadingIndicatorsEnabled
var prevDate: DateTimeAgo? = null
for ((manga, history) in list) {
if (grouped) {
@@ -116,16 +112,10 @@ class HistoryListViewModel @Inject constructor(
}
prevDate = date
}
val counter = if (settings.isTrackerEnabled) {
trackingRepository.getNewChaptersCount(manga.id)
} else {
0
}
val percent = if (showPercent) history.percent else PROGRESS_NONE
result += when (mode) {
ListMode.LIST -> manga.toListModel(counter, percent)
ListMode.DETAILED_LIST -> manga.toListDetailedModel(counter, percent, tagHighlighter)
ListMode.GRID -> manga.toGridModel(counter, percent)
ListMode.LIST -> manga.toListModel(extraProvider)
ListMode.DETAILED_LIST -> manga.toListDetailedModel(extraProvider)
ListMode.GRID -> manga.toGridModel(extraProvider)
}
}
return result

View File

@@ -1,8 +1,60 @@
package org.koitharu.kotatsu.list.domain
interface ListExtraProvider {
import android.content.Context
import androidx.annotation.ColorRes
import dagger.Reusable
import dagger.hilt.android.qualifiers.ApplicationContext
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.history.data.HistoryRepository
import org.koitharu.kotatsu.history.data.PROGRESS_NONE
import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
import javax.inject.Inject
suspend fun getCounter(mangaId: Long): Int
@Reusable
class ListExtraProvider @Inject constructor(
@ApplicationContext context: Context,
private val settings: AppSettings,
private val trackingRepository: TrackingRepository,
private val historyRepository: HistoryRepository,
) {
suspend fun getProgress(mangaId: Long): Float
}
private val dict by lazy {
context.resources.openRawResource(R.raw.tags_redlist).use {
val set = HashSet<String>()
it.bufferedReader().forEachLine { x ->
val line = x.trim()
if (line.isNotEmpty()) {
set.add(line)
}
}
set
}
}
suspend fun getCounter(mangaId: Long): Int {
return if (settings.isTrackerEnabled) {
trackingRepository.getNewChaptersCount(mangaId)
} else {
0
}
}
suspend fun getProgress(mangaId: Long): Float {
return if (settings.isReadingIndicatorsEnabled) {
historyRepository.getProgress(mangaId)
} else {
PROGRESS_NONE
}
}
@ColorRes
fun getTagTint(tag: MangaTag): Int {
return if (tag.title.lowercase() in dict) {
R.color.warning
} else {
0
}
}
}

View File

@@ -1,32 +0,0 @@
package org.koitharu.kotatsu.list.domain
import dagger.Reusable
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.history.data.HistoryRepository
import org.koitharu.kotatsu.history.data.PROGRESS_NONE
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
import javax.inject.Inject
@Reusable
class ListExtraProviderImpl @Inject constructor(
private val settings: AppSettings,
private val trackingRepository: TrackingRepository,
private val historyRepository: HistoryRepository,
) : ListExtraProvider {
override suspend fun getCounter(mangaId: Long): Int {
return if (settings.isTrackerEnabled) {
trackingRepository.getNewChaptersCount(mangaId)
} else {
0
}
}
override suspend fun getProgress(mangaId: Long): Float {
return if (settings.isReadingIndicatorsEnabled) {
historyRepository.getProgress(mangaId)
} else {
PROGRESS_NONE
}
}
}

View File

@@ -3,7 +3,6 @@ package org.koitharu.kotatsu.list.ui.model
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
import org.koitharu.kotatsu.core.parser.MangaTagHighlighter
import org.koitharu.kotatsu.core.prefs.ListMode
import org.koitharu.kotatsu.core.ui.widgets.ChipsView
import org.koitharu.kotatsu.core.util.ext.ifZero
@@ -14,34 +13,31 @@ import org.koitharu.kotatsu.parsers.model.Manga
import java.net.SocketTimeoutException
import java.net.UnknownHostException
fun Manga.toListModel(
counter: Int,
progress: Float,
suspend fun Manga.toListModel(
extraProvider: ListExtraProvider?
) = MangaListModel(
id = id,
title = title,
subtitle = tags.joinToString(", ") { it.title },
coverUrl = coverUrl,
manga = this,
counter = counter,
progress = progress,
counter = extraProvider?.getCounter(id) ?: 0,
progress = extraProvider?.getProgress(id) ?: PROGRESS_NONE,
)
fun Manga.toListDetailedModel(
counter: Int,
progress: Float,
tagHighlighter: MangaTagHighlighter?,
suspend fun Manga.toListDetailedModel(
extraProvider: ListExtraProvider?,
) = MangaListDetailedModel(
id = id,
title = title,
subtitle = altTitle,
coverUrl = coverUrl,
manga = this,
counter = counter,
progress = progress,
counter = extraProvider?.getCounter(id) ?: 0,
progress = extraProvider?.getProgress(id) ?: PROGRESS_NONE,
tags = tags.map {
ChipsView.ChipModel(
tint = tagHighlighter?.getTint(it) ?: 0,
tint = extraProvider?.getTagTint(it) ?: 0,
title = it.title,
icon = 0,
isCheckable = false,
@@ -51,53 +47,30 @@ fun Manga.toListDetailedModel(
},
)
fun Manga.toGridModel(counter: Int, progress: Float) = MangaGridModel(
suspend fun Manga.toGridModel(
extraProvider: ListExtraProvider?,
) = MangaGridModel(
id = id,
title = title,
coverUrl = coverUrl,
manga = this,
counter = counter,
progress = progress,
counter = extraProvider?.getCounter(id) ?: 0,
progress = extraProvider?.getProgress(id) ?: PROGRESS_NONE,
)
suspend fun List<Manga>.toUi(
mode: ListMode,
extraProvider: ListExtraProvider,
tagHighlighter: MangaTagHighlighter?,
): List<MangaItemModel> = toUi(ArrayList(size), mode, extraProvider, tagHighlighter)
fun List<Manga>.toUi(
mode: ListMode,
tagHighlighter: MangaTagHighlighter?,
): List<MangaItemModel> = toUi(ArrayList(size), mode, tagHighlighter)
fun <C : MutableCollection<in MangaItemModel>> List<Manga>.toUi(
destination: C,
mode: ListMode,
tagHighlighter: MangaTagHighlighter?,
): C = when (mode) {
ListMode.LIST -> mapTo(destination) { it.toListModel(0, PROGRESS_NONE) }
ListMode.DETAILED_LIST -> mapTo(destination) { it.toListDetailedModel(0, PROGRESS_NONE, tagHighlighter) }
ListMode.GRID -> mapTo(destination) { it.toGridModel(0, PROGRESS_NONE) }
}
): List<MangaItemModel> = toUi(ArrayList(size), mode, extraProvider)
suspend fun <C : MutableCollection<in MangaItemModel>> List<Manga>.toUi(
destination: C,
mode: ListMode,
extraProvider: ListExtraProvider,
tagHighlighter: MangaTagHighlighter?,
): C = when (mode) {
ListMode.LIST -> mapTo(destination) {
it.toListModel(extraProvider.getCounter(it.id), extraProvider.getProgress(it.id))
}
ListMode.DETAILED_LIST -> mapTo(destination) {
it.toListDetailedModel(extraProvider.getCounter(it.id), extraProvider.getProgress(it.id), tagHighlighter)
}
ListMode.GRID -> mapTo(destination) {
it.toGridModel(extraProvider.getCounter(it.id), extraProvider.getProgress(it.id))
}
ListMode.LIST -> mapTo(destination) { it.toListModel(extraProvider) }
ListMode.DETAILED_LIST -> mapTo(destination) { it.toListDetailedModel(extraProvider) }
ListMode.GRID -> mapTo(destination) { it.toGridModel(extraProvider) }
}
fun Throwable.toErrorState(canRetry: Boolean = true) = ErrorState(

View File

@@ -6,7 +6,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.SharedFlow
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.parser.MangaTagHighlighter
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
import org.koitharu.kotatsu.core.util.ext.call
@@ -25,7 +24,6 @@ class LocalListViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
mangaRepositoryFactory: MangaRepository.Factory,
filter: FilterCoordinator,
tagHighlighter: MangaTagHighlighter,
settings: AppSettings,
downloadScheduler: DownloadWorker.Scheduler,
listExtraProvider: ListExtraProvider,
@@ -35,7 +33,6 @@ class LocalListViewModel @Inject constructor(
savedStateHandle,
mangaRepositoryFactory,
filter,
tagHighlighter,
settings,
listExtraProvider,
downloadScheduler,

View File

@@ -18,7 +18,6 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.plus
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.parser.MangaTagHighlighter
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.util.ext.call
import org.koitharu.kotatsu.core.util.ext.require
@@ -47,7 +46,6 @@ open class RemoteListViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
mangaRepositoryFactory: MangaRepository.Factory,
private val filter: FilterCoordinator,
private val tagHighlighter: MangaTagHighlighter,
settings: AppSettings,
listExtraProvider: ListExtraProvider,
downloadScheduler: DownloadWorker.Scheduler,
@@ -72,7 +70,7 @@ open class RemoteListViewModel @Inject constructor(
list == null -> add(LoadingState)
list.isEmpty() -> add(createEmptyState(header.value.hasSelectedTags))
else -> {
list.toUi(this, mode, listExtraProvider, tagHighlighter)
list.toUi(this, mode, listExtraProvider)
when {
error != null -> add(error.toErrorFooter())
hasNext -> add(LoadingFooter())

View File

@@ -13,10 +13,10 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.plus
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.parser.MangaTagHighlighter
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.util.ext.require
import org.koitharu.kotatsu.download.ui.worker.DownloadWorker
import org.koitharu.kotatsu.list.domain.ListExtraProvider
import org.koitharu.kotatsu.list.ui.MangaListViewModel
import org.koitharu.kotatsu.list.ui.model.EmptyState
import org.koitharu.kotatsu.list.ui.model.ListModel
@@ -33,7 +33,7 @@ class SearchViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
repositoryFactory: MangaRepository.Factory,
settings: AppSettings,
private val tagHighlighter: MangaTagHighlighter,
private val extraProvider: ListExtraProvider,
downloadScheduler: DownloadWorker.Scheduler,
) : MangaListViewModel(settings, downloadScheduler) {
@@ -64,7 +64,7 @@ class SearchViewModel @Inject constructor(
else -> {
val result = ArrayList<ListModel>(list.size + 1)
list.toUi(result, mode, tagHighlighter)
list.toUi(result, mode, extraProvider)
when {
error != null -> result += error.toErrorFooter()
hasNext -> result += LoadingFooter()

View File

@@ -25,6 +25,7 @@ import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
import org.koitharu.kotatsu.core.util.ext.call
import org.koitharu.kotatsu.download.ui.worker.DownloadWorker
import org.koitharu.kotatsu.list.domain.ListExtraProvider
import org.koitharu.kotatsu.list.ui.model.EmptyState
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.list.ui.model.LoadingFooter
@@ -42,6 +43,7 @@ private const val MIN_HAS_MORE_ITEMS = 8
@HiltViewModel
class MultiSearchViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
private val extraProvider: ListExtraProvider,
private val settings: AppSettings,
private val mangaRepositoryFactory: MangaRepository.Factory,
private val downloadScheduler: DownloadWorker.Scheduler,
@@ -128,7 +130,7 @@ class MultiSearchViewModel @Inject constructor(
async(dispatcher) {
runCatchingCancellable {
val list = mangaRepositoryFactory.create(source).getList(offset = 0, query = q)
.toUi(ListMode.GRID, null)
.toUi(ListMode.GRID, extraProvider)
if (list.isNotEmpty()) {
MultiSearchListModel(source, list.size > MIN_HAS_MORE_ITEMS, list)
} else {

View File

@@ -25,6 +25,7 @@ import org.koitharu.kotatsu.suggestions.domain.SuggestionRepository
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
import javax.inject.Inject
@Suppress("SameParameterValue")
class ShelfContentObserveUseCase @Inject constructor(
private val localMangaRepository: LocalMangaRepository,
private val historyRepository: HistoryRepository,
@@ -35,20 +36,20 @@ class ShelfContentObserveUseCase @Inject constructor(
) {
operator fun invoke(): Flow<ShelfContent> = combine(
historyRepository.observeAllWithHistory(),
observeLocalManga(SortOrder.UPDATED),
historyRepository.observeAll(20),
observeLocalManga(SortOrder.UPDATED, 20),
observeFavourites(),
trackingRepository.observeUpdatedManga(),
suggestionRepository.observeAll(16),
suggestionRepository.observeAll(20),
) { history, local, favorites, updated, suggestions ->
ShelfContent(history, favorites, updated, local, suggestions)
}
private fun observeLocalManga(sortOrder: SortOrder): Flow<List<Manga>> {
private fun observeLocalManga(sortOrder: SortOrder, limit: Int): Flow<List<Manga>> {
return localStorageChanges
.onStart { emit(null) }
.mapLatest {
localMangaRepository.getList(0, null, sortOrder)
localMangaRepository.getList(0, null, sortOrder).take(limit)
}.distinctUntilChanged()
}

View File

@@ -1,13 +1,12 @@
package org.koitharu.kotatsu.shelf.domain.model
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.history.domain.model.MangaWithHistory
import org.koitharu.kotatsu.parsers.model.Manga
class ShelfContent(
val history: List<MangaWithHistory>,
val history: List<Manga>,
val favourites: Map<FavouriteCategory, List<Manga>>,
val updated: Map<Manga, Int>,
val updated: List<Manga>,
val local: List<Manga>,
val suggestions: List<Manga>,
) {

View File

@@ -23,8 +23,6 @@ import org.koitharu.kotatsu.core.util.ext.call
import org.koitharu.kotatsu.download.ui.worker.DownloadWorker
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
import org.koitharu.kotatsu.history.data.HistoryRepository
import org.koitharu.kotatsu.history.data.PROGRESS_NONE
import org.koitharu.kotatsu.history.domain.model.MangaWithHistory
import org.koitharu.kotatsu.list.domain.ListExtraProvider
import org.koitharu.kotatsu.list.ui.model.EmptyHint
import org.koitharu.kotatsu.list.ui.model.EmptyState
@@ -41,21 +39,20 @@ import org.koitharu.kotatsu.shelf.domain.model.ShelfContent
import org.koitharu.kotatsu.shelf.domain.model.ShelfSection
import org.koitharu.kotatsu.shelf.ui.model.ShelfSectionModel
import org.koitharu.kotatsu.sync.domain.SyncController
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
import javax.inject.Inject
@HiltViewModel
class ShelfViewModel @Inject constructor(
private val historyRepository: HistoryRepository,
private val favouritesRepository: FavouritesRepository,
private val trackingRepository: TrackingRepository,
private val settings: AppSettings,
private val downloadScheduler: DownloadWorker.Scheduler,
private val deleteLocalMangaUseCase: DeleteLocalMangaUseCase,
private val listExtraProvider: ListExtraProvider,
shelfContentObserveUseCase: ShelfContentObserveUseCase,
syncController: SyncController,
networkState: NetworkState,
) : BaseViewModel(), ListExtraProvider {
) : BaseViewModel() {
val onActionDone = MutableEventFlow<ReversibleAction>()
val onDownloadStarted = MutableEventFlow<Unit>()
@@ -78,22 +75,6 @@ class ShelfViewModel @Inject constructor(
}
}
override suspend fun getCounter(mangaId: Long): Int {
return if (settings.isTrackerEnabled) {
trackingRepository.getNewChaptersCount(mangaId)
} else {
0
}
}
override suspend fun getProgress(mangaId: Long): Float {
return if (settings.isReadingIndicatorsEnabled) {
historyRepository.getProgress(mangaId)
} else {
PROGRESS_NONE
}
}
fun removeFromFavourites(category: FavouriteCategory, ids: Set<Long>) {
if (ids.isEmpty()) {
return
@@ -194,7 +175,7 @@ class ShelfViewModel @Inject constructor(
when (section) {
ShelfSection.HISTORY -> mapHistory(
result,
content.history.filter { it.manga.source == MangaSource.LOCAL },
content.history.filter { it.source == MangaSource.LOCAL },
)
ShelfSection.LOCAL -> mapLocal(result, content.local)
@@ -222,17 +203,14 @@ class ShelfViewModel @Inject constructor(
private suspend fun mapHistory(
destination: MutableList<in ShelfSectionModel.History>,
list: List<MangaWithHistory>,
list: List<Manga>,
) {
if (list.isEmpty()) {
return
}
val showPercent = settings.isReadingIndicatorsEnabled
destination += ShelfSectionModel.History(
items = list.map { (manga, history) ->
val counter = getCounter(manga.id)
val percent = if (showPercent) history.percent else PROGRESS_NONE
manga.toGridModel(counter, percent)
items = list.map { manga ->
manga.toGridModel(listExtraProvider)
},
showAllButtonText = R.string.show_all,
)
@@ -240,16 +218,15 @@ class ShelfViewModel @Inject constructor(
private suspend fun mapUpdated(
destination: MutableList<in ShelfSectionModel.Updated>,
updated: Map<Manga, Int>,
updated: List<Manga>,
) {
if (updated.isEmpty()) {
return
}
val showPercent = settings.isReadingIndicatorsEnabled
settings.isReadingIndicatorsEnabled
destination += ShelfSectionModel.Updated(
items = updated.map { (manga, counter) ->
val percent = if (showPercent) getProgress(manga.id) else PROGRESS_NONE
manga.toGridModel(counter, percent)
items = updated.map { manga ->
manga.toGridModel(listExtraProvider)
},
showAllButtonText = R.string.show_all,
)
@@ -263,7 +240,7 @@ class ShelfViewModel @Inject constructor(
return
}
destination += ShelfSectionModel.Local(
items = local.toUi(ListMode.GRID, this, null),
items = local.toUi(ListMode.GRID, listExtraProvider),
showAllButtonText = R.string.show_all,
)
}
@@ -276,7 +253,7 @@ class ShelfViewModel @Inject constructor(
return
}
destination += ShelfSectionModel.Suggestions(
items = suggestions.toUi(ListMode.GRID, this, null),
items = suggestions.toUi(ListMode.GRID, listExtraProvider),
showAllButtonText = R.string.show_all,
)
}
@@ -291,7 +268,7 @@ class ShelfViewModel @Inject constructor(
for ((category, list) in favourites) {
if (list.isNotEmpty()) {
destination += ShelfSectionModel.Favourites(
items = list.toUi(ListMode.GRID, this, null),
items = list.toUi(ListMode.GRID, listExtraProvider),
category = category,
showAllButtonText = R.string.show_all,
)

View File

@@ -10,10 +10,10 @@ import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.plus
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.parser.MangaTagHighlighter
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.util.ext.onFirst
import org.koitharu.kotatsu.download.ui.worker.DownloadWorker
import org.koitharu.kotatsu.list.domain.ListExtraProvider
import org.koitharu.kotatsu.list.ui.MangaListViewModel
import org.koitharu.kotatsu.list.ui.model.EmptyState
import org.koitharu.kotatsu.list.ui.model.LoadingState
@@ -26,7 +26,7 @@ import javax.inject.Inject
class SuggestionsViewModel @Inject constructor(
repository: SuggestionRepository,
settings: AppSettings,
private val tagHighlighter: MangaTagHighlighter,
private val extraProvider: ListExtraProvider,
downloadScheduler: DownloadWorker.Scheduler,
) : MangaListViewModel(settings, downloadScheduler) {
@@ -44,7 +44,7 @@ class SuggestionsViewModel @Inject constructor(
),
)
else -> list.toUi(mode, tagHighlighter)
else -> list.toUi(mode, extraProvider)
}
}.onStart {
loadingCounter.increment()

View File

@@ -34,9 +34,8 @@ abstract class TracksDao {
abstract fun observeNewChapters(mangaId: Long): Flow<Int?>
@Transaction
@MapInfo(valueColumn = "chapters_new")
@Query("SELECT manga.*, chapters_new FROM tracks LEFT JOIN manga ON manga.manga_id = tracks.manga_id WHERE chapters_new > 0 ORDER BY chapters_new DESC")
abstract fun observeUpdatedManga(): Flow<Map<MangaWithTags, Int>>
@Query("SELECT manga.* FROM tracks LEFT JOIN manga ON manga.manga_id = tracks.manga_id WHERE chapters_new > 0 ORDER BY chapters_new DESC")
abstract fun observeUpdatedManga(): Flow<List<MangaWithTags>>
@Query("DELETE FROM tracks")
abstract suspend fun clear()

View File

@@ -12,6 +12,7 @@ import org.koitharu.kotatsu.core.db.MangaDatabase
import org.koitharu.kotatsu.core.db.entity.MangaEntity
import org.koitharu.kotatsu.core.db.entity.toManga
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.core.util.ext.mapItems
import org.koitharu.kotatsu.favourites.data.toFavouriteCategory
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaSource
@@ -44,9 +45,9 @@ class TrackingRepository @Inject constructor(
return db.tracksDao.observeNewChapters().map { list -> list.count { it > 0 } }
}
fun observeUpdatedManga(): Flow<Map<Manga, Int>> {
fun observeUpdatedManga(): Flow<List<Manga>> {
return db.tracksDao.observeUpdatedManga()
.map { x -> x.mapKeys { it.key.toManga() } }
.mapItems { it.toManga() }
.distinctUntilChanged()
}

View File

@@ -10,22 +10,16 @@ import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.plus
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.parser.MangaTagHighlighter
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.prefs.ListMode
import org.koitharu.kotatsu.core.util.ext.onFirst
import org.koitharu.kotatsu.download.ui.worker.DownloadWorker
import org.koitharu.kotatsu.history.data.HistoryRepository
import org.koitharu.kotatsu.history.data.PROGRESS_NONE
import org.koitharu.kotatsu.list.domain.ListExtraProvider
import org.koitharu.kotatsu.list.ui.MangaListViewModel
import org.koitharu.kotatsu.list.ui.model.EmptyState
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.list.ui.model.LoadingState
import org.koitharu.kotatsu.list.ui.model.toErrorState
import org.koitharu.kotatsu.list.ui.model.toGridModel
import org.koitharu.kotatsu.list.ui.model.toListDetailedModel
import org.koitharu.kotatsu.list.ui.model.toListModel
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.list.ui.model.toUi
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
import javax.inject.Inject
@@ -34,16 +28,16 @@ class UpdatesViewModel @Inject constructor(
private val repository: TrackingRepository,
private val settings: AppSettings,
private val historyRepository: HistoryRepository,
private val tagHighlighter: MangaTagHighlighter,
private val extraProvider: ListExtraProvider,
downloadScheduler: DownloadWorker.Scheduler,
) : MangaListViewModel(settings, downloadScheduler) {
override val content = combine(
repository.observeUpdatedManga(),
listMode,
) { mangaMap, mode ->
) { mangaList, mode ->
when {
mangaMap.isEmpty() -> listOf(
mangaList.isEmpty() -> listOf(
EmptyState(
icon = R.drawable.ic_empty_history,
textPrimary = R.string.text_history_holder_primary,
@@ -52,7 +46,7 @@ class UpdatesViewModel @Inject constructor(
),
)
else -> mapList(mangaMap, mode)
else -> mangaList.toUi(mode, extraProvider)
}
}.onStart {
loadingCounter.increment()
@@ -65,19 +59,4 @@ class UpdatesViewModel @Inject constructor(
override fun onRefresh() = Unit
override fun onRetry() = Unit
private suspend fun mapList(
mangaMap: Map<Manga, Int>,
mode: ListMode,
): List<ListModel> {
val showPercent = settings.isReadingIndicatorsEnabled
return mangaMap.map { (manga, counter) ->
val percent = if (showPercent) historyRepository.getProgress(manga.id) else PROGRESS_NONE
when (mode) {
ListMode.LIST -> manga.toListModel(counter, percent)
ListMode.DETAILED_LIST -> manga.toListDetailedModel(counter, percent, tagHighlighter)
ListMode.GRID -> manga.toGridModel(counter, percent)
}
}
}
}