mirror of
https://github.com/mollyim/monero-wallet-sdk.git
synced 2024-10-01 03:45:36 -04:00
demo: minor UI improvements
This commit is contained in:
parent
ec4ebe6f78
commit
db85c2234f
@ -1,10 +1,9 @@
|
|||||||
package im.molly.monero.demo.ui
|
package im.molly.monero.demo.ui
|
||||||
|
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.mutableStateMapOf
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import im.molly.monero.MoneroNetwork
|
import im.molly.monero.MoneroNetwork
|
||||||
import im.molly.monero.BlockchainTime
|
|
||||||
import im.molly.monero.RestorePoint
|
import im.molly.monero.RestorePoint
|
||||||
import im.molly.monero.SecretKey
|
import im.molly.monero.SecretKey
|
||||||
import im.molly.monero.demo.AppModule
|
import im.molly.monero.demo.AppModule
|
||||||
@ -17,7 +16,6 @@ import im.molly.monero.util.parseHex
|
|||||||
import im.molly.monero.util.toHex
|
import im.molly.monero.util.toHex
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.flow.SharingStarted.Companion.WhileSubscribed
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
|
|
||||||
@ -27,33 +25,24 @@ class AddWalletViewModel(
|
|||||||
private val walletRepository: WalletRepository = AppModule.walletRepository,
|
private val walletRepository: WalletRepository = AppModule.walletRepository,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
var network by mutableStateOf(DefaultMoneroNetwork)
|
private val viewModelState = MutableStateFlow(AddWalletUiState())
|
||||||
private set
|
|
||||||
|
|
||||||
var walletName by mutableStateOf("")
|
val uiState = viewModelState.stateIn(
|
||||||
private set
|
scope = viewModelScope,
|
||||||
|
started = SharingStarted.WhileSubscribed(5_000),
|
||||||
var secretSpendKeyHex by mutableStateOf("")
|
initialValue = viewModelState.value
|
||||||
private set
|
)
|
||||||
|
|
||||||
var creationDate by mutableStateOf("")
|
|
||||||
private set
|
|
||||||
|
|
||||||
var restoreHeight by mutableStateOf("")
|
|
||||||
private set
|
|
||||||
|
|
||||||
val currentRemoteNodes: StateFlow<List<RemoteNode>> =
|
val currentRemoteNodes: StateFlow<List<RemoteNode>> =
|
||||||
snapshotFlow { network }
|
uiState.flatMapLatest {
|
||||||
.flatMapLatest {
|
remoteNodeRepository.getAllRemoteNodes(
|
||||||
remoteNodeRepository.getAllRemoteNodes(
|
filterNetworkIds = setOf(it.network.id),
|
||||||
filterNetworkIds = setOf(network.id),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.stateIn(
|
|
||||||
scope = viewModelScope,
|
|
||||||
started = WhileSubscribed(5000),
|
|
||||||
initialValue = emptyList(),
|
|
||||||
)
|
)
|
||||||
|
}.stateIn(
|
||||||
|
scope = viewModelScope,
|
||||||
|
started = SharingStarted.WhileSubscribed(5_000),
|
||||||
|
initialValue = emptyList(),
|
||||||
|
)
|
||||||
|
|
||||||
val selectedRemoteNodes = mutableStateMapOf<Long?, Boolean>()
|
val selectedRemoteNodes = mutableStateMapOf<Long?, Boolean>()
|
||||||
|
|
||||||
@ -73,21 +62,23 @@ class AddWalletViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun toggleSelectedNetwork(network: MoneroNetwork) {
|
fun toggleSelectedNetwork(network: MoneroNetwork) {
|
||||||
this.network = network
|
viewModelState.update { it.copy(network = network) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateWalletName(name: String) {
|
fun updateWalletName(name: String) {
|
||||||
this.walletName = name
|
viewModelState.update { it.copy(walletName = name) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateSecretSpendKeyHex(value: String) {
|
fun updateSecretSpendKeyHex(value: String) {
|
||||||
this.secretSpendKeyHex = value
|
viewModelState.update { it.copy(secretSpendKeyHex = value) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun recoverFromMnemonic(words: String): Boolean {
|
fun recoverFromMnemonic(words: String): Boolean {
|
||||||
MoneroMnemonic.recoverEntropy(words)?.use { mnemonicCode ->
|
MoneroMnemonic.recoverEntropy(words)?.use { mnemonicCode ->
|
||||||
val secretKey = SecretKey(mnemonicCode.entropy)
|
val secretKey = SecretKey(mnemonicCode.entropy)
|
||||||
secretSpendKeyHex = secretKey.bytes.toHex()
|
viewModelState.update {
|
||||||
|
it.copy(secretSpendKeyHex = secretKey.bytes.toHex())
|
||||||
|
}
|
||||||
secretKey.destroy()
|
secretKey.destroy()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -95,40 +86,76 @@ class AddWalletViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun updateCreationDate(value: String) {
|
fun updateCreationDate(value: String) {
|
||||||
this.creationDate = value
|
viewModelState.update { it.copy(creationDate = value) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateRestoreHeight(value: String) {
|
fun updateRestoreHeight(value: String) {
|
||||||
this.restoreHeight = value
|
viewModelState.update { it.copy(restoreHeight = value) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun validateSecretSpendKeyHex(): Boolean =
|
fun validateSecretSpendKeyHex(): Boolean =
|
||||||
secretSpendKeyHex.length == 64 && runCatching { secretSpendKeyHex.parseHex() }.isSuccess
|
with(viewModelState.value) {
|
||||||
|
return secretSpendKeyHex.length == 64 && runCatching {
|
||||||
|
secretSpendKeyHex.parseHex()
|
||||||
|
}.isSuccess
|
||||||
|
}
|
||||||
|
|
||||||
fun validateCreationDate(): Boolean =
|
fun validateCreationDate(): Boolean =
|
||||||
creationDate.isEmpty() || runCatching { RestorePoint.creationTime(LocalDate.parse(creationDate)) }.isSuccess
|
with(viewModelState.value) {
|
||||||
|
creationDate.isEmpty() || runCatching {
|
||||||
|
RestorePoint.creationTime(LocalDate.parse(creationDate))
|
||||||
|
}.isSuccess
|
||||||
|
}
|
||||||
|
|
||||||
fun validateRestoreHeight(): Boolean =
|
fun validateRestoreHeight(): Boolean =
|
||||||
restoreHeight.isEmpty() || runCatching { RestorePoint.blockHeight(restoreHeight.toInt()) }.isSuccess
|
with(viewModelState.value) {
|
||||||
|
restoreHeight.isEmpty() || runCatching {
|
||||||
|
RestorePoint.blockHeight(restoreHeight.toInt())
|
||||||
|
}.isSuccess
|
||||||
|
}
|
||||||
|
|
||||||
fun createWallet() = viewModelScope.launch {
|
fun createWallet() {
|
||||||
walletRepository.addWallet(network, walletName, getSelectedRemoteNodeIds())
|
val state = viewModelState.getAndUpdate { it.copy(isInProgress = true) }
|
||||||
|
viewModelScope.launch {
|
||||||
|
walletRepository.addWallet(state.network, state.walletName, getSelectedRemoteNodeIds())
|
||||||
|
viewModelState.update { it.copy(walletAdded = true) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun restoreWallet() = viewModelScope.launch {
|
fun restoreWallet() {
|
||||||
val restorePoint = when {
|
val state = viewModelState.getAndUpdate {
|
||||||
creationDate.isNotEmpty() -> RestorePoint.creationTime(LocalDate.parse(creationDate))
|
it.copy(isInProgress = true)
|
||||||
restoreHeight.isNotEmpty() -> RestorePoint.blockHeight(restoreHeight.toInt())
|
|
||||||
else -> RestorePoint.Genesis
|
|
||||||
}
|
}
|
||||||
SecretKey(secretSpendKeyHex.parseHex()).use { secretSpendKey ->
|
viewModelScope.launch {
|
||||||
walletRepository.restoreWallet(
|
val restorePoint = when {
|
||||||
network,
|
state.creationDate.isNotEmpty() ->
|
||||||
walletName,
|
RestorePoint.creationTime(LocalDate.parse(state.creationDate))
|
||||||
getSelectedRemoteNodeIds(),
|
|
||||||
secretSpendKey,
|
state.restoreHeight.isNotEmpty() ->
|
||||||
restorePoint
|
RestorePoint.blockHeight(state.restoreHeight.toInt())
|
||||||
)
|
|
||||||
|
else -> RestorePoint.Genesis
|
||||||
|
}
|
||||||
|
SecretKey(state.secretSpendKeyHex.parseHex()).use { secretSpendKey ->
|
||||||
|
walletRepository.restoreWallet(
|
||||||
|
state.network,
|
||||||
|
state.walletName,
|
||||||
|
getSelectedRemoteNodeIds(),
|
||||||
|
secretSpendKey,
|
||||||
|
restorePoint
|
||||||
|
)
|
||||||
|
}
|
||||||
|
viewModelState.update { it.copy(walletAdded = true) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class AddWalletUiState(
|
||||||
|
val network: MoneroNetwork = DefaultMoneroNetwork,
|
||||||
|
val walletName: String = "",
|
||||||
|
val secretSpendKeyHex: String = "",
|
||||||
|
val creationDate: String = "",
|
||||||
|
val restoreHeight: String = "",
|
||||||
|
val isInProgress: Boolean = false,
|
||||||
|
val walletAdded: Boolean = false,
|
||||||
|
)
|
||||||
|
@ -9,6 +9,7 @@ import androidx.compose.material3.*
|
|||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
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
|
||||||
@ -88,8 +89,15 @@ fun AddWalletSecondStepRoute(
|
|||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
val remoteNodes by viewModel.currentRemoteNodes.collectAsStateWithLifecycle()
|
val remoteNodes by viewModel.currentRemoteNodes.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
|
LaunchedEffect(uiState) {
|
||||||
|
if (uiState.walletAdded) {
|
||||||
|
onNavigateToHome()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SecondStepScreen(
|
SecondStepScreen(
|
||||||
showRestoreOptions = showRestoreOptions,
|
showRestoreOptions = showRestoreOptions,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
@ -100,15 +108,10 @@ fun AddWalletSecondStepRoute(
|
|||||||
} else {
|
} else {
|
||||||
viewModel.createWallet()
|
viewModel.createWallet()
|
||||||
}
|
}
|
||||||
onNavigateToHome()
|
|
||||||
},
|
},
|
||||||
walletName = viewModel.walletName,
|
uiState = uiState,
|
||||||
network = viewModel.network,
|
|
||||||
secretSpendKeyHex = viewModel.secretSpendKeyHex,
|
|
||||||
secretSpendKeyHexError = !viewModel.validateSecretSpendKeyHex(),
|
secretSpendKeyHexError = !viewModel.validateSecretSpendKeyHex(),
|
||||||
creationDate = viewModel.creationDate,
|
|
||||||
creationDateError = !viewModel.validateCreationDate(),
|
creationDateError = !viewModel.validateCreationDate(),
|
||||||
restoreHeight = viewModel.restoreHeight,
|
|
||||||
restoreHeightError = !viewModel.validateRestoreHeight(),
|
restoreHeightError = !viewModel.validateRestoreHeight(),
|
||||||
onWalletNameChanged = { name -> viewModel.updateWalletName(name) },
|
onWalletNameChanged = { name -> viewModel.updateWalletName(name) },
|
||||||
onNetworkChanged = { network -> viewModel.toggleSelectedNetwork(network) },
|
onNetworkChanged = { network -> viewModel.toggleSelectedNetwork(network) },
|
||||||
@ -133,14 +136,10 @@ private fun SecondStepScreen(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onBackClick: () -> Unit = {},
|
onBackClick: () -> Unit = {},
|
||||||
onCreateClick: () -> Unit = {},
|
onCreateClick: () -> Unit = {},
|
||||||
walletName: String,
|
uiState: AddWalletUiState,
|
||||||
secretSpendKeyHex: String,
|
secretSpendKeyHexError: Boolean = false,
|
||||||
secretSpendKeyHexError: Boolean,
|
creationDateError: Boolean = false,
|
||||||
creationDate: String,
|
restoreHeightError: Boolean = false,
|
||||||
creationDateError: Boolean,
|
|
||||||
restoreHeight: String,
|
|
||||||
restoreHeightError: Boolean,
|
|
||||||
network: MoneroNetwork,
|
|
||||||
onWalletNameChanged: (String) -> Unit = {},
|
onWalletNameChanged: (String) -> Unit = {},
|
||||||
onNetworkChanged: (MoneroNetwork) -> Unit = {},
|
onNetworkChanged: (MoneroNetwork) -> Unit = {},
|
||||||
onSecretSpendKeyHexChanged: (String) -> Unit = {},
|
onSecretSpendKeyHexChanged: (String) -> Unit = {},
|
||||||
@ -172,10 +171,11 @@ private fun SecondStepScreen(
|
|||||||
.verticalScroll(rememberScrollState()),
|
.verticalScroll(rememberScrollState()),
|
||||||
) {
|
) {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = walletName,
|
value = uiState.walletName,
|
||||||
label = { Text("Wallet name") },
|
label = { Text("Wallet name") },
|
||||||
onValueChange = onWalletNameChanged,
|
onValueChange = onWalletNameChanged,
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
|
enabled = !uiState.isInProgress,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(start = 16.dp, end = 16.dp),
|
.padding(start = 16.dp, end = 16.dp),
|
||||||
@ -183,10 +183,11 @@ private fun SecondStepScreen(
|
|||||||
SelectListBox(
|
SelectListBox(
|
||||||
label = "Network",
|
label = "Network",
|
||||||
options = MoneroNetwork.entries.associateWith { it.name },
|
options = MoneroNetwork.entries.associateWith { it.name },
|
||||||
selectedOption = network,
|
selectedOption = uiState.network,
|
||||||
onOptionClick = {
|
onOptionClick = {
|
||||||
onNetworkChanged(it)
|
onNetworkChanged(it)
|
||||||
},
|
},
|
||||||
|
enabled = !uiState.isInProgress,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(16.dp),
|
.padding(16.dp),
|
||||||
@ -194,31 +195,36 @@ private fun SecondStepScreen(
|
|||||||
Text(
|
Text(
|
||||||
text = "Remote node selection",
|
text = "Remote node selection",
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
modifier = Modifier.padding(16.dp),
|
modifier = (if (uiState.isInProgress) Modifier.alpha(0.3f) else Modifier)
|
||||||
|
.padding(16.dp),
|
||||||
)
|
)
|
||||||
MultiSelectRemoteNodeList(
|
MultiSelectRemoteNodeList(
|
||||||
remoteNodes = remoteNodes,
|
remoteNodes = remoteNodes,
|
||||||
selectedIds = selectedRemoteNodeIds,
|
selectedIds = selectedRemoteNodeIds,
|
||||||
|
enabled = !uiState.isInProgress,
|
||||||
modifier = Modifier.padding(start = 16.dp),
|
modifier = Modifier.padding(start = 16.dp),
|
||||||
)
|
)
|
||||||
if (showRestoreOptions) {
|
if (showRestoreOptions) {
|
||||||
Text(
|
Text(
|
||||||
text = "Deterministic wallet recovery",
|
text = "Deterministic wallet recovery",
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
modifier = Modifier.padding(16.dp),
|
modifier = (if (uiState.isInProgress) Modifier.alpha(0.3f) else Modifier)
|
||||||
|
.padding(16.dp),
|
||||||
)
|
)
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = secretSpendKeyHex,
|
value = uiState.secretSpendKeyHex,
|
||||||
label = { Text("Secret spend key") },
|
label = { Text("Secret spend key") },
|
||||||
onValueChange = onSecretSpendKeyHexChanged,
|
onValueChange = onSecretSpendKeyHexChanged,
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
isError = secretSpendKeyHexError,
|
isError = secretSpendKeyHexError,
|
||||||
|
enabled = !uiState.isInProgress,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(start = 16.dp, end = 16.dp),
|
.padding(start = 16.dp, end = 16.dp),
|
||||||
)
|
)
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = { showMnemonicDialog = true },
|
onClick = { showMnemonicDialog = true },
|
||||||
|
enabled = !uiState.isInProgress,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(start = 16.dp),
|
.padding(start = 16.dp),
|
||||||
) {
|
) {
|
||||||
@ -230,23 +236,23 @@ private fun SecondStepScreen(
|
|||||||
modifier = Modifier.padding(16.dp),
|
modifier = Modifier.padding(16.dp),
|
||||||
)
|
)
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = creationDate,
|
value = uiState.creationDate,
|
||||||
label = { Text("Wallet creation date") },
|
label = { Text("Wallet creation date") },
|
||||||
onValueChange = onCreationDateChanged,
|
onValueChange = onCreationDateChanged,
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
isError = creationDateError,
|
isError = creationDateError,
|
||||||
enabled = restoreHeight.isEmpty(),
|
enabled = uiState.restoreHeight.isEmpty() && !uiState.isInProgress,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(start = 16.dp, end = 16.dp),
|
.padding(start = 16.dp, end = 16.dp),
|
||||||
)
|
)
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = restoreHeight,
|
value = uiState.restoreHeight,
|
||||||
label = { Text("Restore height") },
|
label = { Text("Restore height") },
|
||||||
onValueChange = onRestoreHeightChanged,
|
onValueChange = onRestoreHeightChanged,
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
isError = restoreHeightError,
|
isError = restoreHeightError,
|
||||||
enabled = creationDate.isEmpty(),
|
enabled = uiState.creationDate.isEmpty() && !uiState.isInProgress,
|
||||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@ -268,10 +274,10 @@ private fun SecondStepScreen(
|
|||||||
onCreateClick()
|
onCreateClick()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
enabled = validInput,
|
enabled = validInput && !uiState.isInProgress,
|
||||||
modifier = Modifier.padding(16.dp),
|
modifier = Modifier.padding(16.dp),
|
||||||
) {
|
) {
|
||||||
Text("Finish")
|
Text(if (uiState.isInProgress) "Adding wallet..." else "Finish")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -336,14 +342,11 @@ private fun CreateWalletScreenPreview() {
|
|||||||
AppTheme {
|
AppTheme {
|
||||||
SecondStepScreen(
|
SecondStepScreen(
|
||||||
showRestoreOptions = false,
|
showRestoreOptions = false,
|
||||||
walletName = "Personal",
|
uiState = AddWalletUiState(
|
||||||
network = DefaultMoneroNetwork,
|
walletName = "Personal",
|
||||||
secretSpendKeyHex = "d2ca26e22489bd9871c910c58dee3ab08e66b9d566825a064c8c0af061cd8706",
|
network = DefaultMoneroNetwork,
|
||||||
secretSpendKeyHexError = false,
|
secretSpendKeyHex = "d2ca26e22489bd9871c910c58dee3ab08e66b9d566825a064c8c0af061cd8706",
|
||||||
creationDate = "",
|
),
|
||||||
creationDateError = false,
|
|
||||||
restoreHeight = "",
|
|
||||||
restoreHeightError = false,
|
|
||||||
remoteNodes = listOf(RemoteNode.EMPTY),
|
remoteNodes = listOf(RemoteNode.EMPTY),
|
||||||
selectedRemoteNodeIds = mutableMapOf(),
|
selectedRemoteNodeIds = mutableMapOf(),
|
||||||
)
|
)
|
||||||
@ -356,14 +359,11 @@ private fun RestoreWalletScreenPreview() {
|
|||||||
AppTheme {
|
AppTheme {
|
||||||
SecondStepScreen(
|
SecondStepScreen(
|
||||||
showRestoreOptions = true,
|
showRestoreOptions = true,
|
||||||
walletName = "Personal",
|
uiState = AddWalletUiState(
|
||||||
network = DefaultMoneroNetwork,
|
walletName = "Personal",
|
||||||
secretSpendKeyHex = "d2ca26e22489bd9871c910c58dee3ab08e66b9d566825a064c8c0af061cd8706",
|
network = DefaultMoneroNetwork,
|
||||||
secretSpendKeyHexError = false,
|
secretSpendKeyHex = "d2ca26e22489bd9871c910c58dee3ab08e66b9d566825a064c8c0af061cd8706",
|
||||||
creationDate = "",
|
),
|
||||||
creationDateError = false,
|
|
||||||
restoreHeight = "",
|
|
||||||
restoreHeightError = false,
|
|
||||||
remoteNodes = listOf(RemoteNode.EMPTY),
|
remoteNodes = listOf(RemoteNode.EMPTY),
|
||||||
selectedRemoteNodeIds = mutableMapOf(),
|
selectedRemoteNodeIds = mutableMapOf(),
|
||||||
)
|
)
|
||||||
|
@ -45,7 +45,7 @@ fun AddressCardExpanded(
|
|||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = "Total Balance: ${totalAmount.toFormattedString(appendSymbol = true)}",
|
text = "Total balance: ${totalAmount.toFormattedString(appendSymbol = true)}",
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
|
@ -49,7 +49,11 @@ fun EditableRecipientList(
|
|||||||
onRecipientChange(updatedList)
|
onRecipientChange(updatedList)
|
||||||
},
|
},
|
||||||
onDeleteItemClick = {
|
onDeleteItemClick = {
|
||||||
updatedList.removeAt(index)
|
if (updatedList.size > 1) {
|
||||||
|
updatedList.removeAt(index)
|
||||||
|
} else {
|
||||||
|
updatedList[0] = "" to ""
|
||||||
|
}
|
||||||
onRecipientChange(updatedList)
|
onRecipientChange(updatedList)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -104,7 +108,7 @@ fun PaymentDetailItem(
|
|||||||
)
|
)
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = onDeleteItemClick,
|
onClick = onDeleteItemClick,
|
||||||
enabled = enabled && itemIndex > 0,
|
enabled = enabled,
|
||||||
modifier = Modifier.padding(top = 12.dp),
|
modifier = Modifier.padding(top = 12.dp),
|
||||||
) {
|
) {
|
||||||
Icon(imageVector = Icons.Default.Delete, contentDescription = "Delete")
|
Icon(imageVector = Icons.Default.Delete, contentDescription = "Delete")
|
||||||
|
@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.*
|
|||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import im.molly.monero.demo.data.model.RemoteNode
|
import im.molly.monero.demo.data.model.RemoteNode
|
||||||
import im.molly.monero.demo.ui.theme.AppIcons
|
import im.molly.monero.demo.ui.theme.AppIcons
|
||||||
@ -14,6 +15,7 @@ fun MultiSelectRemoteNodeList(
|
|||||||
remoteNodes: List<RemoteNode>,
|
remoteNodes: List<RemoteNode>,
|
||||||
selectedIds: MutableMap<Long?, Boolean>,
|
selectedIds: MutableMap<Long?, Boolean>,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true,
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
@ -27,6 +29,7 @@ fun MultiSelectRemoteNodeList(
|
|||||||
onCheckedChange = { checked ->
|
onCheckedChange = { checked ->
|
||||||
selectedIds[remoteNode.id] = checked
|
selectedIds[remoteNode.id] = checked
|
||||||
},
|
},
|
||||||
|
enabled = enabled,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -63,17 +66,29 @@ private fun RemoteNodeItem(
|
|||||||
checked: Boolean = false,
|
checked: Boolean = false,
|
||||||
showCheckbox: Boolean = false,
|
showCheckbox: Boolean = false,
|
||||||
showMenu: Boolean = false,
|
showMenu: Boolean = false,
|
||||||
|
enabled: Boolean = true,
|
||||||
onCheckedChange: (Boolean) -> Unit = {},
|
onCheckedChange: (Boolean) -> Unit = {},
|
||||||
onEditClick: (RemoteNode) -> Unit = {},
|
onEditClick: (RemoteNode) -> Unit = {},
|
||||||
onDeleteClick: (RemoteNode) -> Unit = {},
|
onDeleteClick: (RemoteNode) -> Unit = {},
|
||||||
) {
|
) {
|
||||||
ListItem(
|
ListItem(
|
||||||
headlineContent = { Text(remoteNode.uri.toString()) },
|
headlineContent = {
|
||||||
overlineContent = { Text(remoteNode.network.name.uppercase()) },
|
Text(
|
||||||
|
text = remoteNode.uri.toString(),
|
||||||
|
modifier = (if (enabled) Modifier else Modifier.alpha(0.3f)),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
overlineContent = {
|
||||||
|
Text(
|
||||||
|
text = remoteNode.network.name.uppercase(),
|
||||||
|
modifier = (if (enabled) Modifier else Modifier.alpha(0.3f)),
|
||||||
|
)
|
||||||
|
},
|
||||||
trailingContent = {
|
trailingContent = {
|
||||||
if (showCheckbox) {
|
if (showCheckbox) {
|
||||||
Checkbox(
|
Checkbox(
|
||||||
checked = checked,
|
checked = checked,
|
||||||
|
enabled = enabled,
|
||||||
onCheckedChange = onCheckedChange,
|
onCheckedChange = onCheckedChange,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user