Balance actions bussiness logic
This commit is contained in:
parent
2b9635502e
commit
64af1a3d50
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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>): 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<Any>.normalize(context: Context): List<Any> =
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
|
|
|
@ -29,11 +29,25 @@ sealed interface WalletAccount : Comparable<WalletAccount> {
|
|||
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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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<BalanceState> =
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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"),
|
|
@ -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
|
||||
)
|
|
@ -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<BalanceWidgetState> =
|
||||
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,
|
||||
)
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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<ZashiTopAppBarViewModel>()
|
||||
val balanceViewModel = koinViewModel<BalanceViewModel>()
|
||||
val balanceWidgetViewModel = koinViewModel<BalanceWidgetViewModel> {
|
||||
parametersOf(
|
||||
BalanceWidgetArgs(
|
||||
isBalanceButtonEnabled = false,
|
||||
isExchangeRateButtonEnabled = true,
|
||||
showDust = false,
|
||||
)
|
||||
)
|
||||
}
|
||||
val homeViewModel = koinViewModel<HomeViewModel>()
|
||||
val transactionHistoryWidgetViewModel = koinViewModel<TransactionHistoryWidgetViewModel>()
|
||||
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
|
||||
)
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -74,6 +74,6 @@ class ShieldFundsInfoViewModel(
|
|||
|
||||
private fun onBack() = navigationRouter.back()
|
||||
|
||||
private fun onShieldClick() = shieldFunds(navigateBackAfterSuccess = true)
|
||||
private fun onShieldClick() = shieldFunds(closeCurrentScreen = true)
|
||||
}
|
||||
|
||||
|
|
|
@ -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<WalletViewModel>()
|
||||
|
||||
val balanceViewModel = koinViewModel<BalanceViewModel>()
|
||||
val balanceWidgetViewModel =
|
||||
koinViewModel<BalanceWidgetViewModel> {
|
||||
parametersOf(
|
||||
BalanceWidgetArgs(
|
||||
isBalanceButtonEnabled = true,
|
||||
isExchangeRateButtonEnabled = false,
|
||||
showDust = true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val accountDataSource = koinInject<AccountDataSource>()
|
||||
|
||||
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="20dp"
|
||||
android:height="20dp"
|
||||
android:viewportWidth="20"
|
||||
android:viewportHeight="20">
|
||||
<path
|
||||
android:pathData="M10.239,0.908C10.08,0.885 9.92,0.885 9.762,0.908C9.58,0.934 9.41,0.998 9.276,1.049L9.239,1.063L4.662,2.779C4.148,2.971 3.695,3.14 3.347,3.447C3.043,3.715 2.808,4.054 2.664,4.433C2.499,4.867 2.499,5.35 2.5,5.899L2.5,10.001C2.5,12.356 3.781,14.32 5.166,15.763C6.56,17.215 8.155,18.241 8.999,18.733L9.033,18.753C9.187,18.843 9.386,18.961 9.651,19.017C9.867,19.064 10.134,19.064 10.35,19.017C10.614,18.961 10.814,18.843 10.968,18.753L11.002,18.733C11.845,18.241 13.44,17.215 14.834,15.763C16.219,14.32 17.5,12.356 17.5,10.001L17.5,5.899C17.501,5.35 17.502,4.867 17.337,4.433C17.192,4.054 16.958,3.715 16.653,3.447C16.305,3.14 15.853,2.971 15.338,2.779L10.761,1.063L10.725,1.049C10.59,0.998 10.421,0.934 10.239,0.908Z"
|
||||
android:fillColor="#231F20"/>
|
||||
</vector>
|
|
@ -0,0 +1,13 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="17dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="17"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:pathData="M9.833,6.667L14.5,2M14.5,2H10.5M14.5,2V6M7.167,9.333L2.5,14M2.5,14H6.5M2.5,14L2.5,10"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="1.33333"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#716C5D"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
|
@ -2,4 +2,5 @@
|
|||
<resources>
|
||||
<string name="balance_action_shield">Shield</string>
|
||||
<string name="balance_action_shield_button_header">Transparent</string>
|
||||
<string name="widget_balances_button_spendable">Spendable</string>
|
||||
</resources>
|
||||
|
|
|
@ -2,4 +2,5 @@
|
|||
<resources>
|
||||
<string name="balance_action_shield">Shield</string>
|
||||
<string name="balance_action_shield_button_header">Transparent</string>
|
||||
<string name="widget_balances_button_spendable">Spendable</string>
|
||||
</resources>
|
|
@ -35,4 +35,6 @@
|
|||
<string name="general_remind_me_in_two_days">two days</string>
|
||||
<string name="general_remind_me_in_two_weeks">two weeks</string>
|
||||
<string name="general_remind_me_in_two_months">two months</string>
|
||||
|
||||
<string name="general_zec">%S ZEC</string>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue