From 4f521bcd1ef45565ddfbc3b84aa8a687160e57bc Mon Sep 17 00:00:00 2001 From: acclorite Date: Mon, 8 Jul 2024 14:35:39 +0300 Subject: [PATCH] BookInfoScreen: Canceling BookInfoViewModel.onEvent + Resetting BookInfoState when exiting screen. --- .../screens/book_info/BookInfoScreen.kt | 7 + .../book_info/data/BookInfoViewModel.kt | 1386 +++++++++-------- 2 files changed, 714 insertions(+), 679 deletions(-) diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/screens/book_info/BookInfoScreen.kt b/app/src/main/java/ua/acclorite/book_story/presentation/screens/book_info/BookInfoScreen.kt index b56a4c59..78178fc0 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/screens/book_info/BookInfoScreen.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/screens/book_info/BookInfoScreen.kt @@ -37,6 +37,7 @@ import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarDefaults 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 @@ -92,6 +93,12 @@ fun BookInfoScreenRoot(screen: Screen.BookInfo) { ) } + DisposableEffect(Unit) { + onDispose { + viewModel.clearViewModel() + } + } + BookInfoScreen( state = state, onNavigate = { navigator.it() }, diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/screens/book_info/data/BookInfoViewModel.kt b/app/src/main/java/ua/acclorite/book_story/presentation/screens/book_info/data/BookInfoViewModel.kt index a45b6eb2..2a8983ef 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/screens/book_info/data/BookInfoViewModel.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/screens/book_info/data/BookInfoViewModel.kt @@ -1,3 +1,5 @@ +@file:Suppress("LABEL_NAME_CLASH") + package ua.acclorite.book_story.presentation.screens.book_info.data import android.content.ClipData @@ -11,6 +13,7 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job +import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -56,745 +59,750 @@ class BookInfoViewModel @Inject constructor( private val _state = MutableStateFlow(BookInfoState()) val state = _state.asStateFlow() - private var job: Job? = null - private var job2: Job? = null + private var eventJob = SupervisorJob() + + private var snackBarJob: Job? = null + private var updateJob: Job? = null fun onEvent(event: BookInfoEvent) { - when (event) { - is BookInfoEvent.OnShowHideChangeCoverBottomSheet -> { - _state.update { - it.copy( - showChangeCoverBottomSheet = !it.showChangeCoverBottomSheet - ) - } - } - - is BookInfoEvent.OnChangeCover -> { - viewModelScope.launch { - val image = event.context.contentResolver?.openInputStream(event.uri)?.use { - BitmapFactory.decodeStream(it) - } ?: return@launch - - updateCoverImageOfBook.execute( - _state.value.book, - image - ) - - val newCoverImage = - getBookById.execute(_state.value.book.id)?.coverImage ?: return@launch - + viewModelScope.launch(eventJob + Dispatchers.Main) { + when (event) { + is BookInfoEvent.OnShowHideChangeCoverBottomSheet -> { _state.update { it.copy( - book = it.book.copy( - coverImage = newCoverImage - ), - showChangeCoverBottomSheet = false, - canResetCover = canResetCover.execute(bookId = it.book.id) - ) - } - event.refreshList(_state.value.book) - } - } - - is BookInfoEvent.OnDeleteCover -> { - viewModelScope.launch { - if (_state.value.book.coverImage == null) { - return@launch - } - - updateCoverImageOfBook.execute( - bookWithOldCover = _state.value.book, - newCoverImage = null - ) - _state.update { - it.copy( - book = it.book.copy( - coverImage = null - ), - showChangeCoverBottomSheet = false, - canResetCover = canResetCover.execute(bookId = it.book.id) - ) - } - event.refreshList(_state.value.book) - } - } - - is BookInfoEvent.OnCheckCoverReset -> { - viewModelScope.launch(Dispatchers.IO) { - _state.update { - it.copy( - canResetCover = canResetCover.execute(_state.value.book.id) + showChangeCoverBottomSheet = !it.showChangeCoverBottomSheet ) } } - } - is BookInfoEvent.OnResetCoverImage -> { - viewModelScope.launch { - val result = resetCoverImage.execute(_state.value.book.id) + is BookInfoEvent.OnChangeCover -> { + launch { + val image = event.context.contentResolver?.openInputStream(event.uri)?.use { + BitmapFactory.decodeStream(it) + } ?: return@launch - if (!result) { - event.showResult(UIText.StringResource(R.string.error_could_not_reset_cover)) - return@launch - } - - val book = getBookById.execute(_state.value.book.id) - - if (book == null) { - event.showResult(UIText.StringResource(R.string.error_something_went_wrong)) - return@launch - } - - _state.update { - it.copy( - book = book, - showChangeCoverBottomSheet = false, - canResetCover = false + updateCoverImageOfBook.execute( + _state.value.book, + image ) - } - event.showResult(UIText.StringResource(R.string.cover_reset)) - event.refreshList(_state.value.book) - } - } - is BookInfoEvent.OnShowHideEditTitle -> { - viewModelScope.launch(Dispatchers.IO) { - val shouldHide = _state.value.editTitle + val newCoverImage = + getBookById.execute(_state.value.book.id)?.coverImage ?: return@launch - if (!shouldHide) { _state.update { it.copy( - titleValue = it.book.title, - hasTitleFocused = false - ) - } - } - - _state.update { - it.copy( - editTitle = !shouldHide - ) - } - } - } - - is BookInfoEvent.OnTitleRequestFocus -> { - if (!_state.value.hasTitleFocused) { - event.focusRequester.requestFocus() - _state.update { - it.copy( - hasTitleFocused = true - ) - } - } - } - - is BookInfoEvent.OnTitleValueChange -> { - _state.update { - it.copy( - titleValue = event.value - ) - } - } - - is BookInfoEvent.OnShowHideEditAuthor -> { - viewModelScope.launch(Dispatchers.IO) { - val shouldHide = _state.value.editAuthor - - if (!shouldHide) { - _state.update { - it.copy( - authorValue = it.book.author.getAsString() ?: "", - hasAuthorFocused = false - ) - } - } - - _state.update { - it.copy( - editAuthor = !shouldHide - ) - } - } - } - - is BookInfoEvent.OnAuthorRequestFocus -> { - if (!_state.value.hasAuthorFocused) { - event.focusRequester.requestFocus() - _state.update { - it.copy( - hasAuthorFocused = true - ) - } - } - } - - is BookInfoEvent.OnAuthorValueChange -> { - _state.update { - it.copy( - authorValue = event.value - ) - } - } - - is BookInfoEvent.OnShowHideEditDescription -> { - viewModelScope.launch(Dispatchers.IO) { - val shouldHide = _state.value.editDescription - - if (!shouldHide) { - _state.update { - it.copy( - descriptionValue = it.book.description ?: "", - hasDescriptionFocused = false - ) - } - } - - _state.update { - it.copy( - editDescription = !shouldHide - ) - } - } - } - - is BookInfoEvent.OnDescriptionRequestFocus -> { - if (!_state.value.hasDescriptionFocused) { - event.focusRequester.requestFocus() - _state.update { - it.copy( - hasDescriptionFocused = true - ) - } - } - } - - is BookInfoEvent.OnDescriptionValueChange -> { - _state.update { - it.copy( - descriptionValue = event.value - ) - } - } - - is BookInfoEvent.OnUpdateData -> { - viewModelScope.launch { - val title = _state.value.titleValue.trim().replace("\n", "") - val author = _state.value.authorValue.trim().replace("\n", "") - val description = _state.value.descriptionValue.trim().replace("\n", "") - - val titleChanged = title != _state.value.book.title - && _state.value.editTitle - && title.isNotBlank() - val authorChanged = author != _state.value.book.author.getAsString() - && _state.value.editAuthor - && author.isNotBlank() - val descriptionChanged = description != _state.value.book.description - && _state.value.editDescription - && description.isNotBlank() - - val book = _state.value.book.copy( - title = if (titleChanged) title else _state.value.book.title, - author = if (authorChanged) UIText.StringValue(author) - else _state.value.book.author, - description = if (descriptionChanged) description - else _state.value.book.description - ) - - updateBooks.execute(listOf(book)) - _state.update { - it.copy( - book = book - ) - } - event.refreshList(_state.value.book) - - if (state.value.editTitle) { - onEvent(BookInfoEvent.OnShowHideEditTitle) - } - - if (state.value.editAuthor) { - onEvent(BookInfoEvent.OnShowHideEditAuthor) - } - - if (state.value.editDescription) { - onEvent(BookInfoEvent.OnShowHideEditDescription) - } - } - } - - is BookInfoEvent.OnShowHideDeleteDialog -> { - _state.update { - it.copy( - showDeleteDialog = !it.showDeleteDialog - ) - } - } - - is BookInfoEvent.OnDeleteBook -> { - viewModelScope.launch { - _state.update { - it.copy( - showDeleteDialog = false - ) - } - - deleteBooks.execute(listOf(_state.value.book)) - event.refreshList() - - event.onNavigate { - navigateBack() - } - } - } - - is BookInfoEvent.OnShowHideMoveDialog -> { - _state.update { - it.copy( - showMoveDialog = !it.showMoveDialog - ) - } - } - - is BookInfoEvent.OnSelectCategory -> { - _state.update { - it.copy( - selectedCategory = event.category - ) - } - } - - is BookInfoEvent.OnMoveBook -> { - viewModelScope.launch { - _state.update { - it.copy( - showMoveDialog = false - ) - } - - _state.update { - it.copy( - book = it.book.copy( - category = it.selectedCategory - ) - ) - } - updateBooks.execute(listOf(_state.value.book)) - - event.refreshList(_state.value.book) - event.updatePage( - Category.entries.dropLastWhile { - it != _state.value.selectedCategory - }.size - 1 - ) - event.onNavigate { - navigate( - Screen.Library, - useBackAnimation = true - ) - } - } - } - - is BookInfoEvent.OnShowHideDetailsBottomSheet -> { - _state.update { - it.copy( - showDetailsBottomSheet = !it.showDetailsBottomSheet - ) - } - } - - is BookInfoEvent.OnCopyToClipboard -> { - val clipboardManager = - event.context.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager - - clipboardManager.setPrimaryClip(ClipData.newPlainText(null, event.text)) - - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) { - event.success() - } - } - - is BookInfoEvent.OnShowSnackbar -> { - viewModelScope.launch { - job?.cancel() - event.snackbarState.currentSnackbarData?.dismiss() - - if (event.durationMillis > 0) { - job = viewModelScope.launch(Dispatchers.IO) { - yield() - delay(event.durationMillis) - yield() - event.snackbarState.currentSnackbarData?.dismiss() - } - } - - val snackbar = event.snackbarState.showSnackbar( - event.text, - actionLabel = event.action - ) - - when (snackbar) { - SnackbarResult.Dismissed -> Unit - SnackbarResult.ActionPerformed -> { - event.onAction() - } - } - } - } - - is BookInfoEvent.OnLoadUpdate -> { - onEvent(BookInfoEvent.OnCancelUpdate) - - job2 = viewModelScope.launch(Dispatchers.IO) { - _state.update { - it.copy( - isLoadingUpdate = true, - editTitle = false, - editAuthor = false, - editDescription = false - ) - } - - yield() - if (!File(_state.value.book.filePath).exists()) { - onEvent( - BookInfoEvent.OnShowSnackbar( - text = event.context.getString( - R.string.file_not_found, - _state.value.book.filePath - .substringAfterLast("/") - .takeLast(25) + book = it.book.copy( + coverImage = newCoverImage ), - action = event.context.getString(R.string.retry), - onAction = { - onEvent( - BookInfoEvent.OnLoadUpdate( - snackbarState = event.snackbarState, - context = event.context + showChangeCoverBottomSheet = false, + canResetCover = canResetCover.execute(bookId = it.book.id) + ) + } + event.refreshList(_state.value.book) + } + } + + is BookInfoEvent.OnDeleteCover -> { + launch { + if (_state.value.book.coverImage == null) { + return@launch + } + + updateCoverImageOfBook.execute( + bookWithOldCover = _state.value.book, + newCoverImage = null + ) + _state.update { + it.copy( + book = it.book.copy( + coverImage = null + ), + showChangeCoverBottomSheet = false, + canResetCover = canResetCover.execute(bookId = it.book.id) + ) + } + event.refreshList(_state.value.book) + } + } + + is BookInfoEvent.OnCheckCoverReset -> { + launch(Dispatchers.IO) { + _state.update { + it.copy( + canResetCover = canResetCover.execute(_state.value.book.id) + ) + } + } + } + + is BookInfoEvent.OnResetCoverImage -> { + launch { + val result = resetCoverImage.execute(_state.value.book.id) + + if (!result) { + event.showResult(UIText.StringResource(R.string.error_could_not_reset_cover)) + return@launch + } + + val book = getBookById.execute(_state.value.book.id) + + if (book == null) { + event.showResult(UIText.StringResource(R.string.error_something_went_wrong)) + return@launch + } + + _state.update { + it.copy( + book = book, + showChangeCoverBottomSheet = false, + canResetCover = false + ) + } + event.showResult(UIText.StringResource(R.string.cover_reset)) + event.refreshList(_state.value.book) + } + } + + is BookInfoEvent.OnShowHideEditTitle -> { + launch(Dispatchers.IO) { + val shouldHide = _state.value.editTitle + + if (!shouldHide) { + _state.update { + it.copy( + titleValue = it.book.title, + hasTitleFocused = false + ) + } + } + + _state.update { + it.copy( + editTitle = !shouldHide + ) + } + } + } + + is BookInfoEvent.OnTitleRequestFocus -> { + if (!_state.value.hasTitleFocused) { + event.focusRequester.requestFocus() + _state.update { + it.copy( + hasTitleFocused = true + ) + } + } + } + + is BookInfoEvent.OnTitleValueChange -> { + _state.update { + it.copy( + titleValue = event.value + ) + } + } + + is BookInfoEvent.OnShowHideEditAuthor -> { + launch(Dispatchers.IO) { + val shouldHide = _state.value.editAuthor + + if (!shouldHide) { + _state.update { + it.copy( + authorValue = it.book.author.getAsString() ?: "", + hasAuthorFocused = false + ) + } + } + + _state.update { + it.copy( + editAuthor = !shouldHide + ) + } + } + } + + is BookInfoEvent.OnAuthorRequestFocus -> { + if (!_state.value.hasAuthorFocused) { + event.focusRequester.requestFocus() + _state.update { + it.copy( + hasAuthorFocused = true + ) + } + } + } + + is BookInfoEvent.OnAuthorValueChange -> { + _state.update { + it.copy( + authorValue = event.value + ) + } + } + + is BookInfoEvent.OnShowHideEditDescription -> { + launch(Dispatchers.IO) { + val shouldHide = _state.value.editDescription + + if (!shouldHide) { + _state.update { + it.copy( + descriptionValue = it.book.description ?: "", + hasDescriptionFocused = false + ) + } + } + + _state.update { + it.copy( + editDescription = !shouldHide + ) + } + } + } + + is BookInfoEvent.OnDescriptionRequestFocus -> { + if (!_state.value.hasDescriptionFocused) { + event.focusRequester.requestFocus() + _state.update { + it.copy( + hasDescriptionFocused = true + ) + } + } + } + + is BookInfoEvent.OnDescriptionValueChange -> { + _state.update { + it.copy( + descriptionValue = event.value + ) + } + } + + is BookInfoEvent.OnUpdateData -> { + launch { + val title = _state.value.titleValue.trim().replace("\n", "") + val author = _state.value.authorValue.trim().replace("\n", "") + val description = _state.value.descriptionValue.trim().replace("\n", "") + + val titleChanged = title != _state.value.book.title + && _state.value.editTitle + && title.isNotBlank() + val authorChanged = author != _state.value.book.author.getAsString() + && _state.value.editAuthor + && author.isNotBlank() + val descriptionChanged = description != _state.value.book.description + && _state.value.editDescription + && description.isNotBlank() + + val book = _state.value.book.copy( + title = if (titleChanged) title else _state.value.book.title, + author = if (authorChanged) UIText.StringValue(author) + else _state.value.book.author, + description = if (descriptionChanged) description + else _state.value.book.description + ) + + updateBooks.execute(listOf(book)) + _state.update { + it.copy( + book = book + ) + } + event.refreshList(_state.value.book) + + if (state.value.editTitle) { + onEvent(BookInfoEvent.OnShowHideEditTitle) + } + + if (state.value.editAuthor) { + onEvent(BookInfoEvent.OnShowHideEditAuthor) + } + + if (state.value.editDescription) { + onEvent(BookInfoEvent.OnShowHideEditDescription) + } + } + } + + is BookInfoEvent.OnShowHideDeleteDialog -> { + _state.update { + it.copy( + showDeleteDialog = !it.showDeleteDialog + ) + } + } + + is BookInfoEvent.OnDeleteBook -> { + launch { + _state.update { + it.copy( + showDeleteDialog = false + ) + } + + deleteBooks.execute(listOf(_state.value.book)) + event.refreshList() + + event.onNavigate { + navigateBack() + } + } + } + + is BookInfoEvent.OnShowHideMoveDialog -> { + _state.update { + it.copy( + showMoveDialog = !it.showMoveDialog + ) + } + } + + is BookInfoEvent.OnSelectCategory -> { + _state.update { + it.copy( + selectedCategory = event.category + ) + } + } + + is BookInfoEvent.OnMoveBook -> { + launch { + _state.update { + it.copy( + showMoveDialog = false + ) + } + + _state.update { + it.copy( + book = it.book.copy( + category = it.selectedCategory + ) + ) + } + updateBooks.execute(listOf(_state.value.book)) + + event.refreshList(_state.value.book) + event.updatePage( + Category.entries.dropLastWhile { + it != _state.value.selectedCategory + }.size - 1 + ) + event.onNavigate { + navigate( + Screen.Library, + useBackAnimation = true + ) + } + } + } + + is BookInfoEvent.OnShowHideDetailsBottomSheet -> { + _state.update { + it.copy( + showDetailsBottomSheet = !it.showDetailsBottomSheet + ) + } + } + + is BookInfoEvent.OnCopyToClipboard -> { + val clipboardManager = + event.context.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager + + clipboardManager.setPrimaryClip(ClipData.newPlainText(null, event.text)) + + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) { + event.success() + } + } + + is BookInfoEvent.OnShowSnackbar -> { + launch { + snackBarJob?.cancel() + event.snackbarState.currentSnackbarData?.dismiss() + + if (event.durationMillis > 0) { + snackBarJob = launch(Dispatchers.IO) { + yield() + delay(event.durationMillis) + yield() + event.snackbarState.currentSnackbarData?.dismiss() + } + } + + val snackbar = event.snackbarState.showSnackbar( + event.text, + actionLabel = event.action + ) + + when (snackbar) { + SnackbarResult.Dismissed -> Unit + SnackbarResult.ActionPerformed -> { + event.onAction() + } + } + } + } + + is BookInfoEvent.OnLoadUpdate -> { + updateJob?.cancel() + updateJob = launch(Dispatchers.IO) { + _state.update { + it.copy( + showConfirmUpdateDialog = false, + isLoadingUpdate = true, + editTitle = false, + editAuthor = false, + editDescription = false + ) + } + + yield() + if (!File(_state.value.book.filePath).exists()) { + onEvent( + BookInfoEvent.OnShowSnackbar( + text = event.context.getString( + R.string.file_not_found, + _state.value.book.filePath + .substringAfterLast("/") + .takeLast(25) + ), + action = event.context.getString(R.string.retry), + onAction = { + onEvent( + BookInfoEvent.OnLoadUpdate( + snackbarState = event.snackbarState, + context = event.context + ) ) - ) - }, - durationMillis = 4000L, - snackbarState = event.snackbarState + }, + durationMillis = 4000L, + snackbarState = event.snackbarState + ) + ) + delay(500) + _state.update { + it.copy( + isLoadingUpdate = false + ) + } + return@launch + } + + yield() + val updatedBook = getBookFromFile.execute(File(_state.value.book.filePath)) + yield() + + if (updatedBook is NullableBook.Null) { + onEvent( + BookInfoEvent.OnShowSnackbar( + text = updatedBook.message?.asString(event.context) + ?: event.context.getString(R.string.error_something_went_wrong_with_file), + action = event.context.getString(R.string.retry), + onAction = { + onEvent( + BookInfoEvent.OnLoadUpdate( + snackbarState = event.snackbarState, + context = event.context + ) + ) + }, + durationMillis = 4000L, + snackbarState = event.snackbarState + ) + ) + + delay(500) + _state.update { + it.copy( + isLoadingUpdate = false + ) + } + return@launch + } + yield() + + val book = _state.value.book + + var authorUpdated = false + var descriptionUpdated = false + var textUpdated = false + + if ( + updatedBook.book!!.author.asString(event.context) != + book.author.asString(event.context) + ) { + authorUpdated = true + } + if (updatedBook.book.description != book.description) { + descriptionUpdated = true + } + + val updatedText = updatedBook.text + val text = getText.execute(book.textPath) + + if (updatedText != text.map { it.value.originalLine }) { + textUpdated = true + } + + yield() + if (!authorUpdated && !descriptionUpdated && !textUpdated) { + onEvent( + BookInfoEvent.OnShowSnackbar( + event.context.getString(R.string.nothing_changed), + action = null, + durationMillis = 4000L, + snackbarState = event.snackbarState + ) + ) + delay(500) + _state.update { + it.copy( + isLoadingUpdate = false + ) + } + return@launch + } + + yield() + onEvent( + BookInfoEvent.OnShowConfirmUpdateDialog( + updatedBook = updatedBook.book to updatedBook.text, + authorUpdated = authorUpdated, + descriptionUpdated = descriptionUpdated, + textUpdated = textUpdated ) ) - delay(500) + _state.update { it.copy( isLoadingUpdate = false ) } - return@launch } + } - yield() - val updatedBook = getBookFromFile.execute(File(_state.value.book.filePath)) - yield() - - if (updatedBook is NullableBook.Null) { - onEvent( - BookInfoEvent.OnShowSnackbar( - text = updatedBook.message?.asString(event.context) - ?: event.context.getString(R.string.error_something_went_wrong_with_file), - action = event.context.getString(R.string.retry), - onAction = { - onEvent( - BookInfoEvent.OnLoadUpdate( - snackbarState = event.snackbarState, - context = event.context - ) - ) - }, - durationMillis = 4000L, - snackbarState = event.snackbarState - ) + is BookInfoEvent.OnDismissConfirmUpdateDialog -> { + _state.update { + it.copy( + showConfirmUpdateDialog = false, + updatedBook = null, + authorChanged = false, + descriptionChanged = false, + textChanged = false ) + } + } - delay(500) + is BookInfoEvent.OnShowConfirmUpdateDialog -> { + _state.update { + it.copy( + showConfirmUpdateDialog = true, + updatedBook = event.updatedBook, + authorChanged = event.authorUpdated, + descriptionChanged = event.descriptionUpdated, + textChanged = event.textUpdated + ) + } + } + + is BookInfoEvent.OnConfirmUpdate -> { + launch(Dispatchers.IO) { _state.update { it.copy( - isLoadingUpdate = false + isRefreshing = true, + showConfirmUpdateDialog = false ) } - return@launch - } - yield() - val book = _state.value.book + if (_state.value.updatedBook == null) { + onEvent( + BookInfoEvent.OnShowSnackbar( + text = event.context.getString( + R.string.error_something_went_wrong_with_file + ), + action = event.context.getString(R.string.retry), + onAction = { + onEvent( + BookInfoEvent.OnLoadUpdate( + snackbarState = event.snackbarState, + context = event.context + ) + ) + }, + durationMillis = 4000L, + snackbarState = event.snackbarState + ) + ) + delay(500) + _state.update { + it.copy( + isRefreshing = false + ) + } + return@launch + } - var authorUpdated = false - var descriptionUpdated = false - var textUpdated = false + val book = _state.value.book + val updatedBook = _state.value.updatedBook ?: return@launch - if ( - updatedBook.book!!.author.asString(event.context) != - book.author.asString(event.context) - ) { - authorUpdated = true - } - if (updatedBook.book.description != book.description) { - descriptionUpdated = true - } + val author = if (_state.value.authorChanged) { + updatedBook.first.author + } else { + book.author + } + val description = if (_state.value.descriptionChanged) { + updatedBook.first.description + } else { + book.description + } - val updatedText = updatedBook.text - val text = getText.execute(book.textPath) + _state.update { + it.copy( + book = it.book.copy( + author = author, + description = description + ) + ) + } - if (updatedText != text.map { it.value.originalLine }) { - textUpdated = true - } + if (_state.value.textChanged) { + val isSuccess = updateBookWithText.execute( + book = _state.value.book, + text = updatedBook.second + ) + + if (!isSuccess) { + onEvent( + BookInfoEvent.OnShowSnackbar( + text = event.context.getString( + R.string.error_something_went_wrong_with_file + ), + action = event.context.getString(R.string.retry), + onAction = { + onEvent( + BookInfoEvent.OnLoadUpdate( + snackbarState = event.snackbarState, + context = event.context + ) + ) + }, + durationMillis = 4000L, + snackbarState = event.snackbarState + ) + ) + delay(500) + _state.update { + it.copy( + isRefreshing = false + ) + } + return@launch + } + } else { + updateBooks.execute( + listOf( + _state.value.book + ) + ) + } + + if (_state.value.textChanged) { + val newBook = getBookById.execute(_state.value.book.id) + + if (newBook == null) { + onEvent( + BookInfoEvent.OnShowSnackbar( + text = event.context.getString( + R.string.error_something_went_wrong_with_file + ), + action = event.context.getString(R.string.retry), + onAction = { + onEvent( + BookInfoEvent.OnLoadUpdate( + snackbarState = event.snackbarState, + context = event.context + ) + ) + }, + durationMillis = 4000L, + snackbarState = event.snackbarState + ) + ) + delay(500) + _state.update { + it.copy( + isRefreshing = false + ) + } + return@launch + } + + _state.update { + it.copy( + book = newBook, + authorChanged = false, + descriptionChanged = false, + textChanged = false, + updatedBook = null + ) + } + } + event.refreshList(_state.value.book) - yield() - if (!authorUpdated && !descriptionUpdated && !textUpdated) { onEvent( BookInfoEvent.OnShowSnackbar( - event.context.getString(R.string.nothing_changed), + event.context.getString(R.string.book_updated), action = null, durationMillis = 4000L, snackbarState = event.snackbarState ) ) - delay(500) - _state.update { - it.copy( - isLoadingUpdate = false - ) - } - return@launch - } - yield() - onEvent( - BookInfoEvent.OnShowConfirmUpdateDialog( - updatedBook = updatedBook.book to updatedBook.text, - authorUpdated = authorUpdated, - descriptionUpdated = descriptionUpdated, - textUpdated = textUpdated - ) - ) - - _state.update { - it.copy( - isLoadingUpdate = false - ) - } - } - } - - is BookInfoEvent.OnDismissConfirmUpdateDialog -> { - _state.update { - it.copy( - showConfirmUpdateDialog = false, - updatedBook = null, - authorChanged = false, - descriptionChanged = false, - textChanged = false - ) - } - } - - is BookInfoEvent.OnShowConfirmUpdateDialog -> { - _state.update { - it.copy( - showConfirmUpdateDialog = true, - updatedBook = event.updatedBook, - authorChanged = event.authorUpdated, - descriptionChanged = event.descriptionUpdated, - textChanged = event.textUpdated - ) - } - } - - is BookInfoEvent.OnConfirmUpdate -> { - viewModelScope.launch(Dispatchers.IO) { - _state.update { - it.copy( - isRefreshing = true, - showConfirmUpdateDialog = false - ) - } - - if (_state.value.updatedBook == null) { - onEvent( - BookInfoEvent.OnShowSnackbar( - text = event.context.getString( - R.string.error_something_went_wrong_with_file - ), - action = event.context.getString(R.string.retry), - onAction = { - onEvent( - BookInfoEvent.OnLoadUpdate( - snackbarState = event.snackbarState, - context = event.context - ) - ) - }, - durationMillis = 4000L, - snackbarState = event.snackbarState - ) - ) delay(500) _state.update { it.copy( isRefreshing = false ) } - return@launch - } - - val book = _state.value.book - val updatedBook = _state.value.updatedBook ?: return@launch - - val author = if (_state.value.authorChanged) { - updatedBook.first.author - } else { - book.author - } - val description = if (_state.value.descriptionChanged) { - updatedBook.first.description - } else { - book.description } + } + is BookInfoEvent.OnCancelUpdate -> { _state.update { + updateJob?.cancel() + it.copy( - book = it.book.copy( - author = author, - description = description - ) - ) - } - - if (_state.value.textChanged) { - val isSuccess = updateBookWithText.execute( - book = _state.value.book, - text = updatedBook.second - ) - - if (!isSuccess) { - onEvent( - BookInfoEvent.OnShowSnackbar( - text = event.context.getString( - R.string.error_something_went_wrong_with_file - ), - action = event.context.getString(R.string.retry), - onAction = { - onEvent( - BookInfoEvent.OnLoadUpdate( - snackbarState = event.snackbarState, - context = event.context - ) - ) - }, - durationMillis = 4000L, - snackbarState = event.snackbarState - ) - ) - delay(500) - _state.update { - it.copy( - isRefreshing = false - ) - } - return@launch - } - } else { - updateBooks.execute( - listOf( - _state.value.book - ) - ) - } - - if (_state.value.textChanged) { - val newBook = getBookById.execute(_state.value.book.id) - - if (newBook == null) { - onEvent( - BookInfoEvent.OnShowSnackbar( - text = event.context.getString( - R.string.error_something_went_wrong_with_file - ), - action = event.context.getString(R.string.retry), - onAction = { - onEvent( - BookInfoEvent.OnLoadUpdate( - snackbarState = event.snackbarState, - context = event.context - ) - ) - }, - durationMillis = 4000L, - snackbarState = event.snackbarState - ) - ) - delay(500) - _state.update { - it.copy( - isRefreshing = false - ) - } - return@launch - } - - _state.update { - it.copy( - book = newBook, - authorChanged = false, - descriptionChanged = false, - textChanged = false, - updatedBook = null - ) - } - } - event.refreshList(_state.value.book) - - onEvent( - BookInfoEvent.OnShowSnackbar( - event.context.getString(R.string.book_updated), - action = null, - durationMillis = 4000L, - snackbarState = event.snackbarState - ) - ) - - delay(500) - _state.update { - it.copy( - isRefreshing = false + showConfirmUpdateDialog = false, + isLoadingUpdate = false ) } } - } - is BookInfoEvent.OnCancelUpdate -> { - _state.update { - job2?.cancel() - it.copy( - showConfirmUpdateDialog = false, - isLoadingUpdate = false - ) - } - } - - is BookInfoEvent.OnNavigateToReaderScreen -> { - viewModelScope.launch { - onEvent(BookInfoEvent.OnCancelUpdate) - _state.value.book.id.let { - insertHistory.execute( - listOf( - History( - bookId = it, - book = null, - time = Date().time + is BookInfoEvent.OnNavigateToReaderScreen -> { + launch { + onEvent(BookInfoEvent.OnCancelUpdate) + _state.value.book.id.let { + insertHistory.execute( + listOf( + History( + bookId = it, + book = null, + time = Date().time + ) ) ) - ) - } - event.onNavigate { - navigate( - Screen.Reader(_state.value.book.id) - ) + } + event.onNavigate { + navigate( + Screen.Reader(_state.value.book.id) + ) + } } } } @@ -802,7 +810,7 @@ class BookInfoViewModel @Inject constructor( } fun init(screen: Screen.BookInfo, onNavigate: OnNavigate) { - viewModelScope.launch { + viewModelScope.launch(Dispatchers.IO) { val book = getBookById.execute(screen.bookId) if (book == null) { @@ -817,7 +825,27 @@ class BookInfoViewModel @Inject constructor( book = book ) } + clear() + onEvent(BookInfoEvent.OnCheckCoverReset) } } + + private suspend fun clear() { + eventJob.cancel() + eventJob.join() + eventJob = SupervisorJob() + } + + fun clearViewModel() { + viewModelScope.launch(Dispatchers.Main) { + _state.update { + BookInfoState() + } + + eventJob.cancel() + eventJob.join() + eventJob = SupervisorJob() + } + } } \ No newline at end of file