Improve the use of BaseDialog

This commit is contained in:
Shinokuni 2024-02-19 22:43:53 +01:00
parent 7b537123c8
commit 5a0affdf3b
6 changed files with 189 additions and 304 deletions

View File

@ -1,26 +1,13 @@
package com.readrops.app.compose.account.selection package com.readrops.app.compose.account.selection
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import com.readrops.app.compose.R import com.readrops.app.compose.R
import com.readrops.app.compose.util.components.BaseDialog
import com.readrops.app.compose.util.components.SelectableImageText import com.readrops.app.compose.util.components.SelectableImageText
import com.readrops.app.compose.util.theme.MediumSpacer
import com.readrops.app.compose.util.theme.spacing import com.readrops.app.compose.util.theme.spacing
import com.readrops.db.entities.account.AccountType import com.readrops.db.entities.account.AccountType
@ -29,52 +16,26 @@ fun AccountSelectionDialog(
onDismiss: () -> Unit, onDismiss: () -> Unit,
onValidate: (AccountType) -> Unit, onValidate: (AccountType) -> Unit,
) { ) {
Dialog( BaseDialog(
onDismissRequest = onDismiss title = stringResource(R.string.new_account),
icon = painterResource(id = R.drawable.ic_add_account),
onDismiss = onDismiss
) { ) {
Card( AccountType.values().forEach { type ->
shape = RoundedCornerShape(24.dp) SelectableImageText(
) { image = painterResource(
Column( id = if (type != AccountType.LOCAL)
verticalArrangement = Arrangement.Center, type.iconRes
horizontalAlignment = Alignment.CenterHorizontally, else
modifier = Modifier R.drawable.ic_rss_feed_grey
.fillMaxWidth() ),
.background(MaterialTheme.colorScheme.background) text = stringResource(id = type.typeName),
.padding(MaterialTheme.spacing.largeSpacing) style = MaterialTheme.typography.titleMedium,
) { spacing = MaterialTheme.spacing.mediumSpacing,
Icon( padding = MaterialTheme.spacing.shortSpacing,
painter = painterResource(id = R.drawable.ic_add_account), imageSize = 36.dp,
contentDescription = null, onClick = { onValidate(type) }
modifier = Modifier.size(MaterialTheme.spacing.largeSpacing) )
)
MediumSpacer()
Text(
text = stringResource(R.string.new_account),
style = MaterialTheme.typography.headlineSmall
)
MediumSpacer()
AccountType.values().forEach { type ->
SelectableImageText(
image = painterResource(
id = if (type != AccountType.LOCAL)
type.iconRes
else
R.drawable.ic_rss_feed_grey
),
text = stringResource(id = type.typeName),
style = MaterialTheme.typography.titleMedium,
spacing = MaterialTheme.spacing.mediumSpacing,
padding = MaterialTheme.spacing.shortSpacing,
imageSize = 36.dp,
onClick = { onValidate(type) }
)
}
}
} }
} }
} }

View File

@ -56,6 +56,8 @@ data class UpdateFeedDialogState(
val isFeedUrlReadOnly: Boolean val isFeedUrlReadOnly: Boolean
get() = accountType != null && !accountType.accountConfig!!.isFeedUrlEditable get() = accountType != null && !accountType.accountConfig!!.isFeedUrlEditable
val hasFolders = folders.isNotEmpty()
} }
data class FolderState( data class FolderState(

View File

@ -1,22 +1,13 @@
package com.readrops.app.compose.feeds.dialogs package com.readrops.app.compose.feeds.dialogs
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Clear import androidx.compose.material.icons.filled.Clear
import androidx.compose.material3.Card
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
@ -25,19 +16,15 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.readrops.app.compose.R import com.readrops.app.compose.R
import com.readrops.app.compose.feeds.FeedViewModel import com.readrops.app.compose.feeds.FeedViewModel
import com.readrops.app.compose.util.components.BaseDialog
import com.readrops.app.compose.util.theme.LargeSpacer import com.readrops.app.compose.util.theme.LargeSpacer
import com.readrops.app.compose.util.theme.MediumSpacer
import com.readrops.app.compose.util.theme.ShortSpacer import com.readrops.app.compose.util.theme.ShortSpacer
import com.readrops.app.compose.util.theme.spacing
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@ -49,96 +36,50 @@ fun AddFeedDialog(
var isExpanded by remember { mutableStateOf(false) } var isExpanded by remember { mutableStateOf(false) }
Dialog( BaseDialog(
onDismissRequest = onDismiss title = stringResource(R.string.add_feed_item),
icon = painterResource(id = R.drawable.ic_rss_feed_grey),
onDismiss = onDismiss
) { ) {
Card( OutlinedTextField(
shape = RoundedCornerShape(24.dp), value = state.url,
) { label = {
Column( Text(text = "URL")
verticalArrangement = Arrangement.Center, },
horizontalAlignment = Alignment.CenterHorizontally, onValueChange = { viewModel.setAddFeedDialogURL(it) },
modifier = Modifier singleLine = true,
.fillMaxWidth() trailingIcon = {
.background(MaterialTheme.colorScheme.background) if (state.url.isNotEmpty()) {
.padding(MaterialTheme.spacing.largeSpacing) IconButton(
) { onClick = { viewModel.setAddFeedDialogURL("") }
Icon(
painter = painterResource(id = R.drawable.ic_rss_feed_grey),
contentDescription = null,
modifier = Modifier.size(MaterialTheme.spacing.largeSpacing)
)
MediumSpacer()
Text(
text = stringResource(R.string.add_feed_item),
style = MaterialTheme.typography.headlineSmall
)
MediumSpacer()
OutlinedTextField(
value = state.url,
label = {
Text(text = "URL")
},
onValueChange = { viewModel.setAddFeedDialogURL(it) },
singleLine = true,
trailingIcon = {
if (state.url.isNotEmpty()) {
IconButton(
onClick = { viewModel.setAddFeedDialogURL("") }
) {
Icon(
imageVector = Icons.Default.Clear,
contentDescription = null
)
}
}
},
isError = state.isError,
supportingText = { Text(state.error?.errorText().orEmpty()) }
)
ShortSpacer()
ExposedDropdownMenuBox(
expanded = isExpanded,
onExpandedChange = { isExpanded = isExpanded.not() }
) {
ExposedDropdownMenu(
expanded = isExpanded,
onDismissRequest = { isExpanded = false }
) { ) {
for (account in state.accounts) { Icon(
DropdownMenuItem( imageVector = Icons.Default.Clear,
text = { Text(text = account.accountName!!) }, contentDescription = null
onClick = { )
isExpanded = false
viewModel.setAddFeedDialogSelectedAccount(account)
},
leadingIcon = {
Icon(
painter = painterResource(
id = if (state.selectedAccount.isLocal) {
R.drawable.ic_rss_feed_grey
} else
state.selectedAccount.accountType!!.iconRes
),
contentDescription = null
)
}
)
}
} }
}
},
isError = state.isError,
supportingText = { Text(state.error?.errorText().orEmpty()) }
)
OutlinedTextField( ShortSpacer()
value = state.selectedAccount.accountName!!,
readOnly = true, ExposedDropdownMenuBox(
onValueChange = {}, expanded = isExpanded,
trailingIcon = { onExpandedChange = { isExpanded = isExpanded.not() }
ExposedDropdownMenuDefaults.TrailingIcon(expanded = isExpanded) ) {
ExposedDropdownMenu(
expanded = isExpanded,
onDismissRequest = { isExpanded = false }
) {
for (account in state.accounts) {
DropdownMenuItem(
text = { Text(text = account.accountName!!) },
onClick = {
isExpanded = false
viewModel.setAddFeedDialogSelectedAccount(account)
}, },
leadingIcon = { leadingIcon = {
Icon( Icon(
@ -150,19 +91,39 @@ fun AddFeedDialog(
), ),
contentDescription = null contentDescription = null
) )
}, }
modifier = Modifier.menuAnchor()
) )
} }
LargeSpacer()
TextButton(
onClick = { viewModel.addFeedDialogValidate() },
) {
Text(text = stringResource(R.string.validate))
}
} }
OutlinedTextField(
value = state.selectedAccount.accountName!!,
readOnly = true,
onValueChange = {},
trailingIcon = {
ExposedDropdownMenuDefaults.TrailingIcon(expanded = isExpanded)
},
leadingIcon = {
Icon(
painter = painterResource(
id = if (state.selectedAccount.isLocal) {
R.drawable.ic_rss_feed_grey
} else
state.selectedAccount.accountType!!.iconRes
),
contentDescription = null
)
},
modifier = Modifier.menuAnchor()
)
}
LargeSpacer()
TextButton(
onClick = { viewModel.addFeedDialogValidate() },
) {
Text(text = stringResource(R.string.validate))
} }
} }
} }

View File

@ -6,6 +6,7 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
@ -14,6 +15,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.readrops.app.compose.R import com.readrops.app.compose.R
import com.readrops.app.compose.feeds.FeedViewModel import com.readrops.app.compose.feeds.FeedViewModel
import com.readrops.app.compose.util.components.BaseDialog import com.readrops.app.compose.util.components.BaseDialog
import com.readrops.app.compose.util.theme.LargeSpacer
@Composable @Composable
fun FolderDialog( fun FolderDialog(
@ -27,8 +29,7 @@ fun FolderDialog(
BaseDialog( BaseDialog(
title = stringResource(id = if (updateFolder) R.string.edit_folder else R.string.add_folder), title = stringResource(id = if (updateFolder) R.string.edit_folder else R.string.add_folder),
icon = painterResource(id = if (updateFolder) R.drawable.ic_folder_grey else R.drawable.ic_new_folder), icon = painterResource(id = if (updateFolder) R.drawable.ic_folder_grey else R.drawable.ic_new_folder),
onDismiss = onDismiss, onDismiss = onDismiss
onValidate = onValidate
) { ) {
OutlinedTextField( OutlinedTextField(
value = state.name.orEmpty(), value = state.name.orEmpty(),
@ -52,5 +53,13 @@ fun FolderDialog(
isError = state.isError, isError = state.isError,
supportingText = { Text(text = state.nameError?.errorText().orEmpty()) } supportingText = { Text(text = state.nameError?.errorText().orEmpty()) }
) )
LargeSpacer()
TextButton(
onClick = { onValidate() },
) {
Text(text = stringResource(R.string.validate))
}
} }
} }

View File

@ -1,36 +1,24 @@
package com.readrops.app.compose.feeds.dialogs package com.readrops.app.compose.feeds.dialogs
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.readrops.app.compose.R import com.readrops.app.compose.R
import com.readrops.app.compose.feeds.FeedViewModel import com.readrops.app.compose.feeds.FeedViewModel
import com.readrops.app.compose.util.components.BaseDialog
import com.readrops.app.compose.util.theme.LargeSpacer import com.readrops.app.compose.util.theme.LargeSpacer
import com.readrops.app.compose.util.theme.MediumSpacer import com.readrops.app.compose.util.theme.MediumSpacer
import com.readrops.app.compose.util.theme.spacing
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@ -41,122 +29,97 @@ fun UpdateFeedDialog(
) { ) {
val state by viewModel.updateFeedDialogState.collectAsStateWithLifecycle() val state by viewModel.updateFeedDialogState.collectAsStateWithLifecycle()
Dialog( BaseDialog(
onDismissRequest = onDismissRequest title = stringResource(R.string.edit_feed),
icon = painterResource(id = R.drawable.ic_rss_feed_grey),
onDismiss = onDismissRequest
) { ) {
Card( OutlinedTextField(
shape = RoundedCornerShape(24.dp) value = state.feedName,
) { onValueChange = { viewModel.setUpdateFeedDialogStateFeedName(it) },
Column( label = { Text(text = stringResource(R.string.feed_name)) },
verticalArrangement = Arrangement.Center, singleLine = true,
horizontalAlignment = Alignment.CenterHorizontally, isError = state.isFeedNameError,
modifier = Modifier supportingText = {
.fillMaxWidth() if (state.isFeedNameError) {
.background(MaterialTheme.colorScheme.background) Text(
.padding(MaterialTheme.spacing.largeSpacing) text = state.feedNameError?.errorText().orEmpty()
) {
Icon(
painter = painterResource(id = R.drawable.ic_rss_feed_grey),
contentDescription = null,
modifier = Modifier.size(MaterialTheme.spacing.largeSpacing)
)
MediumSpacer()
Text(
text = stringResource(R.string.edit_feed),
style = MaterialTheme.typography.headlineSmall
)
MediumSpacer()
OutlinedTextField(
value = state.feedName,
onValueChange = { viewModel.setUpdateFeedDialogStateFeedName(it) },
label = { Text(text = stringResource(R.string.feed_name)) },
singleLine = true,
isError = state.isFeedNameError,
supportingText = {
if (state.isFeedNameError) {
Text(
text = state.feedNameError?.errorText().orEmpty()
)
}
}
)
MediumSpacer()
OutlinedTextField(
value = state.feedUrl,
onValueChange = { viewModel.setUpdateFeedDialogFeedUrl(it) },
label = { Text(text = stringResource(R.string.feed_url)) },
singleLine = true,
readOnly = state.isFeedUrlReadOnly,
isError = state.isFeedUrlError,
supportingText = {
if (state.isFeedUrlError) {
Text(
text = state.feedUrlError?.errorText().orEmpty()
)
}
}
)
MediumSpacer()
ExposedDropdownMenuBox(
expanded = state.isAccountDropDownExpanded,
onExpandedChange = { viewModel.setAccountDropDownState(state.isAccountDropDownExpanded.not()) }
) {
ExposedDropdownMenu(
expanded = state.isAccountDropDownExpanded,
onDismissRequest = { viewModel.setAccountDropDownState(false) }
) {
for (folder in state.folders) {
DropdownMenuItem(
text = { Text(text = folder.name!!) },
onClick = {
viewModel.setSelectedFolder(folder)
viewModel.setAccountDropDownState(false)
},
leadingIcon = {
Icon(
painterResource(id = R.drawable.ic_folder_grey),
contentDescription = null,
)
}
)
}
}
OutlinedTextField(
value = state.selectedFolder?.name.orEmpty(),
readOnly = true,
onValueChange = {},
trailingIcon = {
ExposedDropdownMenuDefaults.TrailingIcon(expanded = state.isAccountDropDownExpanded)
},
leadingIcon = {
if (state.selectedFolder != null) {
Icon(
painterResource(id = R.drawable.ic_folder_grey),
contentDescription = null,
)
}
},
modifier = Modifier.menuAnchor()
) )
} }
}
)
LargeSpacer() MediumSpacer()
TextButton( OutlinedTextField(
onClick = { viewModel.updateFeedDialogValidate() }, value = state.feedUrl,
) { onValueChange = { viewModel.setUpdateFeedDialogFeedUrl(it) },
Text(text = stringResource(R.string.validate)) label = { Text(text = stringResource(R.string.feed_url)) },
singleLine = true,
readOnly = state.isFeedUrlReadOnly,
isError = state.isFeedUrlError,
supportingText = {
if (state.isFeedUrlError) {
Text(
text = state.feedUrlError?.errorText().orEmpty()
)
} }
} }
)
MediumSpacer()
ExposedDropdownMenuBox(
expanded = state.isAccountDropDownExpanded && state.hasFolders,
onExpandedChange = { viewModel.setAccountDropDownState(state.isAccountDropDownExpanded.not()) }
) {
ExposedDropdownMenu(
expanded = state.isAccountDropDownExpanded && state.hasFolders,
onDismissRequest = { viewModel.setAccountDropDownState(false) }
) {
for (folder in state.folders) {
DropdownMenuItem(
text = { Text(text = folder.name!!) },
onClick = {
viewModel.setSelectedFolder(folder)
viewModel.setAccountDropDownState(false)
},
leadingIcon = {
Icon(
painterResource(id = R.drawable.ic_folder_grey),
contentDescription = null,
)
}
)
}
}
OutlinedTextField(
value = state.selectedFolder?.name.orEmpty(),
readOnly = true,
enabled = state.hasFolders,
onValueChange = {},
trailingIcon = {
ExposedDropdownMenuDefaults.TrailingIcon(expanded = state.isAccountDropDownExpanded)
},
leadingIcon = {
if (state.selectedFolder != null) {
Icon(
painterResource(id = R.drawable.ic_folder_grey),
contentDescription = null,
)
}
},
modifier = Modifier.menuAnchor()
)
}
LargeSpacer()
TextButton(
onClick = { viewModel.updateFeedDialogValidate() },
) {
Text(text = stringResource(R.string.validate))
} }
} }
} }

View File

@ -11,14 +11,12 @@ import androidx.compose.material3.Card
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
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.painter.Painter import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import com.readrops.app.compose.util.theme.LargeSpacer
import com.readrops.app.compose.util.theme.MediumSpacer import com.readrops.app.compose.util.theme.MediumSpacer
import com.readrops.app.compose.util.theme.spacing import com.readrops.app.compose.util.theme.spacing
@ -27,7 +25,6 @@ fun BaseDialog(
title: String, title: String,
icon: Painter, icon: Painter,
onDismiss: () -> Unit, onDismiss: () -> Unit,
onValidate: () -> Unit,
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
Dialog( Dialog(
@ -60,14 +57,6 @@ fun BaseDialog(
MediumSpacer() MediumSpacer()
content() content()
LargeSpacer()
TextButton(
onClick = { onValidate() },
) {
Text(text = "Validate")
}
} }
} }
} }