validate user input for all poll fields,

add user's pubkey to recipients field,
add poll field saveable texts to pollViewModel,
This commit is contained in:
toadlyBroodle
2023-03-17 16:48:26 +09:00
parent 29c4c42547
commit f0b4660719
9 changed files with 169 additions and 45 deletions

View File

@@ -39,6 +39,11 @@ fun NewPollView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = n
val scrollState = rememberScrollState() val scrollState = rememberScrollState()
// if no recipients, add user's pubkey
if (pollViewModel.zapRecipients.isEmpty()) {
pollViewModel.zapRecipients.add(account.userProfile().pubkeyHex)
}
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
pollViewModel.load(account, baseReplyTo, quote) pollViewModel.load(account, baseReplyTo, quote)
delay(100) delay(100)
@@ -109,7 +114,8 @@ fun NewPollView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = n
} }
Text(stringResource(R.string.poll_heading_required)) Text(stringResource(R.string.poll_heading_required))
PollPrimaryDescription(pollViewModel = pollViewModel) PollRecipientsField(pollViewModel)
PollPrimaryDescription(pollViewModel)
pollViewModel.pollOptions.forEachIndexed { index, element -> pollViewModel.pollOptions.forEachIndexed { index, element ->
PollOption(pollViewModel, index) PollOption(pollViewModel, index)
} }
@@ -127,10 +133,9 @@ fun NewPollView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = n
) )
} }
Text(stringResource(R.string.poll_heading_optional)) Text(stringResource(R.string.poll_heading_optional))
PollRecipientsField() PollVoteValueRange(pollViewModel)
PollVoteValueRange() PollConsensusThreshold(pollViewModel)
PollConsensusThreshold() PollClosing(pollViewModel)
PollClosing()
} }
} }

View File

@@ -3,12 +3,18 @@ package com.vitorpamplona.amethyst.ui.actions
import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateListOf
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.HexKey
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.User
class NewPollViewModel : NewPostViewModel() { class NewPollViewModel : NewPostViewModel() {
var zapRecipients = mutableStateListOf<HexKey>()
var pollOptions = mutableStateListOf("", "") var pollOptions = mutableStateListOf("", "")
var zapMax: Int? = null
var zapMin: Int? = null
var consensus: Int? = null
var closedAfter: Int? = null
override fun load(account: Account, replyingTo: Note?, quote: Note?) { override fun load(account: Account, replyingTo: Note?, quote: Note?) {
super.load(account, replyingTo, quote) super.load(account, replyingTo, quote)
@@ -32,13 +38,15 @@ class NewPollViewModel : NewPostViewModel() {
override fun sendPost() { override fun sendPost() {
super.sendPost() super.sendPost()
// delete existing pollOptions
pollOptions = mutableStateListOf("", "")
} }
override fun cancel() { override fun cancel() {
// delete existing pollOptions
pollOptions = mutableStateListOf("", "")
super.cancel() super.cancel()
pollOptions = mutableStateListOf("", "")
} }
override fun findUrlInMessage(): String? { override fun findUrlInMessage(): String? {

View File

@@ -8,22 +8,44 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.material.TextFieldDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.actions.NewPollViewModel
@Composable @Composable
fun PollClosing() { fun PollClosing(pollViewModel: NewPollViewModel) {
var text by rememberSaveable { mutableStateOf("") } var text by rememberSaveable { mutableStateOf("") }
var isInputValid = true
if (text.isNotEmpty()) {
try {
val int = text.toInt()
if (int < 0) {
isInputValid = false
} else { pollViewModel.closedAfter = int }
} catch (e: Exception) { isInputValid = false }
}
val colorInValid = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = MaterialTheme.colors.error,
unfocusedBorderColor = Color.Red
)
val colorValid = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = MaterialTheme.colors.primary,
unfocusedBorderColor = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
Row( Row(
Modifier.fillMaxWidth(), Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center horizontalArrangement = Arrangement.Center
@@ -33,6 +55,7 @@ fun PollClosing() {
onValueChange = { text = it }, onValueChange = { text = it },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
modifier = Modifier.width(150.dp), modifier = Modifier.width(150.dp),
colors = if (isInputValid) colorValid else colorInValid,
label = { label = {
Text( Text(
text = stringResource(R.string.poll_closing_time), text = stringResource(R.string.poll_closing_time),
@@ -52,5 +75,5 @@ fun PollClosing() {
@Preview @Preview
@Composable @Composable
fun PollClosingPreview() { fun PollClosingPreview() {
PollClosing() PollClosing(NewPollViewModel())
} }

View File

@@ -8,22 +8,44 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.material.TextFieldDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.actions.NewPollViewModel
@Composable @Composable
fun PollConsensusThreshold() { fun PollConsensusThreshold(pollViewModel: NewPollViewModel) {
var text by rememberSaveable { mutableStateOf("") } var text by rememberSaveable { mutableStateOf("") }
var isInputValid = true
if (text.isNotEmpty()) {
try {
val int = text.toInt()
if (int < 0 || int > 100) {
isInputValid = false
} else { pollViewModel.consensus = int }
} catch (e: Exception) { isInputValid = false }
}
val colorInValid = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = MaterialTheme.colors.error,
unfocusedBorderColor = Color.Red
)
val colorValid = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = MaterialTheme.colors.primary,
unfocusedBorderColor = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
Row( Row(
Modifier.fillMaxWidth(), Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center horizontalArrangement = Arrangement.Center
@@ -33,6 +55,7 @@ fun PollConsensusThreshold() {
onValueChange = { text = it }, onValueChange = { text = it },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
modifier = Modifier.width(150.dp), modifier = Modifier.width(150.dp),
colors = if (isInputValid) colorValid else colorInValid,
label = { label = {
Text( Text(
text = stringResource(R.string.poll_consensus_threshold), text = stringResource(R.string.poll_consensus_threshold),
@@ -52,5 +75,5 @@ fun PollConsensusThreshold() {
@Preview @Preview
@Composable @Composable
fun PollConsensusThresholdPreview() { fun PollConsensusThresholdPreview() {
PollConsensusThreshold() PollConsensusThreshold(NewPollViewModel())
} }

View File

@@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.*
import androidx.compose.material.* import androidx.compose.material.*
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
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.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
@@ -15,6 +16,20 @@ import com.vitorpamplona.amethyst.ui.actions.NewPollViewModel
@Composable @Composable
fun PollOption(pollViewModel: NewPollViewModel, optionIndex: Int) { fun PollOption(pollViewModel: NewPollViewModel, optionIndex: Int) {
var isInputValid = true
if (pollViewModel.pollOptions[optionIndex].isEmpty()) {
isInputValid = false
}
val colorInValid = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = MaterialTheme.colors.error,
unfocusedBorderColor = Color.Red
)
val colorValid = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = MaterialTheme.colors.primary,
unfocusedBorderColor = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
Row { Row {
OutlinedTextField( OutlinedTextField(
modifier = Modifier modifier = Modifier
@@ -32,8 +47,8 @@ fun PollOption(pollViewModel: NewPollViewModel, optionIndex: Int) {
text = stringResource(R.string.poll_option_description), text = stringResource(R.string.poll_option_description),
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f) color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
) )
} },
colors = if (isInputValid) colorValid else colorInValid
) )
if (optionIndex > 1) { if (optionIndex > 1) {
Button( Button(

View File

@@ -1,13 +1,10 @@
package com.vitorpamplona.amethyst.ui.components package com.vitorpamplona.amethyst.ui.components
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.* import androidx.compose.material.*
import androidx.compose.runtime.Composable import androidx.compose.runtime.*
import androidx.compose.runtime.remember
import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester
@@ -30,6 +27,20 @@ fun PollPrimaryDescription(pollViewModel: NewPollViewModel) {
val focusRequester = remember { FocusRequester() } val focusRequester = remember { FocusRequester() }
val keyboardController = LocalSoftwareKeyboardController.current val keyboardController = LocalSoftwareKeyboardController.current
var isInputValid = true
if (pollViewModel.message.text.isEmpty()) {
isInputValid = false
}
val colorInValid = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = MaterialTheme.colors.error,
unfocusedBorderColor = Color.Red
)
val colorValid = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = MaterialTheme.colors.primary,
unfocusedBorderColor = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
OutlinedTextField( OutlinedTextField(
value = pollViewModel.message, value = pollViewModel.message,
onValueChange = { onValueChange = {
@@ -47,11 +58,6 @@ fun PollPrimaryDescription(pollViewModel: NewPollViewModel) {
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(top = 8.dp) .padding(top = 8.dp)
.border(
width = 1.dp,
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f),
shape = RoundedCornerShape(8.dp)
)
.focusRequester(focusRequester) .focusRequester(focusRequester)
.onFocusChanged { .onFocusChanged {
if (it.isFocused) { if (it.isFocused) {
@@ -64,11 +70,7 @@ fun PollPrimaryDescription(pollViewModel: NewPollViewModel) {
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f) color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
) )
}, },
colors = TextFieldDefaults colors = if (isInputValid) colorValid else colorInValid,
.outlinedTextFieldColors(
unfocusedBorderColor = Color.Transparent,
focusedBorderColor = Color.Transparent
),
visualTransformation = UrlUserTagTransformation(MaterialTheme.colors.primary), visualTransformation = UrlUserTagTransformation(MaterialTheme.colors.primary),
textStyle = LocalTextStyle.current.copy(textDirection = TextDirection.Content) textStyle = LocalTextStyle.current.copy(textDirection = TextDirection.Content)
) )

View File

@@ -5,23 +5,20 @@ import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.actions.NewPollViewModel
@Composable @Composable
fun PollRecipientsField() { fun PollRecipientsField(pollViewModel: NewPollViewModel) {
var text by rememberSaveable() { mutableStateOf("") }
OutlinedTextField( OutlinedTextField(
modifier = Modifier modifier = Modifier
.fillMaxWidth(), .fillMaxWidth(),
value = text, value = pollViewModel.zapRecipients[0],
onValueChange = { text = it }, onValueChange = { /* TODO */ },
enabled = false, // TODO enable add recipients
label = { label = {
Text( Text(
text = stringResource(R.string.poll_zap_recipients), text = stringResource(R.string.poll_zap_recipients),

View File

@@ -8,32 +8,82 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.material.TextFieldDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.actions.NewPollViewModel
@Composable @Composable
fun PollVoteValueRange() { fun PollVoteValueRange(pollViewModel: NewPollViewModel) {
var minText by rememberSaveable { mutableStateOf("") } var textMax by rememberSaveable { mutableStateOf("") }
var maxText by rememberSaveable { mutableStateOf("") } var textMin by rememberSaveable { mutableStateOf("") }
// check for zapMax amounts < 1
var isMaxValid = true
if (textMax.isNotEmpty()) {
try {
val int = textMax.toInt()
if ( int < 1)
isMaxValid = false
else pollViewModel.zapMax = int
} catch (e: Exception) { isMaxValid = false }
}
// check for minZap amounts < 1
var isMinValid = true
if (textMin.isNotEmpty()) {
try {
val int = textMin.toInt()
if ( int < 1)
isMinValid = false
else pollViewModel.zapMin = int
} catch (e: Exception) { isMinValid = false }
}
// check for zapMin > zapMax
if (textMin.isNotEmpty() && textMax.isNotEmpty()) {
try {
val intMin = textMin.toInt()
val intMax = textMax.toInt()
if ( intMin > intMax) {
isMinValid = false
isMaxValid = false
}
} catch (e: Exception) {
isMinValid = false
isMaxValid = false
}
}
val colorInValid = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = MaterialTheme.colors.error,
unfocusedBorderColor = Color.Red)
val colorValid = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = MaterialTheme.colors.primary,
unfocusedBorderColor = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
Row( Row(
Modifier.fillMaxWidth(), Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center horizontalArrangement = Arrangement.Center
) { ) {
OutlinedTextField( OutlinedTextField(
value = minText, value = textMin,
onValueChange = { minText = it }, onValueChange = { textMin = it },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
modifier = Modifier.width(150.dp), modifier = Modifier.width(150.dp),
colors = if (isMinValid) colorValid else colorInValid,
label = { label = {
Text( Text(
text = stringResource(R.string.poll_zap_value_min), text = stringResource(R.string.poll_zap_value_min),
@@ -48,10 +98,11 @@ fun PollVoteValueRange() {
} }
) )
OutlinedTextField( OutlinedTextField(
value = maxText, value = textMax,
onValueChange = { maxText = it }, onValueChange = { textMax = it },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
modifier = Modifier.width(150.dp), modifier = Modifier.width(150.dp),
colors = if (isMaxValid) colorValid else colorInValid,
label = { label = {
Text( Text(
text = stringResource(R.string.poll_zap_value_max), text = stringResource(R.string.poll_zap_value_max),
@@ -71,5 +122,5 @@ fun PollVoteValueRange() {
@Preview @Preview
@Composable @Composable
fun PollVoteValueRangePreview() { fun PollVoteValueRangePreview() {
PollVoteValueRange() PollVoteValueRange(NewPollViewModel())
} }

View File

@@ -230,7 +230,7 @@
<string name="poll_zap_value_min">Zap minimum</string> <string name="poll_zap_value_min">Zap minimum</string>
<string name="poll_zap_value_max">Zap maximum</string> <string name="poll_zap_value_max">Zap maximum</string>
<string name="poll_consensus_threshold">Consensus</string> <string name="poll_consensus_threshold">Consensus</string>
<string name="poll_consensus_threshold_percent">%</string> <string name="poll_consensus_threshold_percent">(0100)%</string>
<string name="poll_zap_amount">sats</string> <string name="poll_zap_amount">sats</string>
<string name="poll_closing_time">Close after</string> <string name="poll_closing_time">Close after</string>
<string name="poll_closing_time_days">days</string> <string name="poll_closing_time_days">days</string>