🚀 Accident scroll prevention
* Now every time you bring bars, a new checkpoint saves * Checkpoint restores exact position * Visual indication of checkpoint position on slider track * Arrow in the direction of checkpoint when changed position Resolves: #61
This commit is contained in:
parent
cf8632c48b
commit
e212733300
14 changed files with 212 additions and 61 deletions
|
|
@ -0,0 +1,8 @@
|
||||||
|
package ua.acclorite.book_story.domain.util
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
enum class Direction {
|
||||||
|
START, NEUTRAL, END
|
||||||
|
}
|
||||||
|
|
@ -34,3 +34,11 @@ fun Intent.launchActivity(
|
||||||
|
|
||||||
success?.invoke()
|
success?.invoke()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Float.calculateProgress(digits: Int): String {
|
||||||
|
return (this * 100)
|
||||||
|
.toDouble()
|
||||||
|
.removeDigits(digits)
|
||||||
|
.removeTrailingZero()
|
||||||
|
.dropWhile { it == '-' }
|
||||||
|
}
|
||||||
|
|
@ -169,7 +169,10 @@ class MainViewModel @Inject constructor(
|
||||||
|
|
||||||
is MainEvent.OnChangeParagraphIndentation -> {
|
is MainEvent.OnChangeParagraphIndentation -> {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
setDatastore.execute(DataStoreConstants.PARAGRAPH_INDENTATION, event.indentation)
|
setDatastore.execute(
|
||||||
|
DataStoreConstants.PARAGRAPH_INDENTATION,
|
||||||
|
event.indentation
|
||||||
|
)
|
||||||
updateStateWithSavedHandle {
|
updateStateWithSavedHandle {
|
||||||
it.copy(
|
it.copy(
|
||||||
paragraphIndentation = event.indentation
|
paragraphIndentation = event.indentation
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ import androidx.compose.material3.LinearProgressIndicator
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.StrokeCap
|
import androidx.compose.ui.graphics.StrokeCap
|
||||||
|
|
@ -17,8 +19,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import ua.acclorite.book_story.R
|
import ua.acclorite.book_story.R
|
||||||
import ua.acclorite.book_story.presentation.core.components.LocalBookInfoViewModel
|
import ua.acclorite.book_story.presentation.core.components.LocalBookInfoViewModel
|
||||||
import ua.acclorite.book_story.presentation.core.util.removeDigits
|
import ua.acclorite.book_story.presentation.core.util.calculateProgress
|
||||||
import ua.acclorite.book_story.presentation.core.util.removeTrailingZero
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Statistic section.
|
* Statistic section.
|
||||||
|
|
@ -27,13 +28,10 @@ import ua.acclorite.book_story.presentation.core.util.removeTrailingZero
|
||||||
fun BookInfoStatisticSection() {
|
fun BookInfoStatisticSection() {
|
||||||
val state = LocalBookInfoViewModel.current.state
|
val state = LocalBookInfoViewModel.current.state
|
||||||
|
|
||||||
val progress = remember(state.value.book) {
|
val progress by remember {
|
||||||
"${
|
derivedStateOf {
|
||||||
(state.value.book.progress * 100)
|
"${state.value.book.progress.calculateProgress(1)}%"
|
||||||
.toDouble()
|
}
|
||||||
.removeDigits(1)
|
|
||||||
.removeTrailingZero()
|
|
||||||
}%"
|
|
||||||
}
|
}
|
||||||
val description = stringResource(
|
val description = stringResource(
|
||||||
if (state.value.book.progress == 1f) R.string.read_done
|
if (state.value.book.progress == 1f) R.string.read_done
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,7 @@ import ua.acclorite.book_story.R
|
||||||
import ua.acclorite.book_story.domain.model.Book
|
import ua.acclorite.book_story.domain.model.Book
|
||||||
import ua.acclorite.book_story.domain.util.Selected
|
import ua.acclorite.book_story.domain.util.Selected
|
||||||
import ua.acclorite.book_story.presentation.core.components.CustomCoverImage
|
import ua.acclorite.book_story.presentation.core.components.CustomCoverImage
|
||||||
import ua.acclorite.book_story.presentation.core.util.removeDigits
|
import ua.acclorite.book_story.presentation.core.util.calculateProgress
|
||||||
import ua.acclorite.book_story.presentation.core.util.removeTrailingZero
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Library list element item.
|
* Library list element item.
|
||||||
|
|
@ -69,13 +68,8 @@ fun LibraryBookItem(
|
||||||
else onSurfaceColor
|
else onSurfaceColor
|
||||||
}
|
}
|
||||||
|
|
||||||
val progress = rememberSaveable(book.first) {
|
val progress = rememberSaveable(book.first.progress) {
|
||||||
"${
|
"${book.first.progress.calculateProgress(1)}%"
|
||||||
(book.first.progress * 100)
|
|
||||||
.toDouble()
|
|
||||||
.removeDigits(1)
|
|
||||||
.removeTrailingZero()
|
|
||||||
}%"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,7 @@ fun ReaderScreenRoot(screen: Screen.Reader) {
|
||||||
onEvent(
|
onEvent(
|
||||||
ReaderEvent.OnShowHideMenu(
|
ReaderEvent.OnShowHideMenu(
|
||||||
fullscreenMode = mainState.value.fullscreen,
|
fullscreenMode = mainState.value.fullscreen,
|
||||||
|
saveCheckpoint = true,
|
||||||
activity = context
|
activity = context
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -138,6 +139,7 @@ fun ReaderScreenRoot(screen: Screen.Reader) {
|
||||||
ReaderEvent.OnShowHideMenu(
|
ReaderEvent.OnShowHideMenu(
|
||||||
show = state.value.showMenu,
|
show = state.value.showMenu,
|
||||||
fullscreenMode = mainState.value.fullscreen,
|
fullscreenMode = mainState.value.fullscreen,
|
||||||
|
saveCheckpoint = false,
|
||||||
activity = context
|
activity = context
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -192,15 +194,16 @@ private fun ReaderScreen(lazyListState: LazyListState) {
|
||||||
source: NestedScrollSource
|
source: NestedScrollSource
|
||||||
): Offset {
|
): Offset {
|
||||||
consumed.y.let { velocity ->
|
consumed.y.let { velocity ->
|
||||||
if(velocity in -70f..70f) return@let
|
if (velocity in -70f..70f) return@let
|
||||||
if(!state.value.showMenu) return@let
|
if (!state.value.showMenu) return@let
|
||||||
if(state.value.lockMenu) return@let
|
if (state.value.lockMenu) return@let
|
||||||
if(!mainState.value.hideBarsOnFastScroll) return@let
|
if (!mainState.value.hideBarsOnFastScroll) return@let
|
||||||
|
|
||||||
onEvent(
|
onEvent(
|
||||||
ReaderEvent.OnShowHideMenu(
|
ReaderEvent.OnShowHideMenu(
|
||||||
show = false,
|
show = false,
|
||||||
fullscreenMode = mainState.value.fullscreen,
|
fullscreenMode = mainState.value.fullscreen,
|
||||||
|
saveCheckpoint = false,
|
||||||
activity = context
|
activity = context
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -414,6 +417,7 @@ private fun ReaderScreen(lazyListState: LazyListState) {
|
||||||
onEvent(
|
onEvent(
|
||||||
ReaderEvent.OnShowHideMenu(
|
ReaderEvent.OnShowHideMenu(
|
||||||
fullscreenMode = mainState.value.fullscreen,
|
fullscreenMode = mainState.value.fullscreen,
|
||||||
|
saveCheckpoint = true,
|
||||||
activity = context
|
activity = context
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,7 @@ fun LazyItemScope.ReaderTextParagraph(
|
||||||
onEvent(
|
onEvent(
|
||||||
ReaderEvent.OnShowHideMenu(
|
ReaderEvent.OnShowHideMenu(
|
||||||
fullscreenMode = fullscreenMode,
|
fullscreenMode = fullscreenMode,
|
||||||
|
saveCheckpoint = true,
|
||||||
activity = context as ComponentActivity
|
activity = context as ComponentActivity
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,28 @@
|
||||||
package ua.acclorite.book_story.presentation.screens.reader.components.app_bar
|
package ua.acclorite.book_story.presentation.screens.reader.components.app_bar
|
||||||
|
|
||||||
|
import androidx.compose.animation.expandHorizontally
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.shrinkHorizontally
|
||||||
|
import androidx.compose.animation.slideInHorizontally
|
||||||
|
import androidx.compose.animation.slideOutHorizontally
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
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.MaterialTheme
|
||||||
import androidx.compose.material3.Slider
|
import androidx.compose.material3.Slider
|
||||||
import androidx.compose.material3.SliderDefaults
|
import androidx.compose.material3.SliderDefaults
|
||||||
|
|
@ -19,19 +33,24 @@ import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
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
|
||||||
|
import ua.acclorite.book_story.domain.util.Direction
|
||||||
|
import ua.acclorite.book_story.presentation.core.components.CustomAnimatedVisibility
|
||||||
|
import ua.acclorite.book_story.presentation.core.components.CustomIconButton
|
||||||
import ua.acclorite.book_story.presentation.core.components.LocalHistoryViewModel
|
import ua.acclorite.book_story.presentation.core.components.LocalHistoryViewModel
|
||||||
import ua.acclorite.book_story.presentation.core.components.LocalLibraryViewModel
|
import ua.acclorite.book_story.presentation.core.components.LocalLibraryViewModel
|
||||||
import ua.acclorite.book_story.presentation.core.components.LocalReaderViewModel
|
import ua.acclorite.book_story.presentation.core.components.LocalReaderViewModel
|
||||||
import ua.acclorite.book_story.presentation.core.util.removeDigits
|
import ua.acclorite.book_story.presentation.core.util.calculateProgress
|
||||||
import ua.acclorite.book_story.presentation.core.util.removeTrailingZero
|
|
||||||
import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent
|
import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent
|
||||||
import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
|
import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
|
||||||
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent
|
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent
|
||||||
import ua.acclorite.book_story.presentation.ui.Colors
|
import ua.acclorite.book_story.presentation.ui.Colors
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reader bottom bar. Has a slider to change progress.
|
* Reader bottom bar.
|
||||||
|
* Has a slider to change progress.
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun ReaderBottomBar() {
|
fun ReaderBottomBar() {
|
||||||
|
|
@ -40,13 +59,26 @@ fun ReaderBottomBar() {
|
||||||
val onLibraryEvent = LocalLibraryViewModel.current.onEvent
|
val onLibraryEvent = LocalLibraryViewModel.current.onEvent
|
||||||
val onHistoryEvent = LocalHistoryViewModel.current.onEvent
|
val onHistoryEvent = LocalHistoryViewModel.current.onEvent
|
||||||
|
|
||||||
val progress by remember(state.value.book.progress) {
|
val progress by remember {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
(state.value.book.progress * 100)
|
"${state.value.book.progress.calculateProgress(4)}%"
|
||||||
.toDouble()
|
}
|
||||||
.removeDigits(4)
|
}
|
||||||
.removeTrailingZero()
|
val arrowDirection by remember {
|
||||||
.dropWhile { it == '-' } + "%"
|
derivedStateOf {
|
||||||
|
val checkpoint = state.value.checkpoint.first
|
||||||
|
val index = state.value.listState.firstVisibleItemIndex
|
||||||
|
|
||||||
|
when {
|
||||||
|
checkpoint > index -> Direction.END
|
||||||
|
checkpoint < index -> Direction.START
|
||||||
|
else -> Direction.NEUTRAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val checkpointProgress by remember {
|
||||||
|
derivedStateOf {
|
||||||
|
(state.value.checkpoint.first / state.value.text.lastIndex.toFloat()) * 0.987f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,7 +92,7 @@ fun ReaderBottomBar() {
|
||||||
onClick = {}
|
onClick = {}
|
||||||
)
|
)
|
||||||
.navigationBarsPadding()
|
.navigationBarsPadding()
|
||||||
.padding(horizontal = 24.dp)
|
.padding(horizontal = 18.dp)
|
||||||
.padding(top = 16.dp),
|
.padding(top = 16.dp),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.Center
|
verticalArrangement = Arrangement.Center
|
||||||
|
|
@ -70,33 +102,99 @@ fun ReaderBottomBar() {
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
style = MaterialTheme.typography.titleLarge
|
style = MaterialTheme.typography.titleLarge
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(3.dp))
|
Row(
|
||||||
Slider(
|
modifier = Modifier.padding(top = 3.dp, bottom = 5.dp),
|
||||||
value = state.value.book.progress,
|
verticalAlignment = Alignment.CenterVertically
|
||||||
enabled = !state.value.lockMenu,
|
) {
|
||||||
onValueChange = {
|
CustomAnimatedVisibility(
|
||||||
if (state.value.listState.layoutInfo.totalItemsCount > 0) {
|
visible = arrowDirection == Direction.START,
|
||||||
onEvent(ReaderEvent.OnScroll(it))
|
enter = expandHorizontally(expandFrom = Alignment.Start) + fadeIn() + slideInHorizontally { -it },
|
||||||
|
exit = shrinkHorizontally(shrinkTowards = Alignment.Start) + fadeOut() + slideOutHorizontally { -it }
|
||||||
|
) {
|
||||||
|
CustomIconButton(
|
||||||
|
icon = Icons.AutoMirrored.Default.ArrowBack,
|
||||||
|
contentDescription = R.string.checkpoint_back_content_desc,
|
||||||
|
modifier = Modifier.size(24.dp),
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
disableOnClick = false
|
||||||
|
) {
|
||||||
onEvent(
|
onEvent(
|
||||||
ReaderEvent.OnChangeProgress(
|
ReaderEvent.OnRestoreCheckpoint { book ->
|
||||||
progress = it,
|
onLibraryEvent(LibraryEvent.OnUpdateBook(book))
|
||||||
firstVisibleItemIndex = state.value.listState.firstVisibleItemIndex,
|
onHistoryEvent(HistoryEvent.OnUpdateBook(book))
|
||||||
firstVisibleItemOffset = 0,
|
}
|
||||||
refreshList = { book ->
|
|
||||||
onLibraryEvent(LibraryEvent.OnUpdateBook(book))
|
|
||||||
onHistoryEvent(HistoryEvent.OnUpdateBook(book))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
colors = SliderDefaults.colors(
|
|
||||||
inactiveTrackColor = MaterialTheme.colorScheme.secondary.copy(0.15f),
|
Box(
|
||||||
disabledActiveTrackColor = MaterialTheme.colorScheme.primary,
|
modifier = Modifier.weight(1f),
|
||||||
disabledThumbColor = MaterialTheme.colorScheme.primary,
|
contentAlignment = Alignment.CenterStart
|
||||||
disabledInactiveTrackColor = MaterialTheme.colorScheme.secondary.copy(0.15f),
|
) {
|
||||||
)
|
Slider(
|
||||||
)
|
value = state.value.book.progress,
|
||||||
Spacer(modifier = Modifier.height(5.dp))
|
enabled = !state.value.lockMenu,
|
||||||
|
onValueChange = {
|
||||||
|
if (state.value.listState.layoutInfo.totalItemsCount > 0) {
|
||||||
|
onEvent(ReaderEvent.OnScroll(it))
|
||||||
|
onEvent(
|
||||||
|
ReaderEvent.OnChangeProgress(
|
||||||
|
progress = it,
|
||||||
|
firstVisibleItemIndex = state.value.listState.firstVisibleItemIndex,
|
||||||
|
firstVisibleItemOffset = 0,
|
||||||
|
refreshList = { book ->
|
||||||
|
onLibraryEvent(LibraryEvent.OnUpdateBook(book))
|
||||||
|
onHistoryEvent(HistoryEvent.OnUpdateBook(book))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (arrowDirection != Direction.NEUTRAL) {
|
||||||
|
Row(Modifier.fillMaxWidth()) {
|
||||||
|
Spacer(
|
||||||
|
modifier = Modifier.fillMaxWidth(checkpointProgress)
|
||||||
|
)
|
||||||
|
Box(
|
||||||
|
Modifier
|
||||||
|
.width(4.dp)
|
||||||
|
.height(16.dp)
|
||||||
|
.clip(RoundedCornerShape(0.5.dp))
|
||||||
|
.background(
|
||||||
|
MaterialTheme.colorScheme.onPrimary.copy(0.6f)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomAnimatedVisibility(
|
||||||
|
visible = arrowDirection == Direction.END,
|
||||||
|
enter = expandHorizontally() + fadeIn() + slideInHorizontally { it },
|
||||||
|
exit = shrinkHorizontally() + fadeOut() + slideOutHorizontally { it }
|
||||||
|
) {
|
||||||
|
CustomIconButton(
|
||||||
|
icon = Icons.AutoMirrored.Default.ArrowForward,
|
||||||
|
contentDescription = R.string.checkpoint_forward_content_desc,
|
||||||
|
modifier = Modifier.size(24.dp),
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
disableOnClick = false
|
||||||
|
) {
|
||||||
|
onEvent(
|
||||||
|
ReaderEvent.OnRestoreCheckpoint { book ->
|
||||||
|
onLibraryEvent(LibraryEvent.OnUpdateBook(book))
|
||||||
|
onHistoryEvent(HistoryEvent.OnUpdateBook(book))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -63,9 +63,10 @@ fun ReaderSettingsBottomSheet() {
|
||||||
)
|
)
|
||||||
onEvent(
|
onEvent(
|
||||||
ReaderEvent.OnShowHideMenu(
|
ReaderEvent.OnShowHideMenu(
|
||||||
pagerState.currentPage != 2,
|
show = pagerState.currentPage != 2,
|
||||||
fullscreenMode = mainState.value.fullscreen,
|
fullscreenMode = mainState.value.fullscreen,
|
||||||
context as ComponentActivity
|
saveCheckpoint = false,
|
||||||
|
activity = context as ComponentActivity
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,14 @@ sealed class ReaderEvent {
|
||||||
data class OnShowHideMenu(
|
data class OnShowHideMenu(
|
||||||
val show: Boolean? = null,
|
val show: Boolean? = null,
|
||||||
val fullscreenMode: Boolean,
|
val fullscreenMode: Boolean,
|
||||||
|
val saveCheckpoint: Boolean,
|
||||||
val activity: ComponentActivity
|
val activity: ComponentActivity
|
||||||
) : ReaderEvent()
|
) : ReaderEvent()
|
||||||
|
|
||||||
|
data class OnRestoreCheckpoint(
|
||||||
|
val refreshList: (Book) -> Unit
|
||||||
|
) : ReaderEvent()
|
||||||
|
|
||||||
data class OnGoBack(
|
data class OnGoBack(
|
||||||
val context: ComponentActivity,
|
val context: ComponentActivity,
|
||||||
val navigate: () -> Unit,
|
val navigate: () -> Unit,
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ data class ReaderState(
|
||||||
val loading: Boolean = true,
|
val loading: Boolean = true,
|
||||||
|
|
||||||
val showMenu: Boolean = false,
|
val showMenu: Boolean = false,
|
||||||
|
val checkpoint: Pair<Int, Int> = 0 to 0,
|
||||||
val lockMenu: Boolean = false,
|
val lockMenu: Boolean = false,
|
||||||
|
|
||||||
val showSettingsBottomSheet: Boolean = false,
|
val showSettingsBottomSheet: Boolean = false,
|
||||||
|
|
|
||||||
|
|
@ -159,12 +159,37 @@ class ReaderViewModel @Inject constructor(
|
||||||
)
|
)
|
||||||
_state.update {
|
_state.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
showMenu = shouldShow
|
showMenu = shouldShow,
|
||||||
|
checkpoint = _state.value.listState.run {
|
||||||
|
if (!shouldShow || !event.saveCheckpoint) return@run it.checkpoint
|
||||||
|
|
||||||
|
firstVisibleItemIndex to firstVisibleItemScrollOffset
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is ReaderEvent.OnRestoreCheckpoint -> {
|
||||||
|
launch(Dispatchers.Main) {
|
||||||
|
_state.value.listState.requestScrollToItem(
|
||||||
|
_state.value.checkpoint.first,
|
||||||
|
_state.value.checkpoint.second
|
||||||
|
)
|
||||||
|
|
||||||
|
onEvent(
|
||||||
|
ReaderEvent.OnChangeProgress(
|
||||||
|
progress = calculateProgress(_state.value.checkpoint.first),
|
||||||
|
firstVisibleItemIndex = _state.value.checkpoint.first,
|
||||||
|
firstVisibleItemOffset = _state.value.checkpoint.second,
|
||||||
|
refreshList = { book ->
|
||||||
|
event.refreshList(book)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
is ReaderEvent.OnGoBack -> {
|
is ReaderEvent.OnGoBack -> {
|
||||||
launch {
|
launch {
|
||||||
yield()
|
yield()
|
||||||
|
|
@ -419,6 +444,7 @@ class ReaderViewModel @Inject constructor(
|
||||||
ReaderEvent.OnShowHideMenu(
|
ReaderEvent.OnShowHideMenu(
|
||||||
show = false,
|
show = false,
|
||||||
fullscreenMode = fullscreenMode,
|
fullscreenMode = fullscreenMode,
|
||||||
|
saveCheckpoint = false,
|
||||||
activity = activity
|
activity = activity
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -594,5 +594,7 @@
|
||||||
<string name="github_profile_content_desc">GitHub Профіль</string>
|
<string name="github_profile_content_desc">GitHub Профіль</string>
|
||||||
<string name="drag_content_desc">Тягнути</string>
|
<string name="drag_content_desc">Тягнути</string>
|
||||||
<string name="filter_content_desc">Фільтр</string>
|
<string name="filter_content_desc">Фільтр</string>
|
||||||
|
<string name="checkpoint_back_content_desc">Чекпоінт назад</string>
|
||||||
|
<string name="checkpoint_forward_content_desc">Чекпоінт вперед</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -613,5 +613,7 @@
|
||||||
<string name="github_profile_content_desc">GitHub Profile</string>
|
<string name="github_profile_content_desc">GitHub Profile</string>
|
||||||
<string name="drag_content_desc">Drag</string>
|
<string name="drag_content_desc">Drag</string>
|
||||||
<string name="filter_content_desc">Filter</string>
|
<string name="filter_content_desc">Filter</string>
|
||||||
|
<string name="checkpoint_back_content_desc">Checkpoint back</string>
|
||||||
|
<string name="checkpoint_forward_content_desc">Checkpoint forward</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue