From 754a0d7cf3c08af21667fc33cc5e5ee32056eff1 Mon Sep 17 00:00:00 2001 From: Acclorite <2ma0bhpk@anonaddy.com> Date: Sat, 5 Jul 2025 16:34:00 +0300 Subject: [PATCH] feat: multiple checkpoints --- .../presentation/reader/ReaderEvent.kt | 5 +- .../presentation/reader/ReaderModel.kt | 94 +++--- .../presentation/reader/ReaderScreen.kt | 2 +- .../presentation/reader/ReaderState.kt | 2 +- .../book_story/ui/reader/ReaderBottomBar.kt | 269 +++++++++++++----- .../ui/reader/ReaderBottomBarSlider.kt | 47 --- .../reader/ReaderBottomBarSliderIndicator.kt | 37 --- .../book_story/ui/reader/ReaderContent.kt | 4 +- .../book_story/ui/reader/ReaderScaffold.kt | 4 +- 9 files changed, 273 insertions(+), 191 deletions(-) delete mode 100644 app/src/main/java/ua/acclorite/book_story/ui/reader/ReaderBottomBarSlider.kt delete mode 100644 app/src/main/java/ua/acclorite/book_story/ui/reader/ReaderBottomBarSliderIndicator.kt diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderEvent.kt b/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderEvent.kt index 8b328bd1..3e52dfd8 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderEvent.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderEvent.kt @@ -9,6 +9,7 @@ package ua.acclorite.book_story.presentation.reader import androidx.activity.ComponentActivity import androidx.compose.runtime.Immutable import ua.acclorite.book_story.domain.model.reader.ReaderText.Chapter +import ua.acclorite.book_story.presentation.reader.model.Checkpoint @Immutable sealed class ReaderEvent { @@ -39,7 +40,9 @@ sealed class ReaderEvent { val progress: Float ) : ReaderEvent() - data object OnRestoreCheckpoint : ReaderEvent() + data class OnRestoreCheckpoint( + val checkpoint: Checkpoint + ) : ReaderEvent() data class OnLeave( val activity: ComponentActivity, diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderModel.kt b/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderModel.kt index 78018686..a4fe9fd2 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderModel.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderModel.kt @@ -143,7 +143,7 @@ class ReaderModel @Inject constructor( } is ReaderEvent.OnMenuVisibility -> { - launch { + launch(Dispatchers.Default) { if (_state.value.lockMenu) return@launch yield() @@ -152,21 +152,31 @@ class ReaderModel @Inject constructor( show = event.show || !event.fullscreenMode, activity = event.activity ) + + val checkpoints = _state.value.checkpoints.toMutableList() + if (event.saveCheckpoint && event.show) { + checkpoints.removeIf { + it.index == _state.value.listState.firstVisibleItemIndex + } + checkpoints.add( + Checkpoint( + _state.value.listState.firstVisibleItemIndex, + _state.value.listState.firstVisibleItemScrollOffset + ) + ) + } + _state.update { it.copy( showMenu = event.show, - checkpoint = _state.value.listState.run { - if (!event.show || !event.saveCheckpoint) return@run it.checkpoint - - Checkpoint(firstVisibleItemIndex, firstVisibleItemScrollOffset) - } + checkpoints = checkpoints ) } } } is ReaderEvent.OnChangeProgress -> { - launch(Dispatchers.IO) { + launch(Dispatchers.Default) { _state.update { it.copy( book = it.book.copy( @@ -185,7 +195,7 @@ class ReaderModel @Inject constructor( } is ReaderEvent.OnScrollToChapter -> { - launch { + launch(Dispatchers.Default) { _state.value.apply { val chapterIndex = text.indexOf(event.chapter).takeIf { it != -1 } if (chapterIndex == null) { @@ -207,40 +217,52 @@ class ReaderModel @Inject constructor( is ReaderEvent.OnScroll -> { scrollJob?.cancel() - scrollJob = launch { + scrollJob = launch(Dispatchers.Main) { delay(300) - yield() val scrollTo = (_state.value.text.lastIndex * event.progress).roundToInt() - _state.value.listState.requestScrollToItem(scrollTo) updateChapter(scrollTo) - } - } + try { + _state.value.listState.requestScrollToItem(scrollTo) + } catch (_: Exception) { - is ReaderEvent.OnRestoreCheckpoint -> { - launch { - _state.value.apply { - listState.requestScrollToItem( - checkpoint.index, - checkpoint.offset - ) - - updateChapter(checkpoint.index) - onEvent( - ReaderEvent.OnChangeProgress( - progress = calculateProgress(checkpoint.index), - firstVisibleItemIndex = checkpoint.index, - firstVisibleItemOffset = checkpoint.offset, - ) - ) } } } - is ReaderEvent.OnLeave -> { - launch { - yield() + is ReaderEvent.OnRestoreCheckpoint -> { + launch(Dispatchers.Default) { + _state.update { + val checkpoints = it.checkpoints.toMutableList() + if (checkpoints.size > 1) checkpoints.remove(event.checkpoint) + it.copy( + checkpoints = checkpoints + ) + } + + try { + _state.value.listState.requestScrollToItem( + event.checkpoint.index, + event.checkpoint.offset + ) + } catch (_: Exception) { + + } + + updateChapter(event.checkpoint.index) + onEvent( + ReaderEvent.OnChangeProgress( + progress = calculateProgress(event.checkpoint.index), + firstVisibleItemIndex = event.checkpoint.index, + firstVisibleItemOffset = event.checkpoint.offset, + ) + ) + } + } + + is ReaderEvent.OnLeave -> { + launch(Dispatchers.Main) { _state.update { it.copy( lockMenu = true @@ -282,7 +304,7 @@ class ReaderModel @Inject constructor( } is ReaderEvent.OnOpenTranslator -> { - launch { + launch(Dispatchers.Default) { val translatorIntent = Intent() val browserIntent = Intent() @@ -324,7 +346,7 @@ class ReaderModel @Inject constructor( } is ReaderEvent.OnOpenShareApp -> { - launch { + launch(Dispatchers.Default) { val shareIntent = Intent() shareIntent.action = Intent.ACTION_SEND @@ -356,7 +378,7 @@ class ReaderModel @Inject constructor( } is ReaderEvent.OnOpenWebBrowser -> { - launch { + launch(Dispatchers.Default) { val browserIntent = Intent() browserIntent.action = Intent.ACTION_WEB_SEARCH @@ -382,7 +404,7 @@ class ReaderModel @Inject constructor( } is ReaderEvent.OnOpenDictionary -> { - launch { + launch(Dispatchers.Default) { val dictionaryIntent = Intent() val browserIntent = Intent() diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderScreen.kt b/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderScreen.kt index ab5914cb..4ebfb372 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderScreen.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderScreen.kt @@ -416,7 +416,7 @@ data class ReaderScreen(val bookId: Int) : Screen, Parcelable { currentChapterProgress = state.value.currentChapterProgress, isLoading = state.value.isLoading, errorMessage = state.value.errorMessage, - checkpoint = state.value.checkpoint, + checkpoints = state.value.checkpoints, showMenu = state.value.showMenu, lockMenu = state.value.lockMenu, contentPadding = contentPadding, diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderState.kt b/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderState.kt index b5e80644..5ecab585 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderState.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderState.kt @@ -29,7 +29,7 @@ data class ReaderState( val isLoading: Boolean = true, val showMenu: Boolean = false, - val checkpoint: Checkpoint = Checkpoint(0, 0), + val checkpoints: List = emptyList(), val lockMenu: Boolean = false, val bottomSheet: BottomSheet? = null, diff --git a/app/src/main/java/ua/acclorite/book_story/ui/reader/ReaderBottomBar.kt b/app/src/main/java/ua/acclorite/book_story/ui/reader/ReaderBottomBar.kt index 615db865..40c5733e 100644 --- a/app/src/main/java/ua/acclorite/book_story/ui/reader/ReaderBottomBar.kt +++ b/app/src/main/java/ua/acclorite/book_story/ui/reader/ReaderBottomBar.kt @@ -6,27 +6,37 @@ package ua.acclorite.book_story.ui.reader +import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.background +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.interaction.collectIsDraggedAsState import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.ArrowForward import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Slider +import androidx.compose.material3.SliderDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import ua.acclorite.book_story.R @@ -48,34 +58,41 @@ fun ReaderBottomBar( text: List, listState: LazyListState, lockMenu: Boolean, - checkpoint: Checkpoint, + checkpoints: List, bottomBarPadding: Dp, restoreCheckpoint: (ReaderEvent.OnRestoreCheckpoint) -> Unit, scroll: (ReaderEvent.OnScroll) -> Unit, changeProgress: (ReaderEvent.OnChangeProgress) -> Unit ) { - val firstVisibleItemIndex = remember { + val currentIndex by remember { derivedStateOf { listState.firstVisibleItemIndex } } - val arrowDirection = remember(checkpoint.index, firstVisibleItemIndex) { - derivedStateOf { - val checkpointIndex = checkpoint.index - val index = firstVisibleItemIndex.value - - when { - checkpointIndex > index -> Direction.END - checkpointIndex < index -> Direction.START - else -> Direction.NEUTRAL + val checkpointsProgress = rememberCheckpointsProgress( + checkpoints = checkpoints, + text = text, + currentIndex = currentIndex + ) + val currentCheckpoint = rememberCurrentCheckpoint( + checkpoints = checkpoints, + currentIndex = currentIndex + ) + val checkpointDirection = rememberCheckpointDirection( + currentCheckpoint = currentCheckpoint, + currentIndex = currentIndex + ) + val restoreCheckpoint = remember(currentCheckpoint) { + { + if (currentCheckpoint != null) { + restoreCheckpoint( + ReaderEvent.OnRestoreCheckpoint( + currentCheckpoint + ) + ) } } } - val checkpointProgress = remember(checkpoint.index, text.lastIndex) { - derivedStateOf { - (checkpoint.index / text.lastIndex.toFloat()) * 0.987f - } - } Column( Modifier @@ -83,12 +100,11 @@ fun ReaderBottomBar( .background(MaterialTheme.colorScheme.readerBarsColor) .noRippleClickable(onClick = {}) .navigationBarsPadding() - .padding(horizontal = 18.dp), + .padding(horizontal = 18.dp) + .padding(top = 16.dp, bottom = 8.dp + bottomBarPadding), horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center + verticalArrangement = Arrangement.spacedBy(6.dp) ) { - Spacer(Modifier.height(16.dp)) - StyledText( text = progress, style = MaterialTheme.typography.titleLarge.copy( @@ -96,57 +112,182 @@ fun ReaderBottomBar( ) ) - Spacer(Modifier.height(6.dp)) - Row(verticalAlignment = Alignment.CenterVertically) { - HorizontalExpandingTransition( - visible = arrowDirection.value == Direction.START, - startDirection = true - ) { - IconButton( - icon = Icons.AutoMirrored.Default.ArrowBack, - contentDescription = R.string.checkpoint_back_content_desc, - modifier = Modifier.size(24.dp), - color = MaterialTheme.colorScheme.secondary, - disableOnClick = false - ) { - restoreCheckpoint(ReaderEvent.OnRestoreCheckpoint) + ReaderBottomBarCheckpoints( + checkpointDirection = checkpointDirection, + startArrow = { + IconButton( + icon = Icons.AutoMirrored.Default.ArrowBack, + contentDescription = R.string.checkpoint_back_content_desc, + modifier = Modifier.size(24.dp), + color = MaterialTheme.colorScheme.secondary, + disableOnClick = false + ) { + restoreCheckpoint() + } + }, + endArrow = { + IconButton( + icon = Icons.AutoMirrored.Default.ArrowForward, + contentDescription = R.string.checkpoint_forward_content_desc, + modifier = Modifier.size(24.dp), + color = MaterialTheme.colorScheme.secondary, + disableOnClick = false + ) { + restoreCheckpoint() + } + }, + slider = { + ReaderBottomBarSlider( + book = book, + lockMenu = lockMenu, + listState = listState, + scroll = scroll, + changeProgress = changeProgress + ) + }, + indicator = { + ReaderBottomBarCheckpointsIndicator( + checkpointsProgress = checkpointsProgress + ) } - } + ) + } + } +} - Box( - modifier = Modifier.weight(1f), - contentAlignment = Alignment.CenterStart - ) { - ReaderBottomBarSlider( - book = book, - lockMenu = lockMenu, - listState = listState, - scroll = scroll, - changeProgress = changeProgress - ) +@Composable +private fun rememberCheckpointsProgress( + checkpoints: List, + text: List, + currentIndex: Int +): List { + return remember(checkpoints, text, currentIndex) { + if (text.isEmpty()) return@remember emptyList() - if (arrowDirection.value != Direction.NEUTRAL) { - ReaderBottomBarSliderIndicator(progress = checkpointProgress.value) - } - } + val progressScale = 0.987f + checkpoints.mapNotNull { checkpoint -> + if (checkpoint.index == currentIndex) return@mapNotNull null + (checkpoint.index / text.lastIndex.toFloat()) * progressScale + } + } +} - HorizontalExpandingTransition( - visible = arrowDirection.value == Direction.END, - startDirection = false - ) { - IconButton( - icon = Icons.AutoMirrored.Default.ArrowForward, - contentDescription = R.string.checkpoint_forward_content_desc, - modifier = Modifier.size(24.dp), - color = MaterialTheme.colorScheme.secondary, - disableOnClick = false - ) { - restoreCheckpoint(ReaderEvent.OnRestoreCheckpoint) - } +@Composable +private fun rememberCurrentCheckpoint( + checkpoints: List, + currentIndex: Int +): Checkpoint? { + return remember(checkpoints, currentIndex) { + checkpoints.lastOrNull { checkpoint -> + when { + checkpoint.index > currentIndex -> true + checkpoint.index < currentIndex -> true + else -> false } } + } +} - Spacer(Modifier.height(8.dp + bottomBarPadding)) +@Composable +private fun rememberCheckpointDirection( + currentCheckpoint: Checkpoint?, + currentIndex: Int +): Direction { + return remember(currentCheckpoint, currentIndex) { + when { + currentCheckpoint == null -> Direction.NEUTRAL + currentCheckpoint.index > currentIndex -> Direction.END + currentCheckpoint.index < currentIndex -> Direction.START + else -> Direction.NEUTRAL + } + } +} + +@Composable +private fun RowScope.ReaderBottomBarCheckpoints( + checkpointDirection: Direction, + startArrow: @Composable () -> Unit, + endArrow: @Composable () -> Unit, + slider: @Composable () -> Unit, + indicator: @Composable () -> Unit +) { + HorizontalExpandingTransition( + visible = checkpointDirection == Direction.START, + startDirection = true + ) { + startArrow() + } + + Box( + modifier = Modifier.weight(1f), + contentAlignment = Alignment.CenterStart + ) { + slider() + indicator() + } + + HorizontalExpandingTransition( + visible = checkpointDirection == Direction.END, + startDirection = false + ) { + endArrow() + } +} + +@Composable +private fun ReaderBottomBarSlider( + book: Book, + lockMenu: Boolean, + listState: LazyListState, + scroll: (ReaderEvent.OnScroll) -> Unit, + changeProgress: (ReaderEvent.OnChangeProgress) -> Unit +) { + val interactionSource = remember { MutableInteractionSource() } + val isDragging = interactionSource.collectIsDraggedAsState() + val animatedProgress = animateFloatAsState(book.progress) + val progress = remember(isDragging.value, book.progress, animatedProgress.value) { + if (isDragging.value) book.progress + else animatedProgress.value + } + + Slider( + value = progress, + enabled = !lockMenu, + onValueChange = { + if (listState.layoutInfo.totalItemsCount > 0) { + scroll(ReaderEvent.OnScroll(it)) + changeProgress( + ReaderEvent.OnChangeProgress( + progress = it, + firstVisibleItemIndex = listState.firstVisibleItemIndex, + firstVisibleItemOffset = 0 + ) + ) + } + }, + interactionSource = interactionSource, + colors = SliderDefaults.colors( + inactiveTrackColor = MaterialTheme.colorScheme.secondary.copy(0.15f), + disabledActiveTrackColor = MaterialTheme.colorScheme.primary, + disabledThumbColor = MaterialTheme.colorScheme.primary, + disabledInactiveTrackColor = MaterialTheme.colorScheme.secondary.copy(0.15f), + ) + ) +} + +@Composable +private fun ReaderBottomBarCheckpointsIndicator(checkpointsProgress: List) { + checkpointsProgress.forEach { checkpoint -> + Row { + Spacer(modifier = Modifier.fillMaxWidth(checkpoint)) + Box( + Modifier + .width(4.dp) + .height(16.dp) + .clip(RoundedCornerShape(0.5.dp)) + .background(MaterialTheme.colorScheme.onPrimary.copy(0.7f)) + ) + } } } \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/ui/reader/ReaderBottomBarSlider.kt b/app/src/main/java/ua/acclorite/book_story/ui/reader/ReaderBottomBarSlider.kt deleted file mode 100644 index 3095a99f..00000000 --- a/app/src/main/java/ua/acclorite/book_story/ui/reader/ReaderBottomBarSlider.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Book's Story — free and open-source Material You eBook reader. - * Copyright (C) 2024-2025 Acclorite - * SPDX-License-Identifier: GPL-3.0-only - */ - -package ua.acclorite.book_story.ui.reader - -import androidx.compose.foundation.lazy.LazyListState -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Slider -import androidx.compose.material3.SliderDefaults -import androidx.compose.runtime.Composable -import ua.acclorite.book_story.domain.model.library.Book -import ua.acclorite.book_story.presentation.reader.ReaderEvent - -@Composable -fun ReaderBottomBarSlider( - book: Book, - lockMenu: Boolean, - listState: LazyListState, - scroll: (ReaderEvent.OnScroll) -> Unit, - changeProgress: (ReaderEvent.OnChangeProgress) -> Unit -) { - Slider( - value = book.progress, - enabled = !lockMenu, - onValueChange = { - if (listState.layoutInfo.totalItemsCount > 0) { - scroll(ReaderEvent.OnScroll(it)) - changeProgress( - ReaderEvent.OnChangeProgress( - progress = it, - firstVisibleItemIndex = listState.firstVisibleItemIndex, - firstVisibleItemOffset = 0 - ) - ) - } - }, - colors = SliderDefaults.colors( - inactiveTrackColor = MaterialTheme.colorScheme.secondary.copy(0.15f), - disabledActiveTrackColor = MaterialTheme.colorScheme.primary, - disabledThumbColor = MaterialTheme.colorScheme.primary, - disabledInactiveTrackColor = MaterialTheme.colorScheme.secondary.copy(0.15f), - ) - ) -} \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/ui/reader/ReaderBottomBarSliderIndicator.kt b/app/src/main/java/ua/acclorite/book_story/ui/reader/ReaderBottomBarSliderIndicator.kt deleted file mode 100644 index 27504a6c..00000000 --- a/app/src/main/java/ua/acclorite/book_story/ui/reader/ReaderBottomBarSliderIndicator.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Book's Story — free and open-source Material You eBook reader. - * Copyright (C) 2024-2025 Acclorite - * SPDX-License-Identifier: GPL-3.0-only - */ - -package ua.acclorite.book_story.ui.reader - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.unit.dp - -@Composable -fun ReaderBottomBarSliderIndicator(progress: Float) { - Row(Modifier.fillMaxWidth()) { - Spacer( - modifier = Modifier.fillMaxWidth(progress) - ) - Box( - Modifier - .width(4.dp) - .height(16.dp) - .clip(RoundedCornerShape(0.5.dp)) - .background(MaterialTheme.colorScheme.onPrimary.copy(0.6f)) - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/ui/reader/ReaderContent.kt b/app/src/main/java/ua/acclorite/book_story/ui/reader/ReaderContent.kt index 6bf9f108..534b2211 100644 --- a/app/src/main/java/ua/acclorite/book_story/ui/reader/ReaderContent.kt +++ b/app/src/main/java/ua/acclorite/book_story/ui/reader/ReaderContent.kt @@ -49,7 +49,7 @@ fun ReaderContent( currentChapterProgress: Float, isLoading: Boolean, errorMessage: UIText?, - checkpoint: Checkpoint, + checkpoints: List, showMenu: Boolean, lockMenu: Boolean, contentPadding: PaddingValues, @@ -128,7 +128,7 @@ fun ReaderContent( perceptionExpanderThickness = perceptionExpanderThickness, currentChapterProgress = currentChapterProgress, isLoading = isLoading, - checkpoint = checkpoint, + checkpoints = checkpoints, showMenu = showMenu, lockMenu = lockMenu, contentPadding = contentPadding, diff --git a/app/src/main/java/ua/acclorite/book_story/ui/reader/ReaderScaffold.kt b/app/src/main/java/ua/acclorite/book_story/ui/reader/ReaderScaffold.kt index 1791e81c..3dc9eed7 100644 --- a/app/src/main/java/ua/acclorite/book_story/ui/reader/ReaderScaffold.kt +++ b/app/src/main/java/ua/acclorite/book_story/ui/reader/ReaderScaffold.kt @@ -53,7 +53,7 @@ fun ReaderScaffold( perceptionExpanderThickness: Dp, currentChapterProgress: Float, isLoading: Boolean, - checkpoint: Checkpoint, + checkpoints: List, showMenu: Boolean, lockMenu: Boolean, contentPadding: PaddingValues, @@ -150,7 +150,7 @@ fun ReaderScaffold( text = text, listState = listState, lockMenu = lockMenu, - checkpoint = checkpoint, + checkpoints = checkpoints, bottomBarPadding = bottomBarPadding, restoreCheckpoint = restoreCheckpoint, scroll = scroll,