demo: minor UI improvements

This commit is contained in:
Oscar Mira 2023-10-22 21:39:43 +02:00
parent 21e8c77e26
commit 894788f3b2
11 changed files with 99 additions and 40 deletions

View File

@ -1,6 +1,7 @@
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3Api
@ -9,6 +10,7 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
@ -17,6 +19,10 @@ import im.molly.monero.MoneroCurrency
import im.molly.monero.Transaction
import im.molly.monero.demo.ui.preview.PreviewParameterData
import im.molly.monero.demo.ui.theme.AppTheme
import im.molly.monero.demo.ui.theme.Blue40
import im.molly.monero.demo.ui.theme.Red40
import java.time.ZoneId
import java.time.format.DateTimeFormatter
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@ -25,29 +31,47 @@ fun TransactionCardExpanded(
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
Card(
onClick = onClick,
modifier = modifier.padding(top = 8.dp, start = 8.dp, end = 8.dp)
modifier = modifier.padding(top = 8.dp, start = 8.dp, end = 8.dp),
) {
Column(
modifier = Modifier.padding(12.dp),
modifier = Modifier.padding(14.dp)
) {
TransactionDetail("State", transaction.state.toString())
Spacer(modifier = Modifier.height(12.dp))
TransactionDetail("Sent", transaction.sent.toString())
Spacer(modifier = Modifier.height(12.dp))
TransactionDetail("Received", transaction.received.toString())
Spacer(modifier = Modifier.height(12.dp))
TransactionDetail("Payments", transaction.payments.toString())
Spacer(modifier = Modifier.height(12.dp))
TransactionDetail("Time lock", transaction.timeLock.toString())
Spacer(modifier = Modifier.height(12.dp))
TransactionDetail("Change", MoneroCurrency.format(transaction.change))
Spacer(modifier = Modifier.height(12.dp))
TransactionDetail("Fee", MoneroCurrency.format(transaction.fee))
Spacer(modifier = Modifier.height(12.dp))
TransactionDetail("Transaction ID", transaction.txId)
Spacer(modifier = Modifier.height(12.dp))
Row(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = modifier.fillMaxWidth(),
) {
val timestamp = transaction.timestamp?.let {
val localDateTime = it.atZone(ZoneId.systemDefault()).toLocalDateTime()
localDateTime.format(formatter)
}
Text(
text = timestamp ?: "",
style = MaterialTheme.typography.titleLarge,
)
Text(
text = MoneroCurrency.format(transaction.amount, precision = 5),
color = if (transaction.amount < 0) Red40 else Blue40,
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold,
)
}
Row(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = modifier.fillMaxWidth(),
) {
Text(
text = "#${transaction.blockHeight}",
style = MaterialTheme.typography.titleSmall,
)
Text(
text = "fee ${MoneroCurrency.format(transaction.fee, precision = 8)}",
style = MaterialTheme.typography.titleSmall,
)
}
}
}
}
@ -82,6 +106,7 @@ private fun TransactionCardExpandedPreview(
}
}
private class TransactionCardPreviewParameterProvider : PreviewParameterProvider<List<Transaction>> {
private class TransactionCardPreviewParameterProvider :
PreviewParameterProvider<List<Transaction>> {
override val values = sequenceOf(PreviewParameterData.transactions)
}

View File

@ -80,6 +80,8 @@ private fun TransactionScreen(
val tx = uiState.transaction
TransactionDetail("State", tx.state.toString())
Spacer(modifier = Modifier.height(12.dp))
TransactionDetail("Amount", MoneroCurrency.format(tx.amount))
Spacer(modifier = Modifier.height(12.dp))
TransactionDetail("Sent", tx.sent.toString())
Spacer(modifier = Modifier.height(12.dp))
TransactionDetail("Received", tx.received.toString())

View File

@ -2,6 +2,7 @@ package im.molly.monero.demo.ui
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3Api
@ -33,17 +34,16 @@ fun WalletCard(
val uiState: WalletUiState by viewModel.uiState.collectAsStateWithLifecycle()
Card(
onClick = onClick, modifier = modifier.padding(8.dp)
onClick = onClick,
modifier = modifier
.fillMaxWidth()
.padding(top = 8.dp, start = 8.dp, end = 8.dp),
) {
Column(
modifier = Modifier.padding(14.dp),
) {
Column {
when (uiState) {
is WalletUiState.Loaded -> {
WalletCardExpanded(
(uiState as WalletUiState.Loaded).config,
(uiState as WalletUiState.Loaded).balance,
)
}
is WalletUiState.Loaded -> WalletCardExpanded(uiState as WalletUiState.Loaded)
WalletUiState.Error -> WalletCardError()
WalletUiState.Loading -> WalletCardLoading()
}
@ -53,14 +53,13 @@ fun WalletCard(
@Composable
fun WalletCardExpanded(
config: WalletConfig,
balance: Balance,
uiState: WalletUiState.Loaded,
) {
Row {
Text(text = "Name: ${config.name}")
Text(text = "Wallet ID #${uiState.config.id} (${uiState.config.name}) ${uiState.config.publicAddress}")
}
Row {
Text(text = "Ledger: $balance")
Text(text = uiState.blockchainTime.toString())
}
}

View File

@ -100,7 +100,7 @@ private fun WalletScreenLoaded(
.padding(padding),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(style = MaterialTheme.typography.headlineLarge, text = buildAnnotatedString {
Text(style = MaterialTheme.typography.headlineMedium, text = buildAnnotatedString {
append(MoneroCurrency.SYMBOL + " ")
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) {
append(

View File

@ -62,7 +62,9 @@ private fun walletUiState(
val balance = ledger.balance
val blockchainTime = ledger.checkedAt
val transactions =
ledger.transactions.map { WalletTransaction(config.id, it.value) }
ledger.transactions
.map { WalletTransaction(config.id, it.value) }
.sortedByDescending { it.transaction.timestamp }
WalletUiState.Loaded(config, blockchainTime, balance, transactions)
}

View File

@ -9,3 +9,5 @@ val Pink80 = Color(0xFFEFB8C8)
val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)
val Red40 = Color(0xFFBA1B1B)
val Blue40 = Color(0xFF1546F6)

View File

@ -23,4 +23,16 @@ class Enote(
override fun hashCode() = System.identityHashCode(this)
override fun equals(other: Any?) = this === other
override fun toString(): String {
return "Enote(" +
"amount=${amount.xmr}" +
", age=$age" +
", spent=$spent" +
", owner=$owner" +
", key=$key" +
", keyImage=$keyImage" +
", sourceTxId=$sourceTxId" +
")"
}
}

View File

@ -2,6 +2,7 @@ package im.molly.monero
import java.text.NumberFormat
import java.util.Locale
import kotlin.math.absoluteValue
object MoneroCurrency {
const val SYMBOL = "XMR"
@ -33,7 +34,11 @@ object MoneroCurrency {
val ExactFormat = object : Format(MoneroAmount.ATOMIC_UNIT_SCALE) {
override fun format(amount: MoneroAmount) = buildString {
val num = amount.atomicUnits.toString()
if (amount.atomicUnits < 0) {
append('-')
}
val num = amount.atomicUnits.absoluteValue.toString()
if (precision < num.length) {
val point = num.length - precision
@ -50,8 +55,11 @@ object MoneroCurrency {
}
}
fun format(amount: MoneroAmount, outputFormat: Format = ExactFormat): String {
return outputFormat.format(amount)
}
fun format(amount: MoneroAmount, precision: Int): String {
return Format(precision = precision).format(amount)
}
}

View File

@ -44,7 +44,7 @@ class MoneroWallet internal constructor(
private fun sendLedger(ledger: Ledger) {
trySend(ledger).onFailure {
logger.e("Too many ledger updates, channel capacity exceeded")
logger.e("Too many ledger updates, channel capacity exceeded", it)
}
}
}

View File

@ -5,4 +5,6 @@ value class PublicKey(private val publicKey: String) {
init {
require(publicKey.length == 64) { "Public key length must be 64 bytes" }
}
override fun toString(): String = publicKey
}

View File

@ -1,5 +1,7 @@
package im.molly.monero
import java.time.Instant
data class Transaction(
val hash: HashDigest,
// TODO: val version: ProtocolInfo,
@ -11,11 +13,16 @@ data class Transaction(
val fee: MoneroAmount,
val change: MoneroAmount,
) {
val amount: MoneroAmount = received.sumOf { it.amount } - sent.sumOf { it.amount }
val txId: String
get() = hash.toString()
val blockHeight: Int?
get() = (state as? TxState.OnChain)?.blockHeader?.height
val timestamp: Instant?
get() = (state as? TxState.OnChain)?.let { Instant.ofEpochSecond(it.blockHeader.epochSecond) }
}
sealed interface TxState {