show all entries going to be checked for Smart update on Upcoming screen

This commit is contained in:
Cuong-Tran 2024-10-20 02:49:37 +07:00
parent 461d709eb1
commit 6fd67bd240
No known key found for this signature in database
GPG key ID: 733AA7624B9315C2
5 changed files with 189 additions and 20 deletions

View file

@ -260,6 +260,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
status.int == trackingExtra
}
}
LibraryGroup.BY_SOURCE -> {
val sourceExtra = groupExtra?.nullIfBlank()?.toIntOrNull()
val source = libraryManga.map { it.manga.source }
@ -269,12 +270,14 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
if (source != null) libraryManga.filter { it.manga.source == source } else emptyList()
}
LibraryGroup.BY_STATUS -> {
val statusExtra = groupExtra?.toLongOrNull() ?: -1
libraryManga.filter {
it.manga.status == statusExtra
}
}
LibraryGroup.UNGROUPED -> libraryManga
else -> libraryManga
}
@ -314,20 +317,14 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
false
}
MANGA_OUTSIDE_RELEASE_PERIOD in restrictions &&
(
it.manga.nextUpdate > fetchWindowUpperBound ||
// KMK -->
MANGA_NON_COMPLETED in restrictions &&
it.manga.status.toInt() == SManga.COMPLETED
// KMK <--
) -> {
MANGA_OUTSIDE_RELEASE_PERIOD in restrictions && it.manga.nextUpdate > fetchWindowUpperBound -> {
skippedUpdates.add(
it.manga to
context.stringResource(MR.strings.skipped_reason_not_in_release_period),
)
false
}
else -> true
}
}
@ -462,6 +459,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
is SourceNotInstalledException -> context.stringResource(
MR.strings.loader_not_implemented_error,
)
else -> e.message
}
failedUpdates.add(manga to errorMessage)
@ -730,7 +728,8 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
}
return file
}
} catch (_: Exception) {}
} catch (_: Exception) {
}
return File("")
}
@ -755,8 +754,6 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
private const val ERROR_LOG_HELP_URL = "https://mihon.app/docs/guides/troubleshooting/"
private const val MANGA_PER_SOURCE_QUEUE_WARNING_THRESHOLD = 60
/**
* Key for category to update.
*/

View file

@ -8,6 +8,7 @@ import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.ui.manga.MangaScreen
import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_OUTSIDE_RELEASE_PERIOD
class UpcomingScreen : Screen() {
@ -22,6 +23,11 @@ class UpcomingScreen : Screen() {
state = state,
setSelectedYearMonth = screenModel::setSelectedYearMonth,
onClickUpcoming = { navigator.push(MangaScreen(it.id)) },
// KMK -->
showUpdatingMangas = screenModel::showUpdatingMangas,
hideUpdatingMangas = screenModel::hideUpdatingMangas,
isPredictReleaseDate = MANGA_OUTSIDE_RELEASE_PERIOD in screenModel.restriction,
// KMK <--
)
}
}

View file

@ -9,9 +9,11 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.HelpOutline
import androidx.compose.material.icons.outlined.NewReleases
import androidx.compose.material3.Badge
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@ -46,49 +48,83 @@ fun UpcomingScreenContent(
state: UpcomingScreenModel.State,
setSelectedYearMonth: (YearMonth) -> Unit,
onClickUpcoming: (manga: Manga) -> Unit,
// KMK -->
showUpdatingMangas: () -> Unit,
hideUpdatingMangas: () -> Unit,
isPredictReleaseDate: Boolean,
// KMK <--
modifier: Modifier = Modifier,
) {
val scope = rememberCoroutineScope()
val listState = rememberLazyListState()
// KMK -->
val headerIndexes = if (state.isShowingUpdatingMangas) state.updatingHeaderIndexes else state.headerIndexes
val items = if (state.isShowingUpdatingMangas) state.updatingItems else state.items
val events = if (state.isShowingUpdatingMangas) state.updatingEvents else state.events
// KMK <--
val onClickDay: (LocalDate, Int) -> Unit = { date, offset ->
state.headerIndexes[date]?.let {
headerIndexes[date]?.let {
scope.launch {
listState.animateScrollToItem(it + offset)
}
}
}
Scaffold(
topBar = { UpcomingToolbar() },
topBar = {
UpcomingToolbar(
// KMK -->
state.isShowingUpdatingMangas,
showUpdatingMangas = showUpdatingMangas,
hideUpdatingMangas = hideUpdatingMangas,
isPredictReleaseDate = isPredictReleaseDate,
// KMK <--
)
},
modifier = modifier,
) { paddingValues ->
if (isTabletUi()) {
UpcomingScreenLargeImpl(
listState = listState,
items = state.items,
events = state.events,
items = items,
events = events,
paddingValues = paddingValues,
selectedYearMonth = state.selectedYearMonth,
setSelectedYearMonth = setSelectedYearMonth,
onClickDay = { onClickDay(it, 0) },
onClickUpcoming = onClickUpcoming,
// KMK -->
state.isShowingUpdatingMangas,
// KMK <--
)
} else {
UpcomingScreenSmallImpl(
listState = listState,
items = state.items,
events = state.events,
items = items,
events = events,
paddingValues = paddingValues,
selectedYearMonth = state.selectedYearMonth,
setSelectedYearMonth = setSelectedYearMonth,
onClickDay = { onClickDay(it, 1) },
onClickUpcoming = onClickUpcoming,
// KMK -->
state.isShowingUpdatingMangas,
// KMK <--
)
}
}
}
@Composable
private fun UpcomingToolbar() {
private fun UpcomingToolbar(
// KMK -->
isShowingUpdatingMangas: Boolean,
showUpdatingMangas: () -> Unit,
hideUpdatingMangas: () -> Unit,
isPredictReleaseDate: Boolean,
// KMK <--
) {
val navigator = LocalNavigator.currentOrThrow
val uriHandler = LocalUriHandler.current
@ -96,6 +132,25 @@ private fun UpcomingToolbar() {
title = stringResource(MR.strings.label_upcoming),
navigateUp = navigator::pop,
actions = {
// KMK -->
if (isPredictReleaseDate) {
IconButton(
onClick = {
if (isShowingUpdatingMangas) {
hideUpdatingMangas()
} else {
showUpdatingMangas()
}
},
) {
Icon(
imageVector = Icons.Outlined.NewReleases,
contentDescription = stringResource(MR.strings.pref_library_update_smart_update),
tint = if (isShowingUpdatingMangas) MaterialTheme.colorScheme.primary else LocalContentColor.current,
)
}
}
// KMK <--
IconButton(onClick = { uriHandler.openUri(Constants.URL_HELP_UPCOMING) }) {
Icon(
imageVector = Icons.AutoMirrored.Outlined.HelpOutline,
@ -143,6 +198,9 @@ private fun UpcomingScreenSmallImpl(
setSelectedYearMonth: (YearMonth) -> Unit,
onClickDay: (LocalDate) -> Unit,
onClickUpcoming: (manga: Manga) -> Unit,
// KMK -->
isShowingUpdatingMangas: Boolean,
// KMK <--
) {
FastScrollLazyColumn(
contentPadding = paddingValues,
@ -158,7 +216,7 @@ private fun UpcomingScreenSmallImpl(
}
items(
items = items,
key = { "upcoming-${it.hashCode()}" },
key = { (if (isShowingUpdatingMangas) "updating" else "upcoming") + it.hashCode() },
contentType = {
when (it) {
is UpcomingUIModel.Header -> "header"
@ -173,6 +231,7 @@ private fun UpcomingScreenSmallImpl(
onClick = { onClickUpcoming(item.manga) },
)
}
is UpcomingUIModel.Header -> {
DateHeading(
date = item.date,
@ -194,6 +253,9 @@ private fun UpcomingScreenLargeImpl(
setSelectedYearMonth: (YearMonth) -> Unit,
onClickDay: (LocalDate) -> Unit,
onClickUpcoming: (manga: Manga) -> Unit,
// KMK -->
isShowingUpdatingMangas: Boolean,
// KMK <--
) {
TwoPanelBox(
modifier = Modifier.padding(paddingValues),
@ -209,7 +271,7 @@ private fun UpcomingScreenLargeImpl(
FastScrollLazyColumn(state = listState) {
items(
items = items,
key = { "upcoming-${it.hashCode()}" },
key = { (if (isShowingUpdatingMangas) "updating" else "upcoming") + it.hashCode() },
contentType = {
when (it) {
is UpcomingUIModel.Header -> "header"
@ -224,6 +286,7 @@ private fun UpcomingScreenLargeImpl(
onClick = { onClickUpcoming(item.manga) },
)
}
is UpcomingUIModel.Header -> {
DateHeading(
date = item.date,

View file

@ -16,6 +16,7 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import mihon.domain.upcoming.interactor.GetUpcomingManga
import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.manga.model.Manga
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@ -25,6 +26,9 @@ import java.time.YearMonth
class UpcomingScreenModel(
private val getUpcomingManga: GetUpcomingManga = Injekt.get(),
) : StateScreenModel<UpcomingScreenModel.State>(State()) {
// KMK -->
private val libraryPreferences: LibraryPreferences = Injekt.get()
// KMK <--
init {
screenModelScope.launch {
@ -39,6 +43,18 @@ class UpcomingScreenModel(
}
}
}
// KMK -->
screenModelScope.launch {
mutableState.update { state ->
val updatingItems = getUpcomingManga.updatingMangas().toUpcomingUIModels()
state.copy(
updatingItems = updatingItems,
updatingEvents = updatingItems.toEvents(),
updatingHeaderIndexes = updatingItems.getHeaderIndexes(),
)
}
}
// KMK <--
}
private fun List<Manga>.toUpcomingUIModels(): ImmutableList<UpcomingUIModel> {
@ -81,10 +97,36 @@ class UpcomingScreenModel(
mutableState.update { it.copy(selectedYearMonth = yearMonth) }
}
// KMK -->
val restriction by lazy { libraryPreferences.autoUpdateMangaRestrictions().get() }
fun showUpdatingMangas() {
mutableState.update { state ->
state.copy(
isShowingUpdatingMangas = true,
)
}
}
fun hideUpdatingMangas() {
mutableState.update { state ->
state.copy(
isShowingUpdatingMangas = false,
)
}
}
// KMK <--
data class State(
val selectedYearMonth: YearMonth = YearMonth.now(),
val items: ImmutableList<UpcomingUIModel> = persistentListOf(),
val events: ImmutableMap<LocalDate, Int> = persistentMapOf(),
val headerIndexes: ImmutableMap<LocalDate, Int> = persistentMapOf(),
// KMK -->
val isShowingUpdatingMangas: Boolean = false,
val updatingItems: ImmutableList<UpcomingUIModel> = persistentListOf(),
val updatingEvents: ImmutableMap<LocalDate, Int> = persistentMapOf(),
val updatingHeaderIndexes: ImmutableMap<LocalDate, Int> = persistentMapOf(),
// KMK <--
)
}

View file

@ -1,13 +1,28 @@
package mihon.domain.upcoming.interactor
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.UpdateStrategy
import kotlinx.coroutines.flow.Flow
import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_HAS_UNREAD
import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_NON_COMPLETED
import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_NON_READ
import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_OUTSIDE_RELEASE_PERIOD
import tachiyomi.domain.manga.interactor.GetLibraryManga
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.repository.MangaRepository
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.time.LocalDate
import java.time.ZoneId
class GetUpcomingManga(
private val mangaRepository: MangaRepository,
) {
// KMK -->
private val libraryPreferences: LibraryPreferences = Injekt.get()
private val getLibraryManga: GetLibraryManga = Injekt.get()
// KMK <--
private val includedStatuses = setOf(
SManga.ONGOING.toLong(),
@ -17,4 +32,50 @@ class GetUpcomingManga(
suspend fun subscribe(): Flow<List<Manga>> {
return mangaRepository.getUpcomingManga(includedStatuses)
}
// KMK -->
suspend fun updatingMangas(): List<Manga> {
val libraryManga = getLibraryManga.await()
val categoriesToUpdate = libraryPreferences.updateCategories().get().map(String::toLong)
val includedManga = if (categoriesToUpdate.isNotEmpty()) {
libraryManga.filter { it.category in categoriesToUpdate }
} else {
libraryManga
}
val categoriesToExclude = libraryPreferences.updateCategoriesExclude().get().map { it.toLong() }
val excludedMangaIds = if (categoriesToExclude.isNotEmpty()) {
libraryManga.filter { it.category in categoriesToExclude }.map { it.manga.id }
} else {
emptyList()
}
val listToUpdate = includedManga
.filterNot { it.manga.id in excludedMangaIds }
val restrictions = libraryPreferences.autoUpdateMangaRestrictions().get()
val today = LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toEpochSecond() * 1000
return listToUpdate
.distinctBy { it.manga.id }
.filter {
when {
it.manga.updateStrategy != UpdateStrategy.ALWAYS_UPDATE -> false
MANGA_NON_COMPLETED in restrictions && it.manga.status.toInt() == SManga.COMPLETED -> false
MANGA_HAS_UNREAD in restrictions && it.unreadCount != 0L -> false
MANGA_NON_READ in restrictions && it.totalChapters > 0L && !it.hasStarted -> false
MANGA_OUTSIDE_RELEASE_PERIOD in restrictions && it.manga.nextUpdate < today -> false
else -> true
}
}
.map { it.manga }
.sortedBy { it.nextUpdate }
}
// KMK <--
}