🚀 Progress Count option
* Option to change Progress Count method (Percentage/Quantity) * Improved progress calculation
This commit is contained in:
parent
fe269fd8a0
commit
ad41bc9a1a
11 changed files with 145 additions and 19 deletions
|
|
@ -0,0 +1,13 @@
|
|||
package ua.acclorite.book_story.domain.reader
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
|
||||
@Immutable
|
||||
enum class ReaderProgressCount {
|
||||
PERCENTAGE,
|
||||
QUANTITY
|
||||
}
|
||||
|
||||
fun String.toProgressCount(): ReaderProgressCount {
|
||||
return ReaderProgressCount.valueOf(this)
|
||||
}
|
||||
|
|
@ -58,6 +58,7 @@ object DataStoreConstants {
|
|||
val PROGRESS_BAR_PADDING = intPreferencesKey("progress_bar_padding")
|
||||
val PROGRESS_BAR_ALIGNMENT = stringPreferencesKey("progress_bar_alignment")
|
||||
val PROGRESS_BAR_FONT_SIZE = intPreferencesKey("progress_bar_font_size")
|
||||
val PROGRESS_COUNT = stringPreferencesKey("progress_count")
|
||||
|
||||
// Browse settings
|
||||
val BROWSE_LAYOUT = stringPreferencesKey("browse_layout")
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import ua.acclorite.book_story.presentation.settings.reader.progress.components.
|
|||
import ua.acclorite.book_story.presentation.settings.reader.progress.components.ProgressBarFontSizeOption
|
||||
import ua.acclorite.book_story.presentation.settings.reader.progress.components.ProgressBarOption
|
||||
import ua.acclorite.book_story.presentation.settings.reader.progress.components.ProgressBarPaddingOption
|
||||
import ua.acclorite.book_story.presentation.settings.reader.progress.components.ProgressCountOption
|
||||
|
||||
fun LazyListScope.ProgressSubcategory(
|
||||
titleColor: @Composable () -> Color = { MaterialTheme.colorScheme.primary },
|
||||
|
|
@ -26,6 +27,10 @@ fun LazyListScope.ProgressSubcategory(
|
|||
showTitle = showTitle,
|
||||
showDivider = showDivider,
|
||||
) {
|
||||
item {
|
||||
ProgressCountOption()
|
||||
}
|
||||
|
||||
item {
|
||||
ProgressBarOption()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
package ua.acclorite.book_story.presentation.settings.reader.progress.components
|
||||
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
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.domain.reader.ReaderProgressCount
|
||||
import ua.acclorite.book_story.domain.ui.ButtonItem
|
||||
import ua.acclorite.book_story.presentation.core.components.settings.SegmentedButtonWithTitle
|
||||
import ua.acclorite.book_story.ui.main.MainEvent
|
||||
import ua.acclorite.book_story.ui.main.MainModel
|
||||
|
||||
@Composable
|
||||
fun ProgressCountOption() {
|
||||
val mainModel = hiltViewModel<MainModel>()
|
||||
val state = mainModel.state.collectAsStateWithLifecycle()
|
||||
|
||||
SegmentedButtonWithTitle(
|
||||
title = stringResource(id = R.string.progress_count_option),
|
||||
buttons = ReaderProgressCount.entries.map {
|
||||
ButtonItem(
|
||||
id = it.toString(),
|
||||
title = when (it) {
|
||||
ReaderProgressCount.PERCENTAGE -> stringResource(id = R.string.progress_count_percentage)
|
||||
ReaderProgressCount.QUANTITY -> stringResource(id = R.string.progress_count_quantity)
|
||||
},
|
||||
textStyle = MaterialTheme.typography.labelLarge,
|
||||
selected = it == state.value.progressCount
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
mainModel.onEvent(
|
||||
MainEvent.OnChangeProgressCount(
|
||||
it.id
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -59,4 +59,5 @@ sealed class MainEvent {
|
|||
data class OnChangeProgressBarFontSize(val value: Int) : MainEvent()
|
||||
data class OnChangeBrowsePinnedPaths(val value: String) : MainEvent()
|
||||
data class OnChangeFontThickness(val value: String) : MainEvent()
|
||||
data class OnChangeProgressCount(val value: String) : MainEvent()
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ import ua.acclorite.book_story.domain.browse.toBrowseSortOrder
|
|||
import ua.acclorite.book_story.domain.reader.toColorEffects
|
||||
import ua.acclorite.book_story.domain.reader.toFontThickness
|
||||
import ua.acclorite.book_story.domain.reader.toHorizontalGesture
|
||||
import ua.acclorite.book_story.domain.reader.toProgressCount
|
||||
import ua.acclorite.book_story.domain.reader.toReaderScreenOrientation
|
||||
import ua.acclorite.book_story.domain.reader.toTextAlignment
|
||||
import ua.acclorite.book_story.domain.use_case.data_store.ChangeLanguage
|
||||
|
|
@ -494,6 +495,14 @@ class MainModel @Inject constructor(
|
|||
it.copy(fontThickness = this.toFontThickness())
|
||||
}
|
||||
)
|
||||
|
||||
is MainEvent.OnChangeProgressCount -> handleDatastoreUpdate(
|
||||
key = DataStoreConstants.PROGRESS_COUNT,
|
||||
value = event.value,
|
||||
updateState = {
|
||||
it.copy(progressCount = this.toProgressCount())
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,11 +14,13 @@ import ua.acclorite.book_story.domain.browse.toBrowseSortOrder
|
|||
import ua.acclorite.book_story.domain.reader.ReaderColorEffects
|
||||
import ua.acclorite.book_story.domain.reader.ReaderFontThickness
|
||||
import ua.acclorite.book_story.domain.reader.ReaderHorizontalGesture
|
||||
import ua.acclorite.book_story.domain.reader.ReaderProgressCount
|
||||
import ua.acclorite.book_story.domain.reader.ReaderScreenOrientation
|
||||
import ua.acclorite.book_story.domain.reader.ReaderTextAlignment
|
||||
import ua.acclorite.book_story.domain.reader.toColorEffects
|
||||
import ua.acclorite.book_story.domain.reader.toFontThickness
|
||||
import ua.acclorite.book_story.domain.reader.toHorizontalGesture
|
||||
import ua.acclorite.book_story.domain.reader.toProgressCount
|
||||
import ua.acclorite.book_story.domain.reader.toReaderScreenOrientation
|
||||
import ua.acclorite.book_story.domain.reader.toTextAlignment
|
||||
import ua.acclorite.book_story.domain.util.HorizontalAlignment
|
||||
|
|
@ -107,6 +109,7 @@ data class MainState(
|
|||
val progressBarPadding: Int = provideDefaultValue { 4 },
|
||||
val progressBarAlignment: HorizontalAlignment = provideDefaultValue { HorizontalAlignment.CENTER },
|
||||
val progressBarFontSize: Int = provideDefaultValue { 8 },
|
||||
val progressCount: ReaderProgressCount = provideDefaultValue { ReaderProgressCount.PERCENTAGE },
|
||||
|
||||
// Browse Settings
|
||||
val browseLayout: BrowseLayout = provideDefaultValue { BrowseLayout.LIST },
|
||||
|
|
@ -357,6 +360,10 @@ data class MainState(
|
|||
fontThickness = provideValue(
|
||||
FONT_THICKNESS, convert = { toFontThickness() }
|
||||
) { fontThickness },
|
||||
|
||||
progressCount = provideValue(
|
||||
PROGRESS_COUNT, convert = { toProgressCount() }
|
||||
) { progressCount },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -179,7 +179,6 @@ class ReaderModel @Inject constructor(
|
|||
launch {
|
||||
_state.value.apply {
|
||||
val chapterIndex = text.indexOf(event.chapter).takeIf { it != -1 }
|
||||
|
||||
if (chapterIndex == null) {
|
||||
return@launch
|
||||
}
|
||||
|
|
@ -203,8 +202,9 @@ class ReaderModel @Inject constructor(
|
|||
delay(300)
|
||||
yield()
|
||||
|
||||
val scrollTo = (_state.value.text.size * event.progress).roundToInt()
|
||||
val scrollTo = (_state.value.text.lastIndex * event.progress).roundToInt()
|
||||
_state.value.listState.requestScrollToItem(scrollTo)
|
||||
updateChapter(scrollTo)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -489,7 +489,7 @@ class ReaderModel @Inject constructor(
|
|||
fun updateProgress(listState: LazyListState) {
|
||||
viewModelScope.launch(Dispatchers.Main) {
|
||||
snapshotFlow {
|
||||
listState.run { firstVisibleItemIndex to firstVisibleItemScrollOffset }
|
||||
listState.firstVisibleItemIndex to listState.firstVisibleItemScrollOffset
|
||||
}.distinctUntilChanged().debounce(300).collectLatest { (index, offset) ->
|
||||
val progress = calculateProgress(index)
|
||||
if (progress == _state.value.book.progress) return@collectLatest
|
||||
|
|
@ -519,6 +519,21 @@ class ReaderModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun findChapterIndexAndLength(index: Int): Pair<Int, Int> {
|
||||
return findCurrentChapter(index)?.let { chapter ->
|
||||
_state.value.text.run {
|
||||
val startIndex = indexOf(chapter).coerceIn(0, lastIndex)
|
||||
val endIndex = (indexOfFirst {
|
||||
it is Chapter && indexOf(it) > startIndex
|
||||
}.takeIf { it != -1 }) ?: (lastIndex + 1)
|
||||
|
||||
val currentIndexInChapter = (index - startIndex).coerceAtLeast(1)
|
||||
val chapterLength = endIndex - (startIndex + 1)
|
||||
currentIndexInChapter to chapterLength
|
||||
}
|
||||
} ?: (-1 to -1)
|
||||
}
|
||||
|
||||
private fun updateChapter(index: Int) {
|
||||
viewModelScope.launch {
|
||||
val (currentChapter, currentChapterProgress) = calculateCurrentChapter(index)
|
||||
|
|
@ -540,13 +555,13 @@ class ReaderModel @Inject constructor(
|
|||
val currentChapter = findCurrentChapter(index)
|
||||
val currentChapterProgress = currentChapter?.let { chapter ->
|
||||
_state.value.text.run {
|
||||
val startIndex = (indexOf(chapter) + 1).coerceAtMost(count())
|
||||
val startIndex = indexOf(chapter).coerceIn(0, lastIndex)
|
||||
val endIndex = (indexOfFirst {
|
||||
it is Chapter && indexOf(it) > startIndex
|
||||
}.takeIf { it != -1 }?.minus(1)) ?: lastIndex
|
||||
}.takeIf { it != -1 }) ?: (lastIndex + 1)
|
||||
|
||||
val currentIndexInChapter = index - startIndex
|
||||
val chapterLength = endIndex - startIndex
|
||||
val currentIndexInChapter = (index - startIndex).coerceAtLeast(1)
|
||||
val chapterLength = endIndex - (startIndex + 1)
|
||||
(currentIndexInChapter / chapterLength.toFloat())
|
||||
}
|
||||
}.coerceAndPreventNaN()
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||
import kotlinx.parcelize.Parcelize
|
||||
import ua.acclorite.book_story.domain.navigator.Screen
|
||||
import ua.acclorite.book_story.domain.reader.ReaderColorEffects
|
||||
import ua.acclorite.book_story.domain.reader.ReaderProgressCount
|
||||
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
|
||||
|
|
@ -51,6 +52,7 @@ import ua.acclorite.book_story.presentation.reader.ReaderContent
|
|||
import ua.acclorite.book_story.ui.book_info.BookInfoScreen
|
||||
import ua.acclorite.book_story.ui.main.MainModel
|
||||
import ua.acclorite.book_story.ui.settings.SettingsModel
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Parcelize
|
||||
data class ReaderScreen(val bookId: Int) : Screen, Parcelable {
|
||||
|
|
@ -289,24 +291,46 @@ 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 bookProgress = remember(
|
||||
state.value.book.progress,
|
||||
state.value.text,
|
||||
mainState.value.progressCount
|
||||
) {
|
||||
when (mainState.value.progressCount) {
|
||||
ReaderProgressCount.PERCENTAGE -> {
|
||||
"${state.value.book.progress.calculateProgress(2)}%"
|
||||
}
|
||||
|
||||
ReaderProgressCount.QUANTITY -> {
|
||||
val index =
|
||||
(state.value.book.progress * state.value.text.lastIndex + 1).roundToInt()
|
||||
"$index / ${state.value.text.size}"
|
||||
}
|
||||
}
|
||||
}
|
||||
val chapterProgress = remember(
|
||||
state.value.text,
|
||||
state.value.book.progress,
|
||||
state.value.currentChapter,
|
||||
state.value.currentChapterProgress
|
||||
state.value.currentChapterProgress,
|
||||
mainState.value.progressCount
|
||||
) {
|
||||
derivedStateOf {
|
||||
if (state.value.currentChapter == null) return@derivedStateOf ""
|
||||
" (${state.value.currentChapterProgress.calculateProgress(2)}%)"
|
||||
if (state.value.currentChapter == null) return@remember ""
|
||||
when (mainState.value.progressCount) {
|
||||
ReaderProgressCount.PERCENTAGE -> {
|
||||
" (${state.value.currentChapterProgress.calculateProgress(2)}%)"
|
||||
}
|
||||
|
||||
ReaderProgressCount.QUANTITY -> {
|
||||
val (index, length) = screenModel.findChapterIndexAndLength(
|
||||
(state.value.book.progress * state.value.text.lastIndex).roundToInt()
|
||||
).apply { if (first == -1 && second == -1) return@remember "" }
|
||||
" (${index} / ${length})"
|
||||
}
|
||||
}
|
||||
}
|
||||
val progress = remember(bookProgress.value, chapterProgress.value) {
|
||||
derivedStateOf {
|
||||
"${bookProgress.value}${chapterProgress.value}"
|
||||
}
|
||||
val progress = remember(bookProgress, chapterProgress) {
|
||||
"${bookProgress}${chapterProgress}"
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
|
|
@ -397,7 +421,7 @@ data class ReaderScreen(val bookId: Int) : Screen, Parcelable {
|
|||
horizontalGestureSensitivity = horizontalGestureSensitivity,
|
||||
highlightedReading = mainState.value.highlightedReading,
|
||||
highlightedReadingThickness = highlightedReadingThickness,
|
||||
progress = progress.value,
|
||||
progress = progress,
|
||||
progressBar = mainState.value.progressBar,
|
||||
progressBarPadding = progressBarPadding,
|
||||
progressBarAlignment = mainState.value.progressBarAlignment,
|
||||
|
|
|
|||
|
|
@ -208,6 +208,7 @@
|
|||
<string name="progress_bar_padding_option">Поля</string>
|
||||
<string name="progress_bar_alignment_option">Розташування</string>
|
||||
<string name="progress_bar_font_size_option">Розмір шрифта</string>
|
||||
<string name="progress_count_option">Підрахунок прогресу</string>
|
||||
|
||||
<!-- Browse -->
|
||||
<string name="browse_layout_option">Режим відображення</string>
|
||||
|
|
@ -286,6 +287,10 @@
|
|||
<string name="font_thickness_normal">Звичайна</string>
|
||||
<string name="font_thickness_medium">Середня</string>
|
||||
|
||||
<!-- Progress Count properties -->
|
||||
<string name="progress_count_percentage">Відсотки</string>
|
||||
<string name="progress_count_quantity">Кількість</string>
|
||||
|
||||
<!-- Themes -->
|
||||
<string name="dynamic_theme">Меркурій</string>
|
||||
<string name="blue_theme">Нептун</string>
|
||||
|
|
|
|||
|
|
@ -265,6 +265,7 @@
|
|||
<string name="progress_bar_padding_option">Margins</string>
|
||||
<string name="progress_bar_alignment_option">Alignment</string>
|
||||
<string name="progress_bar_font_size_option">Font size</string>
|
||||
<string name="progress_count_option">Progress count</string>
|
||||
|
||||
<!-- Browse -->
|
||||
<string name="browse_layout_option">Display mode</string>
|
||||
|
|
@ -343,6 +344,10 @@
|
|||
<string name="font_thickness_normal">Normal</string>
|
||||
<string name="font_thickness_medium">Medium</string>
|
||||
|
||||
<!-- Progress Count properties -->
|
||||
<string name="progress_count_percentage">Percentage</string>
|
||||
<string name="progress_count_quantity">Quantity</string>
|
||||
|
||||
<!-- Themes -->
|
||||
<string name="dynamic_theme">Mercury</string>
|
||||
<string name="blue_theme">Neptune</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue