🛠️ New dialog & update default

* Updated default dialog
* Added new dialog with text field
This commit is contained in:
Acclorite 2025-01-24 14:15:36 +02:00
parent 4f8da6ba8e
commit f232cd3688
13 changed files with 187 additions and 107 deletions

View file

@ -22,11 +22,10 @@ fun AboutUpdateDialog(
Dialog(
title = stringResource(id = R.string.update_query, updateInfo?.tagName ?: ""),
imageVectorIcon = Icons.Default.Update,
icon = Icons.Default.Update,
description = stringResource(
id = R.string.update_app_description
),
actionText = stringResource(id = R.string.proceed),
actionEnabled = true,
disableOnClick = false,
onDismiss = { dismissDialog(AboutEvent.OnDismissDialog) },
@ -39,6 +38,6 @@ fun AboutUpdateDialog(
)
dismissDialog(AboutEvent.OnDismissDialog)
},
withDivider = false
withContent = false
)
}

View file

@ -19,13 +19,12 @@ fun BookInfoDeleteDialog(
Dialog(
title = stringResource(id = R.string.delete_book),
imageVectorIcon = Icons.Outlined.DeleteOutline,
icon = Icons.Outlined.DeleteOutline,
description = stringResource(
id = R.string.delete_book_description
),
actionText = stringResource(id = R.string.delete),
onDismiss = { dismissDialog(BookInfoEvent.OnDismissDialog) },
withDivider = false,
withContent = false,
actionEnabled = true,
onAction = {
actionDeleteDialog(

View file

@ -33,11 +33,10 @@ fun BookInfoMoveDialog(
Dialog(
title = stringResource(id = R.string.move_book),
imageVectorIcon = Icons.AutoMirrored.Outlined.DriveFileMove,
icon = Icons.AutoMirrored.Outlined.DriveFileMove,
description = stringResource(
id = R.string.move_book_description
),
actionText = stringResource(id = R.string.move),
actionEnabled = true,
onDismiss = { dismissDialog(BookInfoEvent.OnDismissDialog) },
onAction = {
@ -49,7 +48,7 @@ fun BookInfoMoveDialog(
)
)
},
withDivider = false,
withContent = true,
items = {
items(categories, key = { it.name }) {
val category = when (it) {

View file

@ -34,9 +34,8 @@ fun BrowseAddDialog(
val context = LocalContext.current
Dialog(
title = stringResource(id = R.string.add_books),
imageVectorIcon = Icons.Default.AddChart,
icon = Icons.Default.AddChart,
description = stringResource(id = R.string.add_books_description),
actionText = stringResource(id = R.string.add),
actionEnabled = !loadingAddDialog && selectedBooksAddDialog.any { it.data is NullableBook.NotNull },
onDismiss = { dismissAddDialog(BrowseEvent.OnDismissAddDialog) },
onAction = {
@ -47,7 +46,7 @@ fun BrowseAddDialog(
)
)
},
withDivider = true,
withContent = true,
items = {
if (loadingAddDialog) {
item {

View file

@ -22,9 +22,8 @@ fun BrowsePermissionDialog(
Dialog(
title = stringResource(id = R.string.storage_permission),
imageVectorIcon = Icons.Outlined.SdStorage,
icon = Icons.Outlined.SdStorage,
description = stringResource(id = R.string.storage_permission_description),
actionText = stringResource(id = R.string.grant),
actionEnabled = true,
disableOnClick = false,
onDismiss = {
@ -42,6 +41,6 @@ fun BrowsePermissionDialog(
)
)
},
withDivider = false
withContent = false
)
}

View file

@ -1,6 +1,7 @@
package ua.acclorite.book_story.presentation.core.components.dialog
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@ -10,12 +11,9 @@ import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.material3.BasicAlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@ -28,7 +26,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
@ -37,23 +34,21 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.DialogProperties
import androidx.compose.ui.window.DialogWindowProvider
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.common.LazyColumnWithScrollbar
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Dialog(
modifier: Modifier = Modifier,
drawableIcon: Painter? = null,
imageVectorIcon: ImageVector? = null,
backgroundTransparency: Float = 0.5f,
icon: ImageVector? = null,
title: String,
description: String?,
actionText: String?,
disableOnClick: Boolean = true,
actionEnabled: Boolean?,
onDismiss: () -> Unit,
onAction: () -> Unit,
withDivider: Boolean,
withContent: Boolean,
items: (LazyListScope.() -> Unit) = {}
) {
var actionClicked by remember { mutableStateOf(false) }
@ -74,25 +69,11 @@ fun Dialog(
dismissOnClickOutside = !actionClicked
)
) {
(LocalView.current.parent as DialogWindowProvider).window.setDimAmount(
backgroundTransparency
)
(LocalView.current.parent as DialogWindowProvider).window.setDimAmount(0.5f)
Column {
if (drawableIcon != null) {
if (icon != null) {
Icon(
painter = drawableIcon,
contentDescription = title,
modifier = Modifier
.align(Alignment.CenterHorizontally)
.size(24.dp),
tint = MaterialTheme.colorScheme.secondary
)
Spacer(modifier = Modifier.height(16.dp))
}
if (imageVectorIcon != null) {
Icon(
imageVector = imageVectorIcon,
imageVector = icon,
contentDescription = title,
modifier = Modifier
.align(Alignment.CenterHorizontally)
@ -106,85 +87,76 @@ fun Dialog(
text = title,
modifier = Modifier
.align(
if (drawableIcon != null || imageVectorIcon != null) Alignment.CenterHorizontally
if (icon != null) Alignment.CenterHorizontally
else Alignment.Start
)
.padding(horizontal = 24.dp),
textAlign = if (drawableIcon != null || imageVectorIcon != null) TextAlign.Center
textAlign = if (icon != null) TextAlign.Center
else TextAlign.Start,
style = MaterialTheme.typography.headlineSmall,
color = MaterialTheme.colorScheme.onSurface
)
if (description != null) {
Spacer(modifier = Modifier.height(16.dp))
Text(
text = description,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
textAlign = TextAlign.Start,
modifier = Modifier.padding(horizontal = 24.dp)
)
}
if (withDivider) {
Spacer(modifier = Modifier.height(12.dp))
HorizontalDivider(
modifier = Modifier.padding(horizontal = 24.dp),
color = MaterialTheme.colorScheme.outlineVariant
)
Spacer(modifier = Modifier.height(12.dp))
} else {
Spacer(modifier = Modifier.height(8.dp))
if (withContent) {
Spacer(modifier = Modifier.height(24.dp))
}
LazyColumn {
LazyColumnWithScrollbar(
modifier = Modifier.fillMaxWidth()
) {
items()
item {
Column(
Spacer(modifier = Modifier.height(16.dp))
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp, Alignment.End)
) {
Spacer(modifier = Modifier.height(16.dp))
Row(
modifier = Modifier
.align(Alignment.End)
.padding(horizontal = 24.dp)
) {
TextButton(
onClick = {
if (disableOnClick) {
actionClicked = true
}
onDismiss()
},
enabled = !actionClicked
) {
Text(
text = stringResource(id = R.string.cancel),
style = MaterialTheme.typography.labelLarge,
color = MaterialTheme.colorScheme.primary
)
}
if (actionText != null) {
Spacer(modifier = Modifier.width(4.dp))
TextButton(
onClick = {
if (disableOnClick) {
actionClicked = true
}
onAction()
},
enabled = actionEnabled == true && !actionClicked
) {
Text(
text = actionText,
style = MaterialTheme.typography.labelLarge,
color =
if (actionEnabled == true) MaterialTheme.colorScheme.primary
else MaterialTheme.colorScheme.primary.copy(0.5f)
)
TextButton(
onClick = {
if (disableOnClick) {
actionClicked = true
}
}
onDismiss()
},
enabled = !actionClicked
) {
Text(
text = stringResource(id = R.string.cancel),
style = MaterialTheme.typography.labelLarge,
color = MaterialTheme.colorScheme.primary
)
}
TextButton(
onClick = {
if (disableOnClick) {
actionClicked = true
}
onAction()
},
enabled = actionEnabled == true && !actionClicked
) {
Text(
text = stringResource(id = R.string.ok),
style = MaterialTheme.typography.labelLarge,
color =
if (actionEnabled == true) MaterialTheme.colorScheme.primary
else MaterialTheme.colorScheme.primary.copy(0.5f)
)
}
}
}

View file

@ -0,0 +1,114 @@
package ua.acclorite.book_story.presentation.core.components.dialog
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.ui.UIText
import ua.acclorite.book_story.ui.theme.ExpandingTransition
@Composable
fun DialogWithTextField(
initialValue: String,
lengthLimit: Int = Int.MAX_VALUE,
onDismiss: () -> Unit,
onAction: (String) -> UIText?
) {
val state = remember(initialValue) {
mutableStateOf(
TextFieldValue(
text = initialValue,
selection = TextRange(0, initialValue.length)
)
)
}
val focusRequester = remember { FocusRequester() }
val focused = remember { mutableStateOf(false) }
val showError = remember { mutableStateOf(false) }
val error = remember { mutableStateOf<UIText?>(null) }
Dialog(
title = stringResource(id = R.string.edit),
description = null,
onDismiss = { onDismiss() },
actionEnabled = true,
disableOnClick = false,
onAction = {
if (state.value.text.isBlank()) {
onDismiss()
return@Dialog
}
error.value = onAction(state.value.text)
showError.value = error.value != null
if (!showError.value) onDismiss()
},
withContent = true,
items = {
item {
OutlinedTextField(
value = state.value,
onValueChange = {
if (it.text.length < lengthLimit || it.text.length < state.value.text.length) {
state.value = it
showError.value = false
}
},
isError = showError.value,
modifier = Modifier
.padding(horizontal = 24.dp)
.fillMaxWidth()
.focusRequester(focusRequester)
.onGloballyPositioned {
if (!focused.value) {
focusRequester.requestFocus()
focused.value = true
}
},
colors = TextFieldDefaults.colors(
errorContainerColor = Color.Transparent,
focusedContainerColor = Color.Transparent,
disabledContainerColor = Color.Transparent,
unfocusedContainerColor = Color.Transparent
),
singleLine = true,
placeholder = {
Text(
text = initialValue,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
},
supportingText = {
ExpandingTransition(
visible = showError.value && error.value != null
) {
Text(
error.value!!.asString(),
color = MaterialTheme.colorScheme.error
)
}
}
)
}
}
)
}

View file

@ -17,9 +17,8 @@ fun HistoryDeleteWholeHistoryDialog(
val context = LocalContext.current
Dialog(
title = stringResource(id = R.string.delete_history),
imageVectorIcon = Icons.Outlined.DeleteOutline,
icon = Icons.Outlined.DeleteOutline,
description = stringResource(id = R.string.delete_history_description),
actionText = stringResource(id = R.string.delete),
actionEnabled = true,
onDismiss = {
dismissDialog(HistoryEvent.OnDismissDialog)
@ -31,6 +30,6 @@ fun HistoryDeleteWholeHistoryDialog(
)
)
},
withDivider = false
withContent = false
)
}

View file

@ -19,12 +19,11 @@ fun LibraryDeleteDialog(
Dialog(
title = stringResource(id = R.string.delete_books),
imageVectorIcon = Icons.Outlined.DeleteOutline,
icon = Icons.Outlined.DeleteOutline,
description = stringResource(
id = R.string.delete_books_description,
selectedItemsCount
),
actionText = stringResource(id = R.string.delete),
actionEnabled = true,
onDismiss = {
dismissDialog(LibraryEvent.OnDismissDialog)
@ -36,6 +35,6 @@ fun LibraryDeleteDialog(
)
)
},
withDivider = false
withContent = false
)
}

View file

@ -46,12 +46,11 @@ fun LibraryMoveDialog(
Dialog(
title = stringResource(id = R.string.move_books),
imageVectorIcon = Icons.AutoMirrored.Outlined.DriveFileMove,
icon = Icons.AutoMirrored.Outlined.DriveFileMove,
description = stringResource(
id = R.string.move_books_description,
selectedItemsCount
),
actionText = stringResource(id = R.string.move),
actionEnabled = true,
onDismiss = {
dismissDialog(LibraryEvent.OnDismissDialog)
@ -65,7 +64,7 @@ fun LibraryMoveDialog(
)
)
},
withDivider = false,
withContent = true,
items = {
items(moveCategories.value) { category ->
SelectableDialogItem(

View file

@ -39,15 +39,14 @@ fun CheckForUpdatesOption() {
Dialog(
title = stringResource(id = R.string.enable_check_for_updates),
description = stringResource(id = R.string.enable_check_for_updates_description),
actionText = stringResource(id = R.string.enable),
imageVectorIcon = Icons.Default.Security,
icon = Icons.Default.Security,
actionEnabled = true,
onDismiss = { showConfirmation.value = false },
onAction = {
mainModel.onEvent(MainEvent.OnChangeCheckForUpdates(true))
showConfirmation.value = false
},
withDivider = false
withContent = false
)
}

View file

@ -25,6 +25,7 @@
<string name="move_book">Перемістити книгу?</string>
<string name="delete_history">Видалити історію читання?</string>
<string name="enable_check_for_updates">Увімнкути перевірку на оновлення?</string>
<string name="edit">Редагувати</string>
<!-- Dialog Descriptions -->
<string name="storage_permission_description">Нам потрібен дозвіл на сховище, щоб просканувати Ваш Девайс на наявність книг. Без цього дозволу ви не зможете додати книгу.</string>

View file

@ -32,6 +32,7 @@
<string name="move_book">Move book?</string>
<string name="delete_history">Delete reading history?</string>
<string name="enable_check_for_updates">Enable check for updates?</string>
<string name="edit">Edit</string>
<!-- Dialog Descriptions -->
<string name="storage_permission_description">
@ -143,6 +144,7 @@
<string name="web_search">Web search</string>
<string name="share">Share</string>
<string name="proceed">Proceed</string>
<string name="ok">OK</string>
<!-- Book -->
<string name="unknown_author">Unknown</string>