🚀 Progress bar in Reader
* Added compact progress bar that always shows current progress Resolves: #148
This commit is contained in:
parent
8c2de20e88
commit
499e5dae7f
17 changed files with 231 additions and 78 deletions
|
|
@ -9,7 +9,6 @@ import android.view.ActionMode
|
|||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
|
|
@ -18,7 +17,6 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Rect
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalTextToolbar
|
||||
|
|
@ -295,7 +293,7 @@ fun SelectionContainer(
|
|||
CompositionLocalProvider(
|
||||
LocalTextToolbar provides selectionToolbar
|
||||
) {
|
||||
SelectionContainer(Modifier.fillMaxSize()) {
|
||||
SelectionContainer {
|
||||
content(isToolbarHidden.value)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ object DataStoreConstants {
|
|||
val IMAGES_ALIGNMENT = stringPreferencesKey("images_alignment")
|
||||
val IMAGES_WIDTH = doublePreferencesKey("images_width")
|
||||
val IMAGES_COLOR_EFFECTS = stringPreferencesKey("images_color_effects")
|
||||
val PROGRESS_BAR = booleanPreferencesKey("progress_bar")
|
||||
|
||||
// Browse settings
|
||||
val BROWSE_FILES_STRUCTURE = stringPreferencesKey("browse_files_structure")
|
||||
|
|
|
|||
|
|
@ -28,10 +28,8 @@ import ua.acclorite.book_story.R
|
|||
import ua.acclorite.book_story.domain.library.book.Book
|
||||
import ua.acclorite.book_story.domain.reader.Checkpoint
|
||||
import ua.acclorite.book_story.domain.reader.ReaderText
|
||||
import ua.acclorite.book_story.domain.reader.ReaderText.Chapter
|
||||
import ua.acclorite.book_story.domain.util.Direction
|
||||
import ua.acclorite.book_story.presentation.core.components.common.IconButton
|
||||
import ua.acclorite.book_story.presentation.core.util.calculateProgress
|
||||
import ua.acclorite.book_story.presentation.core.util.noRippleClickable
|
||||
import ua.acclorite.book_story.ui.reader.ReaderEvent
|
||||
import ua.acclorite.book_story.ui.theme.Colors
|
||||
|
|
@ -40,34 +38,16 @@ import ua.acclorite.book_story.ui.theme.HorizontalExpandingTransition
|
|||
@Composable
|
||||
fun ReaderBottomBar(
|
||||
book: Book,
|
||||
progress: String,
|
||||
text: List<ReaderText>,
|
||||
listState: LazyListState,
|
||||
lockMenu: Boolean,
|
||||
currentChapter: Chapter?,
|
||||
currentChapterProgress: Float,
|
||||
checkpoint: Checkpoint,
|
||||
bottomBarPadding: Dp,
|
||||
restoreCheckpoint: (ReaderEvent.OnRestoreCheckpoint) -> Unit,
|
||||
scroll: (ReaderEvent.OnScroll) -> Unit,
|
||||
changeProgress: (ReaderEvent.OnChangeProgress) -> Unit
|
||||
) {
|
||||
val bookProgress = remember(book.progress) {
|
||||
derivedStateOf {
|
||||
"${book.progress.calculateProgress(2)}%"
|
||||
}
|
||||
}
|
||||
val chapterProgress = remember(currentChapter, currentChapterProgress) {
|
||||
derivedStateOf {
|
||||
if (currentChapter == null) return@derivedStateOf ""
|
||||
" (${currentChapterProgress.calculateProgress(2)}%)"
|
||||
}
|
||||
}
|
||||
val progress = remember(bookProgress.value, chapterProgress.value) {
|
||||
derivedStateOf {
|
||||
"${bookProgress.value}${chapterProgress.value}"
|
||||
}
|
||||
}
|
||||
|
||||
val arrowDirection = remember(checkpoint.index, listState.firstVisibleItemIndex) {
|
||||
derivedStateOf {
|
||||
val checkpointIndex = checkpoint.index
|
||||
|
|
@ -99,7 +79,7 @@ fun ReaderBottomBar(
|
|||
Spacer(Modifier.height(16.dp))
|
||||
|
||||
Text(
|
||||
text = progress.value,
|
||||
text = progress,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
style = MaterialTheme.typography.titleLarge
|
||||
)
|
||||
|
|
|
|||
|
|
@ -52,6 +52,8 @@ fun ReaderContent(
|
|||
horizontalGestureSensitivity: Dp,
|
||||
highlightedReading: Boolean,
|
||||
highlightedReadingThickness: FontWeight,
|
||||
progress: String,
|
||||
progressBar: Boolean,
|
||||
paragraphHeight: Dp,
|
||||
sidePadding: Dp,
|
||||
bottomBarPadding: Dp,
|
||||
|
|
@ -122,6 +124,8 @@ fun ReaderContent(
|
|||
horizontalGestureSensitivity = horizontalGestureSensitivity,
|
||||
highlightedReading = highlightedReading,
|
||||
highlightedReadingThickness = highlightedReadingThickness,
|
||||
progress = progress,
|
||||
progressBar = progressBar,
|
||||
paragraphHeight = paragraphHeight,
|
||||
sidePadding = sidePadding,
|
||||
bottomBarPadding = bottomBarPadding,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
package ua.acclorite.book_story.presentation.reader
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.asPaddingValues
|
||||
|
|
@ -27,6 +32,7 @@ import ua.acclorite.book_story.domain.reader.ReaderHorizontalGesture
|
|||
import ua.acclorite.book_story.domain.reader.ReaderImagesAlignment
|
||||
import ua.acclorite.book_story.domain.reader.ReaderText
|
||||
import ua.acclorite.book_story.domain.reader.ReaderTextAlignment
|
||||
import ua.acclorite.book_story.presentation.core.components.common.AnimatedVisibility
|
||||
import ua.acclorite.book_story.presentation.core.components.common.LazyColumnWithScrollbar
|
||||
import ua.acclorite.book_story.presentation.core.components.common.SelectionContainer
|
||||
import ua.acclorite.book_story.presentation.core.components.common.SpacedItem
|
||||
|
|
@ -46,6 +52,8 @@ fun ReaderLayout(
|
|||
horizontalGestureSensitivity: Dp,
|
||||
highlightedReading: Boolean,
|
||||
highlightedReadingThickness: FontWeight,
|
||||
progress: String,
|
||||
progressBar: Boolean,
|
||||
paragraphHeight: Dp,
|
||||
sidePadding: Dp,
|
||||
backgroundColor: Color,
|
||||
|
|
@ -116,10 +124,8 @@ fun ReaderLayout(
|
|||
)
|
||||
}
|
||||
) { toolbarHidden ->
|
||||
LazyColumnWithScrollbar(
|
||||
state = listState,
|
||||
enableScrollbar = false,
|
||||
modifier = Modifier
|
||||
Column(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.background(backgroundColor)
|
||||
.then(
|
||||
|
|
@ -146,7 +152,13 @@ fun ReaderLayout(
|
|||
horizontalGestureScroll = horizontalGestureScroll,
|
||||
horizontalGestureSensitivity = horizontalGestureSensitivity,
|
||||
isLoading = isLoading
|
||||
),
|
||||
)
|
||||
) {
|
||||
LazyColumnWithScrollbar(
|
||||
state = listState,
|
||||
enableScrollbar = false,
|
||||
parentModifier = Modifier.weight(1f),
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentPadding = PaddingValues(
|
||||
top = (WindowInsets.displayCutout.asPaddingValues()
|
||||
.calculateTopPadding() + paragraphHeight)
|
||||
|
|
@ -199,5 +211,17 @@ fun ReaderLayout(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = !showMenu && progressBar,
|
||||
enter = slideInVertically { it } + expandVertically(),
|
||||
exit = slideOutVertically { it } + shrinkVertically()
|
||||
) {
|
||||
ReaderProgressBar(
|
||||
progress = progress,
|
||||
fontColor = fontColor
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package ua.acclorite.book_story.presentation.reader
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.selection.DisableSelection
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun ReaderProgressBar(
|
||||
progress: String,
|
||||
fontColor: Color
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(14.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
DisableSelection {
|
||||
Text(
|
||||
progress,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = fontColor,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -56,6 +56,8 @@ fun ReaderScaffold(
|
|||
horizontalGestureSensitivity: Dp,
|
||||
highlightedReading: Boolean,
|
||||
highlightedReadingThickness: FontWeight,
|
||||
progress: String,
|
||||
progressBar: Boolean,
|
||||
paragraphHeight: Dp,
|
||||
sidePadding: Dp,
|
||||
bottomBarPadding: Dp,
|
||||
|
|
@ -130,11 +132,10 @@ fun ReaderScaffold(
|
|||
) {
|
||||
ReaderBottomBar(
|
||||
book = book,
|
||||
progress = progress,
|
||||
text = text,
|
||||
listState = listState,
|
||||
lockMenu = lockMenu,
|
||||
currentChapter = currentChapter,
|
||||
currentChapterProgress = currentChapterProgress,
|
||||
checkpoint = checkpoint,
|
||||
bottomBarPadding = bottomBarPadding,
|
||||
restoreCheckpoint = restoreCheckpoint,
|
||||
|
|
@ -154,6 +155,8 @@ fun ReaderScaffold(
|
|||
horizontalGestureSensitivity = horizontalGestureSensitivity,
|
||||
highlightedReading = highlightedReading,
|
||||
highlightedReadingThickness = highlightedReadingThickness,
|
||||
progress = progress,
|
||||
progressBar = progressBar,
|
||||
paragraphHeight = paragraphHeight,
|
||||
sidePadding = sidePadding,
|
||||
backgroundColor = backgroundColor,
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import ua.acclorite.book_story.presentation.settings.reader.font.FontSubcategory
|
|||
import ua.acclorite.book_story.presentation.settings.reader.images.ImagesSubcategory
|
||||
import ua.acclorite.book_story.presentation.settings.reader.misc.MiscSubcategory
|
||||
import ua.acclorite.book_story.presentation.settings.reader.padding.PaddingSubcategory
|
||||
import ua.acclorite.book_story.presentation.settings.reader.progress.ProgressSubcategory
|
||||
import ua.acclorite.book_story.presentation.settings.reader.reading_mode.ReadingModeSubcategory
|
||||
import ua.acclorite.book_story.presentation.settings.reader.reading_speed.ReadingSpeedSubcategory
|
||||
import ua.acclorite.book_story.presentation.settings.reader.system.SystemSubcategory
|
||||
|
|
@ -128,6 +129,9 @@ fun ReaderSettingsBottomSheet(
|
|||
ChaptersSubcategory(
|
||||
titleColor = { MaterialTheme.colorScheme.onSurface }
|
||||
)
|
||||
ProgressSubcategory(
|
||||
titleColor = { MaterialTheme.colorScheme.onSurface }
|
||||
)
|
||||
TranslatorSubcategory(
|
||||
titleColor = { MaterialTheme.colorScheme.onSurface },
|
||||
showDivider = false
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import ua.acclorite.book_story.presentation.settings.reader.font.FontSubcategory
|
|||
import ua.acclorite.book_story.presentation.settings.reader.images.ImagesSubcategory
|
||||
import ua.acclorite.book_story.presentation.settings.reader.misc.MiscSubcategory
|
||||
import ua.acclorite.book_story.presentation.settings.reader.padding.PaddingSubcategory
|
||||
import ua.acclorite.book_story.presentation.settings.reader.progress.ProgressSubcategory
|
||||
import ua.acclorite.book_story.presentation.settings.reader.reading_mode.ReadingModeSubcategory
|
||||
import ua.acclorite.book_story.presentation.settings.reader.reading_speed.ReadingSpeedSubcategory
|
||||
import ua.acclorite.book_story.presentation.settings.reader.system.SystemSubcategory
|
||||
|
|
@ -44,6 +45,9 @@ fun LazyListScope.ReaderSettingsCategory(
|
|||
ReadingSpeedSubcategory(
|
||||
titleColor = titleColor
|
||||
)
|
||||
ProgressSubcategory(
|
||||
titleColor = titleColor
|
||||
)
|
||||
TranslatorSubcategory(
|
||||
titleColor = titleColor
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
@file:Suppress("FunctionName")
|
||||
|
||||
package ua.acclorite.book_story.presentation.settings.reader.progress
|
||||
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import ua.acclorite.book_story.R
|
||||
import ua.acclorite.book_story.presentation.settings.components.SettingsSubcategory
|
||||
import ua.acclorite.book_story.presentation.settings.reader.progress.components.ProgressBarOption
|
||||
|
||||
fun LazyListScope.ProgressSubcategory(
|
||||
titleColor: @Composable () -> Color = { MaterialTheme.colorScheme.primary },
|
||||
title: @Composable () -> String = { stringResource(id = R.string.progress_reader_settings) },
|
||||
showTitle: Boolean = true,
|
||||
showDivider: Boolean = true
|
||||
) {
|
||||
SettingsSubcategory(
|
||||
titleColor = titleColor,
|
||||
title = title,
|
||||
showTitle = showTitle,
|
||||
showDivider = showDivider,
|
||||
) {
|
||||
item {
|
||||
ProgressBarOption()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package ua.acclorite.book_story.presentation.settings.reader.progress.components
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import ua.acclorite.book_story.R
|
||||
import ua.acclorite.book_story.presentation.core.components.settings.SwitchWithTitle
|
||||
import ua.acclorite.book_story.ui.main.MainEvent
|
||||
import ua.acclorite.book_story.ui.main.MainModel
|
||||
|
||||
@Composable
|
||||
fun ProgressBarOption() {
|
||||
val mainModel = hiltViewModel<MainModel>()
|
||||
val state = mainModel.state.collectAsStateWithLifecycle()
|
||||
|
||||
SwitchWithTitle(
|
||||
selected = state.value.progressBar,
|
||||
title = stringResource(id = R.string.progress_bar_option),
|
||||
onClick = {
|
||||
mainModel.onEvent(
|
||||
MainEvent.OnChangeProgressBar(
|
||||
!state.value.progressBar
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -55,4 +55,5 @@ sealed class MainEvent {
|
|||
data class OnChangeImagesAlignment(val value: String) : MainEvent()
|
||||
data class OnChangeImagesWidth(val value: Float) : MainEvent()
|
||||
data class OnChangeImagesColorEffects(val value: String) : MainEvent()
|
||||
data class OnChangeProgressBar(val value: Boolean) : MainEvent()
|
||||
}
|
||||
|
|
@ -464,6 +464,14 @@ class MainModel @Inject constructor(
|
|||
it.copy(imagesColorEffects = this.toColorEffects())
|
||||
}
|
||||
)
|
||||
|
||||
is MainEvent.OnChangeProgressBar -> handleDatastoreUpdate(
|
||||
key = DataStoreConstants.PROGRESS_BAR,
|
||||
value = event.value,
|
||||
updateState = {
|
||||
it.copy(progressBar = this)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ data class MainState(
|
|||
val imagesAlignment: ReaderImagesAlignment = provideDefaultValue { ReaderImagesAlignment.START },
|
||||
val imagesWidth: Float = provideDefaultValue { 0.8f },
|
||||
val imagesColorEffects: ReaderColorEffects = provideDefaultValue { ReaderColorEffects.OFF },
|
||||
val progressBar: Boolean = provideDefaultValue { false },
|
||||
|
||||
// Browse Settings
|
||||
val browseFilesStructure: BrowseFilesStructure = provideDefaultValue {
|
||||
|
|
@ -339,6 +340,10 @@ data class MainState(
|
|||
imagesColorEffects = provideValue(
|
||||
IMAGES_COLOR_EFFECTS, convert = { toColorEffects() }
|
||||
) { imagesColorEffects },
|
||||
|
||||
progressBar = provideValue(
|
||||
PROGRESS_BAR
|
||||
) { progressBar },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import ua.acclorite.book_story.domain.reader.ReaderTextAlignment
|
|||
import ua.acclorite.book_story.presentation.core.constants.Constants
|
||||
import ua.acclorite.book_story.presentation.core.constants.provideFonts
|
||||
import ua.acclorite.book_story.presentation.core.util.LocalActivity
|
||||
import ua.acclorite.book_story.presentation.core.util.calculateProgress
|
||||
import ua.acclorite.book_story.presentation.core.util.setBrightness
|
||||
import ua.acclorite.book_story.presentation.navigator.LocalNavigator
|
||||
import ua.acclorite.book_story.presentation.reader.ReaderContent
|
||||
|
|
@ -280,6 +281,26 @@ data class ReaderScreen(val bookId: Int) : Screen, Parcelable {
|
|||
(mainState.value.bottomBarPadding * 4f).dp
|
||||
}
|
||||
|
||||
val bookProgress = remember(state.value.book.progress) {
|
||||
derivedStateOf {
|
||||
"${state.value.book.progress.calculateProgress(2)}%"
|
||||
}
|
||||
}
|
||||
val chapterProgress = remember(
|
||||
state.value.currentChapter,
|
||||
state.value.currentChapterProgress
|
||||
) {
|
||||
derivedStateOf {
|
||||
if (state.value.currentChapter == null) return@derivedStateOf ""
|
||||
" (${state.value.currentChapterProgress.calculateProgress(2)}%)"
|
||||
}
|
||||
}
|
||||
val progress = remember(bookProgress.value, chapterProgress.value) {
|
||||
derivedStateOf {
|
||||
"${bookProgress.value}${chapterProgress.value}"
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
screenModel.init(
|
||||
bookId = bookId,
|
||||
|
|
@ -368,6 +389,8 @@ data class ReaderScreen(val bookId: Int) : Screen, Parcelable {
|
|||
horizontalGestureSensitivity = horizontalGestureSensitivity,
|
||||
highlightedReading = mainState.value.highlightedReading,
|
||||
highlightedReadingThickness = highlightedReadingThickness,
|
||||
progress = progress.value,
|
||||
progressBar = mainState.value.progressBar,
|
||||
paragraphHeight = paragraphHeight,
|
||||
sidePadding = sidePadding,
|
||||
bottomBarPadding = bottomBarPadding,
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@
|
|||
<string name="system_reader_settings">Система</string>
|
||||
<string name="chapters_reader_settings">Розділи</string>
|
||||
<string name="images_reader_settings">Зображення</string>
|
||||
<string name="progress_reader_settings">Прогрес</string>
|
||||
<string name="theme_appearance_settings">Налаштування теми</string>
|
||||
<string name="colors_appearance_settings">Кольори</string>
|
||||
<string name="general_browse_settings">Головні</string>
|
||||
|
|
@ -203,6 +204,7 @@
|
|||
<string name="images_alignment_option">Розташування</string>
|
||||
<string name="images_width_option">Розмір</string>
|
||||
<string name="images_color_effects_option">Ефекти кольору</string>
|
||||
<string name="progress_bar_option">Панель прогресу</string>
|
||||
|
||||
<!-- Browse -->
|
||||
<string name="browse_files_structure_option">Структура файлів</string>
|
||||
|
|
|
|||
|
|
@ -191,6 +191,7 @@
|
|||
<string name="system_reader_settings">System</string>
|
||||
<string name="chapters_reader_settings">Chapters</string>
|
||||
<string name="images_reader_settings">Images</string>
|
||||
<string name="progress_reader_settings">Progress</string>
|
||||
<string name="theme_appearance_settings">Theme preferences</string>
|
||||
<string name="colors_appearance_settings">Colors</string>
|
||||
<string name="general_browse_settings">General</string>
|
||||
|
|
@ -258,6 +259,7 @@
|
|||
<string name="images_alignment_option">Alignment</string>
|
||||
<string name="images_width_option">Size</string>
|
||||
<string name="images_color_effects_option">Color effects</string>
|
||||
<string name="progress_bar_option">Progress bar</string>
|
||||
|
||||
<!-- Browse -->
|
||||
<string name="browse_files_structure_option">Files structure</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue