Extend shielding state (#1322)

- This adds `ShieldState.Shielded` that helps us keep the correct UI state of the Transparent funds widget after the user starts shielding action
- This also brings little UI improvement in PrimaryButton sizing
This commit is contained in:
Honza Rychnovský 2024-04-08 07:49:53 +02:00 committed by GitHub
parent 8c027003cc
commit b34d086cc0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 40 additions and 13 deletions

View File

@ -86,7 +86,7 @@ fun PrimaryButton(
horizontal = ZcashTheme.dimens.spacingNone, horizontal = ZcashTheme.dimens.spacingNone,
vertical = ZcashTheme.dimens.spacingSmall vertical = ZcashTheme.dimens.spacingSmall
), ),
contentPaddingValues: PaddingValues = PaddingValues(all = 15.dp) contentPaddingValues: PaddingValues = PaddingValues(all = 15.5.dp)
) { ) {
Button( Button(
shape = RectangleShape, shape = RectangleShape,

View File

@ -8,12 +8,14 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import cash.z.ecc.android.sdk.SdkSynchronizer import cash.z.ecc.android.sdk.SdkSynchronizer
import cash.z.ecc.android.sdk.Synchronizer import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.model.UnifiedSpendingKey import cash.z.ecc.android.sdk.model.UnifiedSpendingKey
import cash.z.ecc.android.sdk.model.Zatoshi import cash.z.ecc.android.sdk.model.Zatoshi
import co.electriccoin.zcash.spackle.Twig import co.electriccoin.zcash.spackle.Twig
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.model.WalletRestoringState import co.electriccoin.zcash.ui.common.model.WalletRestoringState
import co.electriccoin.zcash.ui.common.model.WalletSnapshot import co.electriccoin.zcash.ui.common.model.WalletSnapshot
import co.electriccoin.zcash.ui.common.viewmodel.CheckUpdateViewModel import co.electriccoin.zcash.ui.common.viewmodel.CheckUpdateViewModel
@ -85,6 +87,8 @@ internal fun WrapBalances(
) { ) {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val context = LocalContext.current
// To show information about the app update, if available // To show information about the app update, if available
val isUpdateAvailable = val isUpdateAvailable =
checkUpdateViewModel.updateInfo.collectAsStateWithLifecycle().value.let { checkUpdateViewModel.updateInfo.collectAsStateWithLifecycle().value.let {
@ -94,15 +98,10 @@ internal fun WrapBalances(
val isFiatConversionEnabled = ConfigurationEntries.IS_FIAT_CONVERSION_ENABLED.getValue(RemoteConfig.current) val isFiatConversionEnabled = ConfigurationEntries.IS_FIAT_CONVERSION_ENABLED.getValue(RemoteConfig.current)
val (shieldState, setShieldState) = val (shieldState, setShieldState) =
rememberSaveable(stateSaver = ShieldState.Saver) { rememberSaveable(stateSaver = ShieldState.Saver) { mutableStateOf(ShieldState.None) }
mutableStateOf(
if (walletSnapshot?.hasTransparentFunds == true) { // Keep the state always up-to-date with the latest transparent balance
ShieldState.Available setShieldState(updateTransparentBalanceState(shieldState, walletSnapshot))
} else {
ShieldState.None
}
)
}
val (isShowingErrorDialog, setShowErrorDialog) = rememberSaveable { mutableStateOf(false) } val (isShowingErrorDialog, setShowErrorDialog) = rememberSaveable { mutableStateOf(false) }
@ -145,10 +144,12 @@ internal fun WrapBalances(
transparentReceiver = null transparentReceiver = null
) )
}.onSuccess { newProposal -> }.onSuccess { newProposal ->
Twig.debug { "Shielding proposal result: ${newProposal?.toPrettyString()}" } Twig.info { "Shielding proposal result: ${newProposal?.toPrettyString()}" }
if (newProposal == null) { if (newProposal == null) {
showShieldingError(null) showShieldingError(
context.getString(R.string.balances_shielding_dialog_error_below_threshold)
)
} else { } else {
val result = val result =
createTransactionsViewModel.runCreateTransactions( createTransactionsViewModel.runCreateTransactions(
@ -159,7 +160,7 @@ internal fun WrapBalances(
when (result) { when (result) {
SubmitResult.Success -> { SubmitResult.Success -> {
Twig.info { "Shielding transaction done successfully" } Twig.info { "Shielding transaction done successfully" }
setShieldState(ShieldState.None) setShieldState(ShieldState.Shielded)
// Triggering transaction history refresh to be notified about the newly created // Triggering transaction history refresh to be notified about the newly created
// transaction asap // transaction asap
(synchronizer as SdkSynchronizer).refreshTransactions() (synchronizer as SdkSynchronizer).refreshTransactions()
@ -188,3 +189,21 @@ internal fun WrapBalances(
) )
} }
} }
fun updateTransparentBalanceState(
currentShieldState: ShieldState,
walletSnapshot: WalletSnapshot?
): ShieldState {
return when {
(walletSnapshot == null) -> {
currentShieldState
}
(
walletSnapshot.transparentBalance >= Zatoshi(DEFAULT_SHIELDING_THRESHOLD) &&
currentShieldState.isEnabled()
) -> ShieldState.Available
else -> {
currentShieldState
}
}
}

View File

@ -9,12 +9,17 @@ sealed class ShieldState {
data object Running : ShieldState() data object Running : ShieldState()
data object Shielded : ShieldState()
data class Failed(val error: String) : ShieldState() data class Failed(val error: String) : ShieldState()
fun isEnabled() = this != Running && this !is Failed && this != Shielded
companion object { companion object {
private const val TYPE_NONE = "none" // $NON-NLS private const val TYPE_NONE = "none" // $NON-NLS
private const val TYPE_AVAILABLE = "available" // $NON-NLS private const val TYPE_AVAILABLE = "available" // $NON-NLS
private const val TYPE_RUNNING = "running" // $NON-NLS private const val TYPE_RUNNING = "running" // $NON-NLS
private const val TYPE_SHIELDED = "shielded" // $NON-NLS
private const val TYPE_FAILED = "failed" // $NON-NLS private const val TYPE_FAILED = "failed" // $NON-NLS
private const val KEY_TYPE = "type" // $NON-NLS private const val KEY_TYPE = "type" // $NON-NLS
@ -34,6 +39,7 @@ sealed class ShieldState {
TYPE_NONE -> None TYPE_NONE -> None
TYPE_AVAILABLE -> Available TYPE_AVAILABLE -> Available
TYPE_RUNNING -> Running TYPE_RUNNING -> Running
TYPE_SHIELDED -> Shielded
TYPE_FAILED -> Failed((it[KEY_ERROR] as String)) TYPE_FAILED -> Failed((it[KEY_ERROR] as String))
else -> null else -> null
} }
@ -48,6 +54,7 @@ sealed class ShieldState {
None -> saverMap[KEY_TYPE] = TYPE_NONE None -> saverMap[KEY_TYPE] = TYPE_NONE
Available -> saverMap[KEY_TYPE] = TYPE_AVAILABLE Available -> saverMap[KEY_TYPE] = TYPE_AVAILABLE
Running -> saverMap[KEY_TYPE] = TYPE_RUNNING Running -> saverMap[KEY_TYPE] = TYPE_RUNNING
Shielded -> saverMap[KEY_TYPE] = TYPE_SHIELDED
is Failed -> { is Failed -> {
saverMap[KEY_TYPE] = TYPE_FAILED saverMap[KEY_TYPE] = TYPE_FAILED
saverMap[KEY_ERROR] = this.error saverMap[KEY_ERROR] = this.error

View File

@ -33,4 +33,5 @@
<string name="balances_shielding_dialog_error_title">Failed to shield funds</string> <string name="balances_shielding_dialog_error_title">Failed to shield funds</string>
<string name="balances_shielding_dialog_error_text">Error: The attempt to shield the transparent funds failed. Try it again, please.</string> <string name="balances_shielding_dialog_error_text">Error: The attempt to shield the transparent funds failed. Try it again, please.</string>
<string name="balances_shielding_dialog_error_btn">OK</string> <string name="balances_shielding_dialog_error_btn">OK</string>
<string name="balances_shielding_dialog_error_below_threshold">The current transparent balance is zero or below the allowed shielding limit.</string>
</resources> </resources>