Message persistence
This commit is contained in:
parent
d6d500eaed
commit
c45d672995
|
@ -203,7 +203,7 @@ object ZashiButtonDefaults {
|
|||
contentColor: Color = source.Btns.Secondary.btnSecondaryFg,
|
||||
borderColor: Color = Color.Unspecified,
|
||||
disabledContainerColor: Color = source.Btns.Secondary.btnSecondaryBgDisabled,
|
||||
disabledContentColor: Color = source.Btns.Secondary.btnSecondaryFg,
|
||||
disabledContentColor: Color = source.Btns.Secondary.btnSecondaryFgDisabled,
|
||||
) = ZashiButtonColors(
|
||||
containerColor = containerColor,
|
||||
contentColor = contentColor,
|
||||
|
|
|
@ -2,6 +2,7 @@ package co.electriccoin.zcash.ui.design.component
|
|||
|
||||
import android.view.WindowManager
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.systemBars
|
||||
import androidx.compose.foundation.layout.windowInsetsBottomHeight
|
||||
|
@ -27,7 +28,7 @@ import co.electriccoin.zcash.ui.design.LocalSheetStateManager
|
|||
fun <T : ModalBottomSheetState> ZashiScreenModalBottomSheet(
|
||||
state: T?,
|
||||
sheetState: SheetState = rememberScreenModalBottomSheetState(),
|
||||
content: @Composable (state: T) -> Unit = {},
|
||||
content: @Composable ColumnScope.(state: T) -> Unit = {},
|
||||
) {
|
||||
val parent = LocalView.current.parent
|
||||
SideEffect {
|
||||
|
|
|
@ -2,10 +2,14 @@ package co.electriccoin.zcash.di
|
|||
|
||||
import co.electriccoin.zcash.ui.common.datasource.AccountDataSource
|
||||
import co.electriccoin.zcash.ui.common.datasource.AccountDataSourceImpl
|
||||
import co.electriccoin.zcash.ui.common.datasource.MessageAvailabilityDataSource
|
||||
import co.electriccoin.zcash.ui.common.datasource.MessageAvailabilityDataSourceImpl
|
||||
import co.electriccoin.zcash.ui.common.datasource.ProposalDataSource
|
||||
import co.electriccoin.zcash.ui.common.datasource.ProposalDataSourceImpl
|
||||
import co.electriccoin.zcash.ui.common.datasource.RestoreTimestampDataSource
|
||||
import co.electriccoin.zcash.ui.common.datasource.RestoreTimestampDataSourceImpl
|
||||
import co.electriccoin.zcash.ui.common.datasource.ShieldFundsDataSource
|
||||
import co.electriccoin.zcash.ui.common.datasource.ShieldFundsDataSourceImpl
|
||||
import co.electriccoin.zcash.ui.common.datasource.WalletBackupDataSource
|
||||
import co.electriccoin.zcash.ui.common.datasource.WalletBackupDataSourceImpl
|
||||
import co.electriccoin.zcash.ui.common.datasource.ZashiSpendingKeyDataSource
|
||||
|
@ -21,4 +25,6 @@ val dataSourceModule =
|
|||
singleOf(::ProposalDataSourceImpl) bind ProposalDataSource::class
|
||||
singleOf(::RestoreTimestampDataSourceImpl) bind RestoreTimestampDataSource::class
|
||||
singleOf(::WalletBackupDataSourceImpl) bind WalletBackupDataSource::class
|
||||
singleOf(::ShieldFundsDataSourceImpl) bind ShieldFundsDataSource::class
|
||||
singleOf(::MessageAvailabilityDataSourceImpl) bind MessageAvailabilityDataSource::class
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ import co.electriccoin.zcash.ui.common.provider.ShieldFundsRemindMeTimestampStor
|
|||
import co.electriccoin.zcash.ui.common.provider.ShieldFundsRemindMeTimestampStorageProviderImpl
|
||||
import co.electriccoin.zcash.ui.common.provider.SynchronizerProvider
|
||||
import co.electriccoin.zcash.ui.common.provider.SynchronizerProviderImpl
|
||||
import co.electriccoin.zcash.ui.common.provider.WalletBackupConsentStorageProvider
|
||||
import co.electriccoin.zcash.ui.common.provider.WalletBackupConsentStorageProviderImpl
|
||||
import co.electriccoin.zcash.ui.common.provider.WalletBackupFlagStorageProvider
|
||||
import co.electriccoin.zcash.ui.common.provider.WalletBackupFlagStorageProviderImpl
|
||||
import co.electriccoin.zcash.ui.common.provider.WalletBackupRemindMeCountStorageProvider
|
||||
|
@ -49,4 +51,5 @@ val providerModule =
|
|||
factoryOf(::WalletBackupRemindMeTimestampStorageProviderImpl) bind
|
||||
WalletBackupRemindMeTimestampStorageProvider::class
|
||||
factoryOf(::WalletBackupFlagStorageProviderImpl) bind WalletBackupFlagStorageProvider::class
|
||||
factoryOf(::WalletBackupConsentStorageProviderImpl) bind WalletBackupConsentStorageProvider::class
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ import co.electriccoin.zcash.ui.common.repository.FlexaRepository
|
|||
import co.electriccoin.zcash.ui.common.repository.FlexaRepositoryImpl
|
||||
import co.electriccoin.zcash.ui.common.repository.KeystoneProposalRepository
|
||||
import co.electriccoin.zcash.ui.common.repository.KeystoneProposalRepositoryImpl
|
||||
import co.electriccoin.zcash.ui.common.repository.ShieldFundsRepository
|
||||
import co.electriccoin.zcash.ui.common.repository.ShieldFundsRepositoryImpl
|
||||
import co.electriccoin.zcash.ui.common.repository.TransactionFilterRepository
|
||||
import co.electriccoin.zcash.ui.common.repository.TransactionFilterRepositoryImpl
|
||||
import co.electriccoin.zcash.ui.common.repository.TransactionRepository
|
||||
|
@ -33,4 +35,5 @@ val repositoryModule =
|
|||
singleOf(::TransactionRepositoryImpl) bind TransactionRepository::class
|
||||
singleOf(::TransactionFilterRepositoryImpl) bind TransactionFilterRepository::class
|
||||
singleOf(::ZashiProposalRepositoryImpl) bind ZashiProposalRepository::class
|
||||
singleOf(::ShieldFundsRepositoryImpl) bind ShieldFundsRepository::class
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import co.electriccoin.zcash.ui.common.usecase.CopyToClipboardUseCase
|
|||
import co.electriccoin.zcash.ui.common.usecase.CreateFlexaTransactionUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.CreateKeystoneAccountUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.CreateKeystoneProposalPCZTEncoderUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.CreateKeystoneShieldProposalUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.CreateOrUpdateTransactionNoteUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.CreateProposalUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.DeleteContactUseCase
|
||||
|
@ -39,21 +38,19 @@ import co.electriccoin.zcash.ui.common.usecase.GetTransparentAddressUseCase
|
|||
import co.electriccoin.zcash.ui.common.usecase.GetWalletAccountsUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.GetWalletRestoringStateUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.GetZashiAccountUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.GetZashiSpendingKeyUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.IsCoinbaseAvailableUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.IsFlexaAvailableUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.IsRestoreSuccessDialogVisibleUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.MarkTxMemoAsReadUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.NavigateToAddressBookUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.NavigateToCoinbaseUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.NavigateToWalletBackupUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.NavigateToTaxExportUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.NavigateToWalletBackupUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ObserveAddressBookContactsUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ObserveClearSendUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ObserveContactByAddressUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ObserveContactPickedUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ObserveFastestServersUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ObserveOnAccountChangedUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ObservePersistableWalletUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ObserveProposalUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ObserveSelectedEndpointUseCase
|
||||
|
@ -70,6 +67,7 @@ import co.electriccoin.zcash.ui.common.usecase.ParseKeystoneUrToZashiAccountsUse
|
|||
import co.electriccoin.zcash.ui.common.usecase.PersistEndpointUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.PrefillSendUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.RefreshFastestServersUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.RemindShieldFundsLaterUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.RemindWalletBackupLaterUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.RescanBlockchainUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ResetInMemoryDataUseCase
|
||||
|
@ -83,6 +81,7 @@ import co.electriccoin.zcash.ui.common.usecase.SendSupportEmailUseCase
|
|||
import co.electriccoin.zcash.ui.common.usecase.SendTransactionAgainUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ShareImageUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.SharePCZTUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ShieldFundsUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.UpdateContactUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ValidateContactAddressUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ValidateContactNameUseCase
|
||||
|
@ -125,7 +124,6 @@ val useCaseModule =
|
|||
factoryOf(::Zip321BuildUriUseCase)
|
||||
factoryOf(::Zip321ParseUriValidationUseCase)
|
||||
factoryOf(::IsCoinbaseAvailableUseCase)
|
||||
factoryOf(::GetZashiSpendingKeyUseCase)
|
||||
factoryOf(::ObservePersistableWalletUseCase)
|
||||
factoryOf(::GetSupportUseCase)
|
||||
factoryOf(::SendEmailUseCase)
|
||||
|
@ -148,14 +146,12 @@ val useCaseModule =
|
|||
factoryOf(::CreateProposalUseCase)
|
||||
factoryOf(::OnZip321ScannedUseCase)
|
||||
factoryOf(::OnAddressScannedUseCase)
|
||||
factoryOf(::CreateKeystoneShieldProposalUseCase)
|
||||
factoryOf(::ParseKeystonePCZTUseCase)
|
||||
factoryOf(::ParseKeystoneSignInRequestUseCase)
|
||||
factoryOf(::CancelProposalFlowUseCase)
|
||||
factoryOf(::ObserveProposalUseCase)
|
||||
factoryOf(::SharePCZTUseCase)
|
||||
factoryOf(::CreateKeystoneProposalPCZTEncoderUseCase)
|
||||
factoryOf(::ObserveOnAccountChangedUseCase)
|
||||
factoryOf(::ViewTransactionsAfterSuccessfulProposalUseCase)
|
||||
factoryOf(::ViewTransactionDetailAfterSuccessfulProposalUseCase)
|
||||
factoryOf(::NavigateToCoinbaseUseCase)
|
||||
|
@ -192,4 +188,6 @@ val useCaseModule =
|
|||
factoryOf(::GetHomeMessageUseCase)
|
||||
factoryOf(::OnUserSavedWalletBackupUseCase)
|
||||
factoryOf(::RemindWalletBackupLaterUseCase)
|
||||
factoryOf(::RemindShieldFundsLaterUseCase)
|
||||
singleOf(::ShieldFundsUseCase)
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ import co.electriccoin.zcash.ui.screen.flexa.FlexaViewModel
|
|||
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.transparentbalance.TransparentBalanceInfoViewModel
|
||||
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
|
||||
import co.electriccoin.zcash.ui.screen.receive.viewmodel.ReceiveViewModel
|
||||
|
@ -38,7 +38,6 @@ import co.electriccoin.zcash.ui.screen.walletbackup.WalletBackupViewModel
|
|||
import co.electriccoin.zcash.ui.screen.selectkeystoneaccount.SelectKeystoneAccount
|
||||
import co.electriccoin.zcash.ui.screen.selectkeystoneaccount.viewmodel.SelectKeystoneAccountViewModel
|
||||
import co.electriccoin.zcash.ui.screen.send.SendViewModel
|
||||
import co.electriccoin.zcash.ui.screen.sendconfirmation.viewmodel.CreateTransactionsViewModel
|
||||
import co.electriccoin.zcash.ui.screen.settings.viewmodel.ScreenBrightnessViewModel
|
||||
import co.electriccoin.zcash.ui.screen.settings.viewmodel.SettingsViewModel
|
||||
import co.electriccoin.zcash.ui.screen.signkeystonetransaction.viewmodel.SignKeystoneTransactionViewModel
|
||||
|
@ -69,7 +68,6 @@ val viewModelModule =
|
|||
viewModelOf(::SettingsViewModel)
|
||||
viewModelOf(::AdvancedSettingsViewModel)
|
||||
viewModelOf(::SupportViewModel)
|
||||
viewModelOf(::CreateTransactionsViewModel)
|
||||
viewModelOf(::RestoreSuccessViewModel)
|
||||
viewModelOf(::WhatsNewViewModel)
|
||||
viewModelOf(::ChooseServerViewModel)
|
||||
|
@ -147,7 +145,7 @@ val viewModelModule =
|
|||
viewModelOf(::RestoreBDHeightViewModel)
|
||||
viewModelOf(::RestoreBDDateViewModel)
|
||||
viewModelOf(::RestoreBDEstimationViewModel)
|
||||
viewModelOf(::TransparentBalanceInfoViewModel)
|
||||
viewModelOf(::ShieldFundsInfoViewModel)
|
||||
viewModelOf(::WalletBackupInfoViewModel)
|
||||
viewModelOf(::ExchangeRateOptInViewModel)
|
||||
viewModelOf(::ExchangeRateSettingsViewModel)
|
||||
|
|
|
@ -78,8 +78,8 @@ import co.electriccoin.zcash.ui.screen.home.disconnected.WalletDisconnectedInfo
|
|||
import co.electriccoin.zcash.ui.screen.home.restoring.WalletRestoringInfo
|
||||
import co.electriccoin.zcash.ui.screen.home.syncing.WalletSyncingInfo
|
||||
import co.electriccoin.zcash.ui.screen.home.updating.WalletUpdatingInfo
|
||||
import co.electriccoin.zcash.ui.screen.home.transparentbalance.AndroidTransparentBalanceInfo
|
||||
import co.electriccoin.zcash.ui.screen.home.transparentbalance.TransparentBalanceInfo
|
||||
import co.electriccoin.zcash.ui.screen.home.shieldfunds.AndroidShieldFundsInfo
|
||||
import co.electriccoin.zcash.ui.screen.home.shieldfunds.ShieldFundsInfo
|
||||
import co.electriccoin.zcash.ui.screen.integrations.AndroidDialogIntegrations
|
||||
import co.electriccoin.zcash.ui.screen.integrations.AndroidIntegrations
|
||||
import co.electriccoin.zcash.ui.screen.integrations.DialogIntegrations
|
||||
|
@ -422,14 +422,14 @@ internal fun MainActivity.Navigation() {
|
|||
) {
|
||||
AndroidWalletBackupInfo()
|
||||
}
|
||||
dialog<TransparentBalanceInfo>(
|
||||
dialog<ShieldFundsInfo>(
|
||||
dialogProperties =
|
||||
DialogProperties(
|
||||
dismissOnBackPress = false,
|
||||
dismissOnClickOutside = false
|
||||
)
|
||||
) {
|
||||
AndroidTransparentBalanceInfo()
|
||||
AndroidShieldFundsInfo()
|
||||
}
|
||||
dialog<WalletDisconnectedInfo>(
|
||||
dialogProperties =
|
||||
|
|
|
@ -24,7 +24,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
|
@ -37,13 +36,10 @@ import kotlinx.coroutines.flow.flowOn
|
|||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.retryWhen
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
interface AccountDataSource {
|
||||
val onAccountChanged: Flow<Unit>
|
||||
|
||||
val allAccounts: StateFlow<List<WalletAccount>?>
|
||||
|
||||
val selectedAccount: Flow<WalletAccount?>
|
||||
|
@ -74,8 +70,6 @@ class AccountDataSourceImpl(
|
|||
) : AccountDataSource {
|
||||
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
|
||||
override val onAccountChanged = MutableSharedFlow<Unit>()
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private val internalAccounts: Flow<List<InternalAccountWithBalances>?> =
|
||||
synchronizerProvider
|
||||
|
@ -224,15 +218,7 @@ class AccountDataSourceImpl(
|
|||
|
||||
override suspend fun selectAccount(account: Account) {
|
||||
withContext(Dispatchers.IO) {
|
||||
val current = selectedAccountUUIDProvider.getUUID()
|
||||
|
||||
selectedAccountUUIDProvider.setUUID(account.accountUuid)
|
||||
|
||||
scope.launch {
|
||||
if (current != account.accountUuid) {
|
||||
onAccountChanged.emit(Unit)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package co.electriccoin.zcash.ui.common.datasource
|
||||
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import co.electriccoin.zcash.ui.common.provider.ApplicationStateProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
interface MessageAvailabilityDataSource {
|
||||
val canShowMessage: Boolean
|
||||
fun observe(): StateFlow<Boolean>
|
||||
fun onMessageShown()
|
||||
}
|
||||
|
||||
class MessageAvailabilityDataSourceImpl(
|
||||
private val applicationStateProvider: ApplicationStateProvider
|
||||
): MessageAvailabilityDataSource {
|
||||
|
||||
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
|
||||
|
||||
private val state = MutableStateFlow(true)
|
||||
|
||||
override val canShowMessage: Boolean
|
||||
get() = state.value
|
||||
|
||||
init {
|
||||
scope.launch {
|
||||
applicationStateProvider.state.collect {
|
||||
if (it == Lifecycle.Event.ON_START) {
|
||||
state.update { true }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun observe(): StateFlow<Boolean> = state.asStateFlow()
|
||||
|
||||
override fun onMessageShown() {
|
||||
state.update { false }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package co.electriccoin.zcash.ui.common.datasource
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import cash.z.ecc.android.sdk.model.AccountUuid
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.provider.ShieldFundsRemindMeCountStorageProvider
|
||||
import co.electriccoin.zcash.ui.common.provider.ShieldFundsRemindMeTimestampStorageProvider
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import java.time.Instant
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
interface ShieldFundsDataSource {
|
||||
suspend fun observe(forAccount: AccountUuid): Flow<ShieldFundsAvailability>
|
||||
|
||||
suspend fun remindMeLater(forAccount: AccountUuid)
|
||||
}
|
||||
|
||||
class ShieldFundsDataSourceImpl(
|
||||
private val shieldFundsRemindMeCountStorageProvider: ShieldFundsRemindMeCountStorageProvider,
|
||||
private val shieldFundsRemindMeTimestampStorageProvider: ShieldFundsRemindMeTimestampStorageProvider
|
||||
): ShieldFundsDataSource {
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override suspend fun observe(forAccount: AccountUuid): Flow<ShieldFundsAvailability> = combine(
|
||||
shieldFundsRemindMeCountStorageProvider.observe(forAccount),
|
||||
shieldFundsRemindMeTimestampStorageProvider.observe(forAccount)
|
||||
) { count, timestamp ->
|
||||
count to timestamp
|
||||
}.flatMapLatest {(count, timestamp) ->
|
||||
when {
|
||||
timestamp == null -> flowOf(ShieldFundsAvailability.Available(ShieldFundsLockoutDuration.TWO_DAYS))
|
||||
count == 1 -> calculateNext(
|
||||
lastTimestamp = timestamp,
|
||||
lastLockoutDuration = ShieldFundsLockoutDuration.TWO_DAYS,
|
||||
nextLockoutDuration = ShieldFundsLockoutDuration.TWO_WEEKS
|
||||
)
|
||||
|
||||
else -> calculateNext(
|
||||
lastTimestamp = timestamp,
|
||||
lastLockoutDuration = if (count == 2) {
|
||||
ShieldFundsLockoutDuration.TWO_WEEKS
|
||||
} else {
|
||||
ShieldFundsLockoutDuration.ONE_MONTH
|
||||
},
|
||||
nextLockoutDuration = ShieldFundsLockoutDuration.ONE_MONTH
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun remindMeLater(forAccount: AccountUuid) {
|
||||
val count = shieldFundsRemindMeCountStorageProvider.get(forAccount)
|
||||
val timestamp = Instant.now()
|
||||
shieldFundsRemindMeCountStorageProvider.store(forAccount, count + 1)
|
||||
shieldFundsRemindMeTimestampStorageProvider.store(forAccount, timestamp)
|
||||
}
|
||||
|
||||
private fun calculateNext(
|
||||
lastTimestamp: Instant,
|
||||
lastLockoutDuration: ShieldFundsLockoutDuration,
|
||||
nextLockoutDuration: ShieldFundsLockoutDuration
|
||||
): Flow<ShieldFundsAvailability> {
|
||||
val nextAvailableTimestamp = lastTimestamp.plusMillis(lastLockoutDuration.duration.inWholeMilliseconds)
|
||||
val now = Instant.now()
|
||||
return if (nextAvailableTimestamp > now) {
|
||||
flow {
|
||||
val remaining = nextAvailableTimestamp.toEpochMilli() - now.toEpochMilli()
|
||||
emit(ShieldFundsAvailability.Unavailable)
|
||||
delay(remaining)
|
||||
emit(ShieldFundsAvailability.Available(nextLockoutDuration))
|
||||
}
|
||||
} else {
|
||||
flowOf(ShieldFundsAvailability.Available(nextLockoutDuration))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface ShieldFundsAvailability {
|
||||
data class Available(val lockoutDuration: ShieldFundsLockoutDuration) : ShieldFundsAvailability
|
||||
data object Unavailable : 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)
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
package co.electriccoin.zcash.ui.common.datasource
|
||||
|
||||
import cash.z.ecc.android.sdk.model.AccountUuid
|
||||
import co.electriccoin.zcash.ui.common.provider.ShieldFundsRemindMeCountStorageProvider
|
||||
import co.electriccoin.zcash.ui.common.provider.ShieldFundsRemindMeTimestampStorageProvider
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.days
|
||||
|
||||
interface ShieldFundsRemindMeDataSource {
|
||||
suspend fun observe(forAccount: AccountUuid): Flow<ShieldFundsAvailability>
|
||||
}
|
||||
|
||||
class ShieldFundsRemindMeDataSourceImpl(
|
||||
private val shieldFundsRemindMeCountStorageProvider: ShieldFundsRemindMeCountStorageProvider,
|
||||
private val shieldFundsRemindMeTimestampStorageProvider: ShieldFundsRemindMeTimestampStorageProvider
|
||||
): ShieldFundsRemindMeDataSource {
|
||||
override suspend fun observe(forAccount: AccountUuid): Flow<ShieldFundsAvailability> = combine(
|
||||
shieldFundsRemindMeCountStorageProvider.observe(forAccount),
|
||||
shieldFundsRemindMeTimestampStorageProvider.observe(forAccount)
|
||||
) { count, timestamp ->
|
||||
when {
|
||||
timestamp == null -> ShieldFundsAvailability.Available(1.days)
|
||||
count == 1 -> ShieldFundsAvailability.Available(2.days)
|
||||
count == 2 -> ShieldFundsAvailability.Available(3.days)
|
||||
else -> ShieldFundsAvailability.Unavailable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface ShieldFundsAvailability {
|
||||
data class Available(val nextLockoutDuration: Duration) : ShieldFundsAvailability
|
||||
data object Unavailable : ShieldFundsAvailability
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package co.electriccoin.zcash.ui.common.datasource
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.provider.WalletBackupFlagStorageProvider
|
||||
import co.electriccoin.zcash.ui.common.provider.WalletBackupRemindMeCountStorageProvider
|
||||
import co.electriccoin.zcash.ui.common.provider.WalletBackupRemindMeTimestampStorageProvider
|
||||
|
@ -38,21 +40,21 @@ class WalletBackupDataSourceImpl(
|
|||
}.flatMapLatest { (isBackedUp, count, timestamp) ->
|
||||
when {
|
||||
isBackedUp -> flowOf(WalletBackupAvailability.Unavailable)
|
||||
timestamp == null -> flowOf(WalletBackupAvailability.Available(WalletBackupLockoutDuration.ONE_DAY))
|
||||
timestamp == null -> flowOf(WalletBackupAvailability.Available(WalletBackupLockoutDuration.TWO_DAYS))
|
||||
count == 1 -> calculateNext(
|
||||
lastTimestamp = timestamp,
|
||||
lastLockoutDuration = WalletBackupLockoutDuration.ONE_DAY,
|
||||
nextLockoutDuration = WalletBackupLockoutDuration.TWO_DAYS
|
||||
lastLockoutDuration = WalletBackupLockoutDuration.TWO_DAYS,
|
||||
nextLockoutDuration = WalletBackupLockoutDuration.TWO_WEEKS
|
||||
)
|
||||
|
||||
else -> calculateNext(
|
||||
lastTimestamp = timestamp,
|
||||
lastLockoutDuration = if (count == 2) {
|
||||
WalletBackupLockoutDuration.TWO_DAYS
|
||||
WalletBackupLockoutDuration.TWO_WEEKS
|
||||
} else {
|
||||
WalletBackupLockoutDuration.THREE_DAYS
|
||||
WalletBackupLockoutDuration.ONE_MONTH
|
||||
},
|
||||
nextLockoutDuration = WalletBackupLockoutDuration.THREE_DAYS
|
||||
nextLockoutDuration = WalletBackupLockoutDuration.ONE_MONTH
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -93,8 +95,8 @@ sealed interface WalletBackupAvailability {
|
|||
data object Unavailable : WalletBackupAvailability
|
||||
}
|
||||
|
||||
enum class WalletBackupLockoutDuration(val duration: Duration) {
|
||||
ONE_DAY(10.seconds),
|
||||
TWO_DAYS(20.seconds),
|
||||
THREE_DAYS(30.seconds)
|
||||
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),
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package co.electriccoin.zcash.ui.common.provider
|
||||
|
||||
import co.electriccoin.zcash.preference.EncryptedPreferenceProvider
|
||||
import co.electriccoin.zcash.preference.model.entry.PreferenceKey
|
||||
|
||||
interface WalletBackupConsentStorageProvider : BooleanStorageProvider
|
||||
|
||||
class WalletBackupConsentStorageProviderImpl(
|
||||
override val preferenceHolder: EncryptedPreferenceProvider
|
||||
) : BaseBooleanStorageProvider(key = PreferenceKey("wallet_backup_consent")),
|
||||
WalletBackupConsentStorageProvider
|
|
@ -0,0 +1,65 @@
|
|||
package co.electriccoin.zcash.ui.common.repository
|
||||
|
||||
import cash.z.ecc.android.sdk.model.Zatoshi
|
||||
import co.electriccoin.zcash.ui.common.datasource.AccountDataSource
|
||||
import co.electriccoin.zcash.ui.common.datasource.ShieldFundsAvailability
|
||||
import co.electriccoin.zcash.ui.common.datasource.ShieldFundsDataSource
|
||||
import co.electriccoin.zcash.ui.common.datasource.ShieldFundsLockoutDuration
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
interface ShieldFundsRepository {
|
||||
val availability: Flow<ShieldFundsData>
|
||||
|
||||
suspend fun remindMeLater()
|
||||
}
|
||||
|
||||
class ShieldFundsRepositoryImpl(
|
||||
private val accountDataSource: AccountDataSource,
|
||||
private val shieldFundsDataSource: ShieldFundsDataSource,
|
||||
) : ShieldFundsRepository {
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override val availability: Flow<ShieldFundsData> = accountDataSource
|
||||
.selectedAccount
|
||||
.flatMapLatest { account ->
|
||||
when {
|
||||
account == null ->
|
||||
flowOf(ShieldFundsData.Unavailable)
|
||||
|
||||
account.transparent.balance >= Zatoshi(DEFAULT_SHIELDING_THRESHOLD) ->
|
||||
shieldFundsDataSource.observe(account.sdkAccount.accountUuid).map {
|
||||
when (it) {
|
||||
is ShieldFundsAvailability.Available -> ShieldFundsData.Available(
|
||||
lockoutDuration = it.lockoutDuration,
|
||||
amount = account.transparent.balance
|
||||
)
|
||||
|
||||
ShieldFundsAvailability.Unavailable -> ShieldFundsData.Unavailable
|
||||
}
|
||||
}
|
||||
|
||||
else -> flowOf(ShieldFundsData.Unavailable)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun remindMeLater() {
|
||||
shieldFundsDataSource.remindMeLater(
|
||||
forAccount = accountDataSource.getSelectedAccount().sdkAccount.accountUuid
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface ShieldFundsData {
|
||||
data class Available(
|
||||
val lockoutDuration: ShieldFundsLockoutDuration,
|
||||
val amount: Zatoshi
|
||||
) : ShieldFundsData
|
||||
|
||||
data object Unavailable : ShieldFundsData
|
||||
}
|
||||
|
||||
private const val DEFAULT_SHIELDING_THRESHOLD = 100000L
|
||||
|
|
@ -13,6 +13,7 @@ import cash.z.ecc.android.sdk.model.proposeSend
|
|||
import cash.z.ecc.android.sdk.type.AddressType
|
||||
import co.electriccoin.zcash.spackle.Twig
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.datasource.ZashiSpendingKeyDataSource
|
||||
import co.electriccoin.zcash.ui.common.model.SubmitResult
|
||||
import co.electriccoin.zcash.ui.common.repository.BiometricRepository
|
||||
import co.electriccoin.zcash.ui.common.repository.BiometricRequest
|
||||
|
@ -25,7 +26,7 @@ import com.flexa.spend.buildSpend
|
|||
class CreateFlexaTransactionUseCase(
|
||||
private val getSynchronizer: GetSynchronizerUseCase,
|
||||
private val getZashiAccount: GetZashiAccountUseCase,
|
||||
private val getSpendingKey: GetZashiSpendingKeyUseCase,
|
||||
private val zashiSpendingKeyDataSource: ZashiSpendingKeyDataSource,
|
||||
private val biometricRepository: BiometricRepository,
|
||||
private val context: Context,
|
||||
) {
|
||||
|
@ -42,7 +43,10 @@ class CreateFlexaTransactionUseCase(
|
|||
)
|
||||
}.onSuccess { proposal ->
|
||||
Twig.debug { "Transaction proposal successful: ${proposal.toPrettyString()}" }
|
||||
val result = submitTransactions(proposal = proposal, spendingKey = getSpendingKey())
|
||||
val result = submitTransactions(
|
||||
proposal = proposal,
|
||||
spendingKey = zashiSpendingKeyDataSource.getZashiSpendingKey()
|
||||
)
|
||||
when (val output = result.first) {
|
||||
is SubmitResult.Success -> {
|
||||
Twig.debug { "Transaction successful $result" }
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
package co.electriccoin.zcash.ui.common.usecase
|
||||
|
||||
import co.electriccoin.zcash.ui.NavigationRouter
|
||||
import co.electriccoin.zcash.ui.common.repository.KeystoneProposalRepository
|
||||
import co.electriccoin.zcash.ui.screen.signkeystonetransaction.SignKeystoneTransaction
|
||||
|
||||
class CreateKeystoneShieldProposalUseCase(
|
||||
private val keystoneProposalRepository: KeystoneProposalRepository,
|
||||
private val navigationRouter: NavigationRouter
|
||||
) {
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
suspend operator fun invoke() {
|
||||
try {
|
||||
keystoneProposalRepository.createShieldProposal()
|
||||
keystoneProposalRepository.createPCZTFromProposal()
|
||||
navigationRouter.forward(SignKeystoneTransaction)
|
||||
} catch (e: Exception) {
|
||||
keystoneProposalRepository.clear()
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,19 +6,17 @@ import co.electriccoin.zcash.ui.common.datasource.WalletBackupAvailability
|
|||
import co.electriccoin.zcash.ui.common.datasource.WalletBackupDataSource
|
||||
import co.electriccoin.zcash.ui.common.model.WalletRestoringState
|
||||
import co.electriccoin.zcash.ui.common.repository.ExchangeRateRepository
|
||||
import co.electriccoin.zcash.ui.common.repository.ShieldFundsData
|
||||
import co.electriccoin.zcash.ui.common.repository.ShieldFundsRepository
|
||||
import co.electriccoin.zcash.ui.common.repository.WalletRepository
|
||||
import co.electriccoin.zcash.ui.common.viewmodel.SynchronizerError
|
||||
import co.electriccoin.zcash.ui.common.wallet.ExchangeRateState
|
||||
import co.electriccoin.zcash.ui.util.Quadruple
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
|
@ -26,23 +24,23 @@ class GetHomeMessageUseCase(
|
|||
private val walletRepository: WalletRepository,
|
||||
private val walletBackupDataSource: WalletBackupDataSource,
|
||||
private val exchangeRateRepository: ExchangeRateRepository,
|
||||
private val shieldFundsRepository: ShieldFundsRepository,
|
||||
) {
|
||||
@OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class)
|
||||
fun observe() = combine(
|
||||
@OptIn(FlowPreview::class)
|
||||
fun observe(): Flow<HomeMessageData?> = combine(
|
||||
walletRepository.currentWalletSnapshot.filterNotNull(),
|
||||
walletRepository.walletRestoringState,
|
||||
walletBackupDataSource.observe(),
|
||||
exchangeRateRepository.state.map { it == ExchangeRateState.OptIn }.distinctUntilChanged(),
|
||||
) { walletSnapshot, walletStateInformation, backup, isCCAvailable ->
|
||||
Quadruple(walletSnapshot, walletStateInformation, backup, isCCAvailable)
|
||||
}.flatMapLatest { (walletSnapshot, walletStateInformation, backup, isCCAvailable) ->
|
||||
shieldFundsRepository.availability
|
||||
) { walletSnapshot, walletStateInformation, backup, isCCAvailable, shieldFunds ->
|
||||
when {
|
||||
walletSnapshot.synchronizerError != null -> {
|
||||
flowOf(HomeMessageData.Error(walletSnapshot.synchronizerError))
|
||||
HomeMessageData.Error(walletSnapshot.synchronizerError)
|
||||
}
|
||||
|
||||
walletSnapshot.status == Synchronizer.Status.DISCONNECTED -> {
|
||||
flowOf(HomeMessageData.Disconnected)
|
||||
HomeMessageData.Disconnected
|
||||
}
|
||||
|
||||
walletSnapshot.status in listOf(
|
||||
|
@ -50,34 +48,35 @@ class GetHomeMessageUseCase(
|
|||
Synchronizer.Status.SYNCING,
|
||||
Synchronizer.Status.STOPPED
|
||||
) -> {
|
||||
flow {
|
||||
val progress = walletSnapshot.progress.decimal * 100f
|
||||
val result = when {
|
||||
walletStateInformation == WalletRestoringState.RESTORING -> {
|
||||
HomeMessageData.Restoring(
|
||||
progress = progress,
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
HomeMessageData.Syncing(progress = progress)
|
||||
}
|
||||
val progress = walletSnapshot.progress.decimal * 100f
|
||||
val result = when {
|
||||
walletStateInformation == WalletRestoringState.RESTORING -> {
|
||||
HomeMessageData.Restoring(
|
||||
progress = progress,
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
HomeMessageData.Syncing(progress = progress)
|
||||
}
|
||||
emit(result)
|
||||
}
|
||||
result
|
||||
}
|
||||
backup is WalletBackupAvailability.Available -> flowOf(HomeMessageData.Backup)
|
||||
|
||||
isCCAvailable -> flowOf(HomeMessageData.EnableCurrencyConversion)
|
||||
shieldFunds is ShieldFundsData.Available -> HomeMessageData.ShieldFunds(shieldFunds.amount)
|
||||
|
||||
else -> flowOf(null)
|
||||
backup is WalletBackupAvailability.Available -> HomeMessageData.Backup
|
||||
|
||||
isCCAvailable -> HomeMessageData.EnableCurrencyConversion
|
||||
|
||||
else -> null
|
||||
}
|
||||
}.debounce(.5.seconds)
|
||||
}
|
||||
|
||||
sealed interface HomeMessageData {
|
||||
data object EnableCurrencyConversion : HomeMessageData
|
||||
data class TransparentBalance(val zatoshi: Zatoshi) : HomeMessageData
|
||||
data class ShieldFunds(val zatoshi: Zatoshi) : HomeMessageData
|
||||
data object Backup : HomeMessageData
|
||||
data object Disconnected : HomeMessageData
|
||||
data class Error(val synchronizerError: SynchronizerError) : HomeMessageData
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
package co.electriccoin.zcash.ui.common.usecase
|
||||
|
||||
import co.electriccoin.zcash.ui.common.datasource.ZashiSpendingKeyDataSource
|
||||
|
||||
class GetZashiSpendingKeyUseCase(
|
||||
private val spendingKeyDataSource: ZashiSpendingKeyDataSource,
|
||||
) {
|
||||
suspend operator fun invoke() = spendingKeyDataSource.getZashiSpendingKey()
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
package co.electriccoin.zcash.ui.common.usecase
|
||||
|
||||
import co.electriccoin.zcash.ui.common.datasource.AccountDataSource
|
||||
|
||||
class ObserveOnAccountChangedUseCase(
|
||||
private val accountDataSource: AccountDataSource
|
||||
) {
|
||||
operator fun invoke() = accountDataSource.onAccountChanged
|
||||
}
|
|
@ -8,7 +8,7 @@ class OnUserSavedWalletBackupUseCase(
|
|||
private val walletBackupDataSource: WalletBackupDataSource
|
||||
) {
|
||||
suspend operator fun invoke() {
|
||||
walletBackupDataSource.remindMeLater()
|
||||
walletBackupDataSource.onUserSavedWalletBackup()
|
||||
navigationRouter.backToRoot()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package co.electriccoin.zcash.ui.common.usecase
|
||||
|
||||
import co.electriccoin.zcash.ui.NavigationRouter
|
||||
import co.electriccoin.zcash.ui.common.datasource.WalletBackupDataSource
|
||||
import co.electriccoin.zcash.ui.common.repository.ShieldFundsRepository
|
||||
|
||||
class RemindShieldFundsLaterUseCase(
|
||||
private val navigationRouter: NavigationRouter,
|
||||
private val shieldFundsRepository: ShieldFundsRepository
|
||||
) {
|
||||
suspend operator fun invoke() {
|
||||
shieldFundsRepository.remindMeLater()
|
||||
navigationRouter.backToRoot()
|
||||
}
|
||||
}
|
|
@ -2,12 +2,17 @@ package co.electriccoin.zcash.ui.common.usecase
|
|||
|
||||
import co.electriccoin.zcash.ui.NavigationRouter
|
||||
import co.electriccoin.zcash.ui.common.datasource.WalletBackupDataSource
|
||||
import co.electriccoin.zcash.ui.common.provider.WalletBackupConsentStorageProvider
|
||||
|
||||
class RemindWalletBackupLaterUseCase(
|
||||
private val navigationRouter: NavigationRouter,
|
||||
private val walletBackupDataSource: WalletBackupDataSource
|
||||
private val walletBackupDataSource: WalletBackupDataSource,
|
||||
private val walletBackupConsentStorageProvider: WalletBackupConsentStorageProvider
|
||||
) {
|
||||
suspend operator fun invoke() {
|
||||
suspend operator fun invoke(persistConsent: Boolean) {
|
||||
if (persistConsent) {
|
||||
walletBackupConsentStorageProvider.store(true)
|
||||
}
|
||||
walletBackupDataSource.remindMeLater()
|
||||
navigationRouter.backToRoot()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
package co.electriccoin.zcash.ui.common.usecase
|
||||
|
||||
import co.electriccoin.zcash.ui.NavigationRouter
|
||||
import co.electriccoin.zcash.ui.common.datasource.AccountDataSource
|
||||
import co.electriccoin.zcash.ui.common.model.KeystoneAccount
|
||||
import co.electriccoin.zcash.ui.common.model.SubmitResult
|
||||
import co.electriccoin.zcash.ui.common.model.ZashiAccount
|
||||
import co.electriccoin.zcash.ui.common.repository.KeystoneProposalRepository
|
||||
import co.electriccoin.zcash.ui.common.repository.SubmitProposalState
|
||||
import co.electriccoin.zcash.ui.common.repository.ZashiProposalRepository
|
||||
import co.electriccoin.zcash.ui.screen.signkeystonetransaction.SignKeystoneTransaction
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ShieldFundsUseCase(
|
||||
private val keystoneProposalRepository: KeystoneProposalRepository,
|
||||
private val zashiProposalRepository: ZashiProposalRepository,
|
||||
private val navigationRouter: NavigationRouter,
|
||||
private val accountDataSource: AccountDataSource
|
||||
) {
|
||||
private val scope = CoroutineScope(Dispatchers.Main.immediate + SupervisorJob())
|
||||
|
||||
operator fun invoke(navigateBackAfterSuccess: Boolean) {
|
||||
scope.launch {
|
||||
when (accountDataSource.getSelectedAccount()) {
|
||||
is KeystoneAccount -> {
|
||||
createKeystoneShieldProposal()
|
||||
}
|
||||
is ZashiAccount -> {
|
||||
if (navigateBackAfterSuccess) {
|
||||
navigationRouter.back()
|
||||
}
|
||||
shieldZashiFunds()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun shieldZashiFunds() {
|
||||
try {
|
||||
zashiProposalRepository.createShieldProposal()
|
||||
zashiProposalRepository.submitTransaction()
|
||||
val result = zashiProposalRepository.submitState
|
||||
.filterIsInstance<SubmitProposalState.Result>()
|
||||
.first()
|
||||
.submitResult
|
||||
|
||||
when (result) {
|
||||
is SubmitResult.Success -> {
|
||||
// do nothing
|
||||
// TODO messages
|
||||
}
|
||||
|
||||
is SubmitResult.SimpleTrxFailure.SimpleTrxFailureGrpc -> {
|
||||
// showShieldingError(ShieldState.FailedGrpc)
|
||||
// TODO messages
|
||||
}
|
||||
|
||||
is SubmitResult.SimpleTrxFailure -> {
|
||||
// showShieldingError(
|
||||
// ShieldState.Failed(
|
||||
// error = result.toErrorMessage(),
|
||||
// stackTrace = result.toErrorStacktrace()
|
||||
// )
|
||||
// )
|
||||
// TODO messages
|
||||
}
|
||||
|
||||
is SubmitResult.MultipleTrxFailure -> {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
zashiProposalRepository.clear()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun createKeystoneShieldProposal() {
|
||||
try {
|
||||
keystoneProposalRepository.createShieldProposal()
|
||||
keystoneProposalRepository.createPCZTFromProposal()
|
||||
navigationRouter.forward(SignKeystoneTransaction)
|
||||
} catch (e: Exception) {
|
||||
keystoneProposalRepository.clear()
|
||||
// TODO messages
|
||||
// showShieldingError(
|
||||
// ShieldState.Failed(
|
||||
// error =
|
||||
// context.getString(
|
||||
// R.string.balances_shielding_dialog_error_text_below_threshold
|
||||
// ),
|
||||
// stackTrace = ""
|
||||
// )
|
||||
// )
|
||||
}
|
||||
}
|
||||
}
|
|
@ -47,8 +47,8 @@ import co.electriccoin.zcash.ui.screen.home.restoring.WalletRestoringMessage
|
|||
import co.electriccoin.zcash.ui.screen.home.restoring.WalletRestoringMessageState
|
||||
import co.electriccoin.zcash.ui.screen.home.syncing.WalletSyncingMessage
|
||||
import co.electriccoin.zcash.ui.screen.home.syncing.WalletSyncingMessageState
|
||||
import co.electriccoin.zcash.ui.screen.home.transparentbalance.TransparentBalanceMessage
|
||||
import co.electriccoin.zcash.ui.screen.home.transparentbalance.TransparentBalanceMessageState
|
||||
import co.electriccoin.zcash.ui.screen.home.shieldfunds.ShieldFundsMessage
|
||||
import co.electriccoin.zcash.ui.screen.home.shieldfunds.ShieldFundsMessageState
|
||||
import co.electriccoin.zcash.ui.screen.home.updating.WalletUpdatingMessage
|
||||
import co.electriccoin.zcash.ui.screen.home.updating.WalletUpdatingMessageState
|
||||
import kotlinx.coroutines.delay
|
||||
|
@ -69,10 +69,11 @@ fun HomeMessage(
|
|||
)
|
||||
val bottomHeight by animateDpAsState(
|
||||
targetValue = if (isVisible) BOTTOM_CUTOUT_HEIGHT_DP.dp else TOP_CUTOUT_HEIGHT_DP.dp,
|
||||
animationSpec =
|
||||
animationSpec(
|
||||
delay = if (isVisible) null else 250.milliseconds
|
||||
)
|
||||
animationSpec = animationSpec(delay = if (isVisible) null else 250.milliseconds)
|
||||
)
|
||||
val elevation by animateDpAsState(
|
||||
targetValue = if (isVisible) 2.dp else 0.dp,
|
||||
animationSpec = elevationAnimationSpec(delay = if (isVisible) 450.milliseconds else null)
|
||||
)
|
||||
|
||||
Box(
|
||||
|
@ -85,7 +86,7 @@ fun HomeMessage(
|
|||
.height(TOP_CUTOUT_HEIGHT_DP.dp)
|
||||
.zIndex(2f)
|
||||
.bottomOnlyShadow(
|
||||
elevation = 2.dp,
|
||||
elevation = elevation,
|
||||
shape =
|
||||
RoundedCornerShape(
|
||||
bottomStart = TOP_CUTOUT_HEIGHT_DP.dp,
|
||||
|
@ -128,10 +129,10 @@ fun HomeMessage(
|
|||
contentPadding = contentPadding
|
||||
)
|
||||
|
||||
is TransparentBalanceMessageState ->
|
||||
TransparentBalanceMessage(
|
||||
is ShieldFundsMessageState ->
|
||||
ShieldFundsMessage(
|
||||
innerModifier = innerModifier,
|
||||
state = normalizedState as TransparentBalanceMessageState,
|
||||
state = normalizedState as ShieldFundsMessageState,
|
||||
contentPadding = contentPadding
|
||||
)
|
||||
|
||||
|
@ -184,7 +185,7 @@ fun HomeMessage(
|
|||
.zIndex(1f)
|
||||
.align(Alignment.BottomCenter)
|
||||
.topOnlyShadow(
|
||||
elevation = 2.dp,
|
||||
elevation = elevation,
|
||||
shape = RoundedCornerShape(topStart = bottomCornerSize, topEnd = bottomCornerSize),
|
||||
backgroundColor = ZashiColors.Surfaces.bgPrimary
|
||||
),
|
||||
|
@ -232,7 +233,17 @@ private fun <T> animationSpec(delay: Duration? = null): TweenSpec<T> {
|
|||
)
|
||||
}
|
||||
|
||||
private fun <T> elevationAnimationSpec(delay: Duration? = null): TweenSpec<T> {
|
||||
val delayMs = delay?.inWholeMilliseconds?.toInt() ?: 0
|
||||
return tween(
|
||||
durationMillis = ELEVATION_ANIMATION_DURATION_MS - delayMs,
|
||||
easing = CubicBezierEasing(0.6f, 0.1f, 0.3f, 0.9f),
|
||||
delayMillis = delayMs
|
||||
)
|
||||
}
|
||||
|
||||
private const val ANIMATION_DURATION_MS = 850
|
||||
private const val ELEVATION_ANIMATION_DURATION_MS = 650
|
||||
private const val ANIMATION_DURATION_BETWEEN_MESSAGES_MS = 1000
|
||||
private const val TOP_CUTOUT_HEIGHT_DP = 32
|
||||
private const val BOTTOM_CUTOUT_HEIGHT_DP = 24
|
||||
|
|
|
@ -15,29 +15,30 @@ import co.electriccoin.zcash.ui.common.usecase.GetSelectedWalletAccountUseCase
|
|||
import co.electriccoin.zcash.ui.common.usecase.HomeMessageData
|
||||
import co.electriccoin.zcash.ui.common.usecase.IsRestoreSuccessDialogVisibleUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.NavigateToCoinbaseUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ShieldFundsUseCase
|
||||
import co.electriccoin.zcash.ui.design.component.BigIconButtonState
|
||||
import co.electriccoin.zcash.ui.design.util.stringRes
|
||||
import co.electriccoin.zcash.ui.screen.exchangerate.optin.ExchangeRateOptIn
|
||||
import co.electriccoin.zcash.ui.screen.home.backup.SeedBackupInfo
|
||||
import co.electriccoin.zcash.ui.screen.home.currency.EnableCurrencyConversionMessageState
|
||||
import co.electriccoin.zcash.ui.screen.home.transparentbalance.TransparentBalanceMessageState
|
||||
import co.electriccoin.zcash.ui.screen.home.backup.WalletBackupDetail
|
||||
import co.electriccoin.zcash.ui.screen.home.backup.WalletBackupMessageState
|
||||
import co.electriccoin.zcash.ui.screen.home.currency.EnableCurrencyConversionMessageState
|
||||
import co.electriccoin.zcash.ui.screen.home.disconnected.WalletDisconnectedInfo
|
||||
import co.electriccoin.zcash.ui.screen.home.disconnected.WalletDisconnectedMessageState
|
||||
import co.electriccoin.zcash.ui.screen.home.error.WalletErrorMessageState
|
||||
import co.electriccoin.zcash.ui.screen.home.restoring.WalletRestoringMessageState
|
||||
import co.electriccoin.zcash.ui.screen.home.syncing.WalletSyncingMessageState
|
||||
import co.electriccoin.zcash.ui.screen.home.updating.WalletUpdatingMessageState
|
||||
import co.electriccoin.zcash.ui.screen.home.restoring.WalletRestoringInfo
|
||||
import co.electriccoin.zcash.ui.screen.home.restoring.WalletRestoringMessageState
|
||||
import co.electriccoin.zcash.ui.screen.home.shieldfunds.ShieldFundsInfo
|
||||
import co.electriccoin.zcash.ui.screen.home.shieldfunds.ShieldFundsMessageState
|
||||
import co.electriccoin.zcash.ui.screen.home.syncing.WalletSyncingInfo
|
||||
import co.electriccoin.zcash.ui.screen.home.transparentbalance.TransparentBalanceInfo
|
||||
import co.electriccoin.zcash.ui.screen.home.syncing.WalletSyncingMessageState
|
||||
import co.electriccoin.zcash.ui.screen.home.updating.WalletUpdatingInfo
|
||||
import co.electriccoin.zcash.ui.screen.home.updating.WalletUpdatingMessageState
|
||||
import co.electriccoin.zcash.ui.screen.integrations.DialogIntegrations
|
||||
import co.electriccoin.zcash.ui.screen.receive.Receive
|
||||
import co.electriccoin.zcash.ui.screen.receive.model.ReceiveAddressType
|
||||
import co.electriccoin.zcash.ui.screen.scan.Scan
|
||||
import co.electriccoin.zcash.ui.screen.scan.ScanFlow
|
||||
import co.electriccoin.zcash.ui.screen.home.backup.WalletBackupDetail
|
||||
import co.electriccoin.zcash.ui.screen.send.Send
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
|
@ -55,6 +56,7 @@ class HomeViewModel(
|
|||
private val navigationRouter: NavigationRouter,
|
||||
private val isRestoreSuccessDialogVisible: IsRestoreSuccessDialogVisibleUseCase,
|
||||
private val navigateToCoinbase: NavigateToCoinbaseUseCase,
|
||||
private val shieldFunds: ShieldFundsUseCase
|
||||
) : ViewModel() {
|
||||
|
||||
private val messageState = getHomeMessage
|
||||
|
@ -177,10 +179,13 @@ class HomeViewModel(
|
|||
onClick = ::onWalletSyncingMessageClick
|
||||
)
|
||||
|
||||
is HomeMessageData.TransparentBalance -> TransparentBalanceMessageState(
|
||||
subtitle = stringRes(zatoshi = it.zatoshi),
|
||||
onClick = ::onTransparentBalanceMessageClick,
|
||||
onButtonClick = ::onTransparentBalanceMessageButtonClick,
|
||||
is HomeMessageData.ShieldFunds -> ShieldFundsMessageState(
|
||||
subtitle = stringRes(
|
||||
R.string.home_message_transparent_balance_subtitle,
|
||||
stringRes(it.zatoshi)
|
||||
),
|
||||
onClick = ::onShieldFundsMessageClick,
|
||||
onButtonClick = ::onShieldFundsMessageButtonClick,
|
||||
)
|
||||
|
||||
HomeMessageData.Updating -> WalletUpdatingMessageState(
|
||||
|
@ -190,73 +195,40 @@ class HomeViewModel(
|
|||
null -> null
|
||||
}
|
||||
|
||||
private fun onRestoreDialogSeenClick() =
|
||||
viewModelScope.launch {
|
||||
isRestoreSuccessDialogVisible.setSeen()
|
||||
}
|
||||
private fun onRestoreDialogSeenClick() = viewModelScope.launch { isRestoreSuccessDialogVisible.setSeen() }
|
||||
|
||||
private fun onMoreButtonClick() {
|
||||
navigationRouter.forward(DialogIntegrations)
|
||||
}
|
||||
private fun onMoreButtonClick() = navigationRouter.forward(DialogIntegrations)
|
||||
|
||||
private fun onSendButtonClick() {
|
||||
navigationRouter.forward(Send())
|
||||
}
|
||||
private fun onSendButtonClick() = navigationRouter.forward(Send())
|
||||
|
||||
private fun onReceiveButtonClick() {
|
||||
navigationRouter.forward(Receive)
|
||||
}
|
||||
private fun onReceiveButtonClick() = navigationRouter.forward(Receive)
|
||||
|
||||
private fun onScanButtonClick() {
|
||||
navigationRouter.forward(Scan(ScanFlow.HOMEPAGE))
|
||||
}
|
||||
private fun onScanButtonClick() = navigationRouter.forward(Scan(ScanFlow.HOMEPAGE))
|
||||
|
||||
private fun onBuyClick() =
|
||||
viewModelScope.launch {
|
||||
navigateToCoinbase(replaceCurrentScreen = false)
|
||||
}
|
||||
private fun onBuyClick() = viewModelScope.launch { navigateToCoinbase(replaceCurrentScreen = false) }
|
||||
|
||||
private fun onRequestClick() {
|
||||
private fun onRequestClick() =
|
||||
navigationRouter.forward("${NavigationTargets.REQUEST}/${ReceiveAddressType.Unified.ordinal}")
|
||||
}
|
||||
|
||||
private fun onWalletUpdatingMessageClick() {
|
||||
navigationRouter.forward(WalletUpdatingInfo)
|
||||
}
|
||||
private fun onWalletUpdatingMessageClick() = navigationRouter.forward(WalletUpdatingInfo)
|
||||
|
||||
private fun onWalletSyncingMessageClick() {
|
||||
navigationRouter.forward(WalletSyncingInfo)
|
||||
}
|
||||
private fun onWalletSyncingMessageClick() = navigationRouter.forward(WalletSyncingInfo)
|
||||
|
||||
private fun onWalletRestoringMessageClick() {
|
||||
navigationRouter.forward(WalletRestoringInfo)
|
||||
}
|
||||
private fun onWalletRestoringMessageClick() = navigationRouter.forward(WalletRestoringInfo)
|
||||
|
||||
private fun onEnableCurrencyConversionClick() {
|
||||
navigationRouter.forward(ExchangeRateOptIn)
|
||||
}
|
||||
private fun onEnableCurrencyConversionClick() = navigationRouter.forward(ExchangeRateOptIn)
|
||||
|
||||
private fun onWalletDisconnectedMessageClick() {
|
||||
navigationRouter.forward(WalletDisconnectedInfo)
|
||||
}
|
||||
private fun onWalletDisconnectedMessageClick() = navigationRouter.forward(WalletDisconnectedInfo)
|
||||
|
||||
private fun onWalletBackupMessageClick() {
|
||||
navigationRouter.forward(SeedBackupInfo)
|
||||
}
|
||||
private fun onWalletBackupMessageClick() = navigationRouter.forward(SeedBackupInfo)
|
||||
|
||||
private fun onWalletBackupMessageButtonClick() {
|
||||
navigationRouter.forward(WalletBackupDetail(false))
|
||||
}
|
||||
private fun onWalletBackupMessageButtonClick() = navigationRouter.forward(WalletBackupDetail(false))
|
||||
|
||||
private fun onTransparentBalanceMessageClick() {
|
||||
navigationRouter.forward(TransparentBalanceInfo)
|
||||
}
|
||||
private fun onShieldFundsMessageClick() = navigationRouter.forward(ShieldFundsInfo)
|
||||
|
||||
private fun onTransparentBalanceMessageButtonClick(): Nothing {
|
||||
TODO()
|
||||
}
|
||||
private fun onShieldFundsMessageButtonClick() = shieldFunds(navigateBackAfterSuccess = false)
|
||||
|
||||
private fun onWalletErrorMessageClick(homeMessageData: HomeMessageData.Error): Nothing {
|
||||
private fun onWalletErrorMessageClick(homeMessageData: HomeMessageData.Error) {
|
||||
// statusText =
|
||||
// context.getString(
|
||||
// R.string.balances_status_error_simple,
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package co.electriccoin.zcash.ui.screen.home.backup
|
||||
|
||||
import co.electriccoin.zcash.ui.design.component.ButtonState
|
||||
import co.electriccoin.zcash.ui.design.component.CheckboxState
|
||||
import co.electriccoin.zcash.ui.design.component.ModalBottomSheetState
|
||||
|
||||
data class WalletBackupInfoState(
|
||||
override val onBack: () -> Unit,
|
||||
val checkboxState: CheckboxState?,
|
||||
val primaryButton: ButtonState,
|
||||
val secondaryButton: ButtonState
|
||||
) : ModalBottomSheetState
|
||||
|
|
|
@ -4,6 +4,8 @@ import androidx.compose.foundation.Image
|
|||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.SheetState
|
||||
import androidx.compose.material3.Text
|
||||
|
@ -15,10 +17,12 @@ import androidx.compose.ui.text.font.FontWeight
|
|||
import androidx.compose.ui.unit.dp
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.design.component.ButtonState
|
||||
import co.electriccoin.zcash.ui.design.component.CheckboxState
|
||||
import co.electriccoin.zcash.ui.design.component.Spacer
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiBulletText
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiButton
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiButtonDefaults
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiCheckbox
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiScreenModalBottomSheet
|
||||
import co.electriccoin.zcash.ui.design.component.rememberScreenModalBottomSheetState
|
||||
import co.electriccoin.zcash.ui.design.newcomponent.PreviewScreens
|
||||
|
@ -38,9 +42,11 @@ fun WalletBackupInfoView(
|
|||
state = state
|
||||
) {
|
||||
Column(
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(horizontal = 24.dp)
|
||||
modifier = Modifier
|
||||
.weight(1f, false)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(horizontal = 24.dp)
|
||||
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.ic_info_backup),
|
||||
|
@ -84,6 +90,12 @@ fun WalletBackupInfoView(
|
|||
style = ZashiTypography.textMd
|
||||
)
|
||||
Spacer(32.dp)
|
||||
it.checkboxState?.let { checkbox ->
|
||||
ZashiCheckbox(
|
||||
state = checkbox
|
||||
)
|
||||
Spacer(12.dp)
|
||||
}
|
||||
ZashiButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
state = it.secondaryButton,
|
||||
|
@ -108,11 +120,17 @@ private fun Preview() =
|
|||
onBack = {},
|
||||
secondaryButton = ButtonState(
|
||||
text = stringRes(R.string.general_remind_me_later),
|
||||
onClick = {}
|
||||
onClick = {},
|
||||
isEnabled = false
|
||||
),
|
||||
primaryButton = ButtonState(
|
||||
text = stringRes(R.string.general_ok),
|
||||
onClick = {}
|
||||
),
|
||||
checkboxState = CheckboxState(
|
||||
isChecked = false,
|
||||
onClick = {},
|
||||
text = stringRes(R.string.home_info_backup_checkbox)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
@ -2,38 +2,83 @@ package co.electriccoin.zcash.ui.screen.home.backup
|
|||
|
||||
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.WalletBackupAvailability
|
||||
import co.electriccoin.zcash.ui.common.datasource.WalletBackupDataSource
|
||||
import co.electriccoin.zcash.ui.common.provider.WalletBackupConsentStorageProvider
|
||||
import co.electriccoin.zcash.ui.common.usecase.RemindWalletBackupLaterUseCase
|
||||
import co.electriccoin.zcash.ui.design.component.ButtonState
|
||||
import co.electriccoin.zcash.ui.design.component.CheckboxState
|
||||
import co.electriccoin.zcash.ui.design.util.stringRes
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.WhileSubscribed
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.take
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class WalletBackupInfoViewModel(
|
||||
walletBackupDataSource: WalletBackupDataSource,
|
||||
walletBackupConsentStorageProvider: WalletBackupConsentStorageProvider,
|
||||
private val navigationRouter: NavigationRouter,
|
||||
private val remindWalletBackupLater: RemindWalletBackupLaterUseCase
|
||||
private val remindWalletBackupLater: RemindWalletBackupLaterUseCase,
|
||||
) : ViewModel() {
|
||||
val state: StateFlow<WalletBackupInfoState?> = MutableStateFlow(
|
||||
WalletBackupInfoState(
|
||||
onBack = ::onBack,
|
||||
secondaryButton = ButtonState(
|
||||
text = stringRes(R.string.general_remind_me_later),
|
||||
onClick = ::onRemindMeLaterClick
|
||||
),
|
||||
primaryButton = ButtonState(
|
||||
text = stringRes(R.string.general_ok),
|
||||
onClick = ::onPrimaryClick
|
||||
)
|
||||
|
||||
private val isConsentChecked = MutableStateFlow(false)
|
||||
|
||||
private val lockoutDuration = walletBackupDataSource
|
||||
.observe()
|
||||
.filterIsInstance<WalletBackupAvailability.Available>()
|
||||
.take(1)
|
||||
.map { it.lockoutDuration }
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(),
|
||||
initialValue = null
|
||||
)
|
||||
)
|
||||
|
||||
private fun onPrimaryClick() {
|
||||
navigationRouter.replace(WalletBackupDetail(true))
|
||||
}
|
||||
val state: StateFlow<WalletBackupInfoState?> = combine(
|
||||
lockoutDuration.filterNotNull(),
|
||||
isConsentChecked,
|
||||
walletBackupConsentStorageProvider.observe().take(1)
|
||||
) { lockout, isConsentChecked, isConsentSaved ->
|
||||
WalletBackupInfoState(
|
||||
onBack = ::onBack,
|
||||
secondaryButton = ButtonState(
|
||||
text = stringRes(R.string.general_remind_me_in, stringRes(lockout.res)),
|
||||
onClick = ::onRemindMeLaterClick,
|
||||
isEnabled = isConsentChecked || isConsentSaved
|
||||
),
|
||||
checkboxState = CheckboxState(
|
||||
isChecked = isConsentChecked,
|
||||
onClick = ::onConsentClick,
|
||||
text = stringRes(R.string.home_info_backup_checkbox)
|
||||
).takeIf { !isConsentSaved },
|
||||
primaryButton = ButtonState(
|
||||
text = stringRes(R.string.general_ok),
|
||||
onClick = ::onPrimaryClick
|
||||
)
|
||||
)
|
||||
}
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
|
||||
initialValue = null
|
||||
)
|
||||
|
||||
private fun onRemindMeLaterClick() = viewModelScope.launch { remindWalletBackupLater() }
|
||||
private fun onConsentClick() = isConsentChecked.update { !it }
|
||||
|
||||
private fun onPrimaryClick() = navigationRouter.replace(WalletBackupDetail(true))
|
||||
|
||||
private fun onRemindMeLaterClick() = viewModelScope.launch { remindWalletBackupLater(persistConsent = true) }
|
||||
|
||||
private fun onBack() = navigationRouter.back()
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package co.electriccoin.zcash.ui.screen.home.transparentbalance
|
||||
package co.electriccoin.zcash.ui.screen.home.shieldfunds
|
||||
|
||||
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 AndroidTransparentBalanceInfo() {
|
||||
val vm = koinViewModel<TransparentBalanceInfoViewModel>()
|
||||
fun AndroidShieldFundsInfo() {
|
||||
val vm = koinViewModel<ShieldFundsInfoViewModel>()
|
||||
val state by vm.state.collectAsStateWithLifecycle()
|
||||
state?.let { TransparentBalanceInfoView(it) }
|
||||
ShieldFundsInfoView(state)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
object TransparentBalanceInfo
|
||||
object ShieldFundsInfo
|
|
@ -1,10 +1,10 @@
|
|||
package co.electriccoin.zcash.ui.screen.home.transparentbalance
|
||||
package co.electriccoin.zcash.ui.screen.home.shieldfunds
|
||||
|
||||
import cash.z.ecc.android.sdk.model.Zatoshi
|
||||
import co.electriccoin.zcash.ui.design.component.ButtonState
|
||||
import co.electriccoin.zcash.ui.design.component.ModalBottomSheetState
|
||||
|
||||
data class TransparentBalanceInfoState(
|
||||
data class ShieldFundsInfoState(
|
||||
val transparentAmount: Zatoshi,
|
||||
override val onBack: () -> Unit,
|
||||
val primaryButton: ButtonState,
|
|
@ -1,4 +1,4 @@
|
|||
package co.electriccoin.zcash.ui.screen.home.transparentbalance
|
||||
package co.electriccoin.zcash.ui.screen.home.shieldfunds
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
@ -34,8 +34,8 @@ import co.electriccoin.zcash.ui.design.util.stringRes
|
|||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun TransparentBalanceInfoView(
|
||||
state: TransparentBalanceInfoState?,
|
||||
fun ShieldFundsInfoView(
|
||||
state: ShieldFundsInfoState?,
|
||||
sheetState: SheetState = rememberScreenModalBottomSheetState(),
|
||||
) {
|
||||
ZashiScreenModalBottomSheet(
|
||||
|
@ -47,7 +47,7 @@ fun TransparentBalanceInfoView(
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun Content(state: TransparentBalanceInfoState) {
|
||||
private fun Content(state: ShieldFundsInfoState) {
|
||||
Column(
|
||||
modifier =
|
||||
Modifier
|
||||
|
@ -132,9 +132,9 @@ private fun Content(state: TransparentBalanceInfoState) {
|
|||
@Composable
|
||||
private fun Preview() =
|
||||
ZcashTheme {
|
||||
TransparentBalanceInfoView(
|
||||
ShieldFundsInfoView(
|
||||
state =
|
||||
TransparentBalanceInfoState(
|
||||
ShieldFundsInfoState(
|
||||
onBack = {},
|
||||
primaryButton =
|
||||
ButtonState(
|
|
@ -0,0 +1,79 @@
|
|||
package co.electriccoin.zcash.ui.screen.home.shieldfunds
|
||||
|
||||
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.WalletBackupAvailability
|
||||
import co.electriccoin.zcash.ui.common.repository.ShieldFundsData
|
||||
import co.electriccoin.zcash.ui.common.repository.ShieldFundsRepository
|
||||
import co.electriccoin.zcash.ui.common.usecase.GetSelectedWalletAccountUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.RemindShieldFundsLaterUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ShieldFundsUseCase
|
||||
import co.electriccoin.zcash.ui.design.component.ButtonState
|
||||
import co.electriccoin.zcash.ui.design.util.stringRes
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.WhileSubscribed
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.take
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ShieldFundsInfoViewModel(
|
||||
getSelectedWalletAccount: GetSelectedWalletAccountUseCase,
|
||||
shieldFundsRepository: ShieldFundsRepository,
|
||||
private val navigationRouter: NavigationRouter,
|
||||
private val remindShieldFundsLater: RemindShieldFundsLaterUseCase,
|
||||
private val shieldFunds: ShieldFundsUseCase,
|
||||
) : ViewModel() {
|
||||
|
||||
private val lockoutDuration = shieldFundsRepository
|
||||
.availability
|
||||
.filterIsInstance<ShieldFundsData.Available>()
|
||||
.take(1)
|
||||
.map { it.lockoutDuration }
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(),
|
||||
initialValue = null
|
||||
)
|
||||
|
||||
val state: StateFlow<ShieldFundsInfoState?> =
|
||||
combine(
|
||||
getSelectedWalletAccount.observe(),
|
||||
lockoutDuration.filterNotNull(),
|
||||
) { account, lockoutDuration ->
|
||||
ShieldFundsInfoState(
|
||||
onBack = ::onBack,
|
||||
primaryButton =
|
||||
ButtonState(
|
||||
onClick = ::onShieldClick,
|
||||
text = stringRes(R.string.home_info_transparent_balance_shield)
|
||||
),
|
||||
secondaryButton =
|
||||
ButtonState(
|
||||
onClick = ::onRemindMeClick,
|
||||
text = stringRes(R.string.general_remind_me_in, stringRes(lockoutDuration.res))
|
||||
),
|
||||
transparentAmount = account?.transparent?.balance ?: Zatoshi(0)
|
||||
)
|
||||
}
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
|
||||
initialValue = null
|
||||
)
|
||||
|
||||
private fun onRemindMeClick() = viewModelScope.launch { remindShieldFundsLater() }
|
||||
|
||||
private fun onBack() = navigationRouter.back()
|
||||
|
||||
private fun onShieldClick() = shieldFunds(navigateBackAfterSuccess = true)
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package co.electriccoin.zcash.ui.screen.home.transparentbalance
|
||||
package co.electriccoin.zcash.ui.screen.home.shieldfunds
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
|
@ -26,9 +26,9 @@ import co.electriccoin.zcash.ui.screen.home.HomeMessageWrapper
|
|||
|
||||
@Suppress("ModifierNaming")
|
||||
@Composable
|
||||
fun TransparentBalanceMessage(
|
||||
fun ShieldFundsMessage(
|
||||
contentPadding: PaddingValues,
|
||||
state: TransparentBalanceMessageState,
|
||||
state: ShieldFundsMessageState,
|
||||
innerModifier: Modifier = Modifier,
|
||||
) {
|
||||
HomeMessageWrapper(
|
||||
|
@ -65,7 +65,7 @@ fun TransparentBalanceMessage(
|
|||
)
|
||||
}
|
||||
|
||||
class TransparentBalanceMessageState(
|
||||
class ShieldFundsMessageState(
|
||||
val subtitle: StringResource,
|
||||
val onClick: () -> Unit,
|
||||
val onButtonClick: () -> Unit,
|
||||
|
@ -76,9 +76,9 @@ class TransparentBalanceMessageState(
|
|||
private fun Preview() =
|
||||
ZcashTheme {
|
||||
BlankSurface {
|
||||
TransparentBalanceMessage(
|
||||
ShieldFundsMessage(
|
||||
state =
|
||||
TransparentBalanceMessageState(
|
||||
ShieldFundsMessageState(
|
||||
subtitle =
|
||||
stringRes(
|
||||
R.string.home_message_transparent_balance_subtitle,
|
|
@ -1,45 +0,0 @@
|
|||
package co.electriccoin.zcash.ui.screen.home.transparentbalance
|
||||
|
||||
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.usecase.GetSelectedWalletAccountUseCase
|
||||
import co.electriccoin.zcash.ui.design.component.ButtonState
|
||||
import co.electriccoin.zcash.ui.design.util.stringRes
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.WhileSubscribed
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
class TransparentBalanceInfoViewModel(
|
||||
getSelectedWalletAccount: GetSelectedWalletAccountUseCase,
|
||||
private val navigationRouter: NavigationRouter
|
||||
) : ViewModel() {
|
||||
val state: StateFlow<TransparentBalanceInfoState?> =
|
||||
getSelectedWalletAccount
|
||||
.observe()
|
||||
.map {
|
||||
TransparentBalanceInfoState(
|
||||
onBack = { navigationRouter.back() },
|
||||
primaryButton =
|
||||
ButtonState(
|
||||
onClick = { navigationRouter.back() },
|
||||
text = stringRes(R.string.home_info_transparent_balance_shield)
|
||||
),
|
||||
secondaryButton =
|
||||
ButtonState(
|
||||
onClick = { navigationRouter.back() },
|
||||
text = stringRes(R.string.general_remind_me_later)
|
||||
),
|
||||
transparentAmount = it?.transparent?.balance ?: Zatoshi(0)
|
||||
)
|
||||
}.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
|
||||
initialValue = null
|
||||
)
|
||||
}
|
|
@ -11,7 +11,6 @@ import co.electriccoin.zcash.ui.common.model.KeystoneAccount
|
|||
import co.electriccoin.zcash.ui.common.model.WalletAccount
|
||||
import co.electriccoin.zcash.ui.common.model.ZashiAccount
|
||||
import co.electriccoin.zcash.ui.common.usecase.CopyToClipboardUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ObserveOnAccountChangedUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ObserveSelectedWalletAccountUseCase
|
||||
import co.electriccoin.zcash.ui.design.util.stringRes
|
||||
import co.electriccoin.zcash.ui.screen.addressbook.viewmodel.ADDRESS_MAX_LENGTH
|
||||
|
@ -24,11 +23,9 @@ import kotlinx.coroutines.flow.WhileSubscribed
|
|||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ReceiveViewModel(
|
||||
observeSelectedWalletAccount: ObserveSelectedWalletAccountUseCase,
|
||||
observeOnAccountChanged: ObserveOnAccountChangedUseCase,
|
||||
private val application: Application,
|
||||
private val copyToClipboard: CopyToClipboardUseCase,
|
||||
private val navigationRouter: NavigationRouter,
|
||||
|
@ -69,14 +66,6 @@ class ReceiveViewModel(
|
|||
)
|
||||
)
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
observeOnAccountChanged().collect {
|
||||
expandedIndex.update { 0 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onBack() = navigationRouter.back()
|
||||
|
||||
private fun createAddressState(
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
package co.electriccoin.zcash.ui.screen.sendconfirmation.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.model.Proposal
|
||||
import cash.z.ecc.android.sdk.model.TransactionSubmitResult
|
||||
import cash.z.ecc.android.sdk.model.UnifiedSpendingKey
|
||||
import co.electriccoin.zcash.spackle.Twig
|
||||
import co.electriccoin.zcash.ui.common.model.SubmitResult
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
class CreateTransactionsViewModel : ViewModel() {
|
||||
// Technically this value will not survive process dead, but will survive all possible configuration changes
|
||||
// Possible solution would be storing the value within [SavedStateHandle]
|
||||
val submissions: MutableStateFlow<List<TransactionSubmitResult>> = MutableStateFlow(emptyList())
|
||||
|
||||
suspend fun runCreateTransactions(
|
||||
synchronizer: Synchronizer,
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
proposal: Proposal
|
||||
): SubmitResult {
|
||||
val submitResults = mutableListOf<TransactionSubmitResult>()
|
||||
|
||||
return runCatching {
|
||||
synchronizer
|
||||
.createProposedTransactions(
|
||||
proposal = proposal,
|
||||
usk = spendingKey
|
||||
).collect { submitResult ->
|
||||
Twig.info { "Transaction submit result: $submitResult" }
|
||||
submitResults.add(submitResult)
|
||||
}
|
||||
if (submitResults.find { it is TransactionSubmitResult.Failure } != null) {
|
||||
if (submitResults.size == 1) {
|
||||
// The first transaction submission failed - user might just be able to re-submit the transaction
|
||||
// proposal. Simple error pop up is fine then
|
||||
val result = (submitResults[0] as TransactionSubmitResult.Failure)
|
||||
if (result.grpcError) {
|
||||
SubmitResult.SimpleTrxFailure.SimpleTrxFailureGrpc(result)
|
||||
} else {
|
||||
SubmitResult.SimpleTrxFailure.SimpleTrxFailureSubmit(result)
|
||||
}
|
||||
} else {
|
||||
// Any subsequent transaction submission failed - user needs to resolve this manually. Multiple
|
||||
// transaction failure screen presented
|
||||
SubmitResult.MultipleTrxFailure(submitResults)
|
||||
}
|
||||
} else {
|
||||
// All transaction submissions were successful
|
||||
SubmitResult.Success(emptyList())
|
||||
}
|
||||
}.onSuccess {
|
||||
Twig.debug { "Transactions submitted successfully" }
|
||||
}.onFailure {
|
||||
Twig.error(it) { "Transactions submission failed" }
|
||||
}.getOrElse {
|
||||
SubmitResult.SimpleTrxFailure.SimpleTrxFailureOther(it)
|
||||
}.also {
|
||||
// Save the submission results for the later MultipleSubmissionError screen
|
||||
if (it is SubmitResult.MultipleTrxFailure) {
|
||||
submissions.value = submitResults
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,7 +13,5 @@ import org.koin.androidx.compose.koinViewModel
|
|||
fun AndroidTransactionFiltersList() {
|
||||
val viewModel = koinViewModel<TransactionFiltersViewModel>()
|
||||
val state by viewModel.state.collectAsStateWithLifecycle()
|
||||
TransactionFiltersView(
|
||||
state = state,
|
||||
)
|
||||
TransactionFiltersView(state = state)
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ 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.WalletBackupAvailability
|
||||
import co.electriccoin.zcash.ui.common.datasource.WalletBackupDataSource
|
||||
import co.electriccoin.zcash.ui.common.usecase.ObservePersistableWalletUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.OnUserSavedWalletBackupUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.RemindWalletBackupLaterUseCase
|
||||
|
@ -17,18 +19,33 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.WhileSubscribed
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.take
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class WalletBackupViewModel(
|
||||
private val args: WalletBackup,
|
||||
walletBackupDataSource: WalletBackupDataSource,
|
||||
observePersistableWallet: ObservePersistableWalletUseCase,
|
||||
private val args: WalletBackup,
|
||||
private val navigationRouter: NavigationRouter,
|
||||
private val onUserSavedWalletBackup: OnUserSavedWalletBackupUseCase,
|
||||
private val remindWalletBackupLater: RemindWalletBackupLaterUseCase
|
||||
private val remindWalletBackupLater: RemindWalletBackupLaterUseCase,
|
||||
) : ViewModel() {
|
||||
|
||||
private val lockoutDuration = walletBackupDataSource
|
||||
.observe()
|
||||
.filterIsInstance<WalletBackupAvailability.Available>()
|
||||
.take(1)
|
||||
.map { it.lockoutDuration }
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(),
|
||||
initialValue = null
|
||||
)
|
||||
|
||||
private val isRevealed = MutableStateFlow(false)
|
||||
|
||||
private val isRemindMeLaterButtonVisible = isRevealed
|
||||
|
@ -42,11 +59,16 @@ class WalletBackupViewModel(
|
|||
combine(
|
||||
isRevealed,
|
||||
isRemindMeLaterButtonVisible,
|
||||
observableWallet
|
||||
) { isRevealed, isRemindMeLaterButtonVisible, wallet ->
|
||||
observableWallet,
|
||||
lockoutDuration
|
||||
) { isRevealed, isRemindMeLaterButtonVisible, wallet, lockoutDuration ->
|
||||
WalletBackupState(
|
||||
secondaryButton = ButtonState(
|
||||
text = stringRes(R.string.general_remind_me_later),
|
||||
text = if (lockoutDuration != null) {
|
||||
stringRes(R.string.general_remind_me_in, stringRes(lockoutDuration.res))
|
||||
} else {
|
||||
stringRes(R.string.general_remind_me_later)
|
||||
},
|
||||
onClick = ::onRemindMeLaterClick
|
||||
).takeIf { isRemindMeLaterButtonVisible },
|
||||
primaryButton =
|
||||
|
@ -105,10 +127,7 @@ class WalletBackupViewModel(
|
|||
)
|
||||
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), null)
|
||||
|
||||
private fun onRemindMeLaterClick() =
|
||||
viewModelScope.launch {
|
||||
remindWalletBackupLater()
|
||||
}
|
||||
private fun onRemindMeLaterClick() = viewModelScope.launch { remindWalletBackupLater(persistConsent = false) }
|
||||
|
||||
private fun onWalletBackupSavedClick() =
|
||||
viewModelScope.launch {
|
||||
|
|
|
@ -30,4 +30,8 @@
|
|||
|
||||
<string name="general_ok">OK</string>
|
||||
<string name="general_remind_me_later">Remind me later</string>
|
||||
<string name="general_remind_me_in">Remind me in %s</string>
|
||||
<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>
|
||||
</resources>
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
Recovery Phrase is the only way to recover your funds. We cannot see it and cannot help you recover it.</string>
|
||||
<string name="home_info_backup_message_2">Anyone with access to your Secret Recovery Phrase will have full control
|
||||
of your wallet, so keep it secure and never show it to anyone.</string>
|
||||
<string name="home_info_backup_checkbox">I read and understand the risks of not backing up my wallet.</string>
|
||||
<string name="home_info_transparent_balance_shield">Shield</string>
|
||||
<string name="home_info_transparent_title">Always Shield Transparent Funds</string>
|
||||
<string name="home_info_transparent_subtitle">To protect user privacy, Zashi doesn\'t support spending transparent
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
Recovery Phrase is the only way to recover your funds. We cannot see it and cannot help you recover it.</string>
|
||||
<string name="home_info_backup_message_2">Anyone with access to your Secret Recovery Phrase will have full control
|
||||
of your wallet, so keep it secure and never show it to anyone.</string>
|
||||
<string name="home_info_backup_checkbox">I read and understand the risks of not backing up my wallet.</string>
|
||||
<string name="home_info_transparent_balance_shield">Shield</string>
|
||||
<string name="home_info_transparent_title">Always Shield Transparent Funds</string>
|
||||
<string name="home_info_transparent_subtitle">To protect user privacy, Zashi doesn\'t support spending transparent
|
||||
|
|
Loading…
Reference in New Issue