rename example
This commit is contained in:
@@ -0,0 +1,218 @@
|
||||
package com.bitcointxoko.gudariwallet.ui
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ContentPaste
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalClipboard
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.bitcointxoko.gudariwallet.ui.send.Bolt11ConfirmCard
|
||||
import com.bitcointxoko.gudariwallet.ui.send.LnurlInvoiceConfirmCard
|
||||
import com.bitcointxoko.gudariwallet.ui.send.LnurlPayForm
|
||||
import com.bitcointxoko.gudariwallet.util.SendInputDetector
|
||||
import com.bitcointxoko.gudariwallet.util.feePpm
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun SendScreen(vm: WalletViewModel) {
|
||||
val sendState: SendState by vm.sendState.collectAsStateWithLifecycle()
|
||||
var input by remember { mutableStateOf("") }
|
||||
val context = LocalContext.current
|
||||
val activity = context as FragmentActivity
|
||||
val clipboard = LocalClipboard.current
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
// Read rate directly from VM cache — same source HomeTab uses.
|
||||
// Both are null when no currency is selected; fiat display is suppressed.
|
||||
val fiatRate = vm.fiatRate // Double?
|
||||
val fiatCurrency = vm.fiatCurrency // String?
|
||||
|
||||
DisposableEffect(Unit) { onDispose { vm.resetSendState() } }
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(24.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Top
|
||||
) {
|
||||
Spacer(Modifier.height(16.dp))
|
||||
Text("Send", style = MaterialTheme.typography.headlineSmall)
|
||||
Spacer(Modifier.height(24.dp))
|
||||
|
||||
if (sendState !is SendState.PaymentSent) {
|
||||
OutlinedTextField(
|
||||
value = input,
|
||||
onValueChange = { input = SendInputDetector.normalize(it) },
|
||||
label = { Text("Invoice, LNURL, or Lightning Address") },
|
||||
placeholder = { Text("lnbc… / lnurl1… / user@domain.com") },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
enabled = sendState is SendState.Idle || sendState is SendState.Error,
|
||||
singleLine = false,
|
||||
maxLines = 4,
|
||||
textStyle = MaterialTheme.typography.bodySmall.copy(fontFamily = FontFamily.Monospace),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Uri,
|
||||
imeAction = ImeAction.Go
|
||||
),
|
||||
keyboardActions = KeyboardActions(
|
||||
onGo = { if (input.isNotBlank()) vm.scan(input) }
|
||||
),
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
}
|
||||
|
||||
when (val state = sendState) {
|
||||
is SendState.Idle -> {
|
||||
Button(
|
||||
onClick = {
|
||||
scope.launch {
|
||||
val text = clipboard.getClipEntry()
|
||||
?.clipData
|
||||
?.getItemAt(0)
|
||||
?.text
|
||||
?.toString()
|
||||
if (!text.isNullOrBlank()) {
|
||||
input = SendInputDetector.normalize(text)
|
||||
vm.scan(text)
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ContentPaste,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text("Paste")
|
||||
}
|
||||
Spacer(Modifier.height(8.dp))
|
||||
OutlinedButton(
|
||||
onClick = { if (input.isNotBlank()) vm.scan(input) },
|
||||
enabled = input.isNotBlank(),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) { Text("Continue") }
|
||||
}
|
||||
|
||||
is SendState.Scanning -> {
|
||||
Spacer(Modifier.height(16.dp))
|
||||
CircularProgressIndicator()
|
||||
Spacer(Modifier.height(8.dp))
|
||||
Text("Resolving…")
|
||||
}
|
||||
|
||||
is SendState.Bolt11Decoded -> {
|
||||
Bolt11ConfirmCard(
|
||||
state = state,
|
||||
activity = activity,
|
||||
vm = vm,
|
||||
fiatRate = fiatRate,
|
||||
fiatCurrency = fiatCurrency
|
||||
)
|
||||
}
|
||||
|
||||
is SendState.LnurlReady -> {
|
||||
LnurlPayForm(
|
||||
state = state,
|
||||
vm = vm,
|
||||
fiatRate = fiatRate,
|
||||
fiatCurrency = fiatCurrency
|
||||
)
|
||||
}
|
||||
|
||||
is SendState.LnurlInvoiceReady -> {
|
||||
LnurlInvoiceConfirmCard(
|
||||
state = state,
|
||||
activity = activity,
|
||||
vm = vm,
|
||||
fiatRate = fiatRate,
|
||||
fiatCurrency = fiatCurrency
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
is SendState.Paying -> {
|
||||
Spacer(Modifier.height(16.dp))
|
||||
CircularProgressIndicator()
|
||||
Spacer(Modifier.height(8.dp))
|
||||
Text("Sending payment…")
|
||||
}
|
||||
|
||||
is SendState.PaymentSent -> {
|
||||
Spacer(Modifier.height(32.dp))
|
||||
Text(
|
||||
text = "✓",
|
||||
style = MaterialTheme.typography.displayLarge,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
Text("Payment sent!", style = MaterialTheme.typography.headlineSmall)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(
|
||||
text = "${state.amountSats} sats",
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(4.dp))
|
||||
val feeText = when {
|
||||
state.feeSats == null || state.feeSats == 0L -> "No fees"
|
||||
else -> {
|
||||
val ppm = feePpm(
|
||||
amountMsat = state.amountSats * 1_000L,
|
||||
feeMsat = state.feeSats * 1_000L
|
||||
)
|
||||
val ppmSuffix = if (ppm != null) " (${ppm} ppm)" else ""
|
||||
"Routing fee: ${state.feeSats} sat${if (state.feeSats == 1L) "" else "s"}$ppmSuffix"
|
||||
}
|
||||
}
|
||||
Text(
|
||||
text = feeText,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
Spacer(Modifier.height(32.dp))
|
||||
Button(
|
||||
onClick = { vm.resetSendState(); input = "" },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) { Text("Send Another") }
|
||||
}
|
||||
|
||||
is SendState.Error -> {
|
||||
Spacer(Modifier.height(8.dp))
|
||||
Text(
|
||||
text = state.message,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Spacer(Modifier.height(12.dp))
|
||||
Button(
|
||||
onClick = { if (input.isNotBlank()) vm.scan(input) },
|
||||
enabled = input.isNotBlank(),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) { Text("Try Again") }
|
||||
Spacer(Modifier.height(8.dp))
|
||||
OutlinedButton(
|
||||
onClick = { vm.resetSendState(); input = "" },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) { Text("Clear") }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user