feat(library): Added a button to refresh selected manga entries (#1038)

* feat: add refresh button to MangaBottomActionMenu

* feat: add refresh functionality for selected manga in LibraryTab

* feat: add refreshSelectedManga function to update selected mangas

* feat: add targeted update functionality for specific manga IDs

* Update CHANGELOG.md

* Refactoring & cleanup

* Move it to overflow menu

* Optimize update logic

* Fix index & improve code

* Remove parentheses

---------

Co-authored-by: Cuong-Tran <cuongtran.tm@gmail.com>
This commit is contained in:
manav_chaudhary 2025-07-17 23:28:15 +05:30 committed by GitHub
parent 13a09d970a
commit 8d65991759
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 77 additions and 1 deletions

View file

@ -66,6 +66,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import tachiyomi.i18n.MR
import tachiyomi.i18n.kmk.KMR
import tachiyomi.i18n.sy.SYMR
import tachiyomi.presentation.core.i18n.stringResource
import kotlin.time.Duration.Companion.seconds
@ -266,8 +267,9 @@ fun LibraryBottomActionMenu(
// SY <--
// KMK -->
onClickMerge: (() -> Unit)?,
// KMK <--
modifier: Modifier = Modifier,
onClickRefreshSelected: (() -> Unit)? = null,
// KMK <--
) {
AnimatedVisibility(
visible = visible,
@ -302,6 +304,7 @@ fun LibraryBottomActionMenu(
// KMK -->
onClickMigrate != null ||
onClickMerge != null ||
onClickRefreshSelected != null ||
// KMK <--
onClickCollectRecommendations != null
val configuration = LocalConfiguration.current
@ -399,6 +402,14 @@ fun LibraryBottomActionMenu(
offset = DpOffset((-10).dp, 0.dp),
// KMK <--
) {
// KMK -->
if (onClickRefreshSelected != null) {
DropdownMenuItem(
text = { Text(stringResource(KMR.strings.action_update)) },
onClick = onClickRefreshSelected,
)
}
// KMK <--
if (!isTabletUi) {
if (onClickMigrate != null) {
DropdownMenuItem(

View file

@ -230,6 +230,27 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
val groupLibraryUpdateType = libraryPreferences.groupLibraryUpdateType().get()
// SY <--
// KMK -->
// Check if specific manga IDs are provided for targeted update
val targetMangaIds = inputData.getLongArray(KEY_MANGA_IDS)?.toSet()
if (targetMangaIds != null) {
// Filter to only the specified manga IDs
mangaToUpdate = libraryManga
.filter {
it.manga.id in targetMangaIds &&
when {
// Apply update restrictions even for targeted updates
it.manga.updateStrategy == UpdateStrategy.ONLY_FETCH_ONCE && it.totalChapters > 0L -> false
// Skip other restrictions for targeted updates to allow forced refresh
else -> true
}
}
notifier.showQueueSizeWarningNotificationIfNeeded(mangaToUpdate)
return
}
// KMK <--
val listToUpdate = if (categoryId != -1L) {
libraryManga.filter { it.category == categoryId }
} else if (
@ -724,6 +745,13 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
const val KEY_GROUP_EXTRA = "group_extra"
// SY <--
// KMK -->
/**
* Key for specific manga IDs to update.
*/
private const val KEY_MANGA_IDS = "manga_ids"
// KMK <--
fun cancelAllWorks(context: Context) {
context.workManager.cancelAllWorkByTag(TAG)
}
@ -785,6 +813,9 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
group: Int = LibraryGroup.BY_DEFAULT,
groupExtra: String? = null,
// SY <--
// KMK -->
mangaIds: List<Long>? = null,
// KMK <--
): Boolean {
val wm = context.workManager
// Check if the LibraryUpdateJob is already running
@ -800,6 +831,9 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
KEY_GROUP to group,
KEY_GROUP_EXTRA to groupExtra,
// SY <--
// KMK -->
KEY_MANGA_IDS to mangaIds?.toLongArray(),
// KMK <--
)
val syncPreferences: SyncPreferences = Injekt.get()

View file

@ -30,6 +30,7 @@ import eu.kanade.presentation.manga.DownloadAction
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.download.DownloadCache
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
import eu.kanade.tachiyomi.data.track.TrackStatus
import eu.kanade.tachiyomi.data.track.TrackerManager
import eu.kanade.tachiyomi.source.Source
@ -932,6 +933,20 @@ class LibraryScreenModel(
}
// SY <--
// KMK -->
/**
* Update Selected Mangas
*/
fun refreshSelectedManga(): Boolean {
val mangaIds = state.value.selection.map { it.manga.id }
return LibraryUpdateJob.startNow(
context = preferences.context,
mangaIds = mangaIds,
target = LibraryUpdateJob.Target.CHAPTERS,
)
}
// KMK <--
/**
* Marks mangas' chapters read status.
*/

View file

@ -261,6 +261,20 @@ data object LibraryTab : Tab {
context.toast(SYMR.strings.no_valid_entry)
}
},
onClickRefreshSelected = {
val started = screenModel.refreshSelectedManga()
scope.launch {
val msgRes = if (started) {
KMR.strings.updating
} else {
MR.strings.update_already_running
}
snackbarHostState.showSnackbar(context.stringResource(msgRes))
if (started) {
screenModel.clearSelection()
}
}
},
// KMK <--
)
},

View file

@ -15,6 +15,7 @@
<string name="action_show_hidden_categories">Show hidden categories</string>
<string name="action_panorama_cover">Panorama cover</string>
<string name="action_open_folder">Open folder</string>
<string name="action_update">Update</string>
<!-- Reader -->
<!-- Reader Actions -->
<string name="action_copy_to_clipboard_first_page">Copy first page to clipboard</string>
@ -78,6 +79,7 @@
<string name="action_display_source_badge">Source icon</string>
<string name="action_display_language_icon">Use language icon</string>
<string name="pref_library_filter_categories_details">Entries in excluded categories will not be shown even if they are also in included categories.</string>
<string name="updating">Updating</string>
<!-- Extension section -->
<string name="extensions_page_need_refresh">Refresh extension page to update list.</string>
<string name="extensions_page_more">More extensions...</string>