GetBooksById → GetBookById(single book) + Canceling all ReaderViewModel.onEvent calls on screen quit(fixes bugs with text being translated after quitting) + Fixed: Progress resets to 0% if leave when Reader is loading + Fixed: Book text does not show correctly after loading (v1.0.1)
This commit is contained in:
parent
9dfa06973f
commit
e033294df0
5 changed files with 862 additions and 831 deletions
|
|
@ -0,0 +1,12 @@
|
|||
package ua.acclorite.book_story.domain.use_case
|
||||
|
||||
import ua.acclorite.book_story.domain.model.Book
|
||||
import ua.acclorite.book_story.domain.repository.BookRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
class GetBookById @Inject constructor(private val repository: BookRepository) {
|
||||
|
||||
suspend fun execute(id: Int): Book? {
|
||||
return repository.getBooksById(listOf(id)).firstOrNull()
|
||||
}
|
||||
}
|
||||
|
|
@ -22,12 +22,12 @@ import androidx.compose.material3.MaterialTheme
|
|||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -84,8 +84,6 @@ fun ReaderScreenRoot() {
|
|||
val libraryViewModel: LibraryViewModel = hiltViewModel()
|
||||
val historyViewModel: HistoryViewModel = hiltViewModel()
|
||||
|
||||
val loading = remember { mutableStateOf(true) }
|
||||
|
||||
val state = viewModel.state.collectAsState()
|
||||
val mainState = mainViewModel.state.collectAsState()
|
||||
|
||||
|
|
@ -119,14 +117,11 @@ fun ReaderScreenRoot() {
|
|||
it.asString(context),
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
},
|
||||
onLoaded = {
|
||||
loading.value = false
|
||||
}
|
||||
)
|
||||
}
|
||||
LaunchedEffect(canScroll) {
|
||||
if (!canScroll && !state.value.showMenu && !loading.value) {
|
||||
if (!canScroll && !state.value.showMenu && !state.value.loading) {
|
||||
viewModel.onEvent(ReaderEvent.OnShowHideMenu(context = context))
|
||||
}
|
||||
}
|
||||
|
|
@ -135,16 +130,20 @@ fun ReaderScreenRoot() {
|
|||
firstVisibleItemIndex = firstVisibleItemIndex.value,
|
||||
firstVisibleItemOffset = firstVisibleItemOffset.value,
|
||||
navigator = navigator,
|
||||
isReaderLoading = loading.value,
|
||||
onLibraryEvent = libraryViewModel::onEvent,
|
||||
onHistoryEvent = historyViewModel::onEvent
|
||||
)
|
||||
}
|
||||
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
viewModel.clearViewModel()
|
||||
}
|
||||
}
|
||||
|
||||
ReaderScreen(
|
||||
state = state,
|
||||
mainState = mainState,
|
||||
loading = loading,
|
||||
navigator = navigator,
|
||||
onEvent = viewModel::onEvent,
|
||||
onMainEvent = mainViewModel::onEvent,
|
||||
|
|
@ -158,7 +157,6 @@ fun ReaderScreenRoot() {
|
|||
private fun ReaderScreen(
|
||||
state: State<ReaderState>,
|
||||
mainState: State<MainState>,
|
||||
loading: State<Boolean>,
|
||||
navigator: Navigator,
|
||||
onEvent: (ReaderEvent) -> Unit,
|
||||
onMainEvent: (MainEvent) -> Unit,
|
||||
|
|
@ -355,7 +353,7 @@ private fun ReaderScreen(
|
|||
.fillMaxSize()
|
||||
.background(backgroundColor)
|
||||
.then(
|
||||
if (!loading.value && toolbarHidden) {
|
||||
if (!state.value.loading && toolbarHidden) {
|
||||
Modifier
|
||||
.clickable(
|
||||
interactionSource = null,
|
||||
|
|
@ -372,7 +370,6 @@ private fun ReaderScreen(
|
|||
),
|
||||
verticalArrangement = Arrangement.spacedBy(paragraphHeight)
|
||||
) {
|
||||
if (state.value.text.isNotEmpty()) {
|
||||
item {
|
||||
DisableSelection {
|
||||
ReaderStartItem(state = state)
|
||||
|
|
@ -380,7 +377,6 @@ private fun ReaderScreen(
|
|||
|
||||
Spacer(modifier = Modifier.height(18.dp))
|
||||
}
|
||||
}
|
||||
|
||||
customItems(
|
||||
text, key = { key -> key.first }
|
||||
|
|
@ -403,7 +399,6 @@ private fun ReaderScreen(
|
|||
)
|
||||
}
|
||||
|
||||
if (state.value.text.isNotEmpty()) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(18.dp))
|
||||
|
||||
|
|
@ -419,9 +414,8 @@ private fun ReaderScreen(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (loading.value || state.value.errorMessage != null) {
|
||||
if (state.value.loading || state.value.errorMessage != null) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
|
|
@ -429,7 +423,7 @@ private fun ReaderScreen(
|
|||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
if (!loading.value) {
|
||||
if (!state.value.loading) {
|
||||
IsError(
|
||||
errorMessage = state.value.errorMessage!!.asString(),
|
||||
icon = painterResource(id = R.drawable.error),
|
||||
|
|
|
|||
|
|
@ -13,10 +13,9 @@ import ua.acclorite.book_story.presentation.data.Navigator
|
|||
|
||||
@Immutable
|
||||
sealed class ReaderEvent {
|
||||
data class OnTextIsEmpty(val onLoaded: () -> Unit) : ReaderEvent()
|
||||
data object OnTextIsEmpty : ReaderEvent()
|
||||
data class OnLoadText(
|
||||
val refreshList: (Book) -> Unit,
|
||||
val onLoaded: () -> Unit,
|
||||
val onError: (UIText) -> Unit,
|
||||
val onTextIsEmpty: () -> Unit
|
||||
) : ReaderEvent()
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ data class ReaderState(
|
|||
val isTranslating: Boolean = false,
|
||||
|
||||
val errorMessage: UIText? = null,
|
||||
val loading: Boolean = true,
|
||||
|
||||
val showMenu: Boolean = false,
|
||||
val lockMenu: Boolean = false,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.delay
|
||||
|
|
@ -31,7 +32,7 @@ import ua.acclorite.book_story.domain.model.LineWithTranslation
|
|||
import ua.acclorite.book_story.domain.model.SelectableLanguage
|
||||
import ua.acclorite.book_story.domain.use_case.DeleteLanguageModel
|
||||
import ua.acclorite.book_story.domain.use_case.DownloadLanguageModel
|
||||
import ua.acclorite.book_story.domain.use_case.GetBooksById
|
||||
import ua.acclorite.book_story.domain.use_case.GetBookById
|
||||
import ua.acclorite.book_story.domain.use_case.GetLatestHistory
|
||||
import ua.acclorite.book_story.domain.use_case.GetText
|
||||
import ua.acclorite.book_story.domain.use_case.IdentifyLanguage
|
||||
|
|
@ -42,7 +43,6 @@ import ua.acclorite.book_story.domain.util.ID
|
|||
import ua.acclorite.book_story.domain.util.LanguageCode
|
||||
import ua.acclorite.book_story.domain.util.Resource
|
||||
import ua.acclorite.book_story.domain.util.UIText
|
||||
import ua.acclorite.book_story.presentation.data.Argument
|
||||
import ua.acclorite.book_story.presentation.data.Navigator
|
||||
import ua.acclorite.book_story.presentation.data.Screen
|
||||
import ua.acclorite.book_story.presentation.data.removeKey
|
||||
|
|
@ -61,7 +61,7 @@ class ReaderViewModel @Inject constructor(
|
|||
private val updateBooks: UpdateBooks,
|
||||
private val getText: GetText,
|
||||
private val getLatestHistory: GetLatestHistory,
|
||||
private val getBooksById: GetBooksById,
|
||||
private val getBookById: GetBookById,
|
||||
private val identifyLanguage: IdentifyLanguage,
|
||||
private val isLanguageModelDownloaded: IsLanguageModelDownloaded,
|
||||
private val downloadLanguageModel: DownloadLanguageModel,
|
||||
|
|
@ -72,6 +72,8 @@ class ReaderViewModel @Inject constructor(
|
|||
private val _state = MutableStateFlow(ReaderState())
|
||||
val state = _state.asStateFlow()
|
||||
|
||||
private var eventJob = SupervisorJob()
|
||||
|
||||
private var dismissTranslatorJob: Job? = null
|
||||
private var cancelTranslationJob: Job? = null
|
||||
private var translatorJob: Job? = null
|
||||
|
|
@ -79,21 +81,18 @@ class ReaderViewModel @Inject constructor(
|
|||
private var downloadLanguageJob: Job? = null
|
||||
|
||||
fun onEvent(event: ReaderEvent) {
|
||||
viewModelScope.launch(eventJob + Dispatchers.Main) {
|
||||
when (event) {
|
||||
is ReaderEvent.OnTextIsEmpty -> {
|
||||
_state.update {
|
||||
event.onLoaded()
|
||||
it.copy(
|
||||
errorMessage = UIText.StringResource(R.string.error_no_text),
|
||||
loading = false,
|
||||
errorMessage = UIText.StringResource(R.string.error_no_text)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is ReaderEvent.OnLoadText -> {
|
||||
viewModelScope.launch {
|
||||
cancelJobs()
|
||||
joinJobs()
|
||||
|
||||
launch(Dispatchers.IO) {
|
||||
val text = getText.execute(_state.value.book.textPath)
|
||||
|
||||
|
|
@ -142,7 +141,7 @@ class ReaderViewModel @Inject constructor(
|
|||
)
|
||||
event.refreshList(_state.value.book)
|
||||
|
||||
viewModelScope.launch {
|
||||
launch {
|
||||
snapshotFlow {
|
||||
_state.value.listState.layoutInfo.totalItemsCount
|
||||
}.collectLatest { itemsCount ->
|
||||
|
|
@ -154,7 +153,7 @@ class ReaderViewModel @Inject constructor(
|
|||
var loaded = false
|
||||
for (i in 1..100) {
|
||||
try {
|
||||
_state.value.listState.scrollToItem(
|
||||
_state.value.listState.requestScrollToItem(
|
||||
index,
|
||||
offset
|
||||
)
|
||||
|
|
@ -175,7 +174,11 @@ class ReaderViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
delay(100)
|
||||
event.onLoaded()
|
||||
_state.update {
|
||||
it.copy(
|
||||
loading = false
|
||||
)
|
||||
}
|
||||
|
||||
if (
|
||||
_state.value.book.translateWhenOpen &&
|
||||
|
|
@ -194,11 +197,10 @@ class ReaderViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is ReaderEvent.OnShowHideMenu -> {
|
||||
if (_state.value.lockMenu) {
|
||||
return
|
||||
return@launch
|
||||
}
|
||||
|
||||
val shouldShow = event.show ?: !_state.value.showMenu
|
||||
|
|
@ -229,13 +231,14 @@ class ReaderViewModel @Inject constructor(
|
|||
is ReaderEvent.OnShowHideTranslatorBottomSheet -> {
|
||||
_state.update {
|
||||
it.copy(
|
||||
showTranslatorBottomSheet = event.show ?: !it.showTranslatorBottomSheet
|
||||
showTranslatorBottomSheet = event.show
|
||||
?: !it.showTranslatorBottomSheet
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is ReaderEvent.OnChangeTranslatorSettings -> {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
launch(Dispatchers.IO) {
|
||||
val book = _state.value.book
|
||||
val updatedBook = book.copy(
|
||||
enableTranslator = event.enableTranslator ?: book.enableTranslator,
|
||||
|
|
@ -243,7 +246,8 @@ class ReaderViewModel @Inject constructor(
|
|||
translateTo = event.translateTo ?: book.translateTo,
|
||||
doubleClickTranslation = event.doubleClickTranslation
|
||||
?: book.doubleClickTranslation,
|
||||
translateWhenOpen = event.translateWhenOpen ?: book.translateWhenOpen
|
||||
translateWhenOpen = event.translateWhenOpen
|
||||
?: book.translateWhenOpen
|
||||
)
|
||||
|
||||
_state.update {
|
||||
|
|
@ -271,7 +275,7 @@ class ReaderViewModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
launch {
|
||||
joinJobs()
|
||||
|
||||
translatorJob = launch(Dispatchers.IO) {
|
||||
|
|
@ -308,12 +312,14 @@ class ReaderViewModel @Inject constructor(
|
|||
|
||||
yield()
|
||||
if (_state.value.book.translateFrom == "auto") {
|
||||
val identifiedLanguages = mutableListOf<Pair<LanguageCode, ID>>()
|
||||
val identifiedLanguages =
|
||||
mutableListOf<Pair<LanguageCode, ID>>()
|
||||
val errors = mutableMapOf<ID, LineWithTranslation>()
|
||||
|
||||
for (line in linesToTranslate.toMap()) {
|
||||
yield()
|
||||
val result = identifyLanguage.execute(line.value.originalLine)
|
||||
val result =
|
||||
identifyLanguage.execute(line.value.originalLine)
|
||||
|
||||
when (result) {
|
||||
is Resource.Success -> {
|
||||
|
|
@ -390,7 +396,8 @@ class ReaderViewModel @Inject constructor(
|
|||
it.uppercase()
|
||||
}
|
||||
|
||||
val translateToCanceled = _state.value.languagesToTranslate.find {
|
||||
val translateToCanceled =
|
||||
_state.value.languagesToTranslate.find {
|
||||
it.languageCode == _state.value.book.translateTo
|
||||
}?.isCanceled ?: false
|
||||
|
||||
|
|
@ -420,7 +427,8 @@ class ReaderViewModel @Inject constructor(
|
|||
it.uppercase()
|
||||
}
|
||||
|
||||
val isCanceled = _state.value.languagesToTranslate.find {
|
||||
val isCanceled =
|
||||
_state.value.languagesToTranslate.find {
|
||||
it.languageCode == language.first
|
||||
}?.isCanceled ?: false
|
||||
|
||||
|
|
@ -460,9 +468,11 @@ class ReaderViewModel @Inject constructor(
|
|||
val line = linesToTranslate[closestLine] ?: continue
|
||||
|
||||
yield()
|
||||
val sourceLanguage = if (_state.value.book.translateFrom == "auto") {
|
||||
val sourceLanguage =
|
||||
if (_state.value.book.translateFrom == "auto") {
|
||||
when (
|
||||
val result = identifyLanguage.execute(line.originalLine)
|
||||
val result =
|
||||
identifyLanguage.execute(line.originalLine)
|
||||
) {
|
||||
is Resource.Success -> result.data!!
|
||||
is Resource.Error -> continue
|
||||
|
|
@ -556,7 +566,7 @@ class ReaderViewModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
launch {
|
||||
joinJobs()
|
||||
|
||||
dismissTranslatorJob = launch(Dispatchers.IO) {
|
||||
|
|
@ -594,7 +604,7 @@ class ReaderViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
is ReaderEvent.OnCancelTranslation -> {
|
||||
viewModelScope.launch {
|
||||
launch {
|
||||
cancelTranslationJob?.join()
|
||||
undoTranslationJob?.join()
|
||||
dismissTranslatorJob?.cancel()
|
||||
|
|
@ -627,7 +637,7 @@ class ReaderViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
is ReaderEvent.OnUndoTranslation -> {
|
||||
viewModelScope.launch {
|
||||
launch {
|
||||
cancelTranslationJob?.join()
|
||||
undoTranslationJob?.join()
|
||||
|
||||
|
|
@ -670,7 +680,7 @@ class ReaderViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
is ReaderEvent.OnDownloadLanguages -> {
|
||||
viewModelScope.launch {
|
||||
launch {
|
||||
downloadLanguageJob?.cancel()
|
||||
|
||||
downloadLanguageJob = launch(Dispatchers.IO) {
|
||||
|
|
@ -722,7 +732,8 @@ class ReaderViewModel @Inject constructor(
|
|||
languageCode = lang.languageCode,
|
||||
onCompleted = {
|
||||
launch(Dispatchers.IO) {
|
||||
val modelDownloaded = isLanguageModelDownloaded.execute(
|
||||
val modelDownloaded =
|
||||
isLanguageModelDownloaded.execute(
|
||||
lang.languageCode
|
||||
)
|
||||
|
||||
|
|
@ -828,7 +839,7 @@ class ReaderViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
is ReaderEvent.OnSelectLanguage -> {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
launch(Dispatchers.IO) {
|
||||
val editedList = _state.value.languagesToTranslate.map {
|
||||
if (it.languageCode == event.language.languageCode) {
|
||||
it.copy(
|
||||
|
|
@ -848,7 +859,7 @@ class ReaderViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
is ReaderEvent.OnUpdateLanguage -> {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
launch(Dispatchers.IO) {
|
||||
val editedList = _state.value.languagesToTranslate.map {
|
||||
if (it.languageCode == event.languageCode) {
|
||||
event.calculation(it)
|
||||
|
|
@ -866,8 +877,7 @@ class ReaderViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
is ReaderEvent.OnGoBack -> {
|
||||
viewModelScope.launch {
|
||||
cancelJobs()
|
||||
launch {
|
||||
_state.update {
|
||||
it.copy(
|
||||
lockMenu = true
|
||||
|
|
@ -879,49 +889,35 @@ class ReaderViewModel @Inject constructor(
|
|||
event.context.window.decorView
|
||||
)
|
||||
|
||||
if (_state.value.listState.layoutInfo.totalItemsCount > 0) {
|
||||
val firstVisibleItemIndex = _state.value.listState.firstVisibleItemIndex
|
||||
val firstVisibleItemOffset =
|
||||
_state.value.listState.firstVisibleItemScrollOffset
|
||||
val lastVisibleItemIndex = _state.value.listState.layoutInfo
|
||||
.visibleItemsInfo.last().index
|
||||
|
||||
val progress = if (firstVisibleItemIndex > 0) {
|
||||
if (lastVisibleItemIndex >= (_state.value.listState.layoutInfo.totalItemsCount - 1)) {
|
||||
1f
|
||||
} else {
|
||||
(firstVisibleItemIndex.toFloat() / (_state.value.text.size - 1)
|
||||
.toFloat())
|
||||
}
|
||||
} else {
|
||||
0f
|
||||
}
|
||||
|
||||
val listState = _state.value.listState
|
||||
if (
|
||||
!_state.value.loading &&
|
||||
listState.layoutInfo.totalItemsCount != 0 &&
|
||||
_state.value.text.isNotEmpty()
|
||||
) {
|
||||
_state.update {
|
||||
it.copy(
|
||||
book = it.book.copy(
|
||||
progress = progress,
|
||||
scrollIndex = firstVisibleItemIndex,
|
||||
scrollOffset = firstVisibleItemOffset
|
||||
progress = calculateProgress(),
|
||||
scrollIndex = listState.firstVisibleItemIndex,
|
||||
scrollOffset = listState.firstVisibleItemScrollOffset
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
updateBooks.execute(
|
||||
listOf(_state.value.book)
|
||||
)
|
||||
event.navigator.putArgument(
|
||||
Argument("book", _state.value.book.id)
|
||||
)
|
||||
event.refreshList(_state.value.book)
|
||||
}
|
||||
|
||||
insetsController.show(WindowInsetsCompat.Type.systemBars())
|
||||
event.navigate(event.navigator)
|
||||
}
|
||||
}
|
||||
|
||||
is ReaderEvent.OnScroll -> {
|
||||
viewModelScope.launch {
|
||||
launch {
|
||||
val scrollTo = (_state.value.text.size * event.progress).roundToInt()
|
||||
|
||||
_state.value.listState.scrollToItem(
|
||||
|
|
@ -931,7 +927,7 @@ class ReaderViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
is ReaderEvent.OnChangeProgress -> {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
launch(Dispatchers.IO) {
|
||||
_state.update {
|
||||
it.copy(
|
||||
book = it.book.copy(
|
||||
|
|
@ -945,18 +941,12 @@ class ReaderViewModel @Inject constructor(
|
|||
updateBooks.execute(
|
||||
listOf(_state.value.book)
|
||||
)
|
||||
event.navigator.putArgument(
|
||||
Argument(
|
||||
"book",
|
||||
_state.value.book.id
|
||||
)
|
||||
)
|
||||
event.refreshList(_state.value.book)
|
||||
}
|
||||
}
|
||||
|
||||
is ReaderEvent.OnShowHideSettingsBottomSheet -> {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
launch(Dispatchers.IO) {
|
||||
_state.update {
|
||||
it.copy(
|
||||
currentPage = if (it.showSettingsBottomSheet) it.currentPage else 0,
|
||||
|
|
@ -967,13 +957,13 @@ class ReaderViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
is ReaderEvent.OnScrollToSettingsPage -> {
|
||||
viewModelScope.launch {
|
||||
launch {
|
||||
event.pagerState.scrollToPage(event.page)
|
||||
}
|
||||
}
|
||||
|
||||
is ReaderEvent.OnMoveBookToAlreadyRead -> {
|
||||
viewModelScope.launch {
|
||||
launch {
|
||||
onEvent(
|
||||
ReaderEvent.OnGoBack(
|
||||
event.context,
|
||||
|
|
@ -983,16 +973,14 @@ class ReaderViewModel @Inject constructor(
|
|||
)
|
||||
)
|
||||
|
||||
val firstVisibleItemIndex = _state.value.listState.firstVisibleItemIndex
|
||||
val firstVisibleItemOffset = _state.value.listState.firstVisibleItemScrollOffset
|
||||
|
||||
_state.update {
|
||||
val listState = it.listState
|
||||
it.copy(
|
||||
book = it.book.copy(
|
||||
category = Category.ALREADY_READ,
|
||||
progress = 1f,
|
||||
scrollIndex = firstVisibleItemIndex,
|
||||
scrollOffset = firstVisibleItemOffset
|
||||
scrollIndex = listState.firstVisibleItemIndex,
|
||||
scrollOffset = listState.firstVisibleItemScrollOffset
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -1007,14 +995,12 @@ class ReaderViewModel @Inject constructor(
|
|||
}.size - 1
|
||||
)
|
||||
|
||||
cancelJobs()
|
||||
|
||||
event.navigator.navigate(Screen.LIBRARY, true)
|
||||
}
|
||||
}
|
||||
|
||||
is ReaderEvent.OnOpenTranslator -> {
|
||||
viewModelScope.launch {
|
||||
launch {
|
||||
val translatorIntent = Intent()
|
||||
val browserIntent = Intent()
|
||||
|
||||
|
|
@ -1023,7 +1009,10 @@ class ReaderViewModel @Inject constructor(
|
|||
|
||||
browserIntent.action = Intent.ACTION_WEB_SEARCH
|
||||
|
||||
translatorIntent.putExtra(Intent.EXTRA_PROCESS_TEXT, event.textToTranslate)
|
||||
translatorIntent.putExtra(
|
||||
Intent.EXTRA_PROCESS_TEXT,
|
||||
event.textToTranslate
|
||||
)
|
||||
translatorIntent.putExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, true)
|
||||
|
||||
browserIntent.putExtra(
|
||||
|
|
@ -1046,7 +1035,7 @@ class ReaderViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
is ReaderEvent.OnOpenDictionary -> {
|
||||
viewModelScope.launch {
|
||||
launch {
|
||||
val browserIntent = Intent()
|
||||
|
||||
browserIntent.action = Intent.ACTION_WEB_SEARCH
|
||||
|
|
@ -1066,13 +1055,6 @@ class ReaderViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun cancelJobs() {
|
||||
cancelTranslationJob?.cancel()
|
||||
translatorJob?.cancel()
|
||||
dismissTranslatorJob?.cancel()
|
||||
undoTranslationJob?.cancel()
|
||||
downloadLanguageJob?.cancel()
|
||||
}
|
||||
|
||||
private suspend fun joinJobs() {
|
||||
|
|
@ -1082,7 +1064,6 @@ class ReaderViewModel @Inject constructor(
|
|||
undoTranslationJob?.join()
|
||||
}
|
||||
|
||||
|
||||
private suspend fun findClosestLineToTranslate(
|
||||
translatingLines: Map<ID, LineWithTranslation>
|
||||
): ID? {
|
||||
|
|
@ -1248,8 +1229,7 @@ class ReaderViewModel @Inject constructor(
|
|||
navigator: Navigator,
|
||||
context: ComponentActivity,
|
||||
refreshList: (Book) -> Unit,
|
||||
onError: (UIText) -> Unit,
|
||||
onLoaded: () -> Unit
|
||||
onError: (UIText) -> Unit
|
||||
) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val bookId = navigator.retrieveArgument("book") as? Int
|
||||
|
|
@ -1259,65 +1239,66 @@ class ReaderViewModel @Inject constructor(
|
|||
return@launch
|
||||
}
|
||||
|
||||
val book = getBooksById.execute(listOf(bookId))
|
||||
val book = getBookById.execute(bookId)
|
||||
|
||||
if (book.isEmpty()) {
|
||||
if (book == null) {
|
||||
navigator.navigateBack()
|
||||
return@launch
|
||||
}
|
||||
|
||||
_state.update {
|
||||
ReaderState(book = book.first())
|
||||
ReaderState(book = book)
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
clear()
|
||||
launch {
|
||||
onEvent(ReaderEvent.OnShowHideMenu(false, context))
|
||||
}
|
||||
|
||||
onEvent(
|
||||
ReaderEvent.OnLoadText(
|
||||
refreshList = { refreshList(it) },
|
||||
onLoaded = {
|
||||
onLoaded()
|
||||
},
|
||||
onError = {
|
||||
onError(it)
|
||||
},
|
||||
onTextIsEmpty = {
|
||||
onEvent(ReaderEvent.OnTextIsEmpty(onLoaded = { onLoaded() }))
|
||||
onEvent(ReaderEvent.OnTextIsEmpty)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
suspend fun onUpdateProgress(
|
||||
firstVisibleItemIndex: Int,
|
||||
firstVisibleItemOffset: Int,
|
||||
navigator: Navigator,
|
||||
isReaderLoading: Boolean,
|
||||
onLibraryEvent: (LibraryEvent) -> Unit,
|
||||
onHistoryEvent: (HistoryEvent) -> Unit
|
||||
) {
|
||||
snapshotFlow {
|
||||
firstVisibleItemIndex to firstVisibleItemOffset
|
||||
}.debounce(500).collectLatest { items ->
|
||||
}
|
||||
.debounce(200)
|
||||
.collectLatest { items ->
|
||||
val listState = _state.value.listState
|
||||
if (
|
||||
!isReaderLoading &&
|
||||
!_state.value.loading &&
|
||||
_state.value.text.isNotEmpty() &&
|
||||
_state.value.listState.layoutInfo.totalItemsCount > 0
|
||||
listState.layoutInfo.totalItemsCount > 0
|
||||
) {
|
||||
val lastVisibleItemIndex = _state.value.listState.layoutInfo.visibleItemsInfo
|
||||
.last().index
|
||||
val lastVisibleItemIndex = listState
|
||||
.layoutInfo
|
||||
.visibleItemsInfo
|
||||
.last()
|
||||
.index
|
||||
val totalItemsCount = listState.layoutInfo.totalItemsCount - 1
|
||||
|
||||
val progress = if (items.first > 0) {
|
||||
if (
|
||||
lastVisibleItemIndex >= (_state.value.listState.layoutInfo.totalItemsCount - 1)
|
||||
) {
|
||||
if (lastVisibleItemIndex >= totalItemsCount) {
|
||||
1f
|
||||
} else {
|
||||
(items.first.toFloat() / (_state.value.text.size - 1).toFloat())
|
||||
items.first / (_state.value.text.size - 1).toFloat()
|
||||
}
|
||||
} else {
|
||||
0f
|
||||
|
|
@ -1338,4 +1319,48 @@ class ReaderViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateProgress(): Float {
|
||||
val listState = _state.value.listState
|
||||
|
||||
if (
|
||||
_state.value.loading ||
|
||||
listState.layoutInfo.totalItemsCount == 0 ||
|
||||
_state.value.text.isEmpty()
|
||||
) {
|
||||
return _state.value.book.progress
|
||||
}
|
||||
|
||||
val lastVisibleItemIndex = listState.layoutInfo
|
||||
.visibleItemsInfo.last().index
|
||||
val totalItemsCount = listState.layoutInfo.totalItemsCount - 1
|
||||
|
||||
if (listState.firstVisibleItemIndex == 0) {
|
||||
return 0f
|
||||
}
|
||||
|
||||
if (lastVisibleItemIndex >= totalItemsCount) {
|
||||
return 1f
|
||||
}
|
||||
|
||||
return listState.firstVisibleItemIndex / (_state.value.text.size - 1).toFloat()
|
||||
}
|
||||
|
||||
private suspend fun clear() {
|
||||
eventJob.cancel()
|
||||
eventJob.join()
|
||||
eventJob = SupervisorJob()
|
||||
}
|
||||
|
||||
fun clearViewModel() {
|
||||
viewModelScope.launch(Dispatchers.Main) {
|
||||
_state.update {
|
||||
ReaderState()
|
||||
}
|
||||
|
||||
eventJob.cancel()
|
||||
eventJob.join()
|
||||
eventJob = SupervisorJob()
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue