Change fetch interval action to show days until next expected update

This commit is contained in:
arkon 2024-01-05 17:08:39 -05:00
parent e0a0942015
commit 32bed9b041
7 changed files with 102 additions and 61 deletions

View file

@ -78,12 +78,13 @@ import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.isScrolledToEnd
import tachiyomi.presentation.core.util.isScrollingUp
import tachiyomi.source.local.isLocal
import java.time.Instant
@Composable
fun MangaScreen(
state: MangaScreenModel.State.Success,
snackbarHostState: SnackbarHostState,
fetchInterval: Int?,
nextUpdate: Instant?,
isTabletUi: Boolean,
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
@ -138,7 +139,7 @@ fun MangaScreen(
MangaScreenSmallImpl(
state = state,
snackbarHostState = snackbarHostState,
fetchInterval = fetchInterval,
nextUpdate = nextUpdate,
chapterSwipeStartAction = chapterSwipeStartAction,
chapterSwipeEndAction = chapterSwipeEndAction,
onBackClicked = onBackClicked,
@ -175,7 +176,7 @@ fun MangaScreen(
snackbarHostState = snackbarHostState,
chapterSwipeStartAction = chapterSwipeStartAction,
chapterSwipeEndAction = chapterSwipeEndAction,
fetchInterval = fetchInterval,
nextUpdate = nextUpdate,
onBackClicked = onBackClicked,
onChapterClicked = onChapterClicked,
onDownloadChapter = onDownloadChapter,
@ -211,7 +212,7 @@ fun MangaScreen(
private fun MangaScreenSmallImpl(
state: MangaScreenModel.State.Success,
snackbarHostState: SnackbarHostState,
fetchInterval: Int?,
nextUpdate: Instant?,
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
onBackClicked: () -> Unit,
@ -402,7 +403,7 @@ private fun MangaScreenSmallImpl(
MangaActionRow(
favorite = state.manga.favorite,
trackingCount = state.trackingCount,
fetchInterval = fetchInterval,
nextUpdate = nextUpdate,
isUserIntervalMode = state.manga.fetchInterval < 0,
onAddToLibraryClicked = onAddToLibraryClicked,
onWebViewClicked = onWebViewClicked,
@ -462,7 +463,7 @@ private fun MangaScreenSmallImpl(
fun MangaScreenLargeImpl(
state: MangaScreenModel.State.Success,
snackbarHostState: SnackbarHostState,
fetchInterval: Int?,
nextUpdate: Instant?,
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
onBackClicked: () -> Unit,
@ -641,7 +642,7 @@ fun MangaScreenLargeImpl(
MangaActionRow(
favorite = state.manga.favorite,
trackingCount = state.trackingCount,
fetchInterval = fetchInterval,
nextUpdate = nextUpdate,
isUserIntervalMode = state.manga.fetchInterval < 0,
onAddToLibraryClicked = onAddToLibraryClicked,
onWebViewClicked = onWebViewClicked,

View file

@ -2,8 +2,11 @@ package eu.kanade.presentation.manga.components
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
@ -20,6 +23,7 @@ import kotlinx.collections.immutable.toImmutableList
import tachiyomi.domain.manga.interactor.FetchInterval
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.WheelTextPicker
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.pluralStringResource
import tachiyomi.presentation.core.i18n.stringResource
import java.time.Instant
@ -59,57 +63,71 @@ fun DeleteChaptersDialog(
@Composable
fun SetIntervalDialog(
interval: Int,
nextUpdate: Long,
nextUpdate: Instant?,
onDismissRequest: () -> Unit,
onValueChanged: (Int) -> Unit,
onValueChanged: ((Int) -> Unit)? = null,
) {
var selectedInterval by rememberSaveable { mutableIntStateOf(if (interval < 0) -interval else 0) }
val nextUpdateDays = remember(nextUpdate) {
val now = Instant.now()
val nextUpdateInstant = Instant.ofEpochMilli(nextUpdate)
now.until(nextUpdateInstant, ChronoUnit.DAYS)
return@remember if (nextUpdate != null) {
val now = Instant.now()
now.until(nextUpdate, ChronoUnit.DAYS).toInt()
} else {
null
}
}
// TODO: selecting "1" then doesn't allow for future changes unless defaulting first?
AlertDialog(
onDismissRequest = onDismissRequest,
title = { Text(stringResource(MR.strings.manga_modify_calculated_interval_title)) },
title = { Text(stringResource(MR.strings.pref_library_update_smart_update)) },
text = {
Column {
if (nextUpdateDays >= 0) {
if (nextUpdateDays != null && nextUpdateDays >= 0) {
Text(
stringResource(
MR.strings.manga_interval_expected_update,
pluralStringResource(
MR.plurals.day,
count = nextUpdateDays.toInt(),
count = nextUpdateDays,
nextUpdateDays,
),
pluralStringResource(
MR.plurals.day,
count = interval,
interval,
),
),
)
Spacer(Modifier.height(MaterialTheme.padding.small))
}
BoxWithConstraints(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
val size = DpSize(width = maxWidth / 2, height = 128.dp)
val items = (0..FetchInterval.MAX_INTERVAL)
.map {
if (it == 0) {
stringResource(MR.strings.label_default)
} else {
it.toString()
if (onValueChanged != null) {
Text(stringResource(MR.strings.manga_interval_custom_amount))
BoxWithConstraints(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
val size = DpSize(width = maxWidth / 2, height = 128.dp)
val items = (0..FetchInterval.MAX_INTERVAL)
.map {
if (it == 0) {
stringResource(MR.strings.label_default)
} else {
it.toString()
}
}
}
.toImmutableList()
WheelTextPicker(
items = items,
size = size,
startIndex = selectedInterval,
onSelectionChanged = { selectedInterval = it },
)
.toImmutableList()
WheelTextPicker(
items = items,
size = size,
startIndex = selectedInterval,
onSelectionChanged = { selectedInterval = it },
)
}
}
}
},
@ -120,7 +138,7 @@ fun SetIntervalDialog(
},
confirmButton = {
TextButton(onClick = {
onValueChanged(selectedInterval)
onValueChanged?.invoke(selectedInterval)
onDismissRequest()
}) {
Text(text = stringResource(MR.strings.action_ok))

View file

@ -86,7 +86,8 @@ import tachiyomi.presentation.core.i18n.pluralStringResource
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.clickableNoIndication
import tachiyomi.presentation.core.util.secondaryItemAlpha
import kotlin.math.absoluteValue
import java.time.Instant
import java.time.temporal.ChronoUnit
import kotlin.math.roundToInt
private val whitespaceLineRegex = Regex("[\\r\\n]{2,}", setOf(RegexOption.MULTILINE))
@ -165,7 +166,7 @@ fun MangaInfoBox(
fun MangaActionRow(
favorite: Boolean,
trackingCount: Int,
fetchInterval: Int?,
nextUpdate: Instant?,
isUserIntervalMode: Boolean,
onAddToLibraryClicked: () -> Unit,
onWebViewClicked: (() -> Unit)?,
@ -177,6 +178,16 @@ fun MangaActionRow(
) {
val defaultActionButtonColor = MaterialTheme.colorScheme.onSurface.copy(alpha = .38f)
// TODO: show something better when using custom interval
val nextUpdateDays = remember(nextUpdate) {
return@remember if (nextUpdate != null) {
val now = Instant.now()
now.until(nextUpdate, ChronoUnit.DAYS).toInt()
} else {
null
}
}
Row(modifier = modifier.padding(start = 16.dp, top = 8.dp, end = 16.dp)) {
MangaActionButton(
title = if (favorite) {
@ -189,18 +200,20 @@ fun MangaActionRow(
onClick = onAddToLibraryClicked,
onLongClick = onEditCategory,
)
if (onEditIntervalClicked != null && fetchInterval != null) {
MangaActionButton(
title = pluralStringResource(
MangaActionButton(
title = if (nextUpdateDays != null) {
pluralStringResource(
MR.plurals.day,
count = fetchInterval.absoluteValue,
fetchInterval.absoluteValue,
),
icon = Icons.Default.HourglassEmpty,
color = if (isUserIntervalMode) MaterialTheme.colorScheme.primary else defaultActionButtonColor,
onClick = onEditIntervalClicked,
)
}
count = nextUpdateDays,
nextUpdateDays,
)
} else {
stringResource(MR.strings.not_applicable)
},
icon = Icons.Default.HourglassEmpty,
color = if (isUserIntervalMode) MaterialTheme.colorScheme.primary else defaultActionButtonColor,
onClick = { onEditIntervalClicked?.invoke() },
)
MangaActionButton(
title = if (trackingCount == 0) {
stringResource(MR.strings.manga_tracking_tab)

View file

@ -198,7 +198,7 @@ object SettingsLibraryScreen : SearchableSettings {
),
Preference.PreferenceItem.MultiSelectListPreference(
pref = libraryPreferences.autoUpdateMangaRestrictions(),
title = stringResource(MR.strings.pref_library_update_manga_restriction),
title = stringResource(MR.strings.pref_library_update_smart_update),
entries = persistentMapOf(
MANGA_HAS_UNREAD to stringResource(MR.strings.pref_update_only_completely_read),
MANGA_NON_READ to stringResource(MR.strings.pref_update_only_started),

View file

@ -104,7 +104,7 @@ class MangaScreen(
MangaScreen(
state = successState,
snackbarHostState = screenModel.snackbarHostState,
fetchInterval = successState.manga.fetchInterval,
nextUpdate = successState.manga.expectedNextUpdate,
isTabletUi = isTabletUi(),
chapterSwipeStartAction = screenModel.chapterSwipeStartAction,
chapterSwipeEndAction = screenModel.chapterSwipeEndAction,
@ -146,7 +146,7 @@ class MangaScreen(
onDownloadActionClicked = screenModel::runDownloadAction.takeIf { !successState.source.isLocalOrStub() },
onEditCategoryClicked = screenModel::showChangeCategoryDialog.takeIf { successState.manga.favorite },
onEditFetchIntervalClicked = screenModel::showSetFetchIntervalDialog.takeIf {
screenModel.isUpdateIntervalEnabled && successState.manga.favorite
successState.manga.favorite
},
onMigrateClicked = {
navigator.push(MigrateSearchScreen(successState.manga.id))
@ -243,9 +243,10 @@ class MangaScreen(
is MangaScreenModel.Dialog.SetFetchInterval -> {
SetIntervalDialog(
interval = dialog.manga.fetchInterval,
nextUpdate = dialog.manga.nextUpdate,
nextUpdate = dialog.manga.expectedNextUpdate,
onDismissRequest = onDismissRequest,
onValueChanged = { screenModel.setFetchInterval(dialog.manga, it) },
onValueChanged = { interval: Int -> screenModel.setFetchInterval(dialog.manga, interval) }
.takeIf { screenModel.isUpdateIntervalEnabled },
)
}
}

View file

@ -1,8 +1,10 @@
package tachiyomi.domain.manga.model
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.UpdateStrategy
import tachiyomi.core.preference.TriState
import java.io.Serializable
import java.time.Instant
data class Manga(
val id: Long,
@ -29,6 +31,11 @@ data class Manga(
val favoriteModifiedAt: Long?,
) : Serializable {
val expectedNextUpdate: Instant?
get() = nextUpdate
.takeIf { status != SManga.COMPLETED.toLong() }
?.let { Instant.ofEpochMilli(it) }
val sorting: Long
get() = chapterFlags and CHAPTER_SORTING_MASK

View file

@ -275,12 +275,12 @@
<string name="charging">When charging</string>
<string name="restrictions">Restrictions: %s</string>
<string name="pref_library_update_manga_restriction">Skip updating entries</string>
<string name="pref_update_only_completely_read">With unread chapter(s)</string>
<string name="pref_update_only_non_completed">With \"Completed\" status</string>
<string name="pref_update_only_started">That haven\'t been started</string>
<string name="pref_library_update_smart_update">Smart update</string>
<string name="pref_update_only_completely_read">Skip entries with unread chapter(s)</string>
<string name="pref_update_only_non_completed">Skip entries with \"Completed\" status</string>
<string name="pref_update_only_started">Skip unstarted entries</string>
<string name="pref_update_only_in_release_period">Predict next release time</string>
<string name="pref_library_update_show_tab_badge">Show unread count on Updates icon</string>
<string name="pref_update_only_in_release_period">Outside expected release period</string>
<string name="pref_library_update_refresh_metadata">Automatically refresh metadata</string>
<string name="pref_library_update_refresh_metadata_summary">Check for new cover and details when updating library</string>
@ -668,9 +668,10 @@
<string name="display_mode_chapter">Chapter %1$s</string>
<string name="manga_display_interval_title">Estimate every</string>
<string name="manga_display_modified_interval_title">Set to update every</string>
<string name="manga_interval_header">Next update</string>
<!-- "... around 2 days" -->
<string name="manga_interval_expected_update">Next update expected in around %s</string>
<string name="manga_modify_calculated_interval_title">Customize interval</string>
<string name="manga_interval_expected_update">Next update expected in around %1$s, checking around every %2$s</string>
<string name="manga_interval_custom_amount">Custom update frequency:</string>
<string name="chapter_downloading_progress">Downloading (%1$d/%2$d)</string>
<string name="chapter_error">Error</string>
<string name="chapter_paused">Paused</string>