Refactor list extra provider
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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>,
|
||||
) {
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user