Messages and balances hotfixes

This commit is contained in:
Milan Cerovsky 2025-04-23 11:33:59 +02:00
parent 0ea9c7ef51
commit e80f583d54
26 changed files with 212 additions and 122 deletions

View File

@ -38,3 +38,7 @@ data class ZecAmountPair(
val main: String,
val suffix: String
)
@Suppress("MagicNumber")
val Zatoshi.Companion.typicalFee: Zatoshi
get() = Zatoshi(100000)

View File

@ -19,7 +19,6 @@ import co.electriccoin.zcash.ui.common.usecase.GetCoinbaseStatusUseCase
import co.electriccoin.zcash.ui.common.usecase.GetConfigurationUseCase
import co.electriccoin.zcash.ui.common.usecase.GetContactByAddressUseCase
import co.electriccoin.zcash.ui.common.usecase.GetCurrentFilteredTransactionsUseCase
import co.electriccoin.zcash.ui.common.usecase.GetCurrentTransactionsUseCase
import co.electriccoin.zcash.ui.common.usecase.GetExchangeRateUseCase
import co.electriccoin.zcash.ui.common.usecase.GetFlexaStatusUseCase
import co.electriccoin.zcash.ui.common.usecase.GetHomeMessageUseCase
@ -34,6 +33,7 @@ import co.electriccoin.zcash.ui.common.usecase.GetSynchronizerUseCase
import co.electriccoin.zcash.ui.common.usecase.GetTransactionDetailByIdUseCase
import co.electriccoin.zcash.ui.common.usecase.GetTransactionFiltersUseCase
import co.electriccoin.zcash.ui.common.usecase.GetTransactionMetadataUseCase
import co.electriccoin.zcash.ui.common.usecase.GetTransactionsUseCase
import co.electriccoin.zcash.ui.common.usecase.GetTransparentAddressUseCase
import co.electriccoin.zcash.ui.common.usecase.GetWalletAccountsUseCase
import co.electriccoin.zcash.ui.common.usecase.GetWalletRestoringStateUseCase
@ -142,7 +142,7 @@ val useCaseModule =
factoryOf(::GetSelectedWalletAccountUseCase)
singleOf(::ObserveClearSendUseCase)
singleOf(::PrefillSendUseCase)
factoryOf(::GetCurrentTransactionsUseCase)
factoryOf(::GetTransactionsUseCase)
factoryOf(::GetCurrentFilteredTransactionsUseCase) onClose ::closeableCallback
factoryOf(::CreateProposalUseCase)
factoryOf(::OnZip321ScannedUseCase)

View File

@ -9,7 +9,7 @@ import co.electriccoin.zcash.ui.screen.addressbook.viewmodel.AddressBookViewMode
import co.electriccoin.zcash.ui.screen.addressbook.viewmodel.SelectRecipientViewModel
import co.electriccoin.zcash.ui.screen.advancedsettings.AdvancedSettingsViewModel
import co.electriccoin.zcash.ui.screen.balances.BalanceWidgetViewModel
import co.electriccoin.zcash.ui.screen.balances.action.BalanceActionViewModel
import co.electriccoin.zcash.ui.screen.balances.spendable.SpendableBalanceViewModel
import co.electriccoin.zcash.ui.screen.chooseserver.ChooseServerViewModel
import co.electriccoin.zcash.ui.screen.contact.viewmodel.AddContactViewModel
import co.electriccoin.zcash.ui.screen.contact.viewmodel.UpdateContactViewModel
@ -23,6 +23,7 @@ import co.electriccoin.zcash.ui.screen.home.HomeViewModel
import co.electriccoin.zcash.ui.screen.home.backup.WalletBackupDetailViewModel
import co.electriccoin.zcash.ui.screen.home.backup.WalletBackupInfoViewModel
import co.electriccoin.zcash.ui.screen.home.reporting.CrashReportOptInViewModel
import co.electriccoin.zcash.ui.screen.home.restoring.WalletRestoringInfoViewModel
import co.electriccoin.zcash.ui.screen.home.shieldfunds.ShieldFundsInfoViewModel
import co.electriccoin.zcash.ui.screen.integrations.IntegrationsViewModel
import co.electriccoin.zcash.ui.screen.qrcode.viewmodel.QrCodeViewModel
@ -156,6 +157,7 @@ val viewModelModule =
viewModelOf(::ExchangeRateSettingsViewModel)
viewModelOf(::WalletBackupDetailViewModel)
viewModelOf(::ErrorViewModel)
viewModelOf(::BalanceActionViewModel)
viewModelOf(::SpendableBalanceViewModel)
viewModelOf(::CrashReportOptInViewModel)
viewModelOf(::WalletRestoringInfoViewModel)
}

View File

@ -49,8 +49,8 @@ import co.electriccoin.zcash.ui.screen.addressbook.WrapAddressBook
import co.electriccoin.zcash.ui.screen.advancedsettings.WrapAdvancedSettings
import co.electriccoin.zcash.ui.screen.authentication.AuthenticationUseCase
import co.electriccoin.zcash.ui.screen.authentication.WrapAuthentication
import co.electriccoin.zcash.ui.screen.balances.action.AndroidBalanceAction
import co.electriccoin.zcash.ui.screen.balances.action.BalanceAction
import co.electriccoin.zcash.ui.screen.balances.spendable.AndroidSpendableBalance
import co.electriccoin.zcash.ui.screen.balances.spendable.SpendableBalance
import co.electriccoin.zcash.ui.screen.chooseserver.WrapChooseServer
import co.electriccoin.zcash.ui.screen.connectkeystone.AndroidConnectKeystone
import co.electriccoin.zcash.ui.screen.connectkeystone.ConnectKeystone
@ -497,14 +497,14 @@ internal fun MainActivity.Navigation() {
) {
AndroidErrorBottomSheet()
}
dialog<BalanceAction>(
dialog<SpendableBalance>(
dialogProperties =
DialogProperties(
dismissOnBackPress = false,
dismissOnClickOutside = false
)
) {
AndroidBalanceAction()
AndroidSpendableBalance()
}
composable<CrashReportOptIn> {
AndroidCrashReportOptIn()

View File

@ -57,6 +57,18 @@ sealed interface WalletAccount : Comparable<WalletAccount> {
val isShieldingAvailable: Boolean
get() = totalTransparentBalance > Zatoshi(100000L)
val isAllShielded: Boolean
get() {
val isAllShielded = totalBalance == spendableShieldedBalance
val isAllShieldedWithTransparentDustLeft =
totalBalance > spendableShieldedBalance &&
spendableShieldedBalance == totalShieldedBalance &&
totalTransparentBalance > Zatoshi(0) &&
!isShieldingAvailable
return isAllShielded || isAllShieldedWithTransparentDustLeft
}
fun canSpend(amount: Zatoshi): Boolean = spendableShieldedBalance >= amount
}

View File

@ -48,8 +48,7 @@ class SynchronizerProviderImpl(
emit(synchronizer)
}
}
}
.flowOn(Dispatchers.IO)
}.flowOn(Dispatchers.IO)
.stateIn(
scope = scope,
started = SharingStarted.Lazily,

View File

@ -415,3 +415,6 @@ sealed interface ShieldTransaction : Transaction {
override val overview: TransactionOverview,
) : ShieldTransaction
}
val Transaction.isPending: Boolean
get() = this is SendTransaction.Pending || this is ShieldTransaction.Pending || this is ReceiveTransaction.Pending

View File

@ -15,12 +15,10 @@ import co.electriccoin.zcash.preference.EncryptedPreferenceProvider
import co.electriccoin.zcash.preference.StandardPreferenceProvider
import co.electriccoin.zcash.ui.common.datasource.AccountDataSource
import co.electriccoin.zcash.ui.common.datasource.RestoreTimestampDataSource
import co.electriccoin.zcash.ui.common.datasource.WalletSnapshotDataSource
import co.electriccoin.zcash.ui.common.model.FastestServersState
import co.electriccoin.zcash.ui.common.model.OnboardingState
import co.electriccoin.zcash.ui.common.model.WalletAccount
import co.electriccoin.zcash.ui.common.model.WalletRestoringState
import co.electriccoin.zcash.ui.common.model.WalletSnapshot
import co.electriccoin.zcash.ui.common.provider.GetDefaultServersProvider
import co.electriccoin.zcash.ui.common.provider.PersistableWalletProvider
import co.electriccoin.zcash.ui.common.provider.SynchronizerProvider
@ -67,7 +65,6 @@ interface WalletRepository {
val allAccounts: Flow<List<WalletAccount>?>
val currentAccount: Flow<WalletAccount?>
val currentWalletSnapshot: StateFlow<WalletSnapshot?>
/**
* A flow of the wallet block synchronization state.
@ -105,7 +102,6 @@ class WalletRepositoryImpl(
private val encryptedPreferenceProvider: EncryptedPreferenceProvider,
private val restoreTimestampDataSource: RestoreTimestampDataSource,
private val walletRestoringStateProvider: WalletRestoringStateProvider,
private val walletSnapshotDataSource: WalletSnapshotDataSource,
) : WalletRepository {
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
@ -185,8 +181,6 @@ class WalletRepositoryImpl(
initialValue = FastestServersState(servers = emptyList(), isLoading = true)
)
override val currentWalletSnapshot: StateFlow<WalletSnapshot?> = walletSnapshotDataSource.observe()
/**
* A flow of the wallet block synchronization state.
*/

View File

@ -10,7 +10,7 @@ import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.mapLatest
class GetCurrentTransactionsUseCase(
class GetTransactionsUseCase(
private val transactionRepository: TransactionRepository,
private val metadataRepository: MetadataRepository,
) {

View File

@ -11,7 +11,7 @@ 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 co.electriccoin.zcash.ui.screen.balances.spendable.SpendableBalance
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.WhileSubscribed
@ -50,13 +50,7 @@ class BalanceWidgetViewModel(
when {
!args.isBalanceButtonEnabled -> null
account == null -> null
account.totalBalance == account.spendableShieldedBalance -> null
account.totalBalance > account.spendableShieldedBalance &&
account.spendableShieldedBalance == account.totalShieldedBalance &&
account.totalTransparentBalance > Zatoshi(0) &&
!account.isShieldingAvailable ->
null
account.isAllShielded -> null
account.totalBalance > account.spendableShieldedBalance &&
!account.isShieldedPending &&
account.totalShieldedBalance > Zatoshi(0) &&
@ -94,7 +88,7 @@ class BalanceWidgetViewModel(
showDust = args.showDust
)
private fun onBalanceButtonClick() = navigationRouter.forward(BalanceAction)
private fun onBalanceButtonClick() = navigationRouter.forward(SpendableBalance)
}
data class BalanceWidgetArgs(

View File

@ -1,4 +1,4 @@
package co.electriccoin.zcash.ui.screen.balances.action
package co.electriccoin.zcash.ui.screen.balances.spendable
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
@ -9,11 +9,11 @@ import org.koin.androidx.compose.koinViewModel
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AndroidBalanceAction() {
val vm = koinViewModel<BalanceActionViewModel>()
fun AndroidSpendableBalance() {
val vm = koinViewModel<SpendableBalanceViewModel>()
val state by vm.state.collectAsStateWithLifecycle()
BalanceActionView(state)
SpendableBalanceView(state)
}
@Serializable
data object BalanceAction
data object SpendableBalance

View File

@ -1,4 +1,4 @@
package co.electriccoin.zcash.ui.screen.balances.action
package co.electriccoin.zcash.ui.screen.balances.spendable
import androidx.compose.runtime.Immutable
import cash.z.ecc.android.sdk.model.Zatoshi
@ -8,24 +8,24 @@ import co.electriccoin.zcash.ui.design.util.ImageResource
import co.electriccoin.zcash.ui.design.util.StringResource
@Immutable
data class BalanceActionState(
data class SpendableBalanceState(
val title: StringResource,
val message: StringResource,
val rows: List<BalanceActionRowState>,
val shieldButton: BalanceShieldButtonState?,
val rows: List<SpendableBalanceRowState>,
val shieldButton: SpendableBalanceShieldButtonState?,
val positive: ButtonState,
override val onBack: () -> Unit,
) : ModalBottomSheetState
@Immutable
data class BalanceActionRowState(
data class SpendableBalanceRowState(
val title: StringResource,
val icon: ImageResource,
val value: StringResource
)
@Immutable
data class BalanceShieldButtonState(
data class SpendableBalanceShieldButtonState(
val amount: Zatoshi,
val onShieldClick: () -> Unit,
)

View File

@ -1,4 +1,4 @@
package co.electriccoin.zcash.ui.screen.balances.action
package co.electriccoin.zcash.ui.screen.balances.spendable
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
@ -42,8 +42,8 @@ import co.electriccoin.zcash.ui.design.util.stringRes
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BalanceActionView(
state: BalanceActionState?,
fun SpendableBalanceView(
state: SpendableBalanceState?,
sheetState: SheetState = rememberScreenModalBottomSheetState(),
) {
ZashiScreenModalBottomSheet(
@ -56,7 +56,7 @@ fun BalanceActionView(
}
@Composable
fun BottomSheetContent(state: BalanceActionState, modifier: Modifier = Modifier) {
fun BottomSheetContent(state: SpendableBalanceState, modifier: Modifier = Modifier) {
Column(
modifier =
modifier
@ -97,7 +97,7 @@ fun BottomSheetContent(state: BalanceActionState, modifier: Modifier = Modifier)
}
@Composable
private fun BalanceActionRow(state: BalanceActionRowState) {
private fun BalanceActionRow(state: SpendableBalanceRowState) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
@ -137,7 +137,7 @@ private fun BalanceActionRow(state: BalanceActionRowState) {
}
@Composable
private fun BalanceShieldButton(state: BalanceShieldButtonState) {
private fun BalanceShieldButton(state: SpendableBalanceShieldButtonState) {
ZashiCard(
modifier = Modifier.fillMaxWidth(),
contentPadding =
@ -193,9 +193,9 @@ private fun BalanceShieldButton(state: BalanceShieldButtonState) {
@Composable
private fun Preview() =
ZcashTheme {
BalanceActionView(
SpendableBalanceView(
state =
BalanceActionState(
SpendableBalanceState(
title = stringRes("Title"),
message = stringRes("Subtitle"),
positive =
@ -205,19 +205,19 @@ private fun Preview() =
onBack = {},
rows =
listOf(
BalanceActionRowState(
SpendableBalanceRowState(
title = stringRes("Row"),
icon = loadingImageRes(),
value = stringRes("Value")
),
BalanceActionRowState(
SpendableBalanceRowState(
title = stringRes("Row"),
icon = imageRes(R.drawable.ic_balance_shield),
value = stringRes("Value")
)
),
shieldButton =
BalanceShieldButtonState(
SpendableBalanceShieldButtonState(
amount = Zatoshi(10000),
onShieldClick = {}
)

View File

@ -1,12 +1,17 @@
package co.electriccoin.zcash.ui.screen.balances.action
package co.electriccoin.zcash.ui.screen.balances.spendable
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 cash.z.ecc.sdk.extension.typicalFee
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.isPending
import co.electriccoin.zcash.ui.common.usecase.GetTransactionsUseCase
import co.electriccoin.zcash.ui.common.usecase.ListTransactionData
import co.electriccoin.zcash.ui.common.usecase.ShieldFundsUseCase
import co.electriccoin.zcash.ui.design.component.ButtonState
import co.electriccoin.zcash.ui.design.util.StringResource
@ -15,35 +20,41 @@ import co.electriccoin.zcash.ui.design.util.loadingImageRes
import co.electriccoin.zcash.ui.design.util.stringRes
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.WhileSubscribed
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.stateIn
class BalanceActionViewModel(
class SpendableBalanceViewModel(
accountDataSource: AccountDataSource,
getTransactions: GetTransactionsUseCase,
private val navigationRouter: NavigationRouter,
private val shieldFunds: ShieldFundsUseCase,
) : ViewModel() {
val state =
accountDataSource.selectedAccount
.mapNotNull {
createState(it)
}.stateIn(
combine(
accountDataSource.selectedAccount,
getTransactions.observe()
) { account, transactions ->
createState(account, transactions)
}.filterNotNull()
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
initialValue =
createState(
accountDataSource.allAccounts.value
.orEmpty()
.firstOrNull { it.isSelected }
account =
accountDataSource.allAccounts.value
.orEmpty()
.firstOrNull { it.isSelected },
transactions = null
)
)
private fun createState(account: WalletAccount?): BalanceActionState? {
private fun createState(account: WalletAccount?, transactions: List<ListTransactionData>?): SpendableBalanceState? {
if (account == null) return null
return BalanceActionState(
return SpendableBalanceState(
title = stringRes(R.string.balance_action_title),
message = createMessage(account),
message = createMessage(account, transactions),
positive = createPositiveButton(account),
onBack = ::onBack,
rows = createInfoRows(account),
@ -51,19 +62,26 @@ class BalanceActionViewModel(
)
}
private fun createMessage(account: WalletAccount): StringResource {
private fun createMessage(account: WalletAccount, transactions: List<ListTransactionData>?): StringResource {
val pending =
when {
account.totalShieldedBalance == account.spendableShieldedBalance ->
stringRes(R.string.balance_action_all_shielded)
account.isAllShielded -> stringRes(R.string.balance_action_all_shielded)
account.totalShieldedBalance > account.spendableShieldedBalance ->
stringRes(R.string.balance_action_pending)
account.totalBalance > account.spendableShieldedBalance ->
if (transactions.orEmpty().any { it.transaction.isPending }) {
stringRes(R.string.balance_action_pending)
} else {
stringRes(R.string.balance_action_syncing)
}
else -> null
}
val shielding = stringRes(R.string.balance_action_shield_message).takeIf { account.isShieldingAvailable }
val shielding =
stringRes(
R.string.balance_action_shield_message,
stringRes(Zatoshi.typicalFee)
).takeIf { account.isShieldingAvailable }
return if (pending != null && shielding != null) {
pending + stringRes("\n\n") + shielding
@ -85,21 +103,21 @@ class BalanceActionViewModel(
private fun createInfoRows(account: WalletAccount) =
listOfNotNull(
BalanceActionRowState(
SpendableBalanceRowState(
title = stringRes(R.string.balance_action_info_shielded),
icon = imageRes(R.drawable.ic_balance_shield),
value = stringRes(R.string.general_zec, stringRes(account.spendableShieldedBalance))
),
when {
account.totalShieldedBalance > account.spendableShieldedBalance && account.isShieldedPending ->
BalanceActionRowState(
SpendableBalanceRowState(
title = stringRes(R.string.balance_action_info_pending),
icon = loadingImageRes(),
value = stringRes(R.string.general_zec, stringRes(account.pendingShieldedBalance))
)
account.totalShieldedBalance > account.spendableShieldedBalance ->
BalanceActionRowState(
SpendableBalanceRowState(
title = stringRes(R.string.balance_action_info_pending),
icon = loadingImageRes(),
value =
@ -113,8 +131,8 @@ class BalanceActionViewModel(
},
)
private fun createShieldButtonState(account: WalletAccount): BalanceShieldButtonState? =
BalanceShieldButtonState(
private fun createShieldButtonState(account: WalletAccount): SpendableBalanceShieldButtonState? =
SpendableBalanceShieldButtonState(
amount = account.transparent.balance,
onShieldClick = ::onShieldClick
).takeIf { account.isShieldingAvailable }

View File

@ -7,11 +7,12 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SheetState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import co.electriccoin.zcash.ui.NavigationRouter
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.component.ButtonState
import co.electriccoin.zcash.ui.design.component.Spacer
@ -24,28 +25,28 @@ import co.electriccoin.zcash.ui.design.newcomponent.PreviewScreens
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.design.theme.colors.ZashiColors
import co.electriccoin.zcash.ui.design.theme.typography.ZashiTypography
import co.electriccoin.zcash.ui.design.util.getValue
import co.electriccoin.zcash.ui.design.util.stringRes
import kotlinx.serialization.Serializable
import org.koin.compose.koinInject
import org.koin.androidx.compose.koinViewModel
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AndroidWalletRestoringInfo() {
val navigationRouter = koinInject<NavigationRouter>()
Content(
onBack = { navigationRouter.back() }
)
val viewModel = koinViewModel<WalletRestoringInfoViewModel>()
val state by viewModel.state.collectAsStateWithLifecycle()
Content(state)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun Content(
onBack: () -> Unit,
state: WalletRestoringInfoState,
sheetState: SheetState = rememberScreenModalBottomSheetState(),
) {
ZashiScreenModalBottomSheet(
sheetState = sheetState,
onDismissRequest = onBack
onDismissRequest = state.onBack
) {
Column(
modifier =
@ -70,17 +71,17 @@ private fun Content(
stringResource(R.string.home_info_restoring_bullet_2),
color = ZashiColors.Text.textTertiary
)
Spacer(32.dp)
ZashiInfoText(
stringResource(R.string.home_info_restoring_note),
)
state.info?.let {
Spacer(32.dp)
ZashiInfoText(it.getValue())
}
Spacer(24.dp)
ZashiButton(
modifier = Modifier.fillMaxWidth(),
state =
ButtonState(
text = stringRes(R.string.general_ok),
onClick = onBack
onClick = state.onBack
)
)
}
@ -93,7 +94,11 @@ private fun Content(
private fun Preview() =
ZcashTheme {
Content(
onBack = {},
state =
WalletRestoringInfoState(
onBack = {},
info = stringRes(R.string.home_info_restoring_message)
)
)
}

View File

@ -0,0 +1,11 @@
package co.electriccoin.zcash.ui.screen.home.restoring
import androidx.compose.runtime.Immutable
import co.electriccoin.zcash.ui.design.component.ModalBottomSheetState
import co.electriccoin.zcash.ui.design.util.StringResource
@Immutable
data class WalletRestoringInfoState(
val info: StringResource?,
override val onBack: () -> Unit
) : ModalBottomSheetState

View File

@ -0,0 +1,37 @@
package co.electriccoin.zcash.ui.screen.home.restoring
import androidx.lifecycle.ViewModel
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.WalletSnapshotDataSource
import co.electriccoin.zcash.ui.common.model.WalletSnapshot
import co.electriccoin.zcash.ui.design.util.stringRes
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.WhileSubscribed
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
class WalletRestoringInfoViewModel(
walletSnapshotDataSource: WalletSnapshotDataSource,
private val navigationRouter: NavigationRouter
) : ViewModel() {
val state =
walletSnapshotDataSource
.observe()
.map { createState(it) }
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
initialValue = createState(walletSnapshotDataSource.observe().value)
)
private fun createState(snapshot: WalletSnapshot?) =
WalletRestoringInfoState(
onBack = ::onBack,
info = stringRes(R.string.home_info_restoring_note).takeIf { snapshot?.isSpendable == false }
)
private fun onBack() = navigationRouter.back()
}

View File

@ -17,6 +17,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import cash.z.ecc.android.sdk.model.Zatoshi
import cash.z.ecc.sdk.extension.typicalFee
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.component.ButtonState
import co.electriccoin.zcash.ui.design.component.Spacer
@ -72,7 +73,10 @@ private fun Content(state: ShieldFundsInfoState) {
)
Spacer(24.dp)
Text(
stringResource(R.string.home_info_transparent_message),
stringRes(
R.string.home_info_transparent_message,
stringRes(Zatoshi.typicalFee)
).getValue(),
color = ZashiColors.Text.textTertiary,
style = ZashiTypography.textMd
)

View File

@ -68,11 +68,12 @@ fun ReceiveShielded(
modifier = Modifier.fillMaxWidth(),
state =
TransactionDetailInfoRowState(
title = if (state.isPending) {
stringRes(R.string.transaction_detail_info_transaction_status)
} else {
stringRes(R.string.transaction_detail_info_transaction_completed)
},
title =
if (state.isPending) {
stringRes(R.string.transaction_detail_info_transaction_status)
} else {
stringRes(R.string.transaction_detail_info_transaction_completed)
},
message = state.completedTimestamp,
shape =
if (state.note != null) {

View File

@ -54,11 +54,12 @@ fun ReceiveTransparent(
modifier = Modifier.fillMaxWidth(),
state =
TransactionDetailInfoRowState(
title = if (state.isPending) {
stringRes(R.string.transaction_detail_info_transaction_status)
} else {
stringRes(R.string.transaction_detail_info_transaction_completed)
},
title =
if (state.isPending) {
stringRes(R.string.transaction_detail_info_transaction_status)
} else {
stringRes(R.string.transaction_detail_info_transaction_completed)
},
message = state.completedTimestamp,
shape =
if (state.note != null) {

View File

@ -118,11 +118,12 @@ fun SendShielded(
modifier = Modifier.fillMaxWidth(),
state =
TransactionDetailInfoRowState(
title = if (state.isPending) {
stringRes(R.string.transaction_detail_info_transaction_status)
} else {
stringRes(R.string.transaction_detail_info_transaction_completed)
},
title =
if (state.isPending) {
stringRes(R.string.transaction_detail_info_transaction_status)
} else {
stringRes(R.string.transaction_detail_info_transaction_completed)
},
message = state.completedTimestamp,
shape =
if (state.note == null) {

View File

@ -115,11 +115,12 @@ fun SendTransparent(
modifier = Modifier.fillMaxWidth(),
state =
TransactionDetailInfoRowState(
title = if (state.isPending) {
stringRes(R.string.transaction_detail_info_transaction_status)
} else {
stringRes(R.string.transaction_detail_info_transaction_completed)
},
title =
if (state.isPending) {
stringRes(R.string.transaction_detail_info_transaction_status)
} else {
stringRes(R.string.transaction_detail_info_transaction_completed)
},
message = state.completedTimestamp,
shape =
if (state.note != null) {

View File

@ -54,11 +54,12 @@ fun Shielding(
modifier = Modifier.fillMaxWidth(),
state =
TransactionDetailInfoRowState(
title = if (state.isPending) {
stringRes(R.string.transaction_detail_info_transaction_status)
} else {
stringRes(R.string.transaction_detail_info_transaction_completed)
},
title =
if (state.isPending) {
stringRes(R.string.transaction_detail_info_transaction_status)
} else {
stringRes(R.string.transaction_detail_info_transaction_completed)
},
message = state.completedTimestamp,
shape = TransactionDetailInfoShape.MIDDLE,
)

View File

@ -10,7 +10,7 @@ import co.electriccoin.zcash.ui.common.datasource.RestoreTimestampDataSource
import co.electriccoin.zcash.ui.common.mapper.TransactionHistoryMapper
import co.electriccoin.zcash.ui.common.model.WalletRestoringState
import co.electriccoin.zcash.ui.common.repository.Transaction
import co.electriccoin.zcash.ui.common.usecase.GetCurrentTransactionsUseCase
import co.electriccoin.zcash.ui.common.usecase.GetTransactionsUseCase
import co.electriccoin.zcash.ui.common.usecase.GetWalletRestoringStateUseCase
import co.electriccoin.zcash.ui.design.component.ButtonState
import co.electriccoin.zcash.ui.design.util.stringRes
@ -23,7 +23,7 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
class TransactionHistoryWidgetViewModel(
getCurrentTransactions: GetCurrentTransactionsUseCase,
getTransactions: GetTransactionsUseCase,
getWalletRestoringState: GetWalletRestoringStateUseCase,
private val transactionHistoryMapper: TransactionHistoryMapper,
private val navigationRouter: NavigationRouter,
@ -31,7 +31,7 @@ class TransactionHistoryWidgetViewModel(
) : ViewModel() {
val state =
combine(
getCurrentTransactions.observe(),
getTransactions.observe(),
getWalletRestoringState.observe(),
) { transactions, restoringState ->
when {

View File

@ -43,7 +43,7 @@
<string name="home_info_transparent_subtitle">To protect user privacy, Zashi doesn\'t support spending transparent
ZEC. Tap the \"Shield\" button below to make your transparent funds spendable and your total Zashi balance private. </string>
<string name="home_info_transparent_message">This will create a shielding in-wallet transaction, consolidating your
transparent and shielded funds. (Typical fee: .001 ZEC)</string>
transparent and shielded funds. (Typical fee: %s ZEC)</string>
<string name="home_info_transparent_subheader">Transparent</string>
<string name="home_info_disconnected_title">Wallet Disconnected</string>
<string name="home_info_disconnected_subtitle">You appear to be offline. Please restore your internet connection
@ -74,7 +74,8 @@
<string name="balance_action_title">Spendable Balance</string>
<string name="balance_action_all_shielded">All your funds are shielded and spendable.</string>
<string name="balance_action_pending">Pending transactions are getting mined and confirmed.</string>
<string name="balance_action_shield_message">Shield your transparent ZEC to make it spendable and private. Doing so will create a shielding in-wallet transaction, consolidating your transparent and shielded funds. (Typical fee: .001 ZEC)</string>
<string name="balance_action_info_shielded">Shielded ZEC (Spendable)</string>
<string name="balance_action_syncing">Zashi is scanning the blockchain to make sure your balances are displayed correctly.</string>
<string name="balance_action_shield_message">Shield your transparent ZEC to make it spendable and private. Doing so will create a shielding in-wallet transaction, consolidating your transparent and shielded funds. (Typical fee: %s ZEC)</string>
<string name="balance_action_info_shielded">Shielded (Spendable)</string>
<string name="balance_action_info_pending">Pending</string>
</resources>

View File

@ -43,7 +43,7 @@
<string name="home_info_transparent_subtitle">To protect user privacy, Zashi doesn\'t support spending transparent
ZEC. Tap the \"Shield\" button below to make your transparent funds spendable and your total Zashi balance private. </string>
<string name="home_info_transparent_message">This will create a shielding in-wallet transaction, consolidating your
transparent and shielded funds. (Typical fee: .001 ZEC)</string>
transparent and shielded funds. (Typical fee: %s ZEC)</string>
<string name="home_info_transparent_subheader">Transparent</string>
<string name="home_info_disconnected_title">Wallet Disconnected</string>
<string name="home_info_disconnected_subtitle">You appear to be offline. Please restore your internet connection
@ -74,7 +74,8 @@
<string name="balance_action_title">Spendable Balance</string>
<string name="balance_action_all_shielded">All your funds are shielded and spendable.</string>
<string name="balance_action_pending">Pending transactions are getting mined and confirmed.</string>
<string name="balance_action_shield_message">Shield your transparent ZEC to make it spendable and private. Doing so will create a shielding in-wallet transaction, consolidating your transparent and shielded funds. (Typical fee: .001 ZEC)</string>
<string name="balance_action_info_shielded">Shielded ZEC (Spendable)</string>
<string name="balance_action_syncing">Zashi is scanning the blockchain to make sure your balances are displayed correctly.</string>
<string name="balance_action_shield_message">Shield your transparent ZEC to make it spendable and private. Doing so will create a shielding in-wallet transaction, consolidating your transparent and shielded funds. (Typical fee: %s ZEC)</string>
<string name="balance_action_info_shielded">Shielded (Spendable)</string>
<string name="balance_action_info_pending">Pending</string>
</resources>