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