241 lines
8.5 KiB
Kotlin
241 lines
8.5 KiB
Kotlin
package cash.z.ecc.android.sdk.demoapp.ui.screen.send.view
|
|
|
|
import androidx.compose.foundation.horizontalScroll
|
|
import androidx.compose.foundation.layout.Column
|
|
import androidx.compose.foundation.layout.PaddingValues
|
|
import androidx.compose.foundation.layout.Row
|
|
import androidx.compose.foundation.layout.Spacer
|
|
import androidx.compose.foundation.layout.fillMaxHeight
|
|
import androidx.compose.foundation.layout.fillMaxWidth
|
|
import androidx.compose.foundation.layout.padding
|
|
import androidx.compose.foundation.layout.size
|
|
import androidx.compose.foundation.rememberScrollState
|
|
import androidx.compose.foundation.text.KeyboardOptions
|
|
import androidx.compose.foundation.verticalScroll
|
|
import androidx.compose.material.icons.Icons
|
|
import androidx.compose.material.icons.filled.ArrowBack
|
|
import androidx.compose.material3.Button
|
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
import androidx.compose.material3.Icon
|
|
import androidx.compose.material3.IconButton
|
|
import androidx.compose.material3.Scaffold
|
|
import androidx.compose.material3.Text
|
|
import androidx.compose.material3.TextField
|
|
import androidx.compose.material3.TopAppBar
|
|
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.platform.LocalContext
|
|
import androidx.compose.ui.res.stringResource
|
|
import androidx.compose.ui.text.input.KeyboardType
|
|
import androidx.compose.ui.unit.dp
|
|
import cash.z.ecc.android.sdk.demoapp.R
|
|
import cash.z.ecc.android.sdk.demoapp.fixture.WalletFixture
|
|
import cash.z.ecc.android.sdk.demoapp.model.Memo
|
|
import cash.z.ecc.android.sdk.demoapp.model.MonetarySeparators
|
|
import cash.z.ecc.android.sdk.demoapp.model.ZecSend
|
|
import cash.z.ecc.android.sdk.demoapp.model.ZecSendExt
|
|
import cash.z.ecc.android.sdk.demoapp.model.ZecString
|
|
import cash.z.ecc.android.sdk.demoapp.model.ZecStringExt
|
|
import cash.z.ecc.android.sdk.demoapp.model.toZecString
|
|
import cash.z.ecc.android.sdk.demoapp.type.fromResources
|
|
import cash.z.ecc.android.sdk.demoapp.ui.common.MINIMAL_WEIGHT
|
|
import cash.z.ecc.android.sdk.demoapp.ui.screen.home.viewmodel.WalletSnapshot
|
|
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
|
|
|
// @Preview
|
|
// @Composable
|
|
// fun ComposablePreview() {
|
|
// MaterialTheme {
|
|
// Send()
|
|
// }
|
|
// }
|
|
|
|
@OptIn(ExperimentalMaterial3Api::class)
|
|
@Composable
|
|
fun Send(
|
|
walletSnapshot: WalletSnapshot,
|
|
onSend: (ZecSend) -> Unit,
|
|
onBack: () -> Unit
|
|
) {
|
|
Scaffold(topBar = {
|
|
SendTopAppBar(onBack)
|
|
}) { paddingValues ->
|
|
SendMainContent(
|
|
paddingValues = paddingValues,
|
|
walletSnapshot = walletSnapshot,
|
|
onSend = onSend
|
|
)
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
@OptIn(ExperimentalMaterial3Api::class)
|
|
private fun SendTopAppBar(onBack: () -> Unit) {
|
|
TopAppBar(
|
|
title = { Text(text = stringResource(id = R.string.menu_send)) },
|
|
navigationIcon = {
|
|
IconButton(
|
|
onClick = onBack
|
|
) {
|
|
Icon(
|
|
imageVector = Icons.Filled.ArrowBack,
|
|
contentDescription = null
|
|
)
|
|
}
|
|
}
|
|
)
|
|
}
|
|
|
|
@OptIn(ExperimentalMaterial3Api::class)
|
|
@Composable
|
|
@Suppress("LongMethod")
|
|
private fun SendMainContent(
|
|
paddingValues: PaddingValues,
|
|
walletSnapshot: WalletSnapshot,
|
|
onSend: (ZecSend) -> Unit
|
|
) {
|
|
val context = LocalContext.current
|
|
val monetarySeparators = MonetarySeparators.current()
|
|
val allowedCharacters = ZecString.allowedCharacters(monetarySeparators)
|
|
|
|
var amountZecString by rememberSaveable {
|
|
mutableStateOf("")
|
|
}
|
|
var recipientAddressString by rememberSaveable {
|
|
mutableStateOf("")
|
|
}
|
|
var memoString by rememberSaveable { mutableStateOf("") }
|
|
|
|
var validation by rememberSaveable {
|
|
mutableStateOf<Set<ZecSendExt.ZecSendValidation.Invalid.ValidationError>>(emptySet())
|
|
}
|
|
|
|
Column(
|
|
Modifier
|
|
.fillMaxHeight()
|
|
.verticalScroll(rememberScrollState())
|
|
.padding(top = paddingValues.calculateTopPadding())
|
|
) {
|
|
Text(text = stringResource(id = R.string.send_available_balance))
|
|
Row(Modifier.fillMaxWidth()) {
|
|
Text(text = walletSnapshot.saplingBalance.available.toZecString())
|
|
}
|
|
|
|
TextField(
|
|
value = amountZecString,
|
|
onValueChange = { newValue ->
|
|
if (!ZecStringExt.filterContinuous(context, monetarySeparators, newValue)) {
|
|
return@TextField
|
|
}
|
|
amountZecString = newValue.filter { allowedCharacters.contains(it) }
|
|
},
|
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
|
label = { Text(stringResource(id = R.string.send_amount)) }
|
|
)
|
|
|
|
Spacer(Modifier.size(8.dp))
|
|
|
|
TextField(
|
|
value = recipientAddressString,
|
|
onValueChange = { recipientAddressString = it },
|
|
label = { Text(stringResource(id = R.string.send_to_address)) }
|
|
)
|
|
|
|
val zcashNetwork = ZcashNetwork.fromResources(context)
|
|
Column(
|
|
Modifier
|
|
.fillMaxWidth()
|
|
) {
|
|
// Alice's addresses
|
|
Row(
|
|
Modifier
|
|
.fillMaxWidth()
|
|
.horizontalScroll(rememberScrollState())
|
|
) {
|
|
Button({ recipientAddressString = WalletFixture.Alice.getAddresses(zcashNetwork).unified }) {
|
|
Text(text = stringResource(id = R.string.send_alyssa_unified))
|
|
}
|
|
|
|
Spacer(Modifier.size(8.dp))
|
|
|
|
Button({ recipientAddressString = WalletFixture.Alice.getAddresses(zcashNetwork).sapling }) {
|
|
Text(text = stringResource(id = R.string.send_alyssa_sapling))
|
|
}
|
|
|
|
Spacer(Modifier.size(8.dp))
|
|
|
|
Button({ recipientAddressString = WalletFixture.Alice.getAddresses(zcashNetwork).transparent }) {
|
|
Text(text = stringResource(id = R.string.send_alyssa_transparent))
|
|
}
|
|
}
|
|
// Bob's addresses
|
|
Row(
|
|
Modifier
|
|
.fillMaxWidth()
|
|
.horizontalScroll(rememberScrollState())
|
|
) {
|
|
Button({ recipientAddressString = WalletFixture.Bob.getAddresses(zcashNetwork).unified }) {
|
|
Text(text = stringResource(id = R.string.send_ben_unified))
|
|
}
|
|
|
|
Spacer(Modifier.size(8.dp))
|
|
|
|
Button({ recipientAddressString = WalletFixture.Bob.getAddresses(zcashNetwork).sapling }) {
|
|
Text(text = stringResource(id = R.string.send_ben_sapling))
|
|
}
|
|
|
|
Spacer(Modifier.size(8.dp))
|
|
|
|
Button({ recipientAddressString = WalletFixture.Bob.getAddresses(zcashNetwork).transparent }) {
|
|
Text(text = stringResource(id = R.string.send_ben_transparent))
|
|
}
|
|
}
|
|
}
|
|
|
|
Spacer(Modifier.size(8.dp))
|
|
|
|
TextField(value = memoString, onValueChange = {
|
|
if (Memo.isWithinMaxLength(it)) {
|
|
memoString = it
|
|
}
|
|
}, label = { Text(stringResource(id = R.string.send_memo)) })
|
|
|
|
Spacer(Modifier.fillMaxHeight(MINIMAL_WEIGHT))
|
|
|
|
if (validation.isNotEmpty()) {
|
|
/*
|
|
* Note: this is not localized in that it uses the enum constant name and joins the string
|
|
* without regard for RTL. This will get resolved once we do proper validation for
|
|
* the fields.
|
|
*/
|
|
Text(validation.joinToString(", "))
|
|
}
|
|
|
|
Button(
|
|
onClick = {
|
|
val zecSendValidation = ZecSendExt.new(
|
|
context,
|
|
recipientAddressString,
|
|
amountZecString,
|
|
memoString,
|
|
monetarySeparators
|
|
)
|
|
|
|
when (zecSendValidation) {
|
|
is ZecSendExt.ZecSendValidation.Valid -> onSend(zecSendValidation.zecSend)
|
|
is ZecSendExt.ZecSendValidation.Invalid -> validation = zecSendValidation.validationErrors
|
|
}
|
|
},
|
|
|
|
// Needs actual validation
|
|
enabled = amountZecString.isNotBlank() && recipientAddressString.isNotBlank()
|
|
) {
|
|
Text(stringResource(id = R.string.send_button))
|
|
}
|
|
}
|
|
}
|