BookInfoScreen: Editing Author and Description + Fixed slow loading (caused by BookInfoEvent.OnCheckCoverReset).
This commit is contained in:
parent
45c1bd2b80
commit
936627e742
11 changed files with 483 additions and 144 deletions
|
|
@ -16,7 +16,7 @@ class BookMapperImpl @Inject constructor() : BookMapper {
|
||||||
scrollIndex = book.scrollIndex,
|
scrollIndex = book.scrollIndex,
|
||||||
scrollOffset = book.scrollOffset,
|
scrollOffset = book.scrollOffset,
|
||||||
progress = book.progress,
|
progress = book.progress,
|
||||||
author = book.author.string,
|
author = book.author.getAsString(),
|
||||||
textPath = book.textPath,
|
textPath = book.textPath,
|
||||||
description = book.description,
|
description = book.description,
|
||||||
image = if (book.coverImage != null) book.coverImage.toString() else null,
|
image = if (book.coverImage != null) book.coverImage.toString() else null,
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,9 @@ import java.io.Serializable
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
@Immutable
|
@Immutable
|
||||||
sealed class UIText(val string: String?) : Parcelable {
|
sealed class UIText : Parcelable {
|
||||||
data class StringValue(val value: String) : UIText(value)
|
data class StringValue(val value: String) : UIText()
|
||||||
class StringResource(@StringRes val resId: Int, vararg val args: Serializable) : UIText(null)
|
class StringResource(@StringRes val resId: Int, vararg val args: Serializable) : UIText()
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun asString(): String {
|
fun asString(): String {
|
||||||
|
|
@ -29,4 +29,11 @@ sealed class UIText(val string: String?) : Parcelable {
|
||||||
is StringResource -> context.getString(resId, *args)
|
is StringResource -> context.getString(resId, *args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getAsString(): String? {
|
||||||
|
return when (this) {
|
||||||
|
is StringValue -> value
|
||||||
|
is StringResource -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
package ua.acclorite.book_story.presentation.screens.book_info
|
package ua.acclorite.book_story.presentation.screens.book_info
|
||||||
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.animation.core.tween
|
|
||||||
import androidx.compose.animation.expandHorizontally
|
import androidx.compose.animation.expandHorizontally
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
|
|
@ -15,6 +13,7 @@ import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
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.imePadding
|
||||||
import androidx.compose.foundation.layout.navigationBars
|
import androidx.compose.foundation.layout.navigationBars
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
|
|
@ -24,10 +23,7 @@ import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.material.ExperimentalMaterialApi
|
import androidx.compose.material.ExperimentalMaterialApi
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
|
||||||
import androidx.compose.material.icons.filled.Done
|
|
||||||
import androidx.compose.material.icons.filled.PlayArrow
|
import androidx.compose.material.icons.filled.PlayArrow
|
||||||
import androidx.compose.material.icons.filled.Refresh
|
|
||||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||||
import androidx.compose.material.pullrefresh.pullRefresh
|
import androidx.compose.material.pullrefresh.pullRefresh
|
||||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||||
|
|
@ -44,32 +40,25 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.derivedStateOf
|
|
||||||
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.graphics.Color
|
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import ua.acclorite.book_story.R
|
import ua.acclorite.book_story.R
|
||||||
import ua.acclorite.book_story.domain.util.OnNavigate
|
import ua.acclorite.book_story.domain.util.OnNavigate
|
||||||
import ua.acclorite.book_story.presentation.components.AnimatedTopAppBar
|
|
||||||
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
|
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
|
||||||
import ua.acclorite.book_story.presentation.components.CustomIconButton
|
|
||||||
import ua.acclorite.book_story.presentation.components.CustomSnackbar
|
import ua.acclorite.book_story.presentation.components.CustomSnackbar
|
||||||
import ua.acclorite.book_story.presentation.components.GoBackButton
|
|
||||||
import ua.acclorite.book_story.presentation.data.LocalNavigator
|
import ua.acclorite.book_story.presentation.data.LocalNavigator
|
||||||
import ua.acclorite.book_story.presentation.data.Screen
|
import ua.acclorite.book_story.presentation.data.Screen
|
||||||
import ua.acclorite.book_story.presentation.screens.book_info.components.BookInfoBackground
|
import ua.acclorite.book_story.presentation.screens.book_info.components.BookInfoBackground
|
||||||
import ua.acclorite.book_story.presentation.screens.book_info.components.BookInfoDescriptionSection
|
import ua.acclorite.book_story.presentation.screens.book_info.components.BookInfoDescriptionSection
|
||||||
import ua.acclorite.book_story.presentation.screens.book_info.components.BookInfoInfoSection
|
import ua.acclorite.book_story.presentation.screens.book_info.components.BookInfoInfoSection
|
||||||
import ua.acclorite.book_story.presentation.screens.book_info.components.BookInfoMoreDropDown
|
|
||||||
import ua.acclorite.book_story.presentation.screens.book_info.components.BookInfoStatisticSection
|
import ua.acclorite.book_story.presentation.screens.book_info.components.BookInfoStatisticSection
|
||||||
|
import ua.acclorite.book_story.presentation.screens.book_info.components.BookInfoTopBar
|
||||||
import ua.acclorite.book_story.presentation.screens.book_info.components.change_cover_bottom_sheet.BookInfoChangeCoverBottomSheet
|
import ua.acclorite.book_story.presentation.screens.book_info.components.change_cover_bottom_sheet.BookInfoChangeCoverBottomSheet
|
||||||
import ua.acclorite.book_story.presentation.screens.book_info.components.confirm_update_dialog.BookInfoConfirmUpdateDialog
|
import ua.acclorite.book_story.presentation.screens.book_info.components.confirm_update_dialog.BookInfoConfirmUpdateDialog
|
||||||
import ua.acclorite.book_story.presentation.screens.book_info.components.details_bottom_sheet.BookInfoDetailsBottomSheet
|
import ua.acclorite.book_story.presentation.screens.book_info.components.details_bottom_sheet.BookInfoDetailsBottomSheet
|
||||||
|
|
@ -84,8 +73,6 @@ import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent
|
||||||
import ua.acclorite.book_story.presentation.screens.history.data.HistoryViewModel
|
import ua.acclorite.book_story.presentation.screens.history.data.HistoryViewModel
|
||||||
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.library.data.LibraryViewModel
|
import ua.acclorite.book_story.presentation.screens.library.data.LibraryViewModel
|
||||||
import ua.acclorite.book_story.presentation.ui.DefaultTransition
|
|
||||||
import ua.acclorite.book_story.presentation.ui.Transitions
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BookInfoScreenRoot(screen: Screen.BookInfo) {
|
fun BookInfoScreenRoot(screen: Screen.BookInfo) {
|
||||||
|
|
@ -140,7 +127,6 @@ private fun BookInfoScreen(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
val firstVisibleItemIndex by remember { derivedStateOf { listState.firstVisibleItemIndex } }
|
|
||||||
|
|
||||||
if (state.value.showChangeCoverBottomSheet) {
|
if (state.value.showChangeCoverBottomSheet) {
|
||||||
BookInfoChangeCoverBottomSheet(
|
BookInfoChangeCoverBottomSheet(
|
||||||
|
|
@ -185,102 +171,22 @@ private fun BookInfoScreen(
|
||||||
Scaffold(
|
Scaffold(
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
.imePadding()
|
||||||
.windowInsetsPadding(WindowInsets.navigationBars)
|
.windowInsetsPadding(WindowInsets.navigationBars)
|
||||||
.pullRefresh(refreshState)
|
.pullRefresh(refreshState)
|
||||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
containerColor = MaterialTheme.colorScheme.surface,
|
containerColor = MaterialTheme.colorScheme.surface,
|
||||||
topBar = {
|
topBar = {
|
||||||
AnimatedTopAppBar(
|
BookInfoTopBar(
|
||||||
containerColor = Color.Transparent,
|
|
||||||
scrollBehavior = scrollBehavior,
|
|
||||||
isTopBarScrolled = null,
|
|
||||||
|
|
||||||
content1NavigationIcon = {
|
|
||||||
if (state.value.editTitle) {
|
|
||||||
CustomIconButton(
|
|
||||||
icon = Icons.AutoMirrored.Filled.ArrowBack,
|
|
||||||
contentDescription = R.string.hide_edit_title_content_desc,
|
|
||||||
disableOnClick = true
|
|
||||||
) {
|
|
||||||
onEvent(BookInfoEvent.OnShowHideEditTitle)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
GoBackButton(onNavigate = onNavigate, enabled = !state.value.isRefreshing) {
|
|
||||||
onEvent(BookInfoEvent.OnCancelUpdate)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
content1Title = {
|
|
||||||
DefaultTransition(visible = firstVisibleItemIndex > 0) {
|
|
||||||
Text(
|
|
||||||
state.value.book.title,
|
|
||||||
style = MaterialTheme.typography.titleLarge,
|
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
maxLines = 1
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
content1Actions = {
|
|
||||||
CustomIconButton(
|
|
||||||
icon = Icons.Default.Refresh,
|
|
||||||
contentDescription = R.string.refresh_book_content_desc,
|
|
||||||
disableOnClick = false,
|
|
||||||
enabled = !state.value.isRefreshing && !state.value.isLoadingUpdate
|
|
||||||
) {
|
|
||||||
onEvent(
|
|
||||||
BookInfoEvent.OnLoadUpdate(
|
|
||||||
snackbarState,
|
|
||||||
context
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Box {
|
|
||||||
DefaultTransition(
|
|
||||||
visible = !state.value.editTitle
|
|
||||||
) {
|
|
||||||
BookInfoMoreDropDown(
|
|
||||||
state = state,
|
state = state,
|
||||||
onEvent = onEvent,
|
onEvent = onEvent,
|
||||||
|
onLibraryEvent = onLibraryEvent,
|
||||||
|
onHistoryEvent = onHistoryEvent,
|
||||||
|
onNavigate = onNavigate,
|
||||||
|
scrollBehavior = scrollBehavior,
|
||||||
|
listState = listState,
|
||||||
snackbarState = snackbarState
|
snackbarState = snackbarState
|
||||||
)
|
)
|
||||||
}
|
|
||||||
CustomAnimatedVisibility(
|
|
||||||
visible = state.value.editTitle,
|
|
||||||
enter = Transitions.DefaultTransitionIn,
|
|
||||||
exit = fadeOut(tween(200))
|
|
||||||
) {
|
|
||||||
CustomIconButton(
|
|
||||||
icon = Icons.Default.Done,
|
|
||||||
contentDescription = R.string.apply_changes_content_desc,
|
|
||||||
disableOnClick = true,
|
|
||||||
enabled = !state.value.isRefreshing &&
|
|
||||||
state.value.titleValue.isNotBlank() &&
|
|
||||||
state.value.titleValue.trim() !=
|
|
||||||
state.value.book.title.trim(),
|
|
||||||
color = if (state.value.titleValue.isNotBlank()
|
|
||||||
&& state.value.titleValue != state.value.book.title
|
|
||||||
) MaterialTheme.colorScheme.onSurface
|
|
||||||
else MaterialTheme.colorScheme.onSurfaceVariant
|
|
||||||
) {
|
|
||||||
onEvent(
|
|
||||||
BookInfoEvent.OnUpdateTitle(
|
|
||||||
refreshList = {
|
|
||||||
onLibraryEvent(LibraryEvent.OnUpdateBook(it))
|
|
||||||
onHistoryEvent(HistoryEvent.OnUpdateBook(it))
|
|
||||||
}
|
|
||||||
))
|
|
||||||
Toast.makeText(
|
|
||||||
context,
|
|
||||||
context.getString(R.string.title_changed),
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
bottomBar = {
|
bottomBar = {
|
||||||
CustomSnackbar(
|
CustomSnackbar(
|
||||||
|
|
@ -324,7 +230,10 @@ private fun BookInfoScreen(
|
||||||
item {
|
item {
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
// Description
|
// Description
|
||||||
BookInfoDescriptionSection(state = state)
|
BookInfoDescriptionSection(
|
||||||
|
state = state,
|
||||||
|
onEvent = onEvent
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -382,8 +291,24 @@ private fun BookInfoScreen(
|
||||||
}
|
}
|
||||||
|
|
||||||
BackHandler {
|
BackHandler {
|
||||||
|
var exitedEditMode = false
|
||||||
|
|
||||||
if (state.value.editTitle) {
|
if (state.value.editTitle) {
|
||||||
onEvent(BookInfoEvent.OnShowHideEditTitle)
|
onEvent(BookInfoEvent.OnShowHideEditTitle)
|
||||||
|
exitedEditMode = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.value.editAuthor) {
|
||||||
|
onEvent(BookInfoEvent.OnShowHideEditAuthor)
|
||||||
|
exitedEditMode = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.value.editDescription) {
|
||||||
|
onEvent(BookInfoEvent.OnShowHideEditDescription)
|
||||||
|
exitedEditMode = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exitedEditMode) {
|
||||||
return@BackHandler
|
return@BackHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,93 @@
|
||||||
package ua.acclorite.book_story.presentation.screens.book_info.components
|
package ua.acclorite.book_story.presentation.screens.book_info.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
import androidx.compose.foundation.combinedClickable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
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.State
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
|
import androidx.compose.ui.focus.focusRequester
|
||||||
|
import androidx.compose.ui.graphics.SolidColor
|
||||||
|
import androidx.compose.ui.layout.onGloballyPositioned
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
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.screens.book_info.data.BookInfoEvent
|
||||||
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoState
|
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoState
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Description section.
|
* Description section.
|
||||||
|
*
|
||||||
|
* @param state [BookInfoState].
|
||||||
|
* @param onEvent [BookInfoEvent] callback.
|
||||||
*/
|
*/
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun BookInfoDescriptionSection(state: State<BookInfoState>) {
|
fun BookInfoDescriptionSection(
|
||||||
SelectionContainer {
|
state: State<BookInfoState>,
|
||||||
|
onEvent: (BookInfoEvent) -> Unit
|
||||||
|
) {
|
||||||
|
val descriptionFocusRequester = remember { FocusRequester() }
|
||||||
|
|
||||||
|
if (!state.value.editDescription) {
|
||||||
Text(
|
Text(
|
||||||
if (state.value.book.description?.isNotBlank() == true) state.value.book.description!!
|
if (state.value.book.description?.isNotBlank() == true) state.value.book.description!!
|
||||||
else stringResource(id = R.string.error_no_description),
|
else stringResource(id = R.string.error_no_description),
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
textAlign = TextAlign.Start,
|
textAlign = TextAlign.Start,
|
||||||
modifier = Modifier.padding(horizontal = 24.dp)
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 24.dp)
|
||||||
|
.combinedClickable(
|
||||||
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
|
indication = null,
|
||||||
|
onClick = {},
|
||||||
|
onLongClick = {
|
||||||
|
onEvent(BookInfoEvent.OnShowHideEditDescription)
|
||||||
|
}
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
BasicTextField(
|
||||||
|
value = state.value.descriptionValue,
|
||||||
|
textStyle = MaterialTheme.typography.bodyLarge.copy(
|
||||||
|
color = MaterialTheme.colorScheme.onSurface
|
||||||
|
),
|
||||||
|
onValueChange = {
|
||||||
|
if (it.length < 5000 || it.length < state.value.authorValue.length) {
|
||||||
|
onEvent(BookInfoEvent.OnDescriptionValueChange(it))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 24.dp)
|
||||||
|
.focusRequester(descriptionFocusRequester)
|
||||||
|
.onGloballyPositioned {
|
||||||
|
onEvent(BookInfoEvent.OnDescriptionRequestFocus(descriptionFocusRequester))
|
||||||
|
},
|
||||||
|
keyboardOptions = KeyboardOptions(KeyboardCapitalization.Sentences),
|
||||||
|
cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurfaceVariant)
|
||||||
|
) { innerText ->
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
contentAlignment = Alignment.CenterStart
|
||||||
|
) {
|
||||||
|
innerText()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(96.dp))
|
Spacer(modifier = Modifier.height(96.dp))
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.text.BasicTextField
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Image
|
import androidx.compose.material.icons.filled.Image
|
||||||
import androidx.compose.material.icons.outlined.Person
|
import androidx.compose.material.icons.outlined.Person
|
||||||
|
|
@ -56,7 +55,8 @@ fun BookInfoInfoSection(
|
||||||
state: State<BookInfoState>,
|
state: State<BookInfoState>,
|
||||||
onEvent: (BookInfoEvent) -> Unit
|
onEvent: (BookInfoEvent) -> Unit
|
||||||
) {
|
) {
|
||||||
val focusRequester = remember { FocusRequester() }
|
val titleFocusRequester = remember { FocusRequester() }
|
||||||
|
val authorFocusRequester = remember { FocusRequester() }
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically, modifier = Modifier
|
verticalAlignment = Alignment.CenterVertically, modifier = Modifier
|
||||||
|
|
@ -135,9 +135,9 @@ fun BookInfoInfoSection(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.focusRequester(focusRequester)
|
.focusRequester(titleFocusRequester)
|
||||||
.onGloballyPositioned {
|
.onGloballyPositioned {
|
||||||
onEvent(BookInfoEvent.OnRequestFocus(focusRequester))
|
onEvent(BookInfoEvent.OnTitleRequestFocus(titleFocusRequester))
|
||||||
},
|
},
|
||||||
keyboardOptions = KeyboardOptions(KeyboardCapitalization.Words),
|
keyboardOptions = KeyboardOptions(KeyboardCapitalization.Words),
|
||||||
cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurfaceVariant)
|
cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurfaceVariant)
|
||||||
|
|
@ -160,19 +160,54 @@ fun BookInfoInfoSection(
|
||||||
tint = MaterialTheme.colorScheme.onSurfaceVariant
|
tint = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.width(4.dp))
|
Spacer(modifier = Modifier.width(4.dp))
|
||||||
SelectionContainer {
|
|
||||||
|
if (!state.value.editAuthor) {
|
||||||
Text(
|
Text(
|
||||||
state.value.book.author.asString(),
|
state.value.book.author.asString(),
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth(),
|
.fillMaxWidth()
|
||||||
|
.combinedClickable(
|
||||||
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
|
indication = null,
|
||||||
|
onClick = {},
|
||||||
|
onLongClick = {
|
||||||
|
onEvent(BookInfoEvent.OnShowHideEditAuthor)
|
||||||
|
}
|
||||||
|
),
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis
|
overflow = TextOverflow.Ellipsis
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
BasicTextField(
|
||||||
|
singleLine = true,
|
||||||
|
value = state.value.authorValue,
|
||||||
|
textStyle = MaterialTheme.typography.bodyLarge.copy(
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
),
|
||||||
|
onValueChange = {
|
||||||
|
if (it.length < 50 || it.length < state.value.authorValue.length) {
|
||||||
|
onEvent(BookInfoEvent.OnAuthorValueChange(it))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.focusRequester(authorFocusRequester)
|
||||||
|
.onGloballyPositioned {
|
||||||
|
onEvent(BookInfoEvent.OnAuthorRequestFocus(authorFocusRequester))
|
||||||
|
},
|
||||||
|
keyboardOptions = KeyboardOptions(KeyboardCapitalization.Words),
|
||||||
|
cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurfaceVariant)
|
||||||
|
) { innerText ->
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
contentAlignment = Alignment.CenterStart
|
||||||
|
) {
|
||||||
|
innerText()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,188 @@
|
||||||
|
package ua.acclorite.book_story.presentation.screens.book_info.components
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
|
import androidx.compose.material.icons.filled.Done
|
||||||
|
import androidx.compose.material.icons.filled.Refresh
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.SnackbarHostState
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import ua.acclorite.book_story.R
|
||||||
|
import ua.acclorite.book_story.domain.util.OnNavigate
|
||||||
|
import ua.acclorite.book_story.presentation.components.AnimatedTopAppBar
|
||||||
|
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
|
||||||
|
import ua.acclorite.book_story.presentation.components.CustomIconButton
|
||||||
|
import ua.acclorite.book_story.presentation.components.GoBackButton
|
||||||
|
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent
|
||||||
|
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoState
|
||||||
|
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.ui.DefaultTransition
|
||||||
|
import ua.acclorite.book_story.presentation.ui.Transitions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Book Info Top Bar.
|
||||||
|
*
|
||||||
|
* @param state [BookInfoState].
|
||||||
|
* @param onEvent [BookInfoEvent] callback.
|
||||||
|
* @param onLibraryEvent [LibraryEvent] callback.
|
||||||
|
* @param onHistoryEvent [HistoryEvent] callback.
|
||||||
|
* @param onNavigate Navigator callback.
|
||||||
|
* @param scrollBehavior [TopAppBarScrollBehavior].
|
||||||
|
* @param listState [LazyListState].
|
||||||
|
* @param snackbarState [SnackbarHostState].
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun BookInfoTopBar(
|
||||||
|
state: State<BookInfoState>,
|
||||||
|
onEvent: (BookInfoEvent) -> Unit,
|
||||||
|
onLibraryEvent: (LibraryEvent) -> Unit,
|
||||||
|
onHistoryEvent: (HistoryEvent) -> Unit,
|
||||||
|
onNavigate: OnNavigate,
|
||||||
|
scrollBehavior: TopAppBarScrollBehavior,
|
||||||
|
listState: LazyListState,
|
||||||
|
snackbarState: SnackbarHostState
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val firstVisibleItemIndex by remember { derivedStateOf { listState.firstVisibleItemIndex } }
|
||||||
|
|
||||||
|
AnimatedTopAppBar(
|
||||||
|
containerColor = Color.Transparent,
|
||||||
|
scrollBehavior = scrollBehavior,
|
||||||
|
isTopBarScrolled = null,
|
||||||
|
|
||||||
|
content1NavigationIcon = {
|
||||||
|
if (
|
||||||
|
state.value.editTitle ||
|
||||||
|
state.value.editAuthor ||
|
||||||
|
state.value.editDescription
|
||||||
|
) {
|
||||||
|
CustomIconButton(
|
||||||
|
icon = Icons.AutoMirrored.Filled.ArrowBack,
|
||||||
|
contentDescription = R.string.exit_editing_content_desc,
|
||||||
|
disableOnClick = true
|
||||||
|
) {
|
||||||
|
if (state.value.editTitle) {
|
||||||
|
onEvent(BookInfoEvent.OnShowHideEditTitle)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.value.editAuthor) {
|
||||||
|
onEvent(BookInfoEvent.OnShowHideEditAuthor)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.value.editDescription) {
|
||||||
|
onEvent(BookInfoEvent.OnShowHideEditDescription)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
GoBackButton(onNavigate = onNavigate, enabled = !state.value.isRefreshing) {
|
||||||
|
onEvent(BookInfoEvent.OnCancelUpdate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
content1Title = {
|
||||||
|
DefaultTransition(
|
||||||
|
firstVisibleItemIndex > 0 && !state.value.editTitle
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
state.value.book.title,
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = 1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
content1Actions = {
|
||||||
|
CustomIconButton(
|
||||||
|
icon = Icons.Default.Refresh,
|
||||||
|
contentDescription = R.string.refresh_book_content_desc,
|
||||||
|
disableOnClick = false,
|
||||||
|
enabled = !state.value.isRefreshing && !state.value.isLoadingUpdate
|
||||||
|
) {
|
||||||
|
onEvent(
|
||||||
|
BookInfoEvent.OnLoadUpdate(
|
||||||
|
snackbarState,
|
||||||
|
context
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Box {
|
||||||
|
DefaultTransition(
|
||||||
|
visible = !state.value.editTitle &&
|
||||||
|
!state.value.editAuthor &&
|
||||||
|
!state.value.editDescription
|
||||||
|
) {
|
||||||
|
BookInfoMoreDropDown(
|
||||||
|
state = state,
|
||||||
|
onEvent = onEvent,
|
||||||
|
snackbarState = snackbarState
|
||||||
|
)
|
||||||
|
}
|
||||||
|
CustomAnimatedVisibility(
|
||||||
|
visible = state.value.editTitle ||
|
||||||
|
state.value.editAuthor ||
|
||||||
|
state.value.editDescription,
|
||||||
|
enter = Transitions.DefaultTransitionIn,
|
||||||
|
exit = fadeOut(tween(200))
|
||||||
|
) {
|
||||||
|
CustomIconButton(
|
||||||
|
icon = Icons.Default.Done,
|
||||||
|
contentDescription = R.string.apply_changes_content_desc,
|
||||||
|
disableOnClick = true,
|
||||||
|
enabled = !state.value.isRefreshing &&
|
||||||
|
(state.value.titleValue.isNotBlank() &&
|
||||||
|
state.value.titleValue.trim() !=
|
||||||
|
state.value.book.title.trim()) ||
|
||||||
|
|
||||||
|
(state.value.authorValue.isNotBlank() &&
|
||||||
|
state.value.authorValue.trim() !=
|
||||||
|
state.value.book.author.getAsString()?.trim()) ||
|
||||||
|
|
||||||
|
(state.value.descriptionValue.isNotBlank() &&
|
||||||
|
state.value.descriptionValue.trim() !=
|
||||||
|
state.value.book.description?.trim()),
|
||||||
|
|
||||||
|
color = if ((state.value.titleValue.isNotBlank() &&
|
||||||
|
state.value.titleValue.trim() !=
|
||||||
|
state.value.book.title.trim()) ||
|
||||||
|
|
||||||
|
(state.value.authorValue.isNotBlank() &&
|
||||||
|
state.value.authorValue.trim() !=
|
||||||
|
state.value.book.author.getAsString()?.trim()) ||
|
||||||
|
|
||||||
|
(state.value.descriptionValue.isNotBlank() &&
|
||||||
|
state.value.descriptionValue.trim() !=
|
||||||
|
state.value.book.description?.trim())
|
||||||
|
) MaterialTheme.colorScheme.onSurface
|
||||||
|
else MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
) {
|
||||||
|
onEvent(
|
||||||
|
BookInfoEvent.OnUpdateData(
|
||||||
|
refreshList = {
|
||||||
|
onLibraryEvent(LibraryEvent.OnUpdateBook(it))
|
||||||
|
onHistoryEvent(HistoryEvent.OnUpdateBook(it))
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -23,10 +23,21 @@ sealed class BookInfoEvent {
|
||||||
val refreshList: (Book) -> Unit,
|
val refreshList: (Book) -> Unit,
|
||||||
val showResult: (UIText) -> Unit
|
val showResult: (UIText) -> Unit
|
||||||
) : BookInfoEvent()
|
) : BookInfoEvent()
|
||||||
|
|
||||||
data object OnShowHideEditTitle : BookInfoEvent()
|
data object OnShowHideEditTitle : BookInfoEvent()
|
||||||
data class OnRequestFocus(val focusRequester: FocusRequester) : BookInfoEvent()
|
data class OnTitleRequestFocus(val focusRequester: FocusRequester) : BookInfoEvent()
|
||||||
data class OnTitleValueChange(val value: String) : BookInfoEvent()
|
data class OnTitleValueChange(val value: String) : BookInfoEvent()
|
||||||
data class OnUpdateTitle(val refreshList: (Book) -> Unit) : BookInfoEvent()
|
|
||||||
|
data object OnShowHideEditAuthor : BookInfoEvent()
|
||||||
|
data class OnAuthorRequestFocus(val focusRequester: FocusRequester) : BookInfoEvent()
|
||||||
|
data class OnAuthorValueChange(val value: String) : BookInfoEvent()
|
||||||
|
|
||||||
|
data object OnShowHideEditDescription : BookInfoEvent()
|
||||||
|
data class OnDescriptionRequestFocus(val focusRequester: FocusRequester) : BookInfoEvent()
|
||||||
|
data class OnDescriptionValueChange(val value: String) : BookInfoEvent()
|
||||||
|
|
||||||
|
data class OnUpdateData(val refreshList: (Book) -> Unit) : BookInfoEvent()
|
||||||
|
|
||||||
data object OnShowHideDeleteDialog : BookInfoEvent()
|
data object OnShowHideDeleteDialog : BookInfoEvent()
|
||||||
data object OnShowHideMoveDialog : BookInfoEvent()
|
data object OnShowHideMoveDialog : BookInfoEvent()
|
||||||
data class OnDeleteBook(val refreshList: () -> Unit, val onNavigate: OnNavigate) :
|
data class OnDeleteBook(val refreshList: () -> Unit, val onNavigate: OnNavigate) :
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,17 @@ data class BookInfoState(
|
||||||
val textChanged: Boolean = false,
|
val textChanged: Boolean = false,
|
||||||
|
|
||||||
val editTitle: Boolean = false,
|
val editTitle: Boolean = false,
|
||||||
val hasFocused: Boolean = false,
|
val hasTitleFocused: Boolean = false,
|
||||||
val titleValue: String = "",
|
val titleValue: String = "",
|
||||||
|
|
||||||
|
val editAuthor: Boolean = false,
|
||||||
|
val hasAuthorFocused: Boolean = false,
|
||||||
|
val authorValue: String = "",
|
||||||
|
|
||||||
|
val editDescription: Boolean = false,
|
||||||
|
val hasDescriptionFocused: Boolean = false,
|
||||||
|
val descriptionValue: String = "",
|
||||||
|
|
||||||
val showChangeCoverBottomSheet: Boolean = false,
|
val showChangeCoverBottomSheet: Boolean = false,
|
||||||
val canResetCover: Boolean = false,
|
val canResetCover: Boolean = false,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,7 @@ class BookInfoViewModel @Inject constructor(
|
||||||
_state.update {
|
_state.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
titleValue = it.book.title,
|
titleValue = it.book.title,
|
||||||
hasFocused = false
|
hasTitleFocused = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -178,12 +178,12 @@ class BookInfoViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is BookInfoEvent.OnRequestFocus -> {
|
is BookInfoEvent.OnTitleRequestFocus -> {
|
||||||
if (!_state.value.hasFocused) {
|
if (!_state.value.hasTitleFocused) {
|
||||||
event.focusRequester.requestFocus()
|
event.focusRequester.requestFocus()
|
||||||
_state.update {
|
_state.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
hasFocused = true
|
hasTitleFocused = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -197,26 +197,130 @@ class BookInfoViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is BookInfoEvent.OnUpdateTitle -> {
|
is BookInfoEvent.OnShowHideEditAuthor -> {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
val title = _state.value.titleValue.trim().replace("\n", "")
|
val shouldHide = _state.value.editAuthor
|
||||||
|
|
||||||
updateBooks.execute(
|
if (!shouldHide) {
|
||||||
listOf(
|
|
||||||
_state.value.book.copy(title = title)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
_state.update {
|
_state.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
book = it.book.copy(
|
authorValue = it.book.author.getAsString() ?: "",
|
||||||
title = title
|
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)
|
event.refreshList(_state.value.book)
|
||||||
|
|
||||||
|
if (state.value.editTitle) {
|
||||||
onEvent(BookInfoEvent.OnShowHideEditTitle)
|
onEvent(BookInfoEvent.OnShowHideEditTitle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state.value.editAuthor) {
|
||||||
|
onEvent(BookInfoEvent.OnShowHideEditAuthor)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.value.editDescription) {
|
||||||
|
onEvent(BookInfoEvent.OnShowHideEditDescription)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is BookInfoEvent.OnShowHideDeleteDialog -> {
|
is BookInfoEvent.OnShowHideDeleteDialog -> {
|
||||||
|
|
@ -346,7 +450,9 @@ class BookInfoViewModel @Inject constructor(
|
||||||
_state.update {
|
_state.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
isLoadingUpdate = true,
|
isLoadingUpdate = true,
|
||||||
editTitle = false
|
editTitle = false,
|
||||||
|
editAuthor = false,
|
||||||
|
editDescription = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -708,10 +814,10 @@ class BookInfoViewModel @Inject constructor(
|
||||||
|
|
||||||
_state.update {
|
_state.update {
|
||||||
BookInfoState(
|
BookInfoState(
|
||||||
book = book,
|
book = book
|
||||||
canResetCover = canResetCover.execute(screen.bookId)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
onEvent(BookInfoEvent.OnCheckCoverReset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -575,7 +575,7 @@
|
||||||
<string name="arrow_anim_content_desc">Анімація стрілки</string>
|
<string name="arrow_anim_content_desc">Анімація стрілки</string>
|
||||||
<string name="required_content_desc">Необхідно</string>
|
<string name="required_content_desc">Необхідно</string>
|
||||||
<string name="reset_start_content_desc">Скинути початковий посібник</string>
|
<string name="reset_start_content_desc">Скинути початковий посібник</string>
|
||||||
<string name="hide_edit_title_content_desc">Вийти з редагування назви</string>
|
<string name="exit_editing_content_desc">Вийти з режиму редагування</string>
|
||||||
<string name="open_in_web_content_desc">Відкрити в браузері</string>
|
<string name="open_in_web_content_desc">Відкрити в браузері</string>
|
||||||
<string name="translator_content_desc">Перекладач</string>
|
<string name="translator_content_desc">Перекладач</string>
|
||||||
<string name="translator_language_content_desc">Поміняти мови місцями</string>
|
<string name="translator_language_content_desc">Поміняти мови місцями</string>
|
||||||
|
|
|
||||||
|
|
@ -596,7 +596,7 @@
|
||||||
<string name="arrow_anim_content_desc">Arrow animation</string>
|
<string name="arrow_anim_content_desc">Arrow animation</string>
|
||||||
<string name="required_content_desc">Required</string>
|
<string name="required_content_desc">Required</string>
|
||||||
<string name="reset_start_content_desc">Reset start guide</string>
|
<string name="reset_start_content_desc">Reset start guide</string>
|
||||||
<string name="hide_edit_title_content_desc">Exit title editing</string>
|
<string name="exit_editing_content_desc">Exit editing mode</string>
|
||||||
<string name="open_in_web_content_desc">Open in web</string>
|
<string name="open_in_web_content_desc">Open in web</string>
|
||||||
<string name="translator_content_desc">Translator</string>
|
<string name="translator_content_desc">Translator</string>
|
||||||
<string name="translator_language_content_desc">Change language places</string>
|
<string name="translator_language_content_desc">Change language places</string>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue