mirror of
https://github.com/mihonapp/mihon.git
synced 2024-12-27 03:18:25 +01:00
MangaScreen: Save selection state (#7560)
This commit is contained in:
parent
473dc688f0
commit
00519e3b93
@ -38,8 +38,6 @@ import androidx.compose.runtime.derivedStateOf
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
|
||||||
import androidx.compose.runtime.toMutableStateList
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||||
@ -106,6 +104,11 @@ fun MangaScreen(
|
|||||||
onMultiMarkAsReadClicked: (List<Chapter>, markAsRead: Boolean) -> Unit,
|
onMultiMarkAsReadClicked: (List<Chapter>, markAsRead: Boolean) -> Unit,
|
||||||
onMarkPreviousAsReadClicked: (Chapter) -> Unit,
|
onMarkPreviousAsReadClicked: (Chapter) -> Unit,
|
||||||
onMultiDeleteClicked: (List<Chapter>) -> Unit,
|
onMultiDeleteClicked: (List<Chapter>) -> Unit,
|
||||||
|
|
||||||
|
// Chapter selection
|
||||||
|
onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit,
|
||||||
|
onAllChapterSelected: (Boolean) -> Unit,
|
||||||
|
onInvertSelection: () -> Unit,
|
||||||
) {
|
) {
|
||||||
if (windowWidthSizeClass == WindowWidthSizeClass.Compact) {
|
if (windowWidthSizeClass == WindowWidthSizeClass.Compact) {
|
||||||
MangaScreenSmallImpl(
|
MangaScreenSmallImpl(
|
||||||
@ -131,6 +134,9 @@ fun MangaScreen(
|
|||||||
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
|
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
|
||||||
onMarkPreviousAsReadClicked = onMarkPreviousAsReadClicked,
|
onMarkPreviousAsReadClicked = onMarkPreviousAsReadClicked,
|
||||||
onMultiDeleteClicked = onMultiDeleteClicked,
|
onMultiDeleteClicked = onMultiDeleteClicked,
|
||||||
|
onChapterSelected = onChapterSelected,
|
||||||
|
onAllChapterSelected = onAllChapterSelected,
|
||||||
|
onInvertSelection = onInvertSelection,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
MangaScreenLargeImpl(
|
MangaScreenLargeImpl(
|
||||||
@ -157,6 +163,9 @@ fun MangaScreen(
|
|||||||
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
|
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
|
||||||
onMarkPreviousAsReadClicked = onMarkPreviousAsReadClicked,
|
onMarkPreviousAsReadClicked = onMarkPreviousAsReadClicked,
|
||||||
onMultiDeleteClicked = onMultiDeleteClicked,
|
onMultiDeleteClicked = onMultiDeleteClicked,
|
||||||
|
onChapterSelected = onChapterSelected,
|
||||||
|
onAllChapterSelected = onAllChapterSelected,
|
||||||
|
onInvertSelection = onInvertSelection,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -191,18 +200,21 @@ private fun MangaScreenSmallImpl(
|
|||||||
onMultiMarkAsReadClicked: (List<Chapter>, markAsRead: Boolean) -> Unit,
|
onMultiMarkAsReadClicked: (List<Chapter>, markAsRead: Boolean) -> Unit,
|
||||||
onMarkPreviousAsReadClicked: (Chapter) -> Unit,
|
onMarkPreviousAsReadClicked: (Chapter) -> Unit,
|
||||||
onMultiDeleteClicked: (List<Chapter>) -> Unit,
|
onMultiDeleteClicked: (List<Chapter>) -> Unit,
|
||||||
|
|
||||||
|
// Chapter selection
|
||||||
|
onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit,
|
||||||
|
onAllChapterSelected: (Boolean) -> Unit,
|
||||||
|
onInvertSelection: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val layoutDirection = LocalLayoutDirection.current
|
val layoutDirection = LocalLayoutDirection.current
|
||||||
val chapterListState = rememberLazyListState()
|
val chapterListState = rememberLazyListState()
|
||||||
|
|
||||||
val insetPadding = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal).asPaddingValues()
|
val insetPadding = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal).asPaddingValues()
|
||||||
val chapters = remember(state) { state.processedChapters.toList() }
|
val chapters = remember(state) { state.processedChapters.toList() }
|
||||||
val selected = remember(chapters) { emptyList<ChapterItem>().toMutableStateList() }
|
|
||||||
val selectedPositions = remember(chapters) { arrayOf(-1, -1) } // first and last selected index in list
|
|
||||||
|
|
||||||
val internalOnBackPressed = {
|
val internalOnBackPressed = {
|
||||||
if (selected.isNotEmpty()) {
|
if (chapters.any { it.selected }) {
|
||||||
selected.clear()
|
onAllChapterSelected(false)
|
||||||
} else {
|
} else {
|
||||||
onBackClicked()
|
onBackClicked()
|
||||||
}
|
}
|
||||||
@ -236,21 +248,14 @@ private fun MangaScreenSmallImpl(
|
|||||||
onDownloadClicked = onDownloadActionClicked,
|
onDownloadClicked = onDownloadActionClicked,
|
||||||
onEditCategoryClicked = onEditCategoryClicked,
|
onEditCategoryClicked = onEditCategoryClicked,
|
||||||
onMigrateClicked = onMigrateClicked,
|
onMigrateClicked = onMigrateClicked,
|
||||||
actionModeCounter = selected.size,
|
actionModeCounter = chapters.count { it.selected },
|
||||||
onSelectAll = {
|
onSelectAll = { onAllChapterSelected(true) },
|
||||||
selected.clear()
|
onInvertSelection = { onInvertSelection() },
|
||||||
selected.addAll(chapters)
|
|
||||||
},
|
|
||||||
onInvertSelection = {
|
|
||||||
val toSelect = chapters - selected
|
|
||||||
selected.clear()
|
|
||||||
selected.addAll(toSelect)
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
bottomBar = {
|
bottomBar = {
|
||||||
SharedMangaBottomActionMenu(
|
SharedMangaBottomActionMenu(
|
||||||
selected = selected,
|
selected = chapters.filter { it.selected },
|
||||||
onMultiBookmarkClicked = onMultiBookmarkClicked,
|
onMultiBookmarkClicked = onMultiBookmarkClicked,
|
||||||
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
|
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
|
||||||
onMarkPreviousAsReadClicked = onMarkPreviousAsReadClicked,
|
onMarkPreviousAsReadClicked = onMarkPreviousAsReadClicked,
|
||||||
@ -262,7 +267,7 @@ private fun MangaScreenSmallImpl(
|
|||||||
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = chapters.any { !it.chapter.read } && selected.isEmpty(),
|
visible = chapters.any { !it.chapter.read } && chapters.none { it.selected },
|
||||||
enter = fadeIn(),
|
enter = fadeIn(),
|
||||||
exit = fadeOut(),
|
exit = fadeOut(),
|
||||||
) {
|
) {
|
||||||
@ -370,10 +375,9 @@ private fun MangaScreenSmallImpl(
|
|||||||
|
|
||||||
sharedChapterItems(
|
sharedChapterItems(
|
||||||
chapters = chapters,
|
chapters = chapters,
|
||||||
selected = selected,
|
|
||||||
selectedPositions = selectedPositions,
|
|
||||||
onChapterClicked = onChapterClicked,
|
onChapterClicked = onChapterClicked,
|
||||||
onDownloadChapter = onDownloadChapter,
|
onDownloadChapter = onDownloadChapter,
|
||||||
|
onChapterSelected = onChapterSelected,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -412,6 +416,11 @@ fun MangaScreenLargeImpl(
|
|||||||
onMultiMarkAsReadClicked: (List<Chapter>, markAsRead: Boolean) -> Unit,
|
onMultiMarkAsReadClicked: (List<Chapter>, markAsRead: Boolean) -> Unit,
|
||||||
onMarkPreviousAsReadClicked: (Chapter) -> Unit,
|
onMarkPreviousAsReadClicked: (Chapter) -> Unit,
|
||||||
onMultiDeleteClicked: (List<Chapter>) -> Unit,
|
onMultiDeleteClicked: (List<Chapter>) -> Unit,
|
||||||
|
|
||||||
|
// Chapter selection
|
||||||
|
onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit,
|
||||||
|
onAllChapterSelected: (Boolean) -> Unit,
|
||||||
|
onInvertSelection: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val layoutDirection = LocalLayoutDirection.current
|
val layoutDirection = LocalLayoutDirection.current
|
||||||
val density = LocalDensity.current
|
val density = LocalDensity.current
|
||||||
@ -436,12 +445,10 @@ fun MangaScreenLargeImpl(
|
|||||||
) {
|
) {
|
||||||
val chapterListState = rememberLazyListState()
|
val chapterListState = rememberLazyListState()
|
||||||
val chapters = remember(state) { state.processedChapters.toList() }
|
val chapters = remember(state) { state.processedChapters.toList() }
|
||||||
val selected = remember(chapters) { emptyList<ChapterItem>().toMutableStateList() }
|
|
||||||
val selectedPositions = remember(chapters) { arrayOf(-1, -1) } // first and last selected index in list
|
|
||||||
|
|
||||||
val internalOnBackPressed = {
|
val internalOnBackPressed = {
|
||||||
if (selected.isNotEmpty()) {
|
if (chapters.any { it.selected }) {
|
||||||
selected.clear()
|
onAllChapterSelected(false)
|
||||||
} else {
|
} else {
|
||||||
onBackClicked()
|
onBackClicked()
|
||||||
}
|
}
|
||||||
@ -454,7 +461,7 @@ fun MangaScreenLargeImpl(
|
|||||||
MangaSmallAppBar(
|
MangaSmallAppBar(
|
||||||
modifier = Modifier.onSizeChanged { onTopBarHeightChanged(it.height) },
|
modifier = Modifier.onSizeChanged { onTopBarHeightChanged(it.height) },
|
||||||
title = state.manga.title,
|
title = state.manga.title,
|
||||||
titleAlphaProvider = { if (selected.isEmpty()) 0f else 1f },
|
titleAlphaProvider = { if (chapters.any { it.selected }) 1f else 0f },
|
||||||
backgroundAlphaProvider = { 1f },
|
backgroundAlphaProvider = { 1f },
|
||||||
incognitoMode = state.isIncognitoMode,
|
incognitoMode = state.isIncognitoMode,
|
||||||
downloadedOnlyMode = state.isDownloadedOnlyMode,
|
downloadedOnlyMode = state.isDownloadedOnlyMode,
|
||||||
@ -463,16 +470,9 @@ fun MangaScreenLargeImpl(
|
|||||||
onDownloadClicked = onDownloadActionClicked,
|
onDownloadClicked = onDownloadActionClicked,
|
||||||
onEditCategoryClicked = onEditCategoryClicked,
|
onEditCategoryClicked = onEditCategoryClicked,
|
||||||
onMigrateClicked = onMigrateClicked,
|
onMigrateClicked = onMigrateClicked,
|
||||||
actionModeCounter = selected.size,
|
actionModeCounter = chapters.count { it.selected },
|
||||||
onSelectAll = {
|
onSelectAll = { onAllChapterSelected(true) },
|
||||||
selected.clear()
|
onInvertSelection = { onInvertSelection() },
|
||||||
selected.addAll(chapters)
|
|
||||||
},
|
|
||||||
onInvertSelection = {
|
|
||||||
val toSelect = chapters - selected
|
|
||||||
selected.clear()
|
|
||||||
selected.addAll(toSelect)
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
bottomBar = {
|
bottomBar = {
|
||||||
@ -481,7 +481,7 @@ fun MangaScreenLargeImpl(
|
|||||||
contentAlignment = Alignment.BottomEnd,
|
contentAlignment = Alignment.BottomEnd,
|
||||||
) {
|
) {
|
||||||
SharedMangaBottomActionMenu(
|
SharedMangaBottomActionMenu(
|
||||||
selected = selected,
|
selected = chapters.filter { it.selected },
|
||||||
onMultiBookmarkClicked = onMultiBookmarkClicked,
|
onMultiBookmarkClicked = onMultiBookmarkClicked,
|
||||||
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
|
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
|
||||||
onMarkPreviousAsReadClicked = onMarkPreviousAsReadClicked,
|
onMarkPreviousAsReadClicked = onMarkPreviousAsReadClicked,
|
||||||
@ -494,7 +494,7 @@ fun MangaScreenLargeImpl(
|
|||||||
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = chapters.any { !it.chapter.read } && selected.isEmpty(),
|
visible = chapters.any { !it.chapter.read } && chapters.none { it.selected },
|
||||||
enter = fadeIn(),
|
enter = fadeIn(),
|
||||||
exit = fadeOut(),
|
exit = fadeOut(),
|
||||||
) {
|
) {
|
||||||
@ -578,10 +578,9 @@ fun MangaScreenLargeImpl(
|
|||||||
|
|
||||||
sharedChapterItems(
|
sharedChapterItems(
|
||||||
chapters = chapters,
|
chapters = chapters,
|
||||||
selected = selected,
|
|
||||||
selectedPositions = selectedPositions,
|
|
||||||
onChapterClicked = onChapterClicked,
|
onChapterClicked = onChapterClicked,
|
||||||
onDownloadChapter = onDownloadChapter,
|
onDownloadChapter = onDownloadChapter,
|
||||||
|
onChapterSelected = onChapterSelected,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -592,7 +591,7 @@ fun MangaScreenLargeImpl(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SharedMangaBottomActionMenu(
|
private fun SharedMangaBottomActionMenu(
|
||||||
selected: SnapshotStateList<ChapterItem>,
|
selected: List<ChapterItem>,
|
||||||
onMultiBookmarkClicked: (List<Chapter>, bookmarked: Boolean) -> Unit,
|
onMultiBookmarkClicked: (List<Chapter>, bookmarked: Boolean) -> Unit,
|
||||||
onMultiMarkAsReadClicked: (List<Chapter>, markAsRead: Boolean) -> Unit,
|
onMultiMarkAsReadClicked: (List<Chapter>, markAsRead: Boolean) -> Unit,
|
||||||
onMarkPreviousAsReadClicked: (Chapter) -> Unit,
|
onMarkPreviousAsReadClicked: (Chapter) -> Unit,
|
||||||
@ -605,33 +604,26 @@ private fun SharedMangaBottomActionMenu(
|
|||||||
modifier = Modifier.fillMaxWidth(fillFraction),
|
modifier = Modifier.fillMaxWidth(fillFraction),
|
||||||
onBookmarkClicked = {
|
onBookmarkClicked = {
|
||||||
onMultiBookmarkClicked.invoke(selected.map { it.chapter }, true)
|
onMultiBookmarkClicked.invoke(selected.map { it.chapter }, true)
|
||||||
selected.clear()
|
|
||||||
}.takeIf { selected.any { !it.chapter.bookmark } },
|
}.takeIf { selected.any { !it.chapter.bookmark } },
|
||||||
onRemoveBookmarkClicked = {
|
onRemoveBookmarkClicked = {
|
||||||
onMultiBookmarkClicked.invoke(selected.map { it.chapter }, false)
|
onMultiBookmarkClicked.invoke(selected.map { it.chapter }, false)
|
||||||
selected.clear()
|
|
||||||
}.takeIf { selected.all { it.chapter.bookmark } },
|
}.takeIf { selected.all { it.chapter.bookmark } },
|
||||||
onMarkAsReadClicked = {
|
onMarkAsReadClicked = {
|
||||||
onMultiMarkAsReadClicked(selected.map { it.chapter }, true)
|
onMultiMarkAsReadClicked(selected.map { it.chapter }, true)
|
||||||
selected.clear()
|
|
||||||
}.takeIf { selected.any { !it.chapter.read } },
|
}.takeIf { selected.any { !it.chapter.read } },
|
||||||
onMarkAsUnreadClicked = {
|
onMarkAsUnreadClicked = {
|
||||||
onMultiMarkAsReadClicked(selected.map { it.chapter }, false)
|
onMultiMarkAsReadClicked(selected.map { it.chapter }, false)
|
||||||
selected.clear()
|
|
||||||
}.takeIf { selected.any { it.chapter.read } },
|
}.takeIf { selected.any { it.chapter.read } },
|
||||||
onMarkPreviousAsReadClicked = {
|
onMarkPreviousAsReadClicked = {
|
||||||
onMarkPreviousAsReadClicked(selected[0].chapter)
|
onMarkPreviousAsReadClicked(selected[0].chapter)
|
||||||
selected.clear()
|
|
||||||
}.takeIf { selected.size == 1 },
|
}.takeIf { selected.size == 1 },
|
||||||
onDownloadClicked = {
|
onDownloadClicked = {
|
||||||
onDownloadChapter!!(selected.toList(), ChapterDownloadAction.START)
|
onDownloadChapter!!(selected.toList(), ChapterDownloadAction.START)
|
||||||
selected.clear()
|
|
||||||
}.takeIf {
|
}.takeIf {
|
||||||
onDownloadChapter != null && selected.any { it.downloadState != Download.State.DOWNLOADED }
|
onDownloadChapter != null && selected.any { it.downloadState != Download.State.DOWNLOADED }
|
||||||
},
|
},
|
||||||
onDeleteClicked = {
|
onDeleteClicked = {
|
||||||
onMultiDeleteClicked(selected.map { it.chapter })
|
onMultiDeleteClicked(selected.map { it.chapter })
|
||||||
selected.clear()
|
|
||||||
}.takeIf {
|
}.takeIf {
|
||||||
onDownloadChapter != null && selected.any { it.downloadState == Download.State.DOWNLOADED }
|
onDownloadChapter != null && selected.any { it.downloadState == Download.State.DOWNLOADED }
|
||||||
},
|
},
|
||||||
@ -640,10 +632,9 @@ private fun SharedMangaBottomActionMenu(
|
|||||||
|
|
||||||
private fun LazyListScope.sharedChapterItems(
|
private fun LazyListScope.sharedChapterItems(
|
||||||
chapters: List<ChapterItem>,
|
chapters: List<ChapterItem>,
|
||||||
selected: SnapshotStateList<ChapterItem>,
|
|
||||||
selectedPositions: Array<Int>,
|
|
||||||
onChapterClicked: (Chapter) -> Unit,
|
onChapterClicked: (Chapter) -> Unit,
|
||||||
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?,
|
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?,
|
||||||
|
onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit,
|
||||||
) {
|
) {
|
||||||
items(
|
items(
|
||||||
items = chapters,
|
items = chapters,
|
||||||
@ -658,24 +649,18 @@ private fun LazyListScope.sharedChapterItems(
|
|||||||
scanlator = chapterItem.chapter.scanlator.takeIf { !it.isNullOrBlank() },
|
scanlator = chapterItem.chapter.scanlator.takeIf { !it.isNullOrBlank() },
|
||||||
read = chapterItem.chapter.read,
|
read = chapterItem.chapter.read,
|
||||||
bookmark = chapterItem.chapter.bookmark,
|
bookmark = chapterItem.chapter.bookmark,
|
||||||
selected = selected.contains(chapterItem),
|
selected = chapterItem.selected,
|
||||||
downloadStateProvider = { chapterItem.downloadState },
|
downloadStateProvider = { chapterItem.downloadState },
|
||||||
downloadProgressProvider = { chapterItem.downloadProgress },
|
downloadProgressProvider = { chapterItem.downloadProgress },
|
||||||
onLongClick = {
|
onLongClick = {
|
||||||
val dispatched = onChapterItemLongClick(
|
onChapterSelected(chapterItem, !chapterItem.selected, true, true)
|
||||||
chapterItem = chapterItem,
|
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||||
selected = selected,
|
|
||||||
chapters = chapters,
|
|
||||||
selectedPositions = selectedPositions,
|
|
||||||
)
|
|
||||||
if (dispatched) haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
|
||||||
},
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
onChapterItemClick(
|
onChapterItemClick(
|
||||||
chapterItem = chapterItem,
|
chapterItem = chapterItem,
|
||||||
selected = selected,
|
|
||||||
chapters = chapters,
|
chapters = chapters,
|
||||||
selectedPositions = selectedPositions,
|
onToggleSelection = { onChapterSelected(chapterItem, !chapterItem.selected, true, false) },
|
||||||
onChapterClicked = onChapterClicked,
|
onChapterClicked = onChapterClicked,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -686,72 +671,15 @@ private fun LazyListScope.sharedChapterItems(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onChapterItemLongClick(
|
|
||||||
chapterItem: ChapterItem,
|
|
||||||
selected: MutableList<ChapterItem>,
|
|
||||||
chapters: List<ChapterItem>,
|
|
||||||
selectedPositions: Array<Int>,
|
|
||||||
): Boolean {
|
|
||||||
if (!selected.contains(chapterItem)) {
|
|
||||||
val selectedIndex = chapters.indexOf(chapterItem)
|
|
||||||
if (selected.isEmpty()) {
|
|
||||||
selected.add(chapterItem)
|
|
||||||
selectedPositions[0] = selectedIndex
|
|
||||||
selectedPositions[1] = selectedIndex
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to select the items in-between when possible
|
|
||||||
val range: IntRange
|
|
||||||
if (selectedIndex < selectedPositions[0]) {
|
|
||||||
range = selectedIndex until selectedPositions[0]
|
|
||||||
selectedPositions[0] = selectedIndex
|
|
||||||
} else if (selectedIndex > selectedPositions[1]) {
|
|
||||||
range = (selectedPositions[1] + 1)..selectedIndex
|
|
||||||
selectedPositions[1] = selectedIndex
|
|
||||||
} else {
|
|
||||||
// Just select itself
|
|
||||||
range = selectedIndex..selectedIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
range.forEach {
|
|
||||||
val toAdd = chapters[it]
|
|
||||||
if (!selected.contains(toAdd)) {
|
|
||||||
selected.add(toAdd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onChapterItemClick(
|
private fun onChapterItemClick(
|
||||||
chapterItem: ChapterItem,
|
chapterItem: ChapterItem,
|
||||||
selected: MutableList<ChapterItem>,
|
|
||||||
chapters: List<ChapterItem>,
|
chapters: List<ChapterItem>,
|
||||||
selectedPositions: Array<Int>,
|
onToggleSelection: (Boolean) -> Unit,
|
||||||
onChapterClicked: (Chapter) -> Unit,
|
onChapterClicked: (Chapter) -> Unit,
|
||||||
) {
|
) {
|
||||||
val selectedIndex = chapters.indexOf(chapterItem)
|
|
||||||
when {
|
when {
|
||||||
selected.contains(chapterItem) -> {
|
chapterItem.selected -> onToggleSelection(false)
|
||||||
val removedIndex = chapters.indexOf(chapterItem)
|
chapters.any { it.selected } -> onToggleSelection(true)
|
||||||
selected.remove(chapterItem)
|
|
||||||
|
|
||||||
if (removedIndex == selectedPositions[0]) {
|
|
||||||
selectedPositions[0] = chapters.indexOfFirst { selected.contains(it) }
|
|
||||||
} else if (removedIndex == selectedPositions[1]) {
|
|
||||||
selectedPositions[1] = chapters.indexOfLast { selected.contains(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
selected.isNotEmpty() -> {
|
|
||||||
if (selectedIndex < selectedPositions[0]) {
|
|
||||||
selectedPositions[0] = selectedIndex
|
|
||||||
} else if (selectedIndex > selectedPositions[1]) {
|
|
||||||
selectedPositions[1] = selectedIndex
|
|
||||||
}
|
|
||||||
selected.add(chapterItem)
|
|
||||||
}
|
|
||||||
else -> onChapterClicked(chapterItem.chapter)
|
else -> onChapterClicked(chapterItem.chapter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,6 +142,9 @@ class MangaController :
|
|||||||
onMultiMarkAsReadClicked = presenter::markChaptersRead,
|
onMultiMarkAsReadClicked = presenter::markChaptersRead,
|
||||||
onMarkPreviousAsReadClicked = presenter::markPreviousChapterRead,
|
onMarkPreviousAsReadClicked = presenter::markPreviousChapterRead,
|
||||||
onMultiDeleteClicked = this::deleteChaptersWithConfirmation,
|
onMultiDeleteClicked = this::deleteChaptersWithConfirmation,
|
||||||
|
onChapterSelected = presenter::toggleSelection,
|
||||||
|
onAllChapterSelected = presenter::toggleAllSelection,
|
||||||
|
onInvertSelection = presenter::invertSelection,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
|
@ -140,6 +140,8 @@ class MangaPresenter(
|
|||||||
val processedChapters: Sequence<ChapterItem>?
|
val processedChapters: Sequence<ChapterItem>?
|
||||||
get() = successState?.processedChapters
|
get() = successState?.processedChapters
|
||||||
|
|
||||||
|
private val selectedPositions: Array<Int> = arrayOf(-1, -1) // first and last selected index in list
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to update the UI state only if it's currently in success state
|
* Helper function to update the UI state only if it's currently in success state
|
||||||
*/
|
*/
|
||||||
@ -583,6 +585,7 @@ class MangaPresenter(
|
|||||||
values = chapters.toTypedArray(),
|
values = chapters.toTypedArray(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
toggleAllSelection(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -592,6 +595,7 @@ class MangaPresenter(
|
|||||||
fun downloadChapters(chapters: List<DomainChapter>) {
|
fun downloadChapters(chapters: List<DomainChapter>) {
|
||||||
val manga = successState?.manga ?: return
|
val manga = successState?.manga ?: return
|
||||||
downloadManager.downloadChapters(manga, chapters.map { it.toDbChapter() })
|
downloadManager.downloadChapters(manga, chapters.map { it.toDbChapter() })
|
||||||
|
toggleAllSelection(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -605,6 +609,7 @@ class MangaPresenter(
|
|||||||
.map { ChapterUpdate(id = it.id, bookmark = bookmarked) }
|
.map { ChapterUpdate(id = it.id, bookmark = bookmarked) }
|
||||||
.let { updateChapter.awaitAll(it) }
|
.let { updateChapter.awaitAll(it) }
|
||||||
}
|
}
|
||||||
|
toggleAllSelection(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -627,12 +632,16 @@ class MangaPresenter(
|
|||||||
deletedChapters.forEach {
|
deletedChapters.forEach {
|
||||||
val index = indexOf(it)
|
val index = indexOf(it)
|
||||||
val toAdd = removeAt(index)
|
val toAdd = removeAt(index)
|
||||||
.copy(downloadState = Download.State.NOT_DOWNLOADED, downloadProgress = 0)
|
.copy(
|
||||||
|
downloadState = Download.State.NOT_DOWNLOADED,
|
||||||
|
downloadProgress = 0,
|
||||||
|
)
|
||||||
add(index, toAdd)
|
add(index, toAdd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
successState.copy(chapters = newChapters)
|
successState.copy(chapters = newChapters)
|
||||||
}
|
}
|
||||||
|
toggleAllSelection(false)
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
logcat(LogPriority.ERROR, e)
|
logcat(LogPriority.ERROR, e)
|
||||||
}
|
}
|
||||||
@ -725,6 +734,89 @@ class MangaPresenter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun toggleSelection(
|
||||||
|
item: ChapterItem,
|
||||||
|
selected: Boolean,
|
||||||
|
userSelected: Boolean = false,
|
||||||
|
fromLongPress: Boolean = false,
|
||||||
|
) {
|
||||||
|
updateSuccessState { successState ->
|
||||||
|
val modifiedIndex = successState.chapters.indexOfFirst { it.chapter.id == item.chapter.id }
|
||||||
|
if (modifiedIndex < 0) return@updateSuccessState successState
|
||||||
|
|
||||||
|
val oldItem = successState.chapters[modifiedIndex]
|
||||||
|
if ((oldItem.selected && selected) || (!oldItem.selected && !selected)) return@updateSuccessState successState
|
||||||
|
|
||||||
|
val newChapters = successState.chapters.toMutableList().apply {
|
||||||
|
val firstSelection = none { it.selected }
|
||||||
|
var newItem = removeAt(modifiedIndex)
|
||||||
|
add(modifiedIndex, newItem.copy(selected = selected))
|
||||||
|
|
||||||
|
if (selected && userSelected && fromLongPress) {
|
||||||
|
if (firstSelection) {
|
||||||
|
selectedPositions[0] = modifiedIndex
|
||||||
|
selectedPositions[1] = modifiedIndex
|
||||||
|
} else {
|
||||||
|
// Try to select the items in-between when possible
|
||||||
|
val range: IntRange
|
||||||
|
if (modifiedIndex < selectedPositions[0]) {
|
||||||
|
range = modifiedIndex + 1 until selectedPositions[0]
|
||||||
|
selectedPositions[0] = modifiedIndex
|
||||||
|
} else if (modifiedIndex > selectedPositions[1]) {
|
||||||
|
range = (selectedPositions[1] + 1) until modifiedIndex
|
||||||
|
selectedPositions[1] = modifiedIndex
|
||||||
|
} else {
|
||||||
|
// Just select itself
|
||||||
|
range = IntRange.EMPTY
|
||||||
|
}
|
||||||
|
|
||||||
|
range.forEach {
|
||||||
|
newItem = removeAt(it)
|
||||||
|
add(it, newItem.copy(selected = true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (userSelected && !fromLongPress) {
|
||||||
|
if (!selected) {
|
||||||
|
if (modifiedIndex == selectedPositions[0]) {
|
||||||
|
selectedPositions[0] = indexOfFirst { it.selected }
|
||||||
|
} else if (modifiedIndex == selectedPositions[1]) {
|
||||||
|
selectedPositions[1] = indexOfLast { it.selected }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (modifiedIndex < selectedPositions[0]) {
|
||||||
|
selectedPositions[0] = modifiedIndex
|
||||||
|
} else if (modifiedIndex > selectedPositions[1]) {
|
||||||
|
selectedPositions[1] = modifiedIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
successState.copy(chapters = newChapters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toggleAllSelection(selected: Boolean) {
|
||||||
|
updateSuccessState { successState ->
|
||||||
|
val newChapters = successState.chapters.map {
|
||||||
|
it.copy(selected = selected)
|
||||||
|
}
|
||||||
|
selectedPositions[0] = -1
|
||||||
|
selectedPositions[1] = -1
|
||||||
|
successState.copy(chapters = newChapters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun invertSelection() {
|
||||||
|
updateSuccessState { successState ->
|
||||||
|
val newChapters = successState.chapters.map {
|
||||||
|
it.copy(selected = !it.selected)
|
||||||
|
}
|
||||||
|
selectedPositions[0] = -1
|
||||||
|
selectedPositions[1] = -1
|
||||||
|
successState.copy(chapters = newChapters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Chapters list - end
|
// Chapters list - end
|
||||||
|
|
||||||
// Track sheet - start
|
// Track sheet - start
|
||||||
@ -962,6 +1054,8 @@ data class ChapterItem(
|
|||||||
val chapterTitleString: String,
|
val chapterTitleString: String,
|
||||||
val dateUploadString: String?,
|
val dateUploadString: String?,
|
||||||
val readProgressString: String?,
|
val readProgressString: String?,
|
||||||
|
|
||||||
|
val selected: Boolean = false,
|
||||||
) {
|
) {
|
||||||
val isDownloaded = downloadState == Download.State.DOWNLOADED
|
val isDownloaded = downloadState == Download.State.DOWNLOADED
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user