diff --git a/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/extension/ZatoshiExt.kt b/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/extension/ZatoshiExt.kt index eb883f939..cd6dbe039 100644 --- a/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/extension/ZatoshiExt.kt +++ b/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/extension/ZatoshiExt.kt @@ -34,11 +34,6 @@ fun Zatoshi.toZecStringAbbreviated(suffix: String): ZecAmountPair { } } -private const val DEFAULT_LESS_THAN_FEE = 100_000L - -val DEFAULT_FEE: String - get() = Zatoshi(DEFAULT_LESS_THAN_FEE).toZecStringFull() - data class ZecAmountPair( val main: String, val suffix: String diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Balance.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Balance.kt index 844414238..147b65699 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Balance.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Balance.kt @@ -2,6 +2,7 @@ package co.electriccoin.zcash.ui.design.component import androidx.compose.foundation.basicMarquee import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable @@ -66,6 +67,7 @@ fun StyledBalance( balanceParts: ZecAmountTriple, modifier: Modifier = Modifier, isHideBalances: Boolean = false, + showDust: Boolean = true, hiddenBalancePlaceholder: String = stringResource(id = R.string.hide_balance_placeholder), textColor: Color = Color.Unspecified, textStyle: BalanceTextStyle = StyledBalanceDefaults.textStyles(), @@ -88,10 +90,12 @@ fun StyledBalance( ) { append(balanceSplit.first) } - withStyle( - style = textStyle.leastSignificantPart.toSpanStyle() - ) { - append(balanceSplit.second) + if (showDust) { + withStyle( + style = textStyle.leastSignificantPart.toSpanStyle() + ) { + append(balanceSplit.second) + } } } } @@ -101,12 +105,14 @@ fun StyledBalance( .basicMarquee() .then(modifier) - Text( - text = content, - color = textColor, - maxLines = 1, - modifier = resultModifier - ) + SelectionContainer { + Text( + text = content, + color = textColor, + maxLines = 1, + modifier = resultModifier + ) + } } private const val CUT_POSITION_OFFSET = 4 diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiBigIconButton.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiBigIconButton.kt index 08bdf63b4..fe12478e7 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiBigIconButton.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiBigIconButton.kt @@ -1,6 +1,7 @@ package co.electriccoin.zcash.ui.design.component import androidx.annotation.DrawableRes +import androidx.compose.animation.core.animateDpAsState import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -13,10 +14,17 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.input.pointer.changedToDown +import androidx.compose.ui.input.pointer.changedToUp +import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp @@ -36,6 +44,9 @@ fun ZashiBigIconButton( state: BigIconButtonState, modifier: Modifier = Modifier, ) { + var isPressed by remember { mutableStateOf(false) } + val shadowElevation by animateDpAsState(if (isPressed) 0.dp else (2.dp orDark 4.dp)) + val darkBgGradient = Brush.verticalGradient( 0f to ZashiColors.Surfaces.strokeSecondary, @@ -54,14 +65,29 @@ fun ZashiBigIconButton( Modifier.background(darkBgGradient) Surface( - modifier = modifier, + modifier = modifier + .pointerInput(Unit) { + awaitPointerEventScope { + while (true) { + val event = awaitPointerEvent() + event.changes.forEach { change -> + if (change.changedToDown()) { + isPressed = true + } + if (change.changedToUp()) { + isPressed = false + } + } + } + } + }, onClick = state.onClick, color = ZashiColors.Surfaces.bgPrimary, shape = RoundedCornerShape(22.dp), border = BorderStroke(.5.dp, ZashiColors.Utility.Gray.utilityGray100) orDark BorderStroke(.5.dp, darkBorderGradient), - shadowElevation = 2.dp orDark 4.dp + shadowElevation = shadowElevation ) { Column( modifier = backgroundModifier.padding(16.dp), diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/util/StringResource.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/util/StringResource.kt index 15a9be8e3..22b8d3fd6 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/util/StringResource.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/util/StringResource.kt @@ -57,8 +57,15 @@ sealed interface StringResource { val address: String, val abbreviated: Boolean ) : StringResource + + operator fun plus(other: StringResource): StringResource = CompositeStringResource(listOf(this, other)) + + operator fun plus(other: String): StringResource = CompositeStringResource(listOf(this, stringRes(other))) } +@Immutable +private data class CompositeStringResource(val resources: List): StringResource + @Stable fun stringRes( @StringRes resource: Int, @@ -109,18 +116,14 @@ fun StringResource.getValue( convertYearMonth: (YearMonth) -> String = StringResourceDefaults::convertYearMonth, convertAddress: (StringResource.ByAddress) -> String = StringResourceDefaults::convertAddress, convertTransactionId: (StringResource.ByTransactionId) -> String = StringResourceDefaults::convertTransactionId -) = when (this) { - is StringResource.ByResource -> { - val context = LocalContext.current - context.getString(resource, *args.normalize(context).toTypedArray()) - } - is StringResource.ByString -> value - is StringResource.ByZatoshi -> convertZatoshi(zatoshi) - is StringResource.ByDateTime -> convertDateTime(this) - is StringResource.ByYearMonth -> convertYearMonth(yearMonth) - is StringResource.ByAddress -> convertAddress(this) - is StringResource.ByTransactionId -> convertTransactionId(this) -} +): String = getString( + context = LocalContext.current, + convertZatoshi = convertZatoshi, + convertDateTime = convertDateTime, + convertYearMonth = convertYearMonth, + convertAddress = convertAddress, + convertTransactionId = convertTransactionId +) @Suppress("SpreadOperator") fun StringResource.getString( @@ -130,7 +133,7 @@ fun StringResource.getString( convertYearMonth: (YearMonth) -> String = StringResourceDefaults::convertYearMonth, convertAddress: (StringResource.ByAddress) -> String = StringResourceDefaults::convertAddress, convertTransactionId: (StringResource.ByTransactionId) -> String = StringResourceDefaults::convertTransactionId -) = when (this) { +): String = when (this) { is StringResource.ByResource -> context.getString(resource, *args.normalize(context).toTypedArray()) is StringResource.ByString -> value is StringResource.ByZatoshi -> convertZatoshi(zatoshi) @@ -138,6 +141,16 @@ fun StringResource.getString( is StringResource.ByYearMonth -> convertYearMonth(yearMonth) is StringResource.ByAddress -> convertAddress(this) is StringResource.ByTransactionId -> convertTransactionId(this) + is CompositeStringResource -> this.resources.joinToString(separator = "") { + it.getString( + context = context, + convertZatoshi = convertZatoshi, + convertDateTime = convertDateTime, + convertYearMonth = convertYearMonth, + convertAddress = convertAddress, + convertTransactionId = convertTransactionId, + ) + } } private fun List.normalize(context: Context): List = diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/SendViewTestSetup.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/SendViewTestSetup.kt index d4d4b8e2f..a9adf32d9 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/SendViewTestSetup.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/SendViewTestSetup.kt @@ -104,7 +104,7 @@ class SendViewTestSetup( // TODO [#1260]: Cover Send.Form screen UI with tests // TODO [#1260]: https://github.com/Electric-Coin-Company/zashi-android/issues/1260 Send( - balanceState = BalanceStateFixture.new(), + balanceWidgetState = BalanceStateFixture.new(), sendStage = sendStage, onCreateZecSend = setZecSend, onBack = onBackAction, diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/di/ViewModelModule.kt b/ui-lib/src/main/java/co/electriccoin/zcash/di/ViewModelModule.kt index f6acb6841..2f61e0af3 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/di/ViewModelModule.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/di/ViewModelModule.kt @@ -8,7 +8,7 @@ import co.electriccoin.zcash.ui.screen.accountlist.viewmodel.AccountListViewMode import co.electriccoin.zcash.ui.screen.addressbook.viewmodel.AddressBookViewModel import co.electriccoin.zcash.ui.screen.addressbook.viewmodel.SelectRecipientViewModel import co.electriccoin.zcash.ui.screen.advancedsettings.AdvancedSettingsViewModel -import co.electriccoin.zcash.ui.screen.balances.BalanceViewModel +import co.electriccoin.zcash.ui.screen.balances.BalanceWidgetViewModel import co.electriccoin.zcash.ui.screen.balances.action.BalanceActionViewModel import co.electriccoin.zcash.ui.screen.chooseserver.ChooseServerViewModel import co.electriccoin.zcash.ui.screen.contact.viewmodel.AddContactViewModel @@ -143,7 +143,7 @@ val viewModelModule = ) } viewModelOf(::TaxExportViewModel) - viewModelOf(::BalanceViewModel) + viewModelOf(::BalanceWidgetViewModel) viewModelOf(::HomeViewModel) viewModelOf(::RestoreBDHeightViewModel) viewModelOf(::RestoreBDDateViewModel) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/datasource/ShieldFundsDataSource.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/datasource/ShieldFundsDataSource.kt index 7cc9ddaa1..65551927e 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/datasource/ShieldFundsDataSource.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/datasource/ShieldFundsDataSource.kt @@ -14,6 +14,7 @@ import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf import java.time.Instant import kotlin.time.Duration +import kotlin.time.Duration.Companion.days import kotlin.time.Duration.Companion.seconds interface ShieldFundsDataSource { @@ -87,7 +88,7 @@ sealed interface ShieldFundsAvailability { } enum class ShieldFundsLockoutDuration(val duration: Duration, @StringRes val res: Int) { - TWO_DAYS(10.seconds, R.string.general_remind_me_in_two_days), - TWO_WEEKS(20.seconds, R.string.general_remind_me_in_two_weeks), - ONE_MONTH(30.seconds, R.string.general_remind_me_in_two_months) + TWO_DAYS(2.days, R.string.general_remind_me_in_two_days), + TWO_WEEKS(2.days, R.string.general_remind_me_in_two_weeks), + ONE_MONTH(30.days, R.string.general_remind_me_in_two_months) } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/datasource/WalletBackupDataSource.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/datasource/WalletBackupDataSource.kt index e88b4f12e..2b4155f0b 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/datasource/WalletBackupDataSource.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/datasource/WalletBackupDataSource.kt @@ -14,6 +14,7 @@ import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf import java.time.Instant import kotlin.time.Duration +import kotlin.time.Duration.Companion.days import kotlin.time.Duration.Companion.seconds interface WalletBackupDataSource { @@ -96,7 +97,7 @@ sealed interface WalletBackupAvailability { } enum class WalletBackupLockoutDuration(val duration: Duration, @StringRes val res: Int) { - TWO_DAYS(10.seconds, R.string.general_remind_me_in_two_days), - TWO_WEEKS(20.seconds, R.string.general_remind_me_in_two_weeks), - ONE_MONTH(30.seconds, R.string.general_remind_me_in_two_months), + TWO_DAYS(2.days, R.string.general_remind_me_in_two_days), + TWO_WEEKS(14.days, R.string.general_remind_me_in_two_weeks), + ONE_MONTH(30.days, R.string.general_remind_me_in_two_months), } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/model/WalletAccount.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/model/WalletAccount.kt index 3bdac928e..8cc1f40de 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/model/WalletAccount.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/model/WalletAccount.kt @@ -29,11 +29,25 @@ sealed interface WalletAccount : Comparable { val totalShieldedBalance: Zatoshi val spendableBalance: Zatoshi val changePendingBalance: Zatoshi - val hasChangePending: Boolean val valuePendingBalance: Zatoshi + + val pendingBalance: Zatoshi + get() = changePendingBalance + valuePendingBalance + + val hasChangePending: Boolean val hasValuePending: Boolean + val isPending: Boolean + get() = pendingBalance > Zatoshi(0) fun canSpend(amount: Zatoshi): Boolean = spendableBalance >= amount + + fun isProcessingZeroAvailableBalance(): Boolean { + if (totalShieldedBalance == Zatoshi(0) && transparent.balance > Zatoshi(0)) { + return false + } + + return totalBalance != totalShieldedBalance && totalShieldedBalance == Zatoshi(0) + } } data class ZashiAccount( @@ -55,10 +69,10 @@ data class ZashiAccount( get() = unified.balance.available + sapling.balance.available override val changePendingBalance: Zatoshi get() = unified.balance.changePending + sapling.balance.changePending - override val hasChangePending: Boolean - get() = changePendingBalance.value > 0L override val valuePendingBalance: Zatoshi get() = unified.balance.valuePending + sapling.balance.valuePending + override val hasChangePending: Boolean + get() = changePendingBalance.value > 0L override val hasValuePending: Boolean get() = valuePendingBalance.value > 0L @@ -88,10 +102,10 @@ data class KeystoneAccount( get() = unified.balance.available override val changePendingBalance: Zatoshi get() = unified.balance.changePending - override val hasChangePending: Boolean - get() = changePendingBalance.value > 0L override val valuePendingBalance: Zatoshi get() = unified.balance.valuePending + override val hasChangePending: Boolean + get() = changePendingBalance.value > 0L override val hasValuePending: Boolean get() = valuePendingBalance.value > 0L @@ -110,7 +124,10 @@ data class UnifiedInfo( data class TransparentInfo( val address: WalletAddress.Transparent, val balance: Zatoshi -) +) { + val isShieldingAvailable: Boolean + get() = balance > Zatoshi(100000L) +} data class SaplingInfo( val address: WalletAddress.Sapling, diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/repository/ShieldFundsRepository.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/repository/ShieldFundsRepository.kt index 5e3f64e41..788d8bf5b 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/repository/ShieldFundsRepository.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/repository/ShieldFundsRepository.kt @@ -29,7 +29,7 @@ class ShieldFundsRepositoryImpl( account == null -> flowOf(ShieldFundsData.Unavailable) - account.transparent.balance >= Zatoshi(DEFAULT_SHIELDING_THRESHOLD) -> + account.transparent.isShieldingAvailable -> shieldFundsDataSource.observe(account.sdkAccount.accountUuid).map { when (it) { is ShieldFundsAvailability.Available -> ShieldFundsData.Available( @@ -60,6 +60,3 @@ sealed interface ShieldFundsData { data object Unavailable : ShieldFundsData } - -private const val DEFAULT_SHIELDING_THRESHOLD = 100000L - diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/usecase/ShieldFundsUseCase.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/usecase/ShieldFundsUseCase.kt index dbbd60508..89894a2da 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/usecase/ShieldFundsUseCase.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/usecase/ShieldFundsUseCase.kt @@ -25,7 +25,7 @@ class ShieldFundsUseCase( ) { private val scope = CoroutineScope(Dispatchers.Main.immediate + SupervisorJob()) - operator fun invoke(navigateBackAfterSuccess: Boolean) { + operator fun invoke(closeCurrentScreen: Boolean) { scope.launch { when (accountDataSource.getSelectedAccount()) { is KeystoneAccount -> { @@ -33,7 +33,7 @@ class ShieldFundsUseCase( } is ZashiAccount -> { - if (navigateBackAfterSuccess) { + if (closeCurrentScreen) { navigationRouter.back() } shieldZashiFunds() diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/fixture/BalanceStateFixture.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/fixture/BalanceStateFixture.kt index a662e37e9..6189562b5 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/fixture/BalanceStateFixture.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/fixture/BalanceStateFixture.kt @@ -2,7 +2,7 @@ package co.electriccoin.zcash.ui.fixture import cash.z.ecc.android.sdk.model.Zatoshi import co.electriccoin.zcash.ui.common.wallet.ExchangeRateState -import co.electriccoin.zcash.ui.screen.balances.BalanceState +import co.electriccoin.zcash.ui.screen.balances.BalanceWidgetState object BalanceStateFixture { private const val BALANCE_VALUE = 0L @@ -12,9 +12,10 @@ object BalanceStateFixture { fun new( totalBalance: Zatoshi = TOTAL_BALANCE, exchangeRate: ExchangeRateState = ObserveFiatCurrencyResultFixture.new() - ) = BalanceState( + ) = BalanceWidgetState( totalBalance = totalBalance, exchangeRate = exchangeRate, - button = null + button = null, + showDust = true ) } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/balances/BalanceViewModel.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/balances/BalanceViewModel.kt deleted file mode 100644 index 7fe084d7d..000000000 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/balances/BalanceViewModel.kt +++ /dev/null @@ -1,44 +0,0 @@ -package co.electriccoin.zcash.ui.screen.balances - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import cash.z.ecc.android.sdk.model.Zatoshi -import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT -import co.electriccoin.zcash.ui.common.datasource.AccountDataSource -import co.electriccoin.zcash.ui.common.model.WalletAccount -import co.electriccoin.zcash.ui.common.repository.ExchangeRateRepository -import co.electriccoin.zcash.ui.common.wallet.ExchangeRateState -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.WhileSubscribed -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.flow.stateIn - -class BalanceViewModel( - accountDataSource: AccountDataSource, - exchangeRateRepository: ExchangeRateRepository, -) : ViewModel() { - val state: StateFlow = - combine( - accountDataSource.selectedAccount.filterNotNull(), - exchangeRateRepository.state, - ) { account, exchangeRateUsd -> - createState(account, exchangeRateUsd) - }.stateIn( - viewModelScope, - SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), - createState( - account = accountDataSource.allAccounts.value?.firstOrNull { it.isSelected }, - exchangeRateUsd = exchangeRateRepository.state.value - ) - ) - - private fun createState(account: WalletAccount?, exchangeRateUsd: ExchangeRateState): BalanceState { - return BalanceState( - totalBalance = account?.totalBalance ?: Zatoshi(0), - exchangeRate = exchangeRateUsd, - button = null - ) - } -} diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/balances/BalanceWidget.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/balances/BalanceWidget.kt index 3fd648538..2c4c62756 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/balances/BalanceWidget.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/balances/BalanceWidget.kt @@ -34,7 +34,7 @@ import co.electriccoin.zcash.ui.screen.balances.BalanceTag.BALANCE_VIEWS import co.electriccoin.zcash.ui.screen.exchangerate.widget.StyledExchangeBalance @Composable -fun BalanceWidget(state: BalanceState, modifier: Modifier = Modifier) { +fun BalanceWidget(state: BalanceWidgetState, modifier: Modifier = Modifier) { Column( modifier = Modifier @@ -44,12 +44,13 @@ fun BalanceWidget(state: BalanceState, modifier: Modifier = Modifier) { horizontalAlignment = Alignment.CenterHorizontally ) { BalanceWidgetHeader( - parts = state.totalBalance.toZecStringFull().asZecAmountTriple() + parts = state.totalBalance.toZecStringFull().asZecAmountTriple(), + showDust = state.showDust ) state.button?.let { Spacer(12.dp) - BalanceButton(it) + BalanceWidgetButton(it) } state.exchangeRate?.let { @@ -57,9 +58,6 @@ fun BalanceWidget(state: BalanceState, modifier: Modifier = Modifier) { Spacer(12.dp) } StyledExchangeBalance(state = it, zatoshi = state.totalBalance) - if (state.exchangeRate is ExchangeRateState.Data) { - Spacer(12.dp) - } } } } @@ -69,6 +67,7 @@ fun BalanceWidgetHeader( parts: ZecAmountTriple, modifier: Modifier = Modifier, isHideBalances: Boolean = LocalBalancesAvailable.current.not(), + showDust: Boolean = true, ) { Row( modifier = modifier, @@ -81,6 +80,7 @@ fun BalanceWidgetHeader( ) Spacer(6.dp) StyledBalance( + showDust = showDust, balanceParts = parts, isHideBalances = isHideBalances, textStyle = @@ -101,7 +101,7 @@ private fun BalanceWidgetPreview() { ) { BalanceWidget( state = - BalanceState( + BalanceWidgetState( totalBalance = Zatoshi(1234567891234567L), button = BalanceButtonState( icon = R.drawable.ic_help, @@ -109,7 +109,8 @@ private fun BalanceWidgetPreview() { amount = Zatoshi(1000), onClick = {} ), - exchangeRate = ObserveFiatCurrencyResultFixture.new() + exchangeRate = ObserveFiatCurrencyResultFixture.new(), + showDust = true ), modifier = Modifier, ) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/balances/BalanceButton.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/balances/BalanceWidgetButton.kt similarity index 91% rename from ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/balances/BalanceButton.kt rename to ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/balances/BalanceWidgetButton.kt index 786ef8c5f..0ccebeed8 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/balances/BalanceButton.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/balances/BalanceWidgetButton.kt @@ -1,5 +1,6 @@ package co.electriccoin.zcash.ui.screen.balances +import androidx.annotation.DrawableRes import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.Image import androidx.compose.foundation.layout.PaddingValues @@ -11,6 +12,7 @@ import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -30,12 +32,12 @@ import co.electriccoin.zcash.ui.design.theme.ZcashTheme import co.electriccoin.zcash.ui.design.theme.colors.ZashiColors import co.electriccoin.zcash.ui.design.theme.dimensions.ZashiDimensions import co.electriccoin.zcash.ui.design.theme.typography.ZashiTypography +import co.electriccoin.zcash.ui.design.util.StringResource import co.electriccoin.zcash.ui.design.util.getValue import co.electriccoin.zcash.ui.design.util.stringRes -@Suppress("LongParameterList") @Composable -fun BalanceButton( +internal fun BalanceWidgetButton( state: BalanceButtonState, modifier: Modifier = Modifier, ) { @@ -52,7 +54,7 @@ fun BalanceButton( contentPadding = PaddingValues(horizontal = 12.dp, vertical = 10.dp), colors = colors.toButtonColors(), elevation = ButtonDefaults.buttonElevation( - defaultElevation = 2.dp, + defaultElevation = 1.dp, pressedElevation = 0.dp ), border = borderColor.takeIf { it != Color.Unspecified }?.let { BorderStroke(1.dp, it) }, @@ -98,11 +100,19 @@ fun BalanceButton( ) } +@Immutable +data class BalanceButtonState( + @DrawableRes val icon: Int, + val text: StringResource, + val amount: Zatoshi?, + val onClick: () -> Unit +) + @PreviewScreens @Composable private fun Preview() = ZcashTheme { BlankSurface { - BalanceButton( + BalanceWidgetButton( state = BalanceButtonState( icon = R.drawable.ic_help, text = stringRes("text"), diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/balances/BalanceState.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/balances/BalanceWidgetState.kt similarity index 53% rename from ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/balances/BalanceState.kt rename to ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/balances/BalanceWidgetState.kt index 76e6f37ed..8b1933f94 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/balances/BalanceState.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/balances/BalanceWidgetState.kt @@ -1,22 +1,13 @@ package co.electriccoin.zcash.ui.screen.balances -import androidx.annotation.DrawableRes import androidx.compose.runtime.Immutable import cash.z.ecc.android.sdk.model.Zatoshi import co.electriccoin.zcash.ui.common.wallet.ExchangeRateState -import co.electriccoin.zcash.ui.design.util.StringResource @Immutable -data class BalanceState( +data class BalanceWidgetState( + val showDust: Boolean, val totalBalance: Zatoshi, val button: BalanceButtonState?, val exchangeRate: ExchangeRateState?, ) - -@Immutable -data class BalanceButtonState( - @DrawableRes val icon: Int, - val text: StringResource, - val amount: Zatoshi?, - val onClick: () -> Unit -) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/balances/BalanceWidgetViewModel.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/balances/BalanceWidgetViewModel.kt new file mode 100644 index 000000000..4aaa5645e --- /dev/null +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/balances/BalanceWidgetViewModel.kt @@ -0,0 +1,81 @@ +package co.electriccoin.zcash.ui.screen.balances + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import cash.z.ecc.android.sdk.model.Zatoshi +import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT +import co.electriccoin.zcash.ui.NavigationRouter +import co.electriccoin.zcash.ui.R +import co.electriccoin.zcash.ui.common.datasource.AccountDataSource +import co.electriccoin.zcash.ui.common.model.WalletAccount +import co.electriccoin.zcash.ui.common.repository.ExchangeRateRepository +import co.electriccoin.zcash.ui.common.wallet.ExchangeRateState +import co.electriccoin.zcash.ui.design.util.stringRes +import co.electriccoin.zcash.ui.screen.balances.action.BalanceAction +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.WhileSubscribed +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.stateIn + +class BalanceWidgetViewModel( + private val args: BalanceWidgetArgs, + accountDataSource: AccountDataSource, + exchangeRateRepository: ExchangeRateRepository, + private val navigationRouter: NavigationRouter, +) : ViewModel() { + val state: StateFlow = + combine( + accountDataSource.selectedAccount.filterNotNull(), + exchangeRateRepository.state, + ) { account, exchangeRateUsd -> + createState(account, exchangeRateUsd) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), + initialValue = createState( + account = accountDataSource.allAccounts.value?.firstOrNull { it.isSelected }, + exchangeRateUsd = exchangeRateRepository.state.value + ) + ) + + + + private fun createState(account: WalletAccount?, exchangeRateUsd: ExchangeRateState): BalanceWidgetState { + return BalanceWidgetState( + totalBalance = account?.totalBalance ?: Zatoshi(0), + exchangeRate = if (args.isExchangeRateButtonEnabled) exchangeRateUsd else null, + button = when { + !args.isBalanceButtonEnabled -> null + account == null -> null + account.totalBalance == account.spendableBalance -> null + account.isProcessingZeroAvailableBalance() && !account.isPending -> + BalanceButtonState( + icon = R.drawable.ic_balances_expand, + text = stringRes(R.string.widget_balances_button_spendable), + amount = null, + onClick = ::onBalanceButtonClick + ) + + account.totalBalance > account.spendableBalance -> BalanceButtonState( + icon = R.drawable.ic_balances_expand, + text = stringRes(R.string.widget_balances_button_spendable), + amount = account.spendableBalance, + onClick = ::onBalanceButtonClick + ) + + else -> null + }, + showDust = args.showDust + ) + } + + private fun onBalanceButtonClick() = navigationRouter.forward(BalanceAction) +} + +data class BalanceWidgetArgs( + val showDust: Boolean, + val isBalanceButtonEnabled: Boolean, + val isExchangeRateButtonEnabled: Boolean, +) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/balances/action/BalanceActionViewModel.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/balances/action/BalanceActionViewModel.kt index b2c7b31f9..977061b74 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/balances/action/BalanceActionViewModel.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/balances/action/BalanceActionViewModel.kt @@ -1,39 +1,108 @@ package co.electriccoin.zcash.ui.screen.balances.action import androidx.lifecycle.ViewModel -import cash.z.ecc.android.sdk.model.Zatoshi +import androidx.lifecycle.viewModelScope +import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT +import co.electriccoin.zcash.ui.NavigationRouter import co.electriccoin.zcash.ui.R +import co.electriccoin.zcash.ui.common.datasource.AccountDataSource +import co.electriccoin.zcash.ui.common.model.WalletAccount +import co.electriccoin.zcash.ui.common.usecase.ShieldFundsUseCase import co.electriccoin.zcash.ui.design.component.ButtonState +import co.electriccoin.zcash.ui.design.util.StringResource import co.electriccoin.zcash.ui.design.util.imageRes import co.electriccoin.zcash.ui.design.util.loadingImageRes import co.electriccoin.zcash.ui.design.util.stringRes -import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.WhileSubscribed +import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.flow.stateIn -class BalanceActionViewModel : ViewModel() { - val state = MutableStateFlow( - BalanceActionState( - title = stringRes("Error"), - message = stringRes("Something went wrong"), - positive = ButtonState( - text = stringRes("Positive") - ), - onBack = {}, - rows = listOf( - BalanceActionRowState( - title = stringRes("Row"), - icon = loadingImageRes(), - value = stringRes("Value") - ), - BalanceActionRowState( - title = stringRes("Row"), - icon = imageRes(R.drawable.ic_home_buy), - value = stringRes("Value") - ) - ), - shieldButton = BalanceShieldButtonState( - amount = Zatoshi(10000), - onShieldClick = {} - ) +class BalanceActionViewModel( + accountDataSource: AccountDataSource, + private val navigationRouter: NavigationRouter, + private val shieldFunds: ShieldFundsUseCase, +) : ViewModel() { + val state = accountDataSource.selectedAccount + .mapNotNull { + createState(it) + } + .stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), + initialValue = createState(accountDataSource.allAccounts.value.orEmpty().firstOrNull { it.isSelected }) ) + + private fun createState(account: WalletAccount?): BalanceActionState? { + if (account == null) return null + + return BalanceActionState( + title = stringRes("Spendable Balance"), + message = createMessage(account), + positive = createPositiveButton(account), + onBack = ::onBack, + rows = createInfoRows(account), + shieldButton = createShieldButtonState(account) + ) + } + + private fun createMessage(account: WalletAccount): StringResource { + val pending = when { + account.totalBalance == account.spendableBalance && !account.isPending -> + stringRes("All your funds are shielded and spendable.") + + account.isPending || account.isProcessingZeroAvailableBalance() -> + stringRes("Pending transactions are getting mined and confirmed.") + else -> null + } + + val shielding = + stringRes("Shield your transparent ZEC to make it spendable and private. Shielding transparent funds will create a shielding in-wallet transaction, consolidating your transparent and shielded funds. (Typical fee: .001 ZEC)") + .takeIf { account.transparent.isShieldingAvailable } + + return if (pending != null && shielding != null) { + pending + stringRes("\n\n") + shielding + } else { + listOfNotNull(pending, shielding).reduceOrNull { acc, stringResource -> acc + stringResource } ?: stringRes( + "" + ) + } + } + + private fun createPositiveButton(account: WalletAccount) = ButtonState( + text = if (account.transparent.isShieldingAvailable) stringRes("Dismiss") else stringRes("Ok"), + onClick = ::onBack ) + + private fun createInfoRows(account: WalletAccount) = listOfNotNull( + BalanceActionRowState( + title = stringRes("Shielded ZEC (Spendable)"), + icon = imageRes(R.drawable.ic_balance_shield), + value = stringRes(R.string.general_zec, stringRes(account.spendableBalance)) + ), + if (!account.isProcessingZeroAvailableBalance()) { + BalanceActionRowState( + title = stringRes("Pending"), + icon = loadingImageRes(), + value = stringRes(R.string.general_zec, stringRes(account.totalBalance)) + ) + } else { + BalanceActionRowState( + title = stringRes("Pending"), + icon = loadingImageRes(), + value = stringRes(R.string.general_zec, stringRes(account.pendingBalance)) + ).takeIf { account.isPending } + }, + ) + + private fun createShieldButtonState(account: WalletAccount): BalanceShieldButtonState? { + return BalanceShieldButtonState( + amount = account.transparent.balance, + onShieldClick = ::onShieldClick + ).takeIf { account.transparent.isShieldingAvailable } + } + + private fun onBack() = navigationRouter.back() + + private fun onShieldClick() = shieldFunds(closeCurrentScreen = true) } \ No newline at end of file diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/exchangerate/settings/ExchangeRateSettingsViewModel.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/exchangerate/settings/ExchangeRateSettingsViewModel.kt index 0582b06f6..7dd177f72 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/exchangerate/settings/ExchangeRateSettingsViewModel.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/exchangerate/settings/ExchangeRateSettingsViewModel.kt @@ -28,7 +28,7 @@ class ExchangeRateSettingsViewModel( private fun createState(it: ExchangeRateState) = ExchangeRateSettingsState( - isOptedIn = it is ExchangeRateState.OptIn, + isOptedIn = it is ExchangeRateState.Data, onSaveClick = ::onOptInExchangeRateUsdClick, onDismiss = ::onBack ) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/AndroidHome.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/AndroidHome.kt index 983ac6038..67123a92e 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/AndroidHome.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/AndroidHome.kt @@ -7,28 +7,38 @@ import androidx.compose.runtime.getValue import androidx.lifecycle.compose.collectAsStateWithLifecycle import co.electriccoin.zcash.di.koinActivityViewModel import co.electriccoin.zcash.ui.common.appbar.ZashiTopAppBarViewModel -import co.electriccoin.zcash.ui.screen.balances.BalanceViewModel +import co.electriccoin.zcash.ui.screen.balances.BalanceWidgetArgs +import co.electriccoin.zcash.ui.screen.balances.BalanceWidgetViewModel import co.electriccoin.zcash.ui.screen.restoresuccess.WrapRestoreSuccess import co.electriccoin.zcash.ui.screen.transactionhistory.widget.TransactionHistoryWidgetViewModel import kotlinx.serialization.Serializable import org.koin.androidx.compose.koinViewModel +import org.koin.core.parameter.parametersOf @Composable internal fun AndroidHome() { val topAppBarViewModel = koinActivityViewModel() - val balanceViewModel = koinViewModel() + val balanceWidgetViewModel = koinViewModel { + parametersOf( + BalanceWidgetArgs( + isBalanceButtonEnabled = false, + isExchangeRateButtonEnabled = true, + showDust = false, + ) + ) + } val homeViewModel = koinViewModel() val transactionHistoryWidgetViewModel = koinViewModel() val restoreDialogState by homeViewModel.restoreDialogState.collectAsStateWithLifecycle() val appBarState by topAppBarViewModel.state.collectAsStateWithLifecycle() - val balanceState by balanceViewModel.state.collectAsStateWithLifecycle() + val balanceState by balanceWidgetViewModel.state.collectAsStateWithLifecycle() val state by homeViewModel.state.collectAsStateWithLifecycle() val transactionWidgetState by transactionHistoryWidgetViewModel.state.collectAsStateWithLifecycle() state?.let { HomeView( appBarState = appBarState, - balanceState = balanceState, + balanceWidgetState = balanceState, state = it, transactionWidgetState = transactionWidgetState ) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/HomeView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/HomeView.kt index 00f65e5b1..00f9ec7d9 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/HomeView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/HomeView.kt @@ -23,14 +23,16 @@ import co.electriccoin.zcash.ui.common.appbar.ZashiMainTopAppBarState import co.electriccoin.zcash.ui.common.appbar.ZashiTopAppBarWithAccountSelection import co.electriccoin.zcash.ui.design.component.BigIconButtonState import co.electriccoin.zcash.ui.design.component.BlankBgScaffold +import co.electriccoin.zcash.ui.design.component.Spacer import co.electriccoin.zcash.ui.design.component.ZashiBigIconButton import co.electriccoin.zcash.ui.design.newcomponent.PreviewScreens import co.electriccoin.zcash.ui.design.theme.ZcashTheme +import co.electriccoin.zcash.ui.design.theme.dimensions.ZashiDimensions import co.electriccoin.zcash.ui.design.util.scaffoldPadding import co.electriccoin.zcash.ui.design.util.stringRes import co.electriccoin.zcash.ui.fixture.BalanceStateFixture import co.electriccoin.zcash.ui.fixture.ZashiMainTopAppBarStateFixture -import co.electriccoin.zcash.ui.screen.balances.BalanceState +import co.electriccoin.zcash.ui.screen.balances.BalanceWidgetState import co.electriccoin.zcash.ui.screen.balances.BalanceWidget import co.electriccoin.zcash.ui.screen.home.error.WalletErrorMessageState import co.electriccoin.zcash.ui.screen.transactionhistory.widget.TransactionHistoryWidgetState @@ -40,7 +42,7 @@ import co.electriccoin.zcash.ui.screen.transactionhistory.widget.createTransacti @Composable internal fun HomeView( appBarState: ZashiMainTopAppBarState?, - balanceState: BalanceState, + balanceWidgetState: BalanceWidgetState, transactionWidgetState: TransactionHistoryWidgetState, state: HomeState ) { @@ -48,10 +50,10 @@ internal fun HomeView( topBar = { ZashiTopAppBarWithAccountSelection(appBarState) } ) { paddingValues -> Content( - modifier = Modifier.padding(top = paddingValues.calculateTopPadding() + 24.dp), + modifier = Modifier.padding(top = paddingValues.calculateTopPadding() + ZashiDimensions.Spacing.spacingLg), paddingValues = paddingValues, transactionHistoryWidgetState = transactionWidgetState, - balanceState = balanceState, + balanceWidgetState = balanceWidgetState, state = state ) } @@ -61,7 +63,7 @@ internal fun HomeView( private fun Content( transactionHistoryWidgetState: TransactionHistoryWidgetState, paddingValues: PaddingValues, - balanceState: BalanceState, + balanceWidgetState: BalanceWidgetState, state: HomeState, modifier: Modifier = Modifier, ) { @@ -71,7 +73,7 @@ private fun Content( Column( horizontalAlignment = Alignment.CenterHorizontally ) { - Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall)) + Spacer(8.dp) BalanceWidget( modifier = Modifier @@ -79,8 +81,9 @@ private fun Content( start = ZcashTheme.dimens.screenHorizontalSpacingRegular, end = ZcashTheme.dimens.screenHorizontalSpacingRegular, ), - state = balanceState, + state = balanceWidgetState, ) + Spacer(16.dp) NavButtons( modifier = Modifier @@ -160,7 +163,7 @@ private fun Preview() { ZcashTheme { HomeView( appBarState = ZashiMainTopAppBarStateFixture.new(), - balanceState = BalanceStateFixture.new(), + balanceWidgetState = BalanceStateFixture.new(), transactionWidgetState = TransactionHistoryWidgetStateFixture.new(), state = HomeState( diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/HomeViewModel.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/HomeViewModel.kt index 560b23d11..58c9299d6 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/HomeViewModel.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/HomeViewModel.kt @@ -237,7 +237,7 @@ class HomeViewModel( private fun onShieldFundsMessageClick() = navigationRouter.forward(ShieldFundsInfo) - private fun onShieldFundsMessageButtonClick() = shieldFunds(navigateBackAfterSuccess = false) + private fun onShieldFundsMessageButtonClick() = shieldFunds(closeCurrentScreen = false) private fun onWalletErrorMessageClick(homeMessageData: HomeMessageData.Error) = navigateToError(ErrorArgs.SyncError(homeMessageData.synchronizerError)) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/shieldfunds/ShieldFundsInfoViewModel.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/shieldfunds/ShieldFundsInfoViewModel.kt index 50c6e2467..f827dd1e1 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/shieldfunds/ShieldFundsInfoViewModel.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/shieldfunds/ShieldFundsInfoViewModel.kt @@ -74,6 +74,6 @@ class ShieldFundsInfoViewModel( private fun onBack() = navigationRouter.back() - private fun onShieldClick() = shieldFunds(navigateBackAfterSuccess = true) + private fun onShieldClick() = shieldFunds(closeCurrentScreen = true) } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/AndroidSend.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/AndroidSend.kt index 7f6f9ce1c..851c40e2b 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/AndroidSend.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/AndroidSend.kt @@ -32,8 +32,9 @@ import co.electriccoin.zcash.ui.common.usecase.PrefillSendUseCase import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel import co.electriccoin.zcash.ui.common.wallet.ExchangeRateState import co.electriccoin.zcash.ui.design.component.CircularScreenProgressIndicator -import co.electriccoin.zcash.ui.screen.balances.BalanceState -import co.electriccoin.zcash.ui.screen.balances.BalanceViewModel +import co.electriccoin.zcash.ui.screen.balances.BalanceWidgetArgs +import co.electriccoin.zcash.ui.screen.balances.BalanceWidgetState +import co.electriccoin.zcash.ui.screen.balances.BalanceWidgetViewModel import co.electriccoin.zcash.ui.screen.scan.Scan import co.electriccoin.zcash.ui.screen.scan.ScanFlow import co.electriccoin.zcash.ui.screen.send.ext.Saver @@ -45,6 +46,7 @@ import co.electriccoin.zcash.ui.screen.send.view.Send import kotlinx.coroutines.launch import org.koin.androidx.compose.koinViewModel import org.koin.compose.koinInject +import org.koin.core.parameter.parametersOf import java.util.Locale @Composable @@ -55,7 +57,16 @@ internal fun WrapSend(args: Send) { val walletViewModel = koinActivityViewModel() - val balanceViewModel = koinViewModel() + val balanceWidgetViewModel = + koinViewModel { + parametersOf( + BalanceWidgetArgs( + isBalanceButtonEnabled = true, + isExchangeRateButtonEnabled = false, + showDust = true + ) + ) + } val accountDataSource = koinInject() @@ -69,12 +80,12 @@ internal fun WrapSend(args: Send) { val monetarySeparators = MonetarySeparators.current(Locale.getDefault()) - val balanceState = balanceViewModel.state.collectAsStateWithLifecycle().value + val balanceState = balanceWidgetViewModel.state.collectAsStateWithLifecycle().value val exchangeRateState = exchangeRateRepository.state.collectAsStateWithLifecycle().value WrapSend( - balanceState = balanceState, + balanceWidgetState = balanceState, exchangeRateState = exchangeRateState, goToQrScanner = { navigationRouter.forward( @@ -97,7 +108,7 @@ internal fun WrapSend(args: Send) { @VisibleForTesting @Composable internal fun WrapSend( - balanceState: BalanceState, + balanceWidgetState: BalanceWidgetState, exchangeRateState: ExchangeRateState, goToQrScanner: () -> Unit, goBack: () -> Unit, @@ -286,7 +297,7 @@ internal fun WrapSend( CircularScreenProgressIndicator() } else { Send( - balanceState = balanceState, + balanceWidgetState = balanceWidgetState, sendStage = sendStage, onCreateZecSend = { newZecSend -> viewModel.onCreateZecSendClick( diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/view/SendView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/view/SendView.kt index a82ad171f..01ff3595f 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/view/SendView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/view/SendView.kt @@ -64,6 +64,7 @@ import co.electriccoin.zcash.ui.common.wallet.ExchangeRateState import co.electriccoin.zcash.ui.design.component.AppAlertDialog import co.electriccoin.zcash.ui.design.component.BlankBgScaffold import co.electriccoin.zcash.ui.design.component.BlankSurface +import co.electriccoin.zcash.ui.design.component.Spacer import co.electriccoin.zcash.ui.design.component.ZashiButton import co.electriccoin.zcash.ui.design.component.ZashiTextField import co.electriccoin.zcash.ui.design.component.ZashiTextFieldDefaults @@ -74,7 +75,7 @@ import co.electriccoin.zcash.ui.design.theme.typography.ZashiTypography import co.electriccoin.zcash.ui.design.util.scaffoldPadding import co.electriccoin.zcash.ui.fixture.BalanceStateFixture import co.electriccoin.zcash.ui.fixture.ZashiMainTopAppBarStateFixture -import co.electriccoin.zcash.ui.screen.balances.BalanceState +import co.electriccoin.zcash.ui.screen.balances.BalanceWidgetState import co.electriccoin.zcash.ui.screen.balances.BalanceWidget import co.electriccoin.zcash.ui.screen.send.SendTag import co.electriccoin.zcash.ui.screen.send.model.AmountState @@ -90,7 +91,7 @@ import java.util.Locale private fun PreviewSendForm() { ZcashTheme(forceDarkMode = false) { Send( - balanceState = BalanceStateFixture.new(), + balanceWidgetState = BalanceStateFixture.new(), sendStage = SendStage.Form, onCreateZecSend = {}, onBack = {}, @@ -125,7 +126,7 @@ private fun PreviewSendForm() { private fun SendFormTransparentAddressPreview() { ZcashTheme(forceDarkMode = false) { Send( - balanceState = BalanceStateFixture.new(), + balanceWidgetState = BalanceStateFixture.new(), sendStage = SendStage.Form, onCreateZecSend = {}, onBack = {}, @@ -164,7 +165,7 @@ private fun SendFormTransparentAddressPreview() { @Suppress("LongParameterList") @Composable fun Send( - balanceState: BalanceState, + balanceWidgetState: BalanceWidgetState, sendStage: SendStage, onCreateZecSend: (ZecSend) -> Unit, onBack: () -> Unit, @@ -193,7 +194,7 @@ fun Send( ) }) { paddingValues -> SendMainContent( - balanceState = balanceState, + balanceWidgetState = balanceWidgetState, selectedAccount = selectedAccount, exchangeRateState = exchangeRateState, onBack = onBack, @@ -216,7 +217,7 @@ fun Send( @Suppress("LongParameterList") @Composable private fun SendMainContent( - balanceState: BalanceState, + balanceWidgetState: BalanceWidgetState, selectedAccount: WalletAccount, exchangeRateState: ExchangeRateState, onBack: () -> Unit, @@ -237,7 +238,7 @@ private fun SendMainContent( // loader if calling the Proposal API takes longer than expected SendForm( - balanceState = balanceState, + balanceWidgetState = balanceWidgetState, selectedAccount = selectedAccount, recipientAddressState = recipientAddressState, exchangeRateState = exchangeRateState, @@ -270,7 +271,7 @@ private fun SendMainContent( @Suppress("LongParameterList", "LongMethod") @Composable private fun SendForm( - balanceState: BalanceState, + balanceWidgetState: BalanceWidgetState, selectedAccount: WalletAccount, recipientAddressState: RecipientAddressState, exchangeRateState: ExchangeRateState, @@ -295,13 +296,13 @@ private fun SendForm( .then(modifier), horizontalAlignment = Alignment.CenterHorizontally ) { - Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall)) + Spacer(8.dp) BalanceWidget( - state = balanceState + state = balanceWidgetState ) - Spacer(modifier = Modifier.height(24.dp)) + Spacer(24.dp) // TODO [#1256]: Consider Send.Form TextFields scrolling // TODO [#1256]: https://github.com/Electric-Coin-Company/zashi-android/issues/1256 diff --git a/ui-lib/src/main/res/ui/balances/drawable/ic_balance_shield.xml b/ui-lib/src/main/res/ui/balances/drawable/ic_balance_shield.xml new file mode 100644 index 000000000..795b2b1e3 --- /dev/null +++ b/ui-lib/src/main/res/ui/balances/drawable/ic_balance_shield.xml @@ -0,0 +1,9 @@ + + + diff --git a/ui-lib/src/main/res/ui/balances/drawable/ic_balances_expand.xml b/ui-lib/src/main/res/ui/balances/drawable/ic_balances_expand.xml new file mode 100644 index 000000000..945eb5c2d --- /dev/null +++ b/ui-lib/src/main/res/ui/balances/drawable/ic_balances_expand.xml @@ -0,0 +1,13 @@ + + + diff --git a/ui-lib/src/main/res/ui/balances/values-es/strings.xml b/ui-lib/src/main/res/ui/balances/values-es/strings.xml index 5fdb759d3..85ba199e1 100644 --- a/ui-lib/src/main/res/ui/balances/values-es/strings.xml +++ b/ui-lib/src/main/res/ui/balances/values-es/strings.xml @@ -2,4 +2,5 @@ Shield Transparent + Spendable diff --git a/ui-lib/src/main/res/ui/balances/values/strings.xml b/ui-lib/src/main/res/ui/balances/values/strings.xml index 4dbf6cfd8..9adf8ab78 100644 --- a/ui-lib/src/main/res/ui/balances/values/strings.xml +++ b/ui-lib/src/main/res/ui/balances/values/strings.xml @@ -2,4 +2,5 @@ Shield Transparent + Spendable \ No newline at end of file diff --git a/ui-lib/src/main/res/ui/common/values/strings.xml b/ui-lib/src/main/res/ui/common/values/strings.xml index f5ca918cf..3bb570da0 100644 --- a/ui-lib/src/main/res/ui/common/values/strings.xml +++ b/ui-lib/src/main/res/ui/common/values/strings.xml @@ -35,4 +35,6 @@ two days two weeks two months + + %S ZEC