feat(updates-selection): Enable whole-group selection & Fix range selection in Updates screen (#913)

* fix(updates-screen): Fix range selection in Updates screen

* feat(updates-screen): select Updates whole-group

* improve performance

* simplify onUpdateSelected parameters using UpdateSelectionOptions
This commit is contained in:
Cuong-Tran 2025-05-15 16:31:19 +07:00 committed by GitHub
parent 2f55a0bc16
commit a3abccb34b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 78 additions and 16 deletions

View file

@ -60,7 +60,7 @@ fun UpdateScreen(
onMultiBookmarkClicked: (List<UpdatesItem>, bookmark: Boolean) -> Unit,
onMultiMarkAsReadClicked: (List<UpdatesItem>, read: Boolean) -> Unit,
onMultiDeleteClicked: (List<UpdatesItem>) -> Unit,
onUpdateSelected: (UpdatesItem, Boolean, Boolean, Boolean) -> Unit,
onUpdateSelected: (UpdatesItem, /* KMK --> */ UpdatesScreenModel.UpdateSelectionOptions /* KMK <-- */) -> Unit,
onOpenChapter: (UpdatesItem) -> Unit,
// KMK -->
collapseToggle: (key: String) -> Unit,

View file

@ -54,6 +54,7 @@ import eu.kanade.presentation.util.relativeTimeSpanString
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.ui.updates.UpdatesItem
import eu.kanade.tachiyomi.ui.updates.UpdatesScreenModel.UpdateSelectionOptions
import eu.kanade.tachiyomi.ui.updates.groupByDateAndManga
import mihon.feature.upcoming.DateHeading
import tachiyomi.domain.updates.model.UpdatesWithRelations
@ -91,7 +92,7 @@ internal fun LazyListScope.updatesUiItems(
// SY -->
preserveReadingPosition: Boolean,
// SY <--
onUpdateSelected: (UpdatesItem, Boolean, Boolean, Boolean) -> Unit,
onUpdateSelected: (UpdatesItem, /* KMK --> */ UpdateSelectionOptions /* KMK <-- */) -> Unit,
onClickCover: (UpdatesItem) -> Unit,
onClickUpdate: (UpdatesItem) -> Unit,
onDownloadChapter: (List<UpdatesItem>, ChapterDownloadAction) -> Unit,
@ -124,10 +125,10 @@ internal fun LazyListScope.updatesUiItems(
}
is UpdatesUiModel.Item -> {
val updatesItem = item.item
// KMK -->
val isLeader = item is UpdatesUiModel.Leader
val isExpanded = expandedState.contains(updatesItem.update.groupByDateAndManga())
// KMK -->
AnimatedVisibility(
visible = isLeader || isExpanded,
enter = fadeIn() + expandVertically(),
@ -153,11 +154,33 @@ internal fun LazyListScope.updatesUiItems(
)
},
onLongClick = {
onUpdateSelected(updatesItem, !updatesItem.selected, true, true)
onUpdateSelected(
updatesItem,
// KMK -->
UpdateSelectionOptions(
selected = !updatesItem.selected,
userSelected = true,
fromLongPress = true,
isGroup = isLeader && item.isExpandable,
isExpanded = isExpanded,
),
// KMK <--
)
},
onClick = {
when {
selectionMode -> onUpdateSelected(updatesItem, !updatesItem.selected, true, false)
selectionMode -> onUpdateSelected(
updatesItem,
// KMK -->
UpdateSelectionOptions(
selected = !updatesItem.selected,
userSelected = true,
fromLongPress = false,
isGroup = isLeader && item.isExpandable,
isExpanded = isExpanded,
),
// KMK <--
)
else -> onClickUpdate(updatesItem)
}
},

View file

@ -94,10 +94,20 @@ class UpdatesScreenModel(
_events.send(Event.InternalError)
}
.collectLatest { updates ->
mutableState.update {
it.copy(
mutableState.update { state ->
state.copy(
isLoading = false,
items = updates.toUpdateItems(),
items = updates.toUpdateItems()
// KMK -->
.groupBy { it.update.mangaId }
.values
.flatMap { mangaChapters ->
val (unread, read) = mangaChapters.partition { !it.update.read }
unread.sortedBy { it.update.dateFetch } +
read.sortedByDescending { it.update.dateFetch }
}
.toPersistentList(),
// KMK <--
)
}
}
@ -282,12 +292,26 @@ class UpdatesScreenModel(
setDialog(Dialog.DeleteConfirmation(updatesItem))
}
// KMK -->
/** Bundles all of the boolean flags for updateselection into one type */
data class UpdateSelectionOptions(
val selected: Boolean,
val userSelected: Boolean = false,
val fromLongPress: Boolean = false,
val isGroup: Boolean = false,
val isExpanded: Boolean = false,
)
// KMK <--
fun toggleSelection(
item: UpdatesItem,
selected: Boolean,
userSelected: Boolean = false,
fromLongPress: Boolean = false,
// KMK -->
selectionOptions: UpdateSelectionOptions,
// KMK <--
) {
// KMK -->
val (selected, userSelected, fromLongPress, isGroup, isExpanded) = selectionOptions
// KMK <--
mutableState.update { state ->
val newItems = state.items.toMutableList().apply {
val selectedIndex = indexOfFirst { it.update.chapterId == item.update.chapterId }
@ -300,6 +324,25 @@ class UpdatesScreenModel(
set(selectedIndex, selectedItem.copy(selected = selected))
selectedChapterIds.addOrRemove(item.update.chapterId, selected)
// KMK -->
if (isGroup && !isExpanded) {
val selectedItemDate = selectedItem.update.dateFetch.toLocalDate()
val zone = java.time.ZoneId.systemDefault()
val dayStartMillis = selectedItemDate.atStartOfDay(zone).toInstant().toEpochMilli()
val dayEndMillis = selectedItemDate.plusDays(1).atStartOfDay(zone).toInstant().toEpochMilli()
state.items.mapIndexed { index, item -> index to item }
.filter {
it.second.update.mangaId == selectedItem.update.mangaId &&
it.second.update.dateFetch in dayStartMillis..<dayEndMillis
}
.forEach { (index, item) ->
set(index, item.copy(selected = selected))
selectedChapterIds.addOrRemove(item.update.chapterId, selected)
}
}
// KMK <--
if (selected && userSelected && fromLongPress) {
if (firstSelection) {
selectedPositions[0] = selectedIndex
@ -413,13 +456,9 @@ class UpdatesScreenModel(
.groupBy { it.update.mangaId }
.values
.flatMap { mangaChapters ->
val (unread, read) = mangaChapters.partition { !it.update.read }
val sortedChapters = unread.sortedBy { it.update.dateFetch } +
read.sortedByDescending { it.update.dateFetch }
val isExpandable = mangaChapters.size > 1
var lastMangaId = -1L
sortedChapters.map { chapter ->
mangaChapters.map { chapter ->
if (chapter.update.mangaId != lastMangaId) {
lastMangaId = chapter.update.mangaId
UpdatesUiModel.Leader(chapter, isExpandable)