mirror of
https://github.com/greenart7c3/Amber.git
synced 2026-10-06 03:18:23 +00:00
Improve feedback screen layout and UX
- Restructure FeedbackScreen to use a cleaner card-based layout - Add proper padding and spacing between feedback items - Group related actions together for better visual hierarchy - Add missing string resources for feedback UI elements - Remove redundant code and simplify composable structure https://claude.ai/code/session_01EYDStguSpVg95qJckEdtPc
This commit is contained in:
@@ -1,47 +1,40 @@
|
||||
package com.greenart7c3.nostrsigner.ui
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.SegmentedButton
|
||||
import androidx.compose.material3.SegmentedButtonDefaults
|
||||
import androidx.compose.material3.SingleChoiceSegmentedButtonRow
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.greenart7c3.nostrsigner.Amber
|
||||
import com.greenart7c3.nostrsigner.R
|
||||
import com.greenart7c3.nostrsigner.models.Account
|
||||
import com.greenart7c3.nostrsigner.models.FeedbackType
|
||||
import com.greenart7c3.nostrsigner.ui.components.AmberButton
|
||||
import com.greenart7c3.nostrsigner.ui.theme.light
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@SuppressLint("UnusedBoxWithConstraintsScope")
|
||||
@Composable
|
||||
fun FeedbackScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -53,90 +46,87 @@ fun FeedbackScreen(
|
||||
var body by remember { mutableStateOf(TextFieldValue("")) }
|
||||
var feedbackType by remember { mutableStateOf(FeedbackType.BUG_REPORT) }
|
||||
|
||||
BoxWithConstraints(
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize(),
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
val maxHeight = maxHeight
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.weight(1f, fill = true),
|
||||
) {
|
||||
LazyRow(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
item {
|
||||
NamedRadio(
|
||||
isSelected = feedbackType == FeedbackType.BUG_REPORT,
|
||||
name = stringResource(R.string.bug_report),
|
||||
onClick = {
|
||||
feedbackType = FeedbackType.BUG_REPORT
|
||||
},
|
||||
)
|
||||
}
|
||||
item { Spacer(modifier = Modifier.width(8.dp)) }
|
||||
item {
|
||||
NamedRadio(
|
||||
isSelected = feedbackType == FeedbackType.ENHANCEMENT_REQUEST,
|
||||
name = stringResource(id = R.string.enhancement_request),
|
||||
onClick = {
|
||||
feedbackType = FeedbackType.ENHANCEMENT_REQUEST
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Text(
|
||||
text = stringResource(R.string.feedback_type),
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
SingleChoiceSegmentedButtonRow(modifier = Modifier.fillMaxWidth()) {
|
||||
FeedbackType.entries.forEachIndexed { index, type ->
|
||||
SegmentedButton(
|
||||
selected = feedbackType == type,
|
||||
onClick = { feedbackType = type },
|
||||
shape = SegmentedButtonDefaults.itemShape(
|
||||
index = index,
|
||||
count = FeedbackType.entries.size,
|
||||
),
|
||||
label = {
|
||||
Text(
|
||||
text = stringResource(
|
||||
when (type) {
|
||||
FeedbackType.BUG_REPORT -> R.string.bug_report
|
||||
FeedbackType.ENHANCEMENT_REQUEST -> R.string.enhancement_request
|
||||
},
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
TextField(
|
||||
header,
|
||||
onValueChange = { header = it },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
maxLines = 3,
|
||||
placeholder = {
|
||||
Text(
|
||||
stringResource(id = R.string.subject),
|
||||
style = MaterialTheme.typography.titleLarge.copy(fontWeight = FontWeight.Bold),
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.onBackground.light(),
|
||||
)
|
||||
},
|
||||
textStyle = MaterialTheme.typography.titleLarge.copy(fontWeight = FontWeight.Bold),
|
||||
colors = TextFieldDefaults.colors(
|
||||
focusedIndicatorColor = Color.Transparent,
|
||||
unfocusedIndicatorColor = Color.Transparent,
|
||||
focusedContainerColor = Color.Transparent,
|
||||
unfocusedContainerColor = Color.Transparent,
|
||||
),
|
||||
)
|
||||
|
||||
val scrollState = rememberScrollState()
|
||||
TextField(
|
||||
value = body,
|
||||
onValueChange = { body = it },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(maxHeight * 0.4f)
|
||||
.verticalScroll(scrollState),
|
||||
placeholder = {
|
||||
Text(
|
||||
stringResource(id = R.string.body_text_optional),
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.onBackground.light(),
|
||||
)
|
||||
},
|
||||
colors = TextFieldDefaults.colors(
|
||||
focusedIndicatorColor = Color.Transparent,
|
||||
unfocusedIndicatorColor = Color.Transparent,
|
||||
focusedContainerColor = Color.Transparent,
|
||||
unfocusedContainerColor = Color.Transparent,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
HorizontalDivider()
|
||||
|
||||
OutlinedTextField(
|
||||
value = header,
|
||||
onValueChange = { header = it },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
maxLines = 3,
|
||||
label = {
|
||||
Text(stringResource(R.string.subject))
|
||||
},
|
||||
textStyle = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.Medium),
|
||||
isError = header.text.isBlank(),
|
||||
supportingText = if (header.text.isBlank()) {
|
||||
{ Text(stringResource(R.string.required)) }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = body,
|
||||
onValueChange = { body = it },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(200.dp),
|
||||
label = {
|
||||
Text(stringResource(R.string.body_text_optional))
|
||||
},
|
||||
textStyle = MaterialTheme.typography.bodyLarge,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 8.dp),
|
||||
) {
|
||||
AmberButton(
|
||||
enabled = header.text.isNotBlank(),
|
||||
text = stringResource(R.string.send),
|
||||
@@ -174,38 +164,3 @@ fun FeedbackScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// taken from https://github.com/dluvian/voyage
|
||||
@Composable
|
||||
fun NamedRadio(
|
||||
isSelected: Boolean,
|
||||
name: String,
|
||||
onClick: () -> Unit,
|
||||
isEnabled: Boolean = true,
|
||||
) {
|
||||
NamedItem(
|
||||
modifier = Modifier.clickable(onClick = onClick),
|
||||
name = name,
|
||||
item = {
|
||||
RadioButton(
|
||||
selected = isSelected,
|
||||
onClick = onClick,
|
||||
enabled = isEnabled,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// taken from https://github.com/dluvian/voyage
|
||||
@Composable
|
||||
fun NamedItem(
|
||||
name: String,
|
||||
item: @Composable () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color = Color.Unspecified,
|
||||
) {
|
||||
Row(modifier = modifier, verticalAlignment = Alignment.CenterVertically) {
|
||||
item()
|
||||
Text(text = name, color = color, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -561,6 +561,8 @@
|
||||
<string name="event_kind_444">Welcome</string>
|
||||
<string name="event_kind_445">Group</string>
|
||||
<string name="give_us_feedback">Give us feedback</string>
|
||||
<string name="feedback_type">Feedback type</string>
|
||||
<string name="required">Required</string>
|
||||
<string name="bug_report">Bug report</string>
|
||||
<string name="enhancement_request">Enhancement request</string>
|
||||
<string name="subject">Subject</string>
|
||||
|
||||
Reference in New Issue
Block a user